On Tue, Feb 7, 2023 at 4:48 PM Archie Cobbs <[email protected]> wrote:
> FWIW in the JEP I've raised the importance of fixing this bug in the spec > <https://bugs.openjdk.org/browse/JDK-8301649> (i.e., that constructor > invocation is not really a "static context" but truly something different - > what we're calling a "pre-initialization context") and made it one of the > "goals" of the JEP, because the gulf between the spec on one hand and the > compiler's current behavior (and what everyone has actually been doing for > the past decade) on the other hand is pretty glaring. > FYI, Along these lines, I've added this new section to the "Motivation" section: (Also, presumably people who are interested in following the JEP can 'watch' it, so I won't post any more such updates after this one.) Fixing a Specification Bug > > JLS §8.1.3 defines *static context* and notes that "The purpose of a > static context is to demarcate code that must not refer explicitly or > implicitly to the current instance of the class whose declaration lexically > encloses the static context". Going by its stated purpose, a static context > would seem to naturally apply to code inside a super() or this() > invocation, and in fact the JLS does just that. Prior to the advent of > generics, inner classes, and captured free variables, this yielded the > correct semantics for superclass construtor invocation. > > However, as §8.1.3 notes a static context prohibits: > > - this expressions (both unqualified and qualified) > - Unqualified references to instance variables of any lexically > enclosing class or interface declaration > - References to type parameters, local variables, formal parameters, > and exception parameters declared by methods or constructors of any > lexically enclosing class or interface declaration that is outside the > immediately enclosing class or interface > > Those rules make the following program illegal: > > import java.util.concurrent.atomic.AtomicReference; > > public class ClassA<T> extends AtomicReference<T> { > > private int intval; > > public ClassA(T obj) { > super(obj); > } > > public class ClassB extends ClassA<T> { > public ClassB() { > super((T)null); // illegal - 'T' > } > } > > public class ClassC extends ClassA<Object> { > ClassC() { > super(ClassA.this); // illegal - 'this' > } > } > > public class ClassD extends ClassA<Integer> { > ClassD() { > super(intval); // illegal - 'intval' > } > } > > public static Object method(int x) { > class ClassE extends ClassA<Float> { > ClassE() { > super((float)x); // illegal - 'x' > } > } > return new ClassE(); > } > } > > But not only has this program compiled successfully since at least Java 8, > the above idioms are in common use. The mental model that the compiler and > developers seem to both be using is indeed that code "must not refer > explicitly or implicitly to the current instance of the class whose > declaration lexically encloses" the code in question. However, this is no > longer what a "static context" prohibits; instead, it goes beyond that, for > example, restricting even references to generic type parameters. > > The underlying issue is that "static context" is applied to two scenarios > which are similar, but not equivalent: > > 1. When there is no 'this' instance defined, e.g., within a static > method > 2. When the 'this' instance is defined but must not be referened, > e.g., prior to superclass initialization > > The current definition of "static context" is appropriate for scenario #1, > but after the addition of generics, inner classes, and captured free > variables to the language, no longer for scenario #2. We need a new, > distinct concept; let's call it a "pre-initialization context". The rules > for a "pre-initialization context" should align with developers' current > mental models (and compiler behavior) for what's allowed prior to > superclass initialization. Once defined, the rules for a > "pre-initialization context" can then extend to statements prior to > superclass initialization, and for the same reason. > -Archie -- Archie L. Cobbs
