diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 9b60fb63c409..2942b3a92930 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -431,10 +431,17 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case Some(b) => return b case None => + /** `recur` shouldn't normally be used with approximated types, according to its + * documentation. In the specific examples where this function is + * called, it is sound to do so as long as we freeze GADTs. + */ + def recurInFrozenGadt(tp1: Type, tp2: Type) = + inFrozenGadt { recur(tp1, tp2) } + def widenOK = (tp2.widenSingletons eq tp2) && (tp1.widenSingletons ne tp1) - && recur(tp1.widenSingletons, tp2) + && recurInFrozenGadt(tp1.widenSingletons, tp2) def joinOK = tp2.dealiasKeepRefiningAnnots match { case tp2: AppliedType if !tp2.tycon.typeSymbol.isClass => @@ -442,7 +449,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling // type parameter, we will instantiate `C` to `A` and then fail when comparing // with `B[Y]`. To do the right thing, we need to instantiate `C` to the // common superclass of `A` and `B`. - recur(tp1.join, tp2) + recurInFrozenGadt(tp1.join, tp2) case _ => false } @@ -469,7 +476,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling widenOK || joinOK || (tp1.isSoft || constrainRHSVars(tp2)) && recur(tp11, tp2) && recur(tp12, tp2) - || containsAnd(tp1) && recur(tp1.join, tp2) + || containsAnd(tp1) && recurInFrozenGadt(tp1.join, tp2) case tp1: MatchType => val reduced = tp1.reduced if (reduced.exists) recur(reduced, tp2) else thirdTry diff --git a/tests/neg/gadt-lhs-union.scala b/tests/neg/gadt-lhs-union.scala new file mode 100644 index 000000000000..af34524c1f3f --- /dev/null +++ b/tests/neg/gadt-lhs-union.scala @@ -0,0 +1,33 @@ +object A { + sealed trait Expr[+T] + final case class FooExpr() extends Expr[1 | 2] + + object Test { + def foo[T](x: Expr[T]): T = x match { + case x: FooExpr => + 3 // error + } + + val x: 1 | 2 = foo(FooExpr()) + } +} + +object B { + trait C + trait D extends C + trait E extends C + trait F extends C + trait G extends C + + sealed trait Expr[+T] + final case class FooExpr[+S >: (D & E) | F]() extends Expr[S] + + object Test { + def foo[T](x: Expr[T]): T = x match { + case x: FooExpr[(D & E) | F] => + new D with E + } + + val x: (D & E) | F = foo(FooExpr[(D & E) | F]()) + } +}