Skip to content

Commit 4e740c5

Browse files
Merge pull request #12786 from dotty-staging/fix-regression-in-provably-disjoint
Fix regression in provablyDisjoint
2 parents fe60c68 + 4d6d72d commit 4e740c5

File tree

2 files changed

+48
-23
lines changed

2 files changed

+48
-23
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,9 +2497,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
24972497
def provablyDisjoint(tp1: Type, tp2: Type)(using Context): Boolean = trace(i"provable disjoint $tp1, $tp2", matchTypes) {
24982498
// println(s"provablyDisjoint(${tp1.show}, ${tp2.show})")
24992499

2500-
def isEnumValueOrModule(ref: TermRef): Boolean =
2500+
def isEnumValue(ref: TermRef): Boolean =
25012501
val sym = ref.termSymbol
2502-
sym.isAllOf(EnumCase, butNot=JavaDefined) || sym.is(Module)
2502+
sym.isAllOf(EnumCase, butNot=JavaDefined)
2503+
2504+
def isEnumValueOrModule(ref: TermRef): Boolean =
2505+
isEnumValue(ref) || ref.termSymbol.is(Module) || (ref.info match {
2506+
case tp: TermRef => isEnumValueOrModule(tp)
2507+
case _ => false
2508+
})
25032509

25042510
/** Can we enumerate all instantiations of this type? */
25052511
def isClosedSum(tp: Symbol): Boolean =
@@ -2512,8 +2518,21 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
25122518
def decompose(sym: Symbol, tp: Type): List[Type] =
25132519
sym.children.map(x => refineUsingParent(tp, x)).filter(_.exists)
25142520

2521+
def fullyInstantiated(tp: Type): Boolean = new TypeAccumulator[Boolean] {
2522+
override def apply(x: Boolean, t: Type) =
2523+
x && {
2524+
t match {
2525+
case tp: TypeRef if tp.symbol.isAbstractOrParamType => false
2526+
case _: SkolemType | _: TypeVar => false
2527+
case _ => foldOver(x, t)
2528+
}
2529+
}
2530+
}.apply(true, tp)
2531+
25152532
(tp1.dealias, tp2.dealias) match {
2516-
case (tp1: TypeRef, tp2: TypeRef) if tp1.symbol == defn.SingletonClass || tp2.symbol == defn.SingletonClass =>
2533+
case (tp1: TypeRef, _) if tp1.symbol == defn.SingletonClass =>
2534+
false
2535+
case (_, tp2: TypeRef) if tp2.symbol == defn.SingletonClass =>
25172536
false
25182537
case (tp1: ConstantType, tp2: ConstantType) =>
25192538
tp1 != tp2
@@ -2557,21 +2576,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
25572576
// doesn't have type tags, meaning that users cannot write patterns
25582577
// that do type tests on higher kinded types.
25592578
def invariantDisjoint(tp1: Type, tp2: Type, tparam: TypeParamInfo): Boolean =
2560-
covariantDisjoint(tp1, tp2, tparam) || !isSameType(tp1, tp2) && {
2561-
// We can only trust a "no" from `isSameType` when both
2562-
// `tp1` and `tp2` are fully instantiated.
2563-
def fullyInstantiated(tp: Type): Boolean = new TypeAccumulator[Boolean] {
2564-
override def apply(x: Boolean, t: Type) =
2565-
x && {
2566-
t match {
2567-
case tp: TypeRef if tp.symbol.isAbstractOrParamType => false
2568-
case _: SkolemType | _: TypeVar => false
2569-
case _ => foldOver(x, t)
2570-
}
2571-
}
2572-
}.apply(true, tp)
2573-
fullyInstantiated(tp1) && fullyInstantiated(tp2)
2574-
}
2579+
covariantDisjoint(tp1, tp2, tparam) ||
2580+
!isSameType(tp1, tp2) &&
2581+
fullyInstantiated(tp1) && // We can only trust a "no" from `isSameType` when
2582+
fullyInstantiated(tp2) // both `tp1` and `tp2` are fully instantiated.
25752583

25762584
args1.lazyZip(args2).lazyZip(tycon1.typeParams).exists {
25772585
(arg1, arg2, tparam) =>
@@ -2608,11 +2616,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
26082616
provablyDisjoint(tp1, gadtBounds(tp2.symbol).hi) || provablyDisjoint(tp1, tp2.superType)
26092617
case (tp1: TermRef, tp2: TermRef) if isEnumValueOrModule(tp1) && isEnumValueOrModule(tp2) =>
26102618
tp1.termSymbol != tp2.termSymbol
2611-
case (tp1: TermRef, tp2: TypeRef) if isEnumValueOrModule(tp1) && !tp1.classSymbols.exists(_.derivesFrom(tp2.classSymbol)) =>
2612-
// Note: enum values may have multiple parents
2613-
true
2614-
case (tp1: TypeRef, tp2: TermRef) if isEnumValueOrModule(tp2) && !tp2.classSymbols.exists(_.derivesFrom(tp1.classSymbol)) =>
2615-
true
2619+
case (tp1: TermRef, tp2: TypeRef) if isEnumValue(tp1) =>
2620+
fullyInstantiated(tp2) && !tp1.classSymbols.exists(_.derivesFrom(tp2.symbol))
2621+
case (tp1: TypeRef, tp2: TermRef) if isEnumValue(tp2) =>
2622+
fullyInstantiated(tp1) && !tp2.classSymbols.exists(_.derivesFrom(tp1.symbol))
26162623
case (tp1: Type, tp2: Type) if defn.isTupleType(tp1) =>
26172624
provablyDisjoint(tp1.toNestedPairs, tp2)
26182625
case (tp1: Type, tp2: Type) if defn.isTupleType(tp2) =>

tests/neg/12549.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
enum Bool {
2+
case True
3+
case False
4+
}
5+
6+
import Bool.*
7+
8+
type Not[B <: Bool] = B match {
9+
case True.type => False.type
10+
case False.type => True.type
11+
case _ => "unreachable"
12+
}
13+
14+
def foo[B <: Bool & Singleton]: Unit = {
15+
implicitly[Not[B] =:= "unreachable"] // error
16+
17+
()
18+
}

0 commit comments

Comments
 (0)