RFR: 8322810: Lambda expression types can't be classes [v7]
Vicente Romero
vromero at openjdk.org
Fri Apr 11 14:03:41 UTC 2025
On Fri, 11 Apr 2025 09:40:21 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:
>> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java line 3346:
>>
>>> 3344: }
>>> 3345: }
>>> 3346: if (bound.tsym != syms.objectType.tsym && (!bound.isInterface() || (bound.tsym.flags() & ANNOTATION) != 0)) {
>>
>> One question: we currently check this condition in `Types.findDescriptorInternal`. That method has this check:
>>
>>
>> if (!origin.isInterface() || (origin.flags() & ANNOTATION) != 0 || origin.isSealed()) {
>> //t must be an interface
>> throw failure("not.a.functional.intf", origin);
>> }
>>
>>
>> Where this goes wrong (I think) is that if `origin` is an intersection type, this check fails, because it doesn't recursively check all the components of the intersection. But that would be easy to fix -- e.g. by having a special guard for intersection types.
>>
>> Digging deeper, the real issue seems to be in how we compute a notional type for the intersection type. The JLS says this (4.9):
>>
>>> If Ck is Object, a notional interface is induced; otherwise, a notional class is induced with direct superclass type Ck. This class or interface has direct superinterface types T1', ..., Tn' and is declared in the package in which the intersection type appears.
>>
>> This means that the notional type we create in `Attr.targetChecker` should NOT blindly do this:
>>
>>
>> notionalIntf.tsym.flags_field |= INTERFACE;
>>
>>
>> Instead, it should set the `INTERFACE` flag only if the class component of the intersection is `Object` (as the JLS says).
>>
>> Once we do this, I'm confident that the existing checks in `Types` will rule out the bad intersection in this issue: because now it would be a class, not an interface -- so the existing check will kick in, and declare it not a suitable functional interface.
>>
>> What do you think?
>
>> Instead, it should set the `INTERFACE` flag only if the class component of the intersection is `Object` (as the JLS says).
>
> Note: in principle one might claim that it should be `types.makeIntersectionType` to do this -- but there are complications, in that the method that creates the intersection type is not allowed to to call `flags()` as this method is also called from `ClassReader` at a very delicate time. There's a boolean `allInterfaces` flag that is used there, which we could use to set the optional `INTERFACE` flag on the intersection, but this relies on the assumption that `allInterfaces` is only set if the first bound is an interface (e.g. this tells the code if an extra `Object` supertype has to be injected into the intersection) -- which is not exactly what you want.
>
> What we might do is to say that if `allInterfaces` is set, OR if the `firstExplicitBound` is `syms.objectType` then we treat this as `INTERFACE`. But if this is too difficult to do in general, then it's ok to keep the `Attr` code manually setting the `INTERFACE` flag depending on the bound (as there's no completion to worry about there).
we could do that but I would prefer failing asap, as in `makeNotionalInterface` instead of waiting on `Types.findDescriptorInternal` to do the right thing, semantically we will get to the same point, what you are proposing seems to me like another way to get there but honestly I don't see the gain
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/24548#discussion_r2039620789
More information about the compiler-dev
mailing list