Type parameters inside super() calls?
Archie Cobbs
archie.cobbs at gmail.com
Thu Feb 2 02:04:13 UTC 2023
I'm starting to have a weird fondness for obscure JLS corners like this :)
Some thoughts...
>From a purely practical point of view, the "static context" concept seems
to exist to prevent these two bad things:
1. Accessing the 'this' object (via this, super, or any implicit means)
when there is no such object available
2. Accessing the 'this' object (via this, super, or any implicit means)
when that object is uninitialized
(Are there others?) By "bad things" I mean problems with language
semantics, not problems because the compiler happens to be implemented a
certain way.
The example super(new Local().aString()) can never be allowed, because of
#2 above. But note you can *define* Local prior to super() ... you just
can't instantiate it.
In other words the type itself is not a problem. There's no mention of
types at all in the above two reasons.
So one thought experiment is: What would happen if we eliminated all
restrictions on generic type parameters in static contexts? What would
actually go wrong?
Put another way, a static method is just an instance method without a
'this' instance. So neither issue #1 or #2 is possible. So, no problem,
right?
You might be able to write code that looks silly, but would it actually be
nonsensical?
public class Foo<T extends Number> {
public static toFloat(T value) {
return value.floatValue(); // why not?
}
}
I'm sure I'm missing something but what?
-Archie
On Wed, Feb 1, 2023 at 7:05 PM Alex Buckley <alex.buckley at oracle.com> wrote:
> 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
> >>>
> >>>
> >
> >
>
--
Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20230201/271808dc/attachment-0001.htm>
More information about the compiler-dev
mailing list