Skip to content

Commit 15d6823

Browse files
committed
Fix #2745: Dealias before either
"either" cuts the search space for subtyping. Before we do this, we should delias the other side to see whether there's something else we can do instead. In the case A <:< B | C we can also safely widen `A` because singleton types are not allowed in disjunctions.
1 parent 1ee888a commit 15d6823

File tree

2 files changed

+27
-0
lines changed

2 files changed

+27
-0
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
473473
}
474474
compareTypeLambda
475475
case OrType(tp21, tp22) =>
476+
val tp1a = tp1.widenDealias
477+
if (tp1a ne tp1)
478+
// Follow the alias; this might avoid truncating the search space in the either below
479+
// Note that it's safe to widen here because singleton types cannot be part of `|`.
480+
return isSubType(tp1a, tp2)
481+
476482
// Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22)
477483
// and analogously for T1 <: T21 | (T221 & T222)
478484
// `|' types to the right of <: are problematic, because
@@ -591,6 +597,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
591597
}
592598
compareHKLambda
593599
case AndType(tp11, tp12) =>
600+
val tp2a = tp2.dealias
601+
if (tp2a ne tp2) // Follow the alias; this might avoid truncating the search space in the either below
602+
return isSubType(tp1, tp2a)
603+
594604
// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
595605
// and analogously for T11 & (T121 | T122) & T12 <: T2
596606
// `&' types to the left of <: are problematic, because

tests/pos/i2745.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
object Test {
2+
type Result[A] = Errors | A
3+
final case class Errors(msgs: Seq[String])
4+
5+
implicit class RichRes[A](val res: Result[A]) extends AnyVal {
6+
def map[B](f: A => B): Result[B] = res match {
7+
case xs: Errors => xs
8+
case a: A => f(a)
9+
}
10+
}
11+
12+
var foo: Result[String] = ???
13+
def f(str: String): Int = ???
14+
15+
foo.map(f(_)) // error
16+
17+
}

0 commit comments

Comments
 (0)