JDK-6209839 : Illegal forward reference to enum constants allowed by javac
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 5.0
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2004-12-17
  • Updated: 2019-02-19
  • Resolved: 2005-12-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 b65Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Consider the code below:

    public enum EnumTest {

        anEnumValue {
            private final EnumTest thisOne = anEnumValue;

            String getMessage() { return "Here is what thisOne gets assigned: " + thisOne; }
        };

        abstract String getMessage();

        public static void main(String[] args) {
            System.out.println(anEnumValue.getMessage());
        }

    }

This code compiles, but when you run it, you get the output:
    Here is what thisOne gets assigned: null
  
What this means is that the final local variable thisOne defined as a constant-specific field inside the enum constant anEnumValue has been assigned null.

This is NOT the behavior that I would have expected: I would have thought that it would really pick up the non-null reference to anEnumValue, especially given that it compiles.

But if the semantics of enums in java do not support this behavior, then at least javac should fail with the error that this is an illegal forward reference, rather than compile and allow it to fail at runtime.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code above.


REPRODUCIBILITY :
This bug can be reproduced always.

CUSTOMER SUBMITTED WORKAROUND :
NONE, and I would really like to code using the technique illustrated above, as it is a great way to program state machines, which is something that enums really should be used for.
###@###.### 2004-12-17 00:39:14 GMT

Comments
SUGGESTED FIX Index: src/share/classes/com/sun/tools/javac/comp/Attr.java =========================================================== @@ -2044,13 +2044,21 @@ // In an enum type, constructors and instance initializers // may not reference its static members unless they are constant. - if (((v.flags() & STATIC) != 0) && - ((v.owner.flags() & ENUM) != 0) && - v.constValue == null && - v.owner == env.info.scope.owner.enclClass() && - Resolve.isInitializer(env)) + checkEnumInitializer: { + if ((v.flags() & STATIC) == 0) + break checkEnumInitializer; + if ((v.owner.flags() & ENUM) == 0) + break checkEnumInitializer; + if (v.constValue != null) + break checkEnumInitializer; + ClassSymbol enclClass = env.info.scope.owner.enclClass(); + if (v.owner != enclClass && v.owner != enclClass.owner.enclClass()) + break checkEnumInitializer; + if (!Resolve.isInitializer(env)) + break checkEnumInitializer; log.error(tree.pos, "illegal.enum.static.ref"); } + } /** Can the given symbol be the owner of code which forms part * if class initialization? This is the case if the symbol is ###@###.### 2005-05-31 05:23:48 GMT
31-05-2005

EVALUATION It is impossible to generate code for the most general instances of this problem. The global static variable holding the reference to anEnumValue is not initialized before the object is constructed. This means that you cannot refer to anEnumValue during it's construction. However, the compiler fails to report this problem. Conclusion: the program can't be expected to work and might even be illegal. ###@###.### 2004-12-17 02:37:35 GMT This is a specification violation as the JLS states the following: "It is a compile-time error to reference a static field of an enum type that is not a compile-time constant (15.28) from constructors, instance initializer blocks, or instance variable initializer expressions of that type. It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e1 to refer to itself or an enum constant of the same type that is declared to the right of e1." ###@###.### 2005-1-20 23:39:26 GMT In response to recent SDN comment: See page 252 of the JLS. ###@###.### 2005-07-02 18:35:46 GMT
17-12-2004

WORK AROUND The trivial work around is to use this. However that will not work for more general cases. Consider an approach like this: public enum EnumTest { anotherEnumConstant, anEnumValue(anotherEnumConstant) { String getMessage() { return "Here is what thisOne gets assigned: " + thisOne; } }; protected final EnumTest thisOne; EnumTest(EnumTest other) { thisOne = other; } EnumTest() { thisOne = null; } String getMessage() { return null; } public static void main(String[] args) { System.out.println(anEnumValue.getMessage()); } } ###@###.### 2004-12-17 02:37:35 GMT
17-12-2004