United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6676362 : Spurious forward reference error with final var + instance variable initializer

Details
Type:
Bug
Submit Date:
2008-03-17
Status:
Closed
Updated Date:
2011-05-18
Project Name:
JDK
Resolved Date:
2011-05-18
Component:
tools
OS:
generic
Sub-Component:
javac
CPU:
generic
Priority:
P4
Resolution:
Fixed
Affected Versions:
7
Fixed Versions:

Related Reports

Sub Tasks

Description
This program wrongly generates an 'illegal forward reference' error (at obj's declaration...even if the error was justified, it should occur at obj's use) :-

class Huh {
    Runnable r = new Runnable() {
        public void run() {
            Object o = obj;
        }
    };
    final Object obj = r;
}

The following program is morally equivalent (the JLS treats instance variable initializers and instance initializers uniformly) and does not generate an error:

class Huh {
    Runnable r;
    {
        r = new Runnable() {
            public void run() {
                Object o = obj;
            }
        };
    }
    final Object obj = r;
}
Simpler test case, given the following program

class Test {
    final int a = b;
    final int b = a;
}

the compiler should report only ONE error, instead of two, as it can be seen from the output:

Test.java:33: illegal forward reference
    final int a = b;
                  ^
Test.java:34: illegal forward reference
    final int b = a;
                  ^
2 errors

While the first error is correct, the second one is a bug, accordingly to JLS 8.3.2.3

                                    

Comments
SUGGESTED FIX

A webrev of this fix is available at http://sa.sfbay.sun.com/projects/langtools_data/7/6676362/
                                     
2008-04-23
EVALUATION

JLS 8.3.2.3 (Restrictions on the use of Fields during Initialization) says that:

"The declaration of a member needs to appear textually before it is used only if the member is an instance (respectively static) field of a class or interface C and all of the following conditions hold:

    * The usage occurs in an instance (respectively static) variable initializer of C or in an instance (respectively static) initializer of C.
    * The usage is not on the left hand side of an assignment.
    * The usage is via a simple name.
    * C is the innermost class or interface enclosing the usage."

1) In the first of the two examples we have that the usage of obj inside the anonymous Runner cannot be flagged as illegal, since such usage appears within the scope of a class that is different wrt the class that defines obj (Huh). As such, the first bullet does not hold, and thus no compiler error should be raised. On the other hand we have that the usage of r within the initializer of obj is perfectly legal, as r's declaration is *textually before* obj's declaration.

2) in this smaller example we have a clear forward reference in the first of the two asignments (usage of b in a's initializer). The second error message cannot be justified by the JLS as, again b's initializer is referring to a, whose declaration precedes b's one (so 8.3.2.3 does not apply).

In the remaining of this section we'll focus on 2), as it is simpler (yet producing an equivalent erroneous behaviour).

The spourious forward ref error is caused by the way in which javac attributes constants (aka final fields). The initializers of such fields are in fact attributed lazily (on-demand). This means that, when we encounter the two declarations:

final int a = b;
final int b = a;

the evaluation of the initializer of a is actually triggering the evaluation of the initializer of b, since b is final. In other words, it's like if javac performs one big attribution for both initializers instead of attributing each initializer separately - since b's attribution occur within a's attribution. The consequence of this is that when javac attributes b's initializer, the reference to a is recognized as a self-reference (since we are still within a's attribution) and thus marked as erroneous.

In order to preserve lazy attribution of final variable's initializers, javac should detect self-reference in initializers in a slightly different way. In particular at any given time during attribution only one variable symbol at a time can be the cause of a forward ref error because of self-referencing. That is, when javac tries to attribute b's initializer within a's attribution, javac should only complain about self-references involving b (the variable whose initializer is currently being attributed).
                                     
2008-04-23



Hardware and Software, Engineered to Work Together