8343306: javac is failing to determine if a class and a sealed interface are disjoint
Reviewed-by: jlahoda, mcimadamore
This commit is contained in:
parent
0c281acfb4
commit
96eed7fa6c
@ -1672,6 +1672,12 @@ public class Types {
|
||||
// where
|
||||
class DisjointChecker {
|
||||
Set<Pair<ClassSymbol, ClassSymbol>> pairsSeen = new HashSet<>();
|
||||
/* there are three cases for ts and ss:
|
||||
* - one is a class and the other one is an interface (case I)
|
||||
* - both are classes (case II)
|
||||
* - both are interfaces (case III)
|
||||
* all those cases are covered in JLS 23, section: "5.1.6.1 Allowed Narrowing Reference Conversion"
|
||||
*/
|
||||
private boolean areDisjoint(ClassSymbol ts, ClassSymbol ss) {
|
||||
Pair<ClassSymbol, ClassSymbol> newPair = new Pair<>(ts, ss);
|
||||
/* if we are seeing the same pair again then there is an issue with the sealed hierarchy
|
||||
@ -1679,32 +1685,38 @@ public class Types {
|
||||
*/
|
||||
if (!pairsSeen.add(newPair))
|
||||
return false;
|
||||
if (isSubtype(erasure(ts.type), erasure(ss.type))) {
|
||||
return false;
|
||||
}
|
||||
// if both are classes or both are interfaces, shortcut
|
||||
if (ts.isInterface() == ss.isInterface() && isSubtype(erasure(ss.type), erasure(ts.type))) {
|
||||
return false;
|
||||
}
|
||||
if (ts.isInterface() && !ss.isInterface()) {
|
||||
/* so ts is interface but ss is a class
|
||||
* an interface is disjoint from a class if the class is disjoint form the interface
|
||||
*/
|
||||
return areDisjoint(ss, ts);
|
||||
}
|
||||
// a final class that is not subtype of ss is disjoint
|
||||
if (!ts.isInterface() && ts.isFinal()) {
|
||||
return true;
|
||||
}
|
||||
// if at least one is sealed
|
||||
if (ts.isSealed() || ss.isSealed()) {
|
||||
// permitted subtypes have to be disjoint with the other symbol
|
||||
ClassSymbol sealedOne = ts.isSealed() ? ts : ss;
|
||||
ClassSymbol other = sealedOne == ts ? ss : ts;
|
||||
return sealedOne.getPermittedSubclasses().stream().allMatch(type -> areDisjoint((ClassSymbol)type.tsym, other));
|
||||
|
||||
if (ts.isInterface() != ss.isInterface()) { // case I: one is a class and the other one is an interface
|
||||
ClassSymbol isym = ts.isInterface() ? ts : ss; // isym is the interface and csym the class
|
||||
ClassSymbol csym = isym == ts ? ss : ts;
|
||||
if (!isSubtype(erasure(csym.type), erasure(isym.type))) {
|
||||
if (csym.isFinal()) {
|
||||
return true;
|
||||
} else if (csym.isSealed()) {
|
||||
return areDisjoint(isym, csym.getPermittedSubclasses());
|
||||
} else if (isym.isSealed()) {
|
||||
// if the class is not final and not sealed then it has to be freely extensible
|
||||
return areDisjoint(csym, isym.getPermittedSubclasses());
|
||||
}
|
||||
} // now both are classes or both are interfaces
|
||||
} else if (!ts.isInterface()) { // case II: both are classes
|
||||
return !isSubtype(erasure(ss.type), erasure(ts.type)) && !isSubtype(erasure(ts.type), erasure(ss.type));
|
||||
} else { // case III: both are interfaces
|
||||
if (!isSubtype(erasure(ts.type), erasure(ss.type)) && !isSubtype(erasure(ss.type), erasure(ts.type))) {
|
||||
if (ts.isSealed()) {
|
||||
return areDisjoint(ss, ts.getPermittedSubclasses());
|
||||
} else if (ss.isSealed()) {
|
||||
return areDisjoint(ts, ss.getPermittedSubclasses());
|
||||
}
|
||||
}
|
||||
}
|
||||
// at this point we haven't been able to statically prove that the classes or interfaces are disjoint
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean areDisjoint(ClassSymbol csym, List<Type> permittedSubtypes) {
|
||||
return permittedSubtypes.stream().allMatch(psubtype -> areDisjoint(csym, (ClassSymbol) psubtype.tsym));
|
||||
}
|
||||
}
|
||||
|
||||
private TypeRelation isCastable = new TypeRelation() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -1178,6 +1178,39 @@ class SealedCompilationTests extends CompilationTestCase {
|
||||
I[] i = (I[]) c;
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
class Test {
|
||||
sealed interface I permits C1 {}
|
||||
non-sealed class C1 implements I {}
|
||||
class C2 extends C1 {}
|
||||
class C3 {}
|
||||
I m(int s, C3 c3) {
|
||||
I i = (I)c3;
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
class Test {
|
||||
sealed interface I permits C1 {}
|
||||
non-sealed class C1 implements I {}
|
||||
class C2 extends C1 {}
|
||||
class C3 {}
|
||||
I m(int s, C3 c3) {
|
||||
I i = (C1)c3;
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
class Test {
|
||||
sealed interface I permits C1 {}
|
||||
non-sealed class C1 implements I {}
|
||||
class C2 extends C1 {}
|
||||
class C3 {}
|
||||
I m(int s, C3 c3) {
|
||||
I i = (C2)c3;
|
||||
}
|
||||
}
|
||||
"""
|
||||
)) {
|
||||
assertFail("compiler.err.prob.found.req", s);
|
||||
|
Loading…
Reference in New Issue
Block a user