@@ -1672,39 +1672,51 @@ public boolean isCastable(Type t, Type s, Warner warn) {
16721672 // where
16731673 class DisjointChecker {
16741674 Set <Pair <ClassSymbol , ClassSymbol >> pairsSeen = new HashSet <>();
1675+ /* there are three cases for ts and ss:
1676+ * - one is a class and the other one is an interface (case I)
1677+ * - both are classes (case II)
1678+ * - both are interfaces (case III)
1679+ * all those cases are covered in JLS 23, section: "5.1.6.1 Allowed Narrowing Reference Conversion"
1680+ */
16751681 private boolean areDisjoint (ClassSymbol ts , ClassSymbol ss ) {
16761682 Pair <ClassSymbol , ClassSymbol > newPair = new Pair <>(ts , ss );
16771683 /* if we are seeing the same pair again then there is an issue with the sealed hierarchy
16781684 * bail out, a detailed error will be reported downstream
16791685 */
16801686 if (!pairsSeen .add (newPair ))
16811687 return false ;
1682- if (isSubtype (erasure (ts .type ), erasure (ss .type ))) {
1683- return false ;
1684- }
1685- // if both are classes or both are interfaces, shortcut
1686- if (ts .isInterface () == ss .isInterface () && isSubtype (erasure (ss .type ), erasure (ts .type ))) {
1687- return false ;
1688- }
1689- if (ts .isInterface () && !ss .isInterface ()) {
1690- /* so ts is interface but ss is a class
1691- * an interface is disjoint from a class if the class is disjoint form the interface
1692- */
1693- return areDisjoint (ss , ts );
1694- }
1695- // a final class that is not subtype of ss is disjoint
1696- if (!ts .isInterface () && ts .isFinal ()) {
1697- return true ;
1698- }
1699- // if at least one is sealed
1700- if (ts .isSealed () || ss .isSealed ()) {
1701- // permitted subtypes have to be disjoint with the other symbol
1702- ClassSymbol sealedOne = ts .isSealed () ? ts : ss ;
1703- ClassSymbol other = sealedOne == ts ? ss : ts ;
1704- return sealedOne .getPermittedSubclasses ().stream ().allMatch (type -> areDisjoint ((ClassSymbol )type .tsym , other ));
1688+
1689+ if (ts .isInterface () != ss .isInterface ()) { // case I: one is a class and the other one is an interface
1690+ ClassSymbol isym = ts .isInterface () ? ts : ss ; // isym is the interface and csym the class
1691+ ClassSymbol csym = isym == ts ? ss : ts ;
1692+ if (!isSubtype (erasure (csym .type ), erasure (isym .type ))) {
1693+ if (csym .isFinal ()) {
1694+ return true ;
1695+ } else if (csym .isSealed ()) {
1696+ return areDisjoint (isym , csym .getPermittedSubclasses ());
1697+ } else if (isym .isSealed ()) {
1698+ // if the class is not final and not sealed then it has to be freely extensible
1699+ return areDisjoint (csym , isym .getPermittedSubclasses ());
1700+ }
1701+ } // now both are classes or both are interfaces
1702+ } else if (!ts .isInterface ()) { // case II: both are classes
1703+ return !isSubtype (erasure (ss .type ), erasure (ts .type )) && !isSubtype (erasure (ts .type ), erasure (ss .type ));
1704+ } else { // case III: both are interfaces
1705+ if (!isSubtype (erasure (ts .type ), erasure (ss .type )) && !isSubtype (erasure (ss .type ), erasure (ts .type ))) {
1706+ if (ts .isSealed ()) {
1707+ return areDisjoint (ss , ts .getPermittedSubclasses ());
1708+ } else if (ss .isSealed ()) {
1709+ return areDisjoint (ts , ss .getPermittedSubclasses ());
1710+ }
1711+ }
17051712 }
1713+ // at this point we haven't been able to statically prove that the classes or interfaces are disjoint
17061714 return false ;
17071715 }
1716+
1717+ boolean areDisjoint (ClassSymbol csym , List <Type > permittedSubtypes ) {
1718+ return permittedSubtypes .stream ().allMatch (psubtype -> areDisjoint (csym , (ClassSymbol ) psubtype .tsym ));
1719+ }
17081720 }
17091721
17101722 private TypeRelation isCastable = new TypeRelation () {
0 commit comments