Skip to content

Commit 8ceb324

Browse files
Backport "Fix #21295: Restrict provablyDisjoint with Nothings in invariant type params." to 3.6.2 (#21906)
Backports #21891 to the 3.6.2 branch. PR submitted by the release tooling.
2 parents bbc2636 + 531f29b commit 8ceb324

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

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

+19-2
Original file line numberDiff line numberDiff line change
@@ -3231,6 +3231,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
32313231
end provablyDisjointClasses
32323232

32333233
private def provablyDisjointTypeArgs(cls: ClassSymbol, args1: List[Type], args2: List[Type], pending: util.HashSet[(Type, Type)])(using Context): Boolean =
3234+
// sjrd: I will not be surprised when this causes further issues in the future.
3235+
// This is a compromise to be able to fix #21295 without breaking the world.
3236+
def cannotBeNothing(tp: Type): Boolean = tp match
3237+
case tp: TypeParamRef => cannotBeNothing(tp.paramInfo)
3238+
case _ => !(tp.loBound.stripTypeVar <:< defn.NothingType)
3239+
32343240
// It is possible to conclude that two types applied are disjoint by
32353241
// looking at covariant type parameters if the said type parameters
32363242
// are disjoint and correspond to fields.
@@ -3239,9 +3245,20 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
32393245
def covariantDisjoint(tp1: Type, tp2: Type, tparam: TypeParamInfo): Boolean =
32403246
provablyDisjoint(tp1, tp2, pending) && typeparamCorrespondsToField(cls.appliedRef, tparam)
32413247

3242-
// In the invariant case, direct type parameter disjointness is enough.
3248+
// In the invariant case, we have more ways to prove disjointness:
3249+
// - either the type param corresponds to a field, like in the covariant case, or
3250+
// - one of the two actual args can never be `Nothing`.
3251+
// The latter condition, as tested by `cannotBeNothing`, is ad hoc and was
3252+
// not carefully evaluated to be sound. We have it because we had to
3253+
// reintroduce the former condition to fix #21295, and alone, that broke a
3254+
// lot of existing test cases.
3255+
// Having either one of the two conditions be true is better than not requiring
3256+
// any, which was the status quo before #21295.
32433257
def invariantDisjoint(tp1: Type, tp2: Type, tparam: TypeParamInfo): Boolean =
3244-
provablyDisjoint(tp1, tp2, pending)
3258+
provablyDisjoint(tp1, tp2, pending) && {
3259+
typeparamCorrespondsToField(cls.appliedRef, tparam)
3260+
|| (cannotBeNothing(tp1) || cannotBeNothing(tp2))
3261+
}
32453262

32463263
args1.lazyZip(args2).lazyZip(cls.typeParams).exists {
32473264
(arg1, arg2, tparam) =>

tests/pos/i21295.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
sealed trait Foo[A]
2+
final class Bar extends Foo[Nothing]
3+
4+
object Test:
5+
type Extract[T] = T match
6+
case Foo[_] => Int
7+
8+
val x: Extract[Bar] = 1

0 commit comments

Comments
 (0)