Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482)

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Wed May 29 14:57:13 UTC 2024


On 28/05/2024 18:20, Stephan Herrmann wrote:

> * §8.1.3: "An instance of an anonymous class whose declaration occurs 
> in a pre-construction context (8.8.7.1) has no immediately enclosing 
> instance." 

Late to the party. I’m kind of questioning that rule a little bit, to be 
honest. More specifically, the text that says that a local/anon class in 
a static context has no enclosing instance seems to cast too wide of a 
net. If that’s the case, then we can’t really prove (following the rules 
in the spec) how references to members in enclosing instances are 
resolved. Consider the following example:

|class Outer { class Inner { Inner() { class Local { void test() { m(); 
} } new Local().test(); super(); } } void m() { } public static void 
main(String[] args) { new Outer().new Inner(); } } |

Here, |Local| occurs in an early context (used to be pre-constructor, or 
static). So it has no enclosing instance. But then, how can it refer to 
|Outer::m| ?

In 15.12.1 we say:

    If there is an enclosing class or interface declaration of which
    that method is a member, let E be the innermost such class or
    interface declaration. The type to search is the type of E.this
    (§15.8.4).

Ok, so the above program is valid if |Outer.this| makes sense. But, as 
per 15.8.4:

    The value of a qualified this expression TypeName.this is the n’th
    lexically enclosing instance of this.

But there’s no enclosing instance for |Local|, so what does |Outer.this| 
evaluates to?

I think this has always been a bit of an issue - we always treated 
anonymous classes occurring in super calls as “static”, but that doesn’t 
explain how such classes seem to have access to members of enclosing 
classes. Under the hood, this works, because javac captures the 
enclosing instance reference of |Inner| and passes that to |Local|. In 
other words, it’s as if, from a translation perspective, the enclosing 
instance of |Local| was |Outer|. In fact, if we look at the generated 
class for |Local| we see this:

|class Outer$Inner$1Local { final Outer val$this$0; 
Outer$Inner$1Local(Outer); void test(); } |

Note that there’s no |this$0| field, but |val$this$0| instead. In other 
words, it’s as if the |this$0| reference in the |Inner| constructor has 
been captured by the local class. While this works out in practice, it 
does seem a very roundabout way to say that |Local| indeed has an 
enclosing instance (|Outer|) - and javac’s backend trickery of course 
doesn’t even explain why the above program is accepted in the first 
place. It seems like this area needs a bit of an overhaul? Or am I 
missing something?

Cheers
Maurizio

​
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20240529/7f30f0b5/attachment-0001.htm>


More information about the amber-spec-experts mailing list