LUB computation involving NULL

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Oct 12 10:29:56 UTC 2020


On 10/10/2020 15:43, B. Blaser wrote:
> Hi,
>
> This one is interesting.
>
> Unless I read JLS15 too quickly, neither §15.28.1 mandates explicitly
> to skip null types before computing the LUB:
>
> "Otherwise, boxing conversion (§5.1.7) is applied to each result
> expression that has a primitive type, after which the type of the
> switch expression is the result of applying capture conversion
> (§5.1.10) to the least upper bound (§4.10.4) of the types of the
> result expressions."
>
> nor §4.10.4 explicitly specifies how to handle them.
>
> So, ignoring null types not to interfere with this computation seems
> implicitly deduced from the fact that they can match any reference
> type and Jan's pull request [1] looks therefore reasonable.
>
> However, compilation currently succeeds with the first arm being null
> (as initially reported), suggesting probably to fix this more directly
> in Types::lub as here under (langtools:tier1 being OK on jdk14u).

For method type inference, we already filter types before applying lub. 
I think we should do the same here. Of course we could also fix deeper, 
but I think it's better to start simple.

 From a spec perspective, I think the filtering is implicit in 18.2.

```
A constraint formula of the form ‹S <: T› is reduced as follows:

     ...

     Otherwise, if S is the null type, the constraint reduces to true.
```

In other words, if the actual type (S) has the null type, we never even 
generate a bound for lub to contemplate - that subtyping constraint is 
instead implicitly satisfied. Which seems to suggest a pre-filtering 
step (which is what the compiler does).

Maurizio

>
> What do you think?
>
> Thanks,
> Bernard
>
> [1] https://git.openjdk.java.net/jdk/pull/583
>
> diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
> b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
> @@ -3970,33 +3970,29 @@
>
>           case CLASS_BOUND:
>               // calculate lub(A, B)
> -            int startIdx = 0;
> -            for (int i = 0; i < ts.length ; i++) {
> -                Type t = ts[i];
> -                if (t.hasTag(CLASS) || t.hasTag(TYPEVAR)) {
> -                    break;
> -                } else {
> -                    startIdx++;
> -                }
> -            }
> -            Assert.check(startIdx < ts.length);
>               //step 1 - compute erased candidate set (EC)
> -            List<Type> cl = erasedSupertypes(ts[startIdx]);
> -            for (int i = startIdx + 1 ; i < ts.length ; i++) {
> -                Type t = ts[i];
> +            List<Type> cl = null;
> +            for (Type t: ts) {
>                   if (t.hasTag(CLASS) || t.hasTag(TYPEVAR))
> -                    cl = intersect(cl, erasedSupertypes(t));
> -            }
> +                    cl = cl == null ? erasedSupertypes(t) :
> intersect(cl, erasedSupertypes(t));
> +            }
> +            Assert.checkNonNull(cl);
> +
>               //step 2 - compute minimal erased candidate set (MEC)
>               List<Type> mec = closureMin(cl);
>               //step 3 - for each element G in MEC, compute lci(Inv(G))
>               List<Type> candidates = List.nil();
>               for (Type erasedSupertype : mec) {
> -                List<Type> lci = List.of(asSuper(ts[startIdx],
> erasedSupertype.tsym));
> -                for (int i = startIdx + 1 ; i < ts.length ; i++) {
> -                    Type superType = asSuper(ts[i], erasedSupertype.tsym);
> -                    lci = intersect(lci, superType != null ?
> List.of(superType) : List.nil());
> +                List<Type> lci = null;
> +                for (Type t: ts) {
> +                    if (t.hasTag(CLASS) || t.hasTag(TYPEVAR)) {
> +                        Type superType = asSuper(t, erasedSupertype.tsym);
> +                        List<Type> stl = superType != null ?
> List.of(superType) : List.nil();
> +                        lci = lci == null ? stl : intersect(lci, stl);
> +                    }
>                   }
> +                Assert.checkNonNull(lci);
> +
>                   candidates = candidates.appendList(lci);
>               }
>               //step 4 - let MEC be { G1, G2 ... Gn }, then we have that
>
> On Fri, 9 Oct 2020 at 11:46, Maurizio Cimadamore
> <maurizio.cimadamore at oracle.com> wrote:
>> Filed this:
>>
>> https://bugs.openjdk.java.net/browse/JDK-8254286
>>
>> Maurizio
>>
>> On 09/10/2020 10:42, Maurizio Cimadamore wrote:
>>
>> Hi Anna,
>> yes, this looks like a compiler bug.
>>
>> Behavior should be the same as:
>>
>> <Z> Z pick(Z z1, Z z2, Z z3) { .... }
>>
>> pick(i1, null, i2)
>>
>> --> lub(I1, I2) --> I
>>
>> Seems like the `null` is tripping javac up
>>
>> Maurizio
>>
>> On 30/09/2020 10:50, Anna Kozlova wrote:
>>
>> Hi all,
>>
>> The following code doesn't compile (infers i_ to Object)
>>
>> interface I {
>>      void m();
>> }
>> interface I1 extends I {}
>> interface I2 extends I {}
>>
>> static void n(I1 i1, I2 i2, int s) {
>>      var i_ = switch (s) {
>>          case 1 -> i1;
>>          case 2 -> null;
>>          default -> i2;
>>      };
>>      if (i_ != null) {
>>          i_.m(); //cannot find symbol m()
>>      }
>> }
>>
>>
>> If you permute "case 1" and "case 2" switch rules, the code starts to compile (infers i_ to I).
>>
>> Looks like a bug?
>>
>> Thanks,
>> Anna


More information about the compiler-dev mailing list