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


CHAPTER 16

Definite Assignment


Each local variable (§14.4) and every blank final (§4.12.4) field (§8.3.1.2) must have a definitely assigned value when any access of its value occurs. An access to its value consists of the simple name of the variable occurring anywhere in an expression except as the left-hand operand of the simple assignment operator =. 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. Such an assignment is defined to occur if and only if either the simple name of the variable, or its simple name qualified by this, occurs on the left hand side of an assignment operator. 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.10), 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. We will use the phrase a is V to mean that a is either the simple name of the variable V , or V `s simple name qualified by this (ignoring parentheses). We will use the phrase a is not V to mean the negation of a is V .

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 Other Expressions of Type boolean

Suppose that e is a an expression of type boolean and is not a boolean constant expression, logical complement expression !a, conditional-and expression a && b, conditional-or expression a || b, or conditional expression a ? b : c.

16.1.8 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.

Note that if a is V and V is not definitely assigned before a compound assignment such as a &= b, then a compile-time error will necessarily occur. The first rule for definite assignment stated above includes the disjunct "a is V" even for compound assignment expressions, not just simple assignments, so that V will be considered to have been definitely assigned at later points in the code. Including the disjunct "a is V" does not affect the binary decision as to whether a program is acceptable or will result in a compile-time error, but it affects how many different points in the code may be regarded as erroneous, and so in practice it can improve the quality of error reporting. A similar remark applies to the inclusion of the conjunct "a is not V" in the first rule for definite unassignment stated above.

16.1.9 Operators ++ and --

16.1.10 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.7.1) are handled specially (§16.9); 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

We say that V is definitely unassigned everywhere in a block B iff

These conditions are counterintuitive and require some explanation. Consider a simple assignment V = e. If V is definitely assigned after e, then either:

  1. The assignment occurs in dead code, and V is vacouusly definitely assigned. In this case, the assignment will not actually take place, and we can assume that V is not being assigned by the assignment expression.
  2. V was already assigned by an earlier expression prior to e. In this case the current assignment will cause a compile-time error.

So, we can conclude that if the conditions are met by a program that causes no compile time error, then any assignments to V in B will not actually take place at run time.

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 assert Statements

The following rules apply both to a statement assert e1 and to a statement assert e1 :e2 :

The following rule applies to a statement assert e1: e2 :

16.2.9 switch Statements

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

16.2.10 while Statements

16.2.11 do Statements

16.2.12 for Statements

The rules herein cover the basic for statement (§14.14.1). Since the enhanced for (§14.14.2) statement is defined by traslation to a basic for statement, no special rules need to be provided for it.

16.2.12.1 Initialization Part

16.2.12.2 Incrementation Part

16.2.13 break, continue, return, and throw Statements

16.2.14 synchronized Statements

16.2.15 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 Enum Constants

The rules determining when a variable is definitely assigned or definitely unassigned before an enum constant are given §16.8.


Discussion

This is because an enum constant is essentially a static final field (§8.3.1.1, §8.3.1.2) that is initialized with a class instance creation expression (§15.9).


The definite assignment/unassignment status of any construct within the class body of an enum constant is governed by the usual rules for classes.

Let y be an argument of an enum constant, but not the first. Then:

Otherwise:

16.6 Definite Assignment and Anonymous Classes


Discussion

It should be clear that if an anonymous class is implicitly defined by an enum constant, the rules of section §16.5 apply.


16.7 Definite Assignment and Member Types

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

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

16.8 Definite Assignment and Static Initializers

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

Note that there are no rules that would allow us to conclude that V is definitely unassigned before a static variable initializer or enum constant. We can informally conclude that V is not definitely unassigned before any static variable initializer of C , but there is no need for such a rule to be stated explicitly.

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.9 Definite Assignment, Constructors, and Instance Initializers

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

Note that there are no rules that would allow us to conclude that V is definitely unassigned before an instance variable initializer. We can informally conclude that V is not definitely unassigned before any instance variable initializer of C , but there is no need for such a rule to be stated explicitly.

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
Third Edition

Copyright © 1996-2005 Sun Microsystems, Inc. All rights reserved
Please send any comments or corrections via our feedback form