diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index e8e59252ed7d..fd318e59bf9d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -18,7 +18,7 @@ import config.Feature import typer.Applications._ import typer.ProtoTypes._ import typer.ForceDegree -import typer.Inferencing.isFullyDefined +import typer.Inferencing._ import typer.IfBottom import scala.annotation.internal.sharable @@ -618,8 +618,9 @@ object TypeOps: val childTp = if (child.isTerm) child.termRef else child.typeRef - instantiateToSubType(childTp, parent)(using ctx.fresh.setNewTyperState()) - .dealias + inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) { + instantiateToSubType(childTp, parent).dealias + } } /** Instantiate type `tp1` to be a subtype of `tp2` @@ -665,10 +666,11 @@ object TypeOps: def minTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false) def maxTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = true) - // Fix subtype checking for child instantiation, - // such that `Foo(Test.this.foo) <:< Foo(Foo.this)` + // Prefix inference, replace `p.C.this.Child` with `X.Child` where `X <: p.C` + // Note: we need to strip ThisType in `p` recursively. + // // See tests/patmat/i3938.scala - class RemoveThisMap extends TypeMap { + class InferPrefixMap extends TypeMap { var prefixTVar: Type = null def apply(tp: Type): Type = tp match { case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner => @@ -685,22 +687,14 @@ object TypeOps: } } - // replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala - def instUndetMap(implicit ctx: Context) = new TypeMap { - def apply(t: Type): Type = t match { - case tvar: TypeVar if !tvar.isInstantiated => WildcardType(tvar.origin.underlying.bounds) - case _ => mapOver(t) - } - } - - val removeThisType = new RemoveThisMap + val inferThisMap = new InferPrefixMap val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } - val protoTp1 = removeThisType.apply(tp1).appliedTo(tvars) + val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars) val force = new ForceDegree.Value( tvar => !(ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) || - (tvar `eq` removeThisType.prefixTVar), + (tvar `eq` inferThisMap.prefixTVar), // always instantiate prefix IfBottom.flip ) @@ -715,14 +709,15 @@ object TypeOps: } } - if (protoTp1 <:< tp2) - if (isFullyDefined(protoTp1, force)) protoTp1 - else instUndetMap.apply(protoTp1) + if (protoTp1 <:< tp2) { + maximizeType(protoTp1, NoSpan, fromScala2x = false) + wildApprox(protoTp1) + } else { val protoTp2 = maxTypeMap.apply(tp2) if (protoTp1 <:< protoTp2 || parentQualify) if (isFullyDefined(AndType(protoTp1, protoTp2), force)) protoTp1 - else instUndetMap.apply(protoTp1) + else wildApprox(protoTp1) else { typr.println(s"$protoTp1 <:< $protoTp2 = false") NoType diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 70026a6b60d9..49ad01275685 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -55,22 +55,20 @@ object TypeTestsCasts { */ def checkable(X: Type, P: Type, span: Span)(using Context): Boolean = { def isAbstract(P: Type) = !P.dealias.typeSymbol.isClass - def isPatternTypeSymbol(sym: Symbol) = !sym.isClass && sym.is(Case) def replaceP(tp: Type)(implicit ctx: Context) = new TypeMap { def apply(tp: Type) = tp match { - case tref: TypeRef - if isPatternTypeSymbol(tref.typeSymbol) => WildcardType - case AnnotatedType(_, annot) - if annot.symbol == defn.UncheckedAnnot => WildcardType + case tref: TypeRef if tref.typeSymbol.isPatternBound => + WildcardType + case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedAnnot => + WildcardType case _ => mapOver(tp) } }.apply(tp) def replaceX(tp: Type)(implicit ctx: Context) = new TypeMap { def apply(tp: Type) = tp match { - case tref: TypeRef - if isPatternTypeSymbol(tref.typeSymbol) => + case tref: TypeRef if tref.typeSymbol.isPatternBound => if (variance == 1) tref.info.hiBound else if (variance == -1) tref.info.loBound else OrType(defn.AnyType, defn.NothingType) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 9d20725a2590..98ba98b4f313 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -464,7 +464,6 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { * } */ private def erase(tp: Type, inArray: Boolean = false): Type = trace(i"$tp erased to", debug) { - def isPatternTypeSymbol(sym: Symbol) = !sym.isClass && sym.is(Case) tp match { case tp @ AppliedType(tycon, args) => @@ -476,7 +475,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { AndType(erase(tp1, inArray), erase(tp2, inArray)) case tp @ RefinedType(parent, _, _) => erase(parent) - case tref: TypeRef if isPatternTypeSymbol(tref.typeSymbol) => + case tref: TypeRef if tref.typeSymbol.isPatternBound => if (inArray) tref.underlying else WildcardType case _ => tp } diff --git a/tests/patmat/i3938.check b/tests/patmat/i3938.check new file mode 100644 index 000000000000..5aaefe2263e1 --- /dev/null +++ b/tests/patmat/i3938.check @@ -0,0 +1 @@ +34: Pattern Match Exhaustivity: bar.C() diff --git a/tests/patmat/i3938.scala b/tests/patmat/i3938.scala index d5c40b6490e5..0a6395b0a727 100644 --- a/tests/patmat/i3938.scala +++ b/tests/patmat/i3938.scala @@ -23,10 +23,15 @@ class Test { val foo = new Foo import foo.bar._ - def test(a: A) = { + def h(a: A) = { a match { case B() => 1 case _ => 2 // unreachable code } } + + def f(a: A) = + a match { + case B() => 1 + } } diff --git a/tests/patmat/i9067.scala b/tests/patmat/i9067.scala new file mode 100644 index 000000000000..3ef8271e16df --- /dev/null +++ b/tests/patmat/i9067.scala @@ -0,0 +1,4 @@ +sealed trait Base[A, +B] +case class Foo[A, +B](f: A => B) extends Base[A, B] + +def bar(x: Base[Any, Any]): Unit = x match { case Foo(_) => }