constant references in enums
Alex Buckley
alex.buckley at oracle.com
Fri Jan 22 22:38:42 UTC 2016
Yes, the 8.3.3 rule doesn't have a special case for forward references
to class variables _which are constant variables_, so FOOE(FOOV) is
illegal prior to the declaration of FOOV. (And it's impossible to
declare the ordinary static field FOOV "first".)
A special case might appear desirable, but then we'd be setting up for
(so would have to disallow) circular references:
static final int x = y+1;
static final int y = x+1;
As to readability of the spec, an enum constants like FOOE(FOOV) has
always been specified by translation to a public static final field. JSR
201 said "Each such field is initialized to the enum constant that
corresponds to it." I sharpened this very slightly in JLS8 to "The field
has a variable initializer consisting of c [c is the enum constant]" but
I recall not wanting to commit to "The field has a variable initializer
consisting of 'new E(/c_arglist/)'." because compilers don't need to
agree on the signature of an enum constructor. (See the related note in
8.9.2.)
As such, your complaint that the JLS doesn't specify the class variable
initializer for FOOE well enough to predict the meaning of FOOV is
valid. A possible fix is to have 8.9.3 reiterate the useful info from
8.9.1: "An enum constant may be followed by arguments, which are passed
to the constructor of the enum when the constant is created during class
initialization as described later in this section."
Alex
On 1/22/2016 12:12 PM, John Rose wrote:
> That all makes sense; it's consistent, although it makes me a little sad.
> Enums should be able to define their own bit-fiddly constants and use
> them to construct themselves, and the qualified expr is the way to do so.
>
> I was confused about the status of the sugary expression "FOOE(FOOV)".
> I guess it is a constructor invocation from a static initializer, but
> the spec
> doesn't really categorize it, clearly enough to predict what should happen.
>
> FTR, the use case comes from a Vector API POC:
>
>> enum LaneType implements Impl.EnumWorkaround {
>> ILLEGAL((byte)0,0), // sentinel for error reporting
>> Z0(Z,0), Z1(Z,1), Z2(Z,2), Z4(Z,4), // zero-bit = void,
>> one-bit, etc.
>> // (signed,unsigned,float) x (2**{0,1,2,3,4)b)
>> U1(U,1), U2(U,2), U4(U,4), U8(U,8), U16(U,16),
>> S1(S,1), S2(S,2), S4(S,4), S8(S,8), S16(S,16),
>> F1(F,1), F2(F,2), F4(F,4), F8(F,8), F16(F,16);
>>
>> /* JLS: "It is a compile-time error to reference a static field
>> * of an enum type from constructors, instance initializers,
>> * or instance variable initializer expressions of the enum
>> * type, unless the field is a constant variable (4.12.4)."
>> * So the following should work, but appears to be broken:
>> */
>> //private static final byte Z = 0x1, U = 0x2, S = 0x4, F = 0x08;
>> private static final int BIT_SIZE_SHIFT = 4, BYTE_SIZE = 8;
>>
>> private int flags;
>> LaneType(byte flags, int size) {
>> int bitSize = (flags & Z) == 0 ? (size * BYTE_SIZE) : size;
>> assert((flags & (-1 << BIT_SIZE_SHIFT)) == 0);
>> this.flags = flags | (bitSize << BIT_SIZE_SHIFT);
>> }
>> public int bitSize() { return flags >> BIT_SIZE_SHIFT; }
>> public int byteSize() { return bitSize() / BYTE_SIZE; }
>> public boolean isUnsigned() { return (flags & U) != 0; }
>> public boolean isSigned() { return (flags & S) != 0; }
>> public boolean isFloat() { return (flags & F) != 0; }
>> }
>
> Ref:
> http://mail.openjdk.java.net/pipermail/panama-dev/2016-January/000288.html
> <http://cr.openjdk.java.net/~jrose/arrays/vector/VectorOp.java>
>
> — John
>
> On Jan 21, 2016, at 4:52 PM, Maurizio Cimadamore
> <maurizio.cimadamore at oracle.com <mailto:maurizio.cimadamore at oracle.com>>
> wrote:
>>
>> Compiler output for my last example:
>>
>> Main.java:48: error: illegal reference to static field from initializer
>> int field1 = e;
>> ^
>> Main.java:52: error: illegal reference to static field from initializer
>> int i1 = e;
>> ^
>> Main.java:57: error: illegal reference to static field from initializer
>> int i1 = e;
>> ^
>> 3 errors
>>
>>
>> As you can see, static reference from instance context is only allowed
>> when the static is also a constant - as per 8.9.2.
>>
>> Maurizio
>>
>> On 22/01/16 00:51, Maurizio Cimadamore wrote:
>>> My bad - the section you quote refers to a different scenario:
>>>
>>> enum Foo {
>>> BAR;
>>>
>>> static int e = 0; //non constant
>>> final static int ce = 42; //constant
>>>
>>> //8.9.2: It is a compile-time error to reference a static field
>>> of an enum type from...
>>>
>>> // instance variable initializer expression
>>> int field1 = e;
>>> int field2 = ce;
>>>
>>> // instance initializers
>>> {
>>> int i1 = e;
>>> int i2 = ce;
>>> }
>>>
>>> //constructors
>>> Foo() {
>>> int i1 = e;
>>> int i2 = ce;
>>> }
>>> }
>>>
>>> So, in your case the access is occurring from another *static*
>>> variable initializer, so 8.9.2 does not apply. Which I think means
>>> that the same rules for ordinary classes apply - meaning that forward
>>> reference for statics is only allowed with fully qualified name. So
>>> it is consistent. And it is not a bug. Let's wait from some spec guru
>>> to chime in.
>>>
>>> Maurizio
>>>
>>> On 22/01/16 00:41, Maurizio Cimadamore wrote:
>>>> If it helps - this is rejected too:
>>>>
>>>> class FooE {
>>>> static FooE fe = new FooE(FOOC); // ?? error: illegal
>>>> forward reference
>>>> FooE(int k) {}
>>>> public static final int FOOC = 42; // a constant variable
>>>> }
>>>>
>>>> But - this isn't:
>>>>
>>>> class FooE {
>>>> static FooE fe = new FooE(*FooE.*FOOC); // ?? error:
>>>> illegal forward reference
>>>> FooE(int k) {}
>>>> public static final int FOOC = 42; // a constant variable
>>>> // look, JLS 8.9.2 says "...unless the field is a constant
>>>> variable"
>>>> }
>>>>
>>>> And I know this is not a bug - i.e. the spec says that fully
>>>> qualified names are allowed to forward references (see 8.3.3).
>>>>
>>>> So, I'm assuming that, since javac desugars away enums at parsing
>>>> time (by translating them into classes similar to the one above), it
>>>> ends up generating illegal code in your specific case. Or maybe the
>>>> spec should be rectified to do something consistent in both cases.
>>>>
>>>> Btw - if you add 'FooE.' your examples compiles too :-)
>>>>
>>>> Maurizio
>>>>
>>>> On 22/01/16 00:16, John Rose wrote:
>>>>> enum FooE {
>>>>> FOOV(FOOC); // ?? error: illegal forward reference
>>>>> FooE(int k) {}
>>>>> public static final int FOOC = 42; // a constant variable
>>>>> // look, JLS 8.9.2 says "...unless the field is a constant variable"
>>>>> }
>>>>
>>>
>>
>
More information about the compiler-dev
mailing list