Summary
Prevent local classes declared in a constructor prologue from having an immediately enclosing instance.
Problem
JLS §15.9.2 specifies that a local class declared in a static context does not have an immediately enclosing instance.
Superclass constructor invocation parameter expressions, which are evaluated prior to the actual super()
or this()
invocation, are considered to be in a static context (§8.1.3).
The problem is that the compiler currently compiles local classes declared within superclass constructor invocation parameter expressions with an immediately enclosing instance, which is incorrect.
This is an arcane bug, because currently the only way to define such local class is to shoehorn it into a switch expression, like this:
import java.util.concurrent.atomic.*;
public class Test extends AtomicReference<Object> {
public Test() {
super(switch (0) {
default -> {
class Local { { Test.this.hashCode(); } } // this should fail
yield null;
}
});
}
}
Moreover, any such class is completely useless, because it can never be instantiated (if you try, you'll get a compiler error). So this is a very contrived situation that likely never occurs in real code.
However, with upcoming support for flexible constructors (JEP 447), which is already in preview, this bug will get more exposed to the real world because it will be much more natural to declare and use local classes in the "prologue" of a constructor.
Solution
The change proposed here is to fix the bug, i.e., change the compiler so that it no longer compiles local classes declared in a constructor prologue with an immediately enclosing instance.
Specification
Change the compiler to treat local classes the same as anonymous classes with respect to immediately enclosing instances. At it's heart, the change amounts to this patch:
- if (ctorProloguePrev && env.tree.hasTag(NEWCLASS)) {
+ if (ctorProloguePrev) {
c.flags_field |= NOOUTERTHIS;
}
- csr of
-
JDK-8328649 Disallow enclosing instances for local classes in constructor prologues
- Resolved