Type parameters inside super() calls?
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Thu Feb 2 00:31:34 UTC 2023
On 02/02/2023 00:13, Alex Buckley wrote:
> 8.1.3 has a nice note about intent:
>
> "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. Consequently,
> code that occurs in a static context is restricted in the following
> ways: ..."
>
> Beyond that, I'm not sure what to say, because all of this has been in
> place since Java 1.1.
I understand that this has been there since the beginning.
And, in a world where (a) there’s no generics and (b) you can’t have
statements before super() - that para specifies the right behavior.
When you throw in type parameters, it seems to me that the “this is a
static context” no longer works - as it cannot explain programs that are
obviously valid (such as the one I have shown).
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? } } |
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.
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
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20230202/b8b7d405/attachment-0001.htm>
More information about the compiler-dev
mailing list