RFR: 8349754: Invalid "early reference" error when class extends an outer class

Maurizio Cimadamore mcimadamore at openjdk.org
Wed Feb 12 10:19:11 UTC 2025


On Mon, 10 Feb 2025 20:51:34 GMT, Archie Cobbs <acobbs at openjdk.org> wrote:

> The compiler is generating a bogus error for this input:
> 
> $ cat Outer.java
> class Outer {
>     private int i;
>     class Sub extends Outer {
>         Sub() {
>             i = 42;
>             super();
>         }
>     }
> }
> $ javac --enable-preview --source 24 Outer.java
> Outer.java:5: error: cannot reference i before supertype constructor has been called
>             i = 42;
>             ^
> Note: Outer.java uses preview features of Java SE 24.
> Note: Recompile with -Xlint:preview for details.
> 1 error 
> 
> 
> The trick here is that the expression `i` refers to `Outer.this.i`, not `this.i`, but the error checking logic is mistakenly assuming the latter. This patch adds an exception for this case, which happens when the field is `private` and declared in a class which is both a superclass and an outer class.

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java line 4015:

> 4013:             // declared in a superclass S if (a) the field is private, and (b) S is also an outer class.
> 4014:             // It's OK because the expression "x" then refers to the outer instance, not the current instance.
> 4015:             if (env.enclClass.type != v.owner.type && (v.flags() & PRIVATE) != 0) {

My feeling is that we should deal with this from a membership point of view. That is, `x` here is NOT a member of the subclass because it is not inherited. I'd suggest to use `Symbol::isMemberOf` (which also does the subtype check). 

(Note that the subclass could declare _another_ field with same name, in which case the superclass field is not inherited, even if non-private. Better to rely on existing membership logic -- the rules are finicky)

If I'm not mistaken, I hope you can rewrite the full check like this:


if (env.info.ctorPrologue &&
                (v.flags() & STATIC) == 0 &&
                v.isMemberOf(env.enclClass.sym)) { ... }


Honestly, I'm also reading the text in the spec draft for JEP 492, I'm not sure it deals with this case correctly:

> If an expression name consists of a single Identifier, then there must be exactly one declaration denoting either a local variable, formal parameter, exception parameter, or field in scope at the point at which the identifier occurs. Otherwise, a compile-time error occurs.

> If the declaration denotes an instance variable of a class C ([8.3.1.1](https://docs.oracle.com/javase/specs/jls/se22/html/jls-8.html#jls-8.3.1.1)), then both all of the following must be true, or a compile-time error occurs:

>     The expression name does not occur in a static context ([8.1.3](https://cr.openjdk.org/~gbierman/jep492/jep492-20241218/specs/flexible-constructor-bodies-jls.html#jls-8.1.3)).

>    If the expression name occurs in an early construction context of C ([8.8.7](https://cr.openjdk.org/~gbierman/jep492/jep492-20241218/specs/flexible-constructor-bodies-jls.html#jls-8.8.7)), then it is the left-hand operand of a simple assignment expression ([15.26](https://docs.oracle.com/javase/specs/jls/se22/html/jls-15.html#jls-15.26)), the declaration of the named variable lacks an initializer, and the simple assignment expression is not enclosed in a lambda expression or inner class declaration that is contained in the early construction context of C.

>    The expression name does not occur in an early construction context of a subclass of C.

There's no reference to the fact that the instance variable being referred to must be a member of C. This is different, for instance, from what happens for 6.5.7.1 (simple method names), where membership is mentioned.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/23545#discussion_r1952360600


More information about the compiler-dev mailing list