Type parameters inside super() calls?
Alex Buckley
alex.buckley at oracle.com
Thu Feb 2 01:04:44 UTC 2023
On 2/1/2023 4:31 PM, Maurizio Cimadamore wrote:> And, if we add
statements before |super| it gets even worse, because now
> we can have types that are non-static, but that it’s not clear as to why
> such types couldn’t be referred to:
>
> |class Foo { Foo() { class Local { String aString() { ... } } super(new
> Local().aString()); // is this legal? } } |
To answer the question: the argument to `super(..)` is presently not
legal because the `new Local()` part is presently not legal. (15.9.2:
"If <<Local>> is an inner local class, then ... if the class instance
creation expression occurs in a static context, then a compile-time
error occurs.")
But you are saying this is "static context overreach", and that perhaps
`new Local()` should be allowed there. Such an expression needs to use
`this` as the immediately enclosing instance of the Local object, and
that's fine as long as no-one can access the immediately enclosing
instance ... but now we invoke a method on the Local object and ????
> Now that we’re planning to do more work in this area [1], it would be
> IMHO a good time to see if the use of “static context” in 8.8.7.1 is
> really what we want.
I don't disagree.
Alex
> Maurizio
>
> [1] - https://bugs.openjdk.org/browse/JDK-8300786
>
>>
>> Alex
>>
>> On 2/1/2023 3:43 PM, Maurizio Cimadamore wrote:
>>> While I agree that what javac does is against the JLS, is this a
>>> javac or a spec bug?
>>>
>>> I mean, consider this hierarchy:
>>>
>>> |class Sup<X> { Sup(X x) { ... } } class Sub<Y> extends Sup<Y> {
>>> Sub(Y y) { super(y); } } |
>>>
>>> What the rules are saying is that “y” in the Sub super call should be
>>> evaluated in a static context. But that doesn’t make any sense -
>>> because Y is not even a valid type in such a static context.
>>>
>>> And yet - this is a valid program. So, if typechecking “y” is
>>> correct, then, surely, typechecking “(Y)y” should also be correct?
>>> E.g. it seems to me that the use of “static context” here is an
>>> “approximation” for the behavior we really want (e.g. no access to
>>> instance fields, whether directly or indirectly).
>>>
>>> Maurizio
>>>
>>> On 01/02/2023 22:39, Alex Buckley wrote:
>>>
>>>> Your reading of the JLS is correct. The cast expression `(T)obj`
>>>> occurs in a static context, so the use of T should be disallowed,
>>>> and javac should reject the program.
>>>>
>>>> These rules in 8.8.7.1 and 8.1.3 are longstanding, and have not
>>>> changed recently, so I'm surprised that javac lets the program
>>>> through. That said, JLS16 saw a reworking of 8.1.3 (driven by
>>>> https://openjdk.org/jeps/395#Static-members-of-inner-classes) and
>>>> perhaps javac got a bit turned around over static contexts.
>>>>
>>>> As an additional test case, make the constructor generic -- `public
>>>> <U extends T> TypeParam ...` -- and cast obj to U rather than T --
>>>> still illegal.
>>>>
>>>> Alex
>>>>
>>>> On 2/1/2023 12:45 PM, Archie Cobbs wrote:
>>>>> This program compiles without error:
>>>>>
>>>>> import java.util.concurrent.atomic.*;
>>>>> public class TypeParamStaticContext<T> extends AtomicReference<T> {
>>>>> public TypeParamStaticContext(Object obj) {
>>>>> super((T)obj);
>>>>> }
>>>>> }
>>>>>
>>>>> Yet according to my reading of the JLS, the appearance of T inside
>>>>> the super() call should be disallowed:
>>>>>
>>>>> §8.8.7.1 <http://8.8.7.1>:
>>>>>
>>>>> An explicit constructor invocation statement introduces a static
>>>>> context (§8.1.3
>>>>> <https://docs.oracle.com/javase/specs/jls/se19/html/jls-8.html#jls-8.1.3>), which limits the use of constructs that refer to the current object. Notably, the keywords |this| and |super| are prohibited in a static context (§15.8.3 <https://docs.oracle.com/javase/specs/jls/se19/html/jls-15.html#jls-15.8.3>, §15.11.2 <https://docs.oracle.com/javase/specs/jls/se19/html/jls-15.html#jls-15.11.2>), as are unqualified references to instance variables, instance methods, and type parameters of lexically enclosing declarations (§6.5.5.1 <https://docs.oracle.com/javase/specs/jls/se19/html/jls-6.html#jls-6.5.5.1>, §6.5.6.1 <https://docs.oracle.com/javase/specs/jls/se19/html/jls-6.html#jls-6.5.6.1>, §15.12.3 <https://docs.oracle.com/javase/specs/jls/se19/html/jls-15.html#jls-15.12.3>).
>>>>>
>>>>> §6.5.5.1 <http://6.5.5.1>:
>>>>>
>>>>> If a type name consists of a single /Identifier/, then the
>>>>> identifier must occur in the scope of exactly one declaration of a
>>>>> class, interface, or type parameter with this name (§6.3
>>>>> <https://docs.oracle.com/javase/specs/jls/se19/html/jls-6.html#jls-6.3>), or a compile-time error occurs.
>>>>>
>>>>> If the declaration denotes a type parameter of a generic class or
>>>>> interface C (§8.1.2
>>>>> <https://docs.oracle.com/javase/specs/jls/se19/html/jls-8.html#jls-8.1.2>, §9.1.2 <https://docs.oracle.com/javase/specs/jls/se19/html/jls-9.html#jls-9.1.2>), then both of the following must be true, or a compile-time error occurs:
>>>>>
>>>>> *
>>>>>
>>>>> The type name does not occur in a static context (§8.1.3
>>>>> <https://docs.oracle.com/javase/specs/jls/se19/html/jls-8.html#jls-8.1.3>)
>>>>>
>>>>> *
>>>>>
>>>>> If the type name appears in a nested class or interface
>>>>> declaration
>>>>> of C, then the immediately enclosing class or interface
>>>>> declaration
>>>>> of the type name is an inner class of C.
>>>>>
>>>>>
>>>>> What am I missing?
>>>>>
>>>>> Thanks,
>>>>> -Archie
>>>>>
>>>>> --
>>>>> Archie L. Cobbs
>>>
>>>
>
>
More information about the compiler-dev
mailing list