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

Archie Cobbs acobbs at openjdk.org
Fri Feb 14 23:37:10 UTC 2025


On Fri, 14 Feb 2025 17:51:02 GMT, Archie Cobbs <acobbs at openjdk.org> wrote:

>> So... N.this is always used to denote an enclosing instance. So, if N is in early construction context, there are two cases:
>> * this is a "simple assignment", in which case the spec says it's ok
>> * it's not an assignment (but a field read), in which case it's an error.
>> 
>> In your example above, `Early` is not in early construction context when we are in the `Sub` constructor, so none of the above applies? I guess in terms of code, what this means is that I'm surprised that we're handling the implicit and explicit cases in the same test. E.g. when using a qualified `X.this`, the membership check seems irrelevant (the only thing that matters is whether `X` is in early construction context).
>
>> So... N.this is always used to denote an enclosing instance...
> 
> I think that's not always true... for example, this compiles just fine (with or without the explicit `super()`):
> 
> class Myself {
>     int x;
>     Myself() {
>         Myself.this.x = 42;
>         super();
>     }
> }

Hmm, re-reading that I think maybe I misinterpreted your statement... For clarity, let's just enumerate the possibilities.

Suppose we are inside a class `C` and there is some instance field `x` accessible to us.

Then any such `x` must be declared in one (_or both_) of these two "hierarchies":
* **Inner hierarchy**  - `x` is declared in `C` or (non-privately) in a superclass of `C`
* **Outer hierarchy**  - `x` is declared in a some outer class `O` containing `C`

In the case `x` is declared in both hierarchies, it may _also_ be the case that `O` is a superclass of `C`, but for our purposes that's "just a coincidence" - it doesn't affect how many `x` field's are accessible to us (i.e., two).

Now what can the various expressions mean depending on which of the two hierarchies `x` is declared in?

| Inner? | Outer? | `C` extends `O` | `x`  | `C.this.x` | `O.this.x` | Comments |
| :---:  | :---: | :---:  | :---: | :---: | :---: | :--- |
| Yes | No | N/A | Inner | Inner | _N/A_ | Simple inner case |
| No | Yes | No | Outer | _N/A_ | Outer | Simple outer case |
| No | Yes | Yes | _N/A_ | _N/A_ | _N/A_ | Not possible! | |
| Yes | Yes | No | Inner | Inner | Outer | |
| Yes | Yes | Yes | Inner | Inner | Outer | |

So it looks like we can say this about an (presumably valid) expression of the form `N.this.x`:
* If `N` = `C`, then we are always referring to the "inner" `x` - therefore, it's an early reference to `C` if and only if we are in an early construction context for `C`
* If `N` ≠ `C`, then we are always referring to the "outer" `x` - therefore, an early reference if and only if we are in an early construction context for `N`

So according to that logic, you are correct in saying, with regards to expressions like `N.this.x`, that "the only thing that matters is whether `N` is in early construction context".

Please check my work...

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

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


More information about the compiler-dev mailing list