Skip to content

Commit 0540e5e

Browse files
authored
Merge pull request #9073 from dotty-staging/fix-8778
Fix #8778: Clarify comment in provablyDisjoint
2 parents 3cd91dc + c9c7bad commit 0540e5e

File tree

2 files changed

+50
-15
lines changed

2 files changed

+50
-15
lines changed

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

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,6 +2376,32 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
23762376
def covariantDisjoint(tp1: Type, tp2: Type, tparam: TypeParamInfo): Boolean =
23772377
provablyDisjoint(tp1, tp2) && typeparamCorrespondsToField(tycon1, tparam)
23782378

2379+
// In the invariant case, we used a weaker version of disjointness:
2380+
// we consider types not equal with respect to =:= to be disjoint
2381+
// (under any context). This is fine because it matches the runtime
2382+
// semantics of pattern matching. To implement a pattern such as
2383+
// `case Inv[T] => ...`, one needs a type tag for `T` and the compiler
2384+
// is used at runtime to check it the scrutinee's type is =:= to `T`.
2385+
// Note that this is currently a theoretical concern since we Dotty
2386+
// doesn't have type tags, meaning that users cannot write patterns
2387+
// that do type tests on higher kinded types.
2388+
def invariantDisjoint(tp1: Type, tp2: Type, tparam: TypeParamInfo): Boolean =
2389+
covariantDisjoint(tp1, tp2, tparam) || !isSameType(tp1, tp2) && {
2390+
// We can only trust a "no" from `isSameType` when both
2391+
// `tp1` and `tp2` are fully instantiated.
2392+
def fullyInstantiated(tp: Type): Boolean = new TypeAccumulator[Boolean] {
2393+
override def apply(x: Boolean, t: Type) =
2394+
x && {
2395+
t match {
2396+
case tp: TypeRef if tp.symbol.isAbstractOrParamType => false
2397+
case _: SkolemType | _: TypeVar => false
2398+
case _ => foldOver(x, t)
2399+
}
2400+
}
2401+
}.apply(true, tp)
2402+
fullyInstantiated(tp1) && fullyInstantiated(tp2)
2403+
}
2404+
23792405
args1.lazyZip(args2).lazyZip(tycon1.typeParams).exists {
23802406
(arg1, arg2, tparam) =>
23812407
val v = tparam.paramVarianceSign
@@ -2386,21 +2412,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
23862412
// instantiated to `Any` belongs to both types.
23872413
false
23882414
else
2389-
covariantDisjoint(arg1, arg2, tparam) || !isSameType(arg1, arg2) && {
2390-
// We can only trust a "no" from `isSameType` when both
2391-
// `arg1` and `arg2` are fully instantiated.
2392-
def fullyInstantiated(tp: Type): Boolean = new TypeAccumulator[Boolean] {
2393-
override def apply(x: Boolean, t: Type) =
2394-
x && {
2395-
t match {
2396-
case tp: TypeRef if tp.symbol.isAbstractOrParamType => false
2397-
case _: SkolemType | _: TypeVar => false
2398-
case _ => foldOver(x, t)
2399-
}
2400-
}
2401-
}.apply(true, tp)
2402-
fullyInstantiated(arg1) && fullyInstantiated(arg2)
2403-
}
2415+
invariantDisjoint(arg1, arg2, tparam)
24042416
}
24052417
case (tp1: HKLambda, tp2: HKLambda) =>
24062418
provablyDisjoint(tp1.resType, tp2.resType)

tests/pos/8778.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
trait Inv[T]
2+
3+
object Test {
4+
type M[X] = X match {
5+
case Inv[Int & String] => Int
6+
// the type test for Inv[Int & String] cannot be checked at runtime
7+
// BUT if it could, ...
8+
case Any => String
9+
}
10+
11+
def m[X](x: X): M[X] = x match {
12+
case _: Inv[Int & String] => 1
13+
case _: Any => "s"
14+
}
15+
16+
// Suppose we somehow manage to convince the compiler of that...
17+
val ev: Inv[Nothing] =:= Inv[Int & String] = (implicitly[Int =:= Int]).asInstanceOf
18+
19+
val invN: Inv[Nothing] = new Inv[Nothing] {}
20+
m(invN) // reduces to Int both as a value and as a type
21+
m(ev(invN)) // reduces to String both as a value and as a type
22+
}
23+

0 commit comments

Comments
 (0)