Contents | Prev | Next | Index Java Language Specification
Second Edition


CHAPTER 16

Definite Assignment


Each local variable (§14.4) and every blank final (§4.5.4) field (§8.3.1.2) must have a definitely assigned value when any access of its value occurs. A Java compiler must carry out a specific conservative flow analysis to make sure that, for every access of a local variable or blank final field f, f is definitely assigned before the access; otherwise a compile-time error must occur.

Similarly, every blank final variable must be assigned at most once; it must be definitely unassigned when an assignment to it occurs. A Java compiler must carry out a specific conservative flow analysis to make sure that, for every assignment to a blank final variable, the variable is definitely unassigned before the assignment; otherwise a compile-time error must occur.

The remainder of this chapter is devoted to a precise explanation of the words "definitely assigned before" and "definitely unassigned before".

The idea behind definite assignment is that an assignment to the local variable or blank final field must occur on every possible execution path to the access. Similarly, the idea behind definite unassignment is that no other assignment to the blank final variable is permitted to occur on any possible execution path to an assignment. The analysis takes into account the structure of statements and expressions; it also provides a special treatment of the expression operators !, &&, ||, and ? :, and of boolean-valued constant expressions.

For example, a Java compiler recognizes that k is definitely assigned before its access (as an argument of a method invocation) in the code:

{
	int k;
	if (v > 0 && (k = System.in.read()) >= 0)
		System.out.println(k);
}
because the access occurs only if the value of the expression:

v > 0 && (k = System.in.read()) >= 0
is true, and the value can be true only if the assignment to k is executed (more properly, evaluated).

Similarly, a Java compiler will recognize that in the code:

{
	int k;
	while (true) {
		k = n;
		if (k >= 5) break;
		n = 6;
	}
	System.out.println(k);
}
the variable k is definitely assigned by the while statement because the condition expression true never has the value false, so only the break statement can cause the while statement to complete normally, and k is definitely assigned before the break statement.

On the other hand, the code

{
	int k;
	while (n < 4) {
		k = n;
		if (k >= 5) break;
		n = 6;
	}
	System.out.println(k);	// k is not "definitely assigned" before this
}
must be rejected by a Java compiler, because in this case the while statement is not guaranteed to execute its body as far as the rules of definite assignment are concerned.

Except for the special treatment of the conditional boolean operators &&, ||, and ? : and of boolean-valued constant expressions, the values of expressions are not taken into account in the flow analysis.

For example, a Java compiler must produce a compile-time error for the code:

{
	int k;
	int n = 5;
	if (n > 2)
		k = 3;
	System.out.println(k);	// k is not "definitely assigned" before this
}
even though the value of n is known at compile time, and in principle it can be known at compile time that the assignment to k will always be executed (more properly, evaluated). A Java compiler must operate according to the rules laid out in this section. The rules recognize only constant expressions; in this example, the expression n > 2 is not a constant expression as defined in §15.28.

As another example, a Java compiler will accept the code:

void flow(boolean flag) {
	int k;
	if (flag)
		k = 3;
	else
		k = 4;
	System.out.println(k);
}
as far as definite assignment of k is concerned, because the rules outlined in this section allow it to tell that k is assigned no matter whether the flag is true or false. But the rules do not accept the variation:

void flow(boolean flag) {
	int k;
	if (flag)
		k = 3;
	if (!flag)
		k = 4;
	System.out.println(k);	// k is not "definitely assigned" before here
}
and so compiling this program must cause a compile-time error to occur.

A related example illustrates rules of definite unassignment. A Java compiler will accept the code:

void unflow(boolean flag) {
	final int k;
	if (flag) {
		k = 3;
		System.out.println(k);
	}
	else {
		k = 4;
		System.out.println(k);
	}
}
as far as definite unassignment of k is concerned, because the rules outlined in this section allow it to tell that k is assigned at most once (indeed, exactly once) no matter whether the flag is true or false. But the rules do not accept the variation:

void unflow(boolean flag) {
	final int k;
	if (flag) {
		k = 3;
		System.out.println(k);
	}
	if (!flag) {
		k = 4; 		// k is not "definitely unassigned" before here
		System.out.println(k);
	}
}
and so compiling this program must cause a compile-time error to occur.

In order to precisely specify all the cases of definite assignment, the rules in this section define several technical terms:

For boolean-valued expressions, the last two are refined into four cases:

Here when true and when false refer to the value of the expression.

For example, the local variable k is definitely assigned a value after evaluation of the expression

a && ((k=m) > 5)
when the expression is true but not when the expression is false (because if a is false, then the assignment to k is not necessarily executed (more properly, evaluated)).

The phrase "V is definitely assigned after X" (where V is a local variable and X is a statement or expression) means "V is definitely assigned after X if X completes normally". If X completes abruptly, the assignment need not have occurred, and the rules stated here take this into account. A peculiar consequence of this definition is that "V is definitely assigned after break;" is always true! Because a break statement never completes normally, it is vacuously true that V has been assigned a value if the break statement completes normally.

Similarly, the statement "V is definitely unassigned after X" (where V is a variable and X is a statement or expression) means "V is definitely unassigned after X if X completes normally". An even more peculiar consequence of this definition is that "V is definitely unassigned after break;" is always true! Because a break statement never completes normally, it is vacuously true that V has not been assigned a value if the break statement completes normally. (For that matter, it is also vacuously true that the moon is made of green cheese if the break statement completes normally.)

In all, there are four possibilities for a variable V after a statement or expression has been executed:

To shorten the rules, the customary abbreviation "iff" is used to mean "if and only if". We also use an abbreviation convention: if a rule contains one or more occurrences of "[un]assigned" then it stands for two rules, one with every occurrence of "[un]assigned" replaced by "definitely assigned" and one with every occurrence of "[un]assigned" replaced by "definitely unassigned".

For example:

V is [un]assigned after an empty statement iff it is [un]assigned before the empty statement.

should be understood to stand for two rules:

The definite unassignment analysis of loop statements raises a special problem. Consider the statement while (e) S. In order to determine whether V is definitely unassigned within some subexpression of e, we need to determine whether V is definitely unassigned before e. One might argue, by analogy with the rule for definite assignment (§16.2.9), that V is definitely unassigned before e iff it is definitely unassigned before the while statement. However, such a rule is inadequate for our purposes. If e evaluates to true, the statement S will be executed. Later, if V is assigned by S, then in the following iteration(s) V will have already been assigned when e is evaluated. Under the rule suggested above, it would be possible to assign V multiple times, which is exactly what we have sought to avoid by introducing these rules.

A revised rule would be: "V is definitely unassigned before e iff it is definitely unassigned before the while statement and definitely unassigned after S". However, when we formulate the rule for S, we find: "V is definitely unassigned before S iff it is definitely unassigned after e when true". This leads to a circularity. In effect, V is definitely unassigned before the loop condition e only if it is unassigned after the loop as a whole!

We break this vicious circle using a hypothetical analysis of the loop condition and body. For example, if we assume that V is definitely unassigned before e (regardless of whether V really is definitely unassigned before e), and can then prove that V was definitely unassigned after e then we know that e does not assign V. This is stated more formally as:

Assuming V is definitely unassigned before e, V is definitely unassigned after e.

Variations on the above analysis are used to define well founded definite unassignment rules for all loop statements in the language.

Throughout the rest of this chapter, we will, unless explicitly stated otherwise, write V to represent a local variable or a blank final field (for rules of definite assignment) or a blank final variable (for rules of definite unassignment). Likewise, we will use a, b, c, and e to represent expressions, and S and T to represent statements.

16.1 Definite Assignment and Expressions

16.1.1 Boolean Constant Expressions

Because a constant expression whose value is true never has the value false, and a constant expression whose value is false never has the value true, the two preceding rules are vacuously satisfied. They are helpful in analyzing expressions involving the operators && (§16.1.2), || (§16.1.3), ! (§16.1.4), and ? : (§16.1.5).

16.1.2 The Boolean Operator &&

16.1.3 The Boolean Operator ||

16.1.4 The Boolean Operator !

16.1.5 The Boolean Operator ? :

Suppose that b and c are boolean-valued expressions.

16.1.6 The Conditional Operator ? :

Suppose that b and c are expressions that are not boolean-valued.

16.1.7 Assignment Expressions

Consider an assignment expression a = b, a += b, a -= b, a *= b, a /= b, a %= b, a <<= b, a >>= b, a >>>= b, a &= b, a |= b, or a ^= b.

If the assignment expression is boolean-valued, then these rules also apply:

16.1.8 Operators ++ and --

16.1.9 Other Expressions

If an expression is not a boolean constant expression, and is not a preincrement expression ++a, predecrement expression --a, postincrement expression a++, postdecrement expression a--, logical complement expression !a, conditional-and expression a && b, conditional-or expression a || b, conditional expression a ? b : c, or assignment expression, then the following rules apply:

There is a piece of subtle reasoning behind the assertion that a variable V can be known to be definitely unassigned after a method invocation. Taken by itself, at face value and without qualification, such an assertion is not always true, because an invoked method can perform assignments. But it must be remembered that, for the purposes of the Java programming language, the concept of definite unassignment is applied only to blank final variables. If V is a blank final local variable, then only the method to which its declaration belongs can perform assignments to V. If V is a blank final field, then only a constructor or an initializer for the class containing the declaration for V can perform assignments to V; no method can perform assignments to V. Finally, explicit constructor invocations (§8.8.5) are handled specially (§16.8); although they are syntactically similar to expression statements containing method invocations, they are not expression statements and therefore the rules of this section do not apply to explicit constructor invocations.

For any immediate subexpression y of an expression x, V is [un]assigned before y iff one of the following situations is true:

16.2 Definite Assignment and Statements

16.2.1 Empty Statements

16.2.2 Blocks

16.2.3 Local Class Declaration Statements

16.2.4 Local Variable Declaration Statements

16.2.5 Labeled Statements

16.2.6 Expression Statements

16.2.7 if Statements

The following rules apply to a statement if (e) S:

The following rules apply to a statement if (e) S else T:

16.2.8 switch Statements

If a switch block contains at least one block-statement-group, then the following rules also apply:

16.2.9 while Statements

16.2.10 do Statements

16.2.11 for Statements

16.2.11.1 Initialization Part

16.2.11.2 Incrementation Part

16.2.12 break, continue, return, and throw Statements

16.2.13 synchronized Statements

16.2.14 try Statements

These rules apply to every try statement, whether or not it has a finally block:

If a try statement does not have a finally block, then this rule also applies:

If a try statement does have a finally block, then these rules also apply:

16.3 Definite Assignment and Parameters

16.4 Definite Assignment and Array Initializers

16.5 Definite Assignment and Anonymous Classes

16.6 Definite Assignment and Member Types

Let C be a class declared within the scope of V. Then:

16.7 Definite Assignment and Static Initializers

Let C be a class declared within the scope of V. Then:

Let C be a class, and let V be a blank final static member field of C, declared in C. Then:

Let C be a class, and let V be a blank final static member field of C, declared in a superclass of C. Then:

16.8 Definite Assignment, Constructors, and Instance Initializers

Let C be a class declared within the scope of V. Then:

Let C be a class, and let V be a blank final non-static member field of C, declared in C. Then:

The following rules hold within the constructors of class C:

Let C be a class, and let V be a blank final member field of C, declared in a superclass of C. Then:


Contents | Prev | Next | Index Java Language Specification
Second Edition
Copyright © 2000 Sun Microsystems, Inc. All rights reserved
Please send any comments or corrections to [email protected]