diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 8e9b95a0c572..3ed498d30dc1 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -257,6 +257,13 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase override def transform(tree: Tree)(using Context): Tree = try tree match { + case CaseDef(pat, _, _) => + val gadtCtx = + pat.removeAttachment(typer.Typer.InferredGadtConstraints) match + case Some(gadt) => ctx.fresh.setGadt(gadt) + case None => + ctx + super.transform(tree)(using gadtCtx) case tree: Ident if !tree.isType => if tree.symbol.is(Inline) && !Inliner.inInlineMethod then ctx.compilationUnit.needsInlining = true @@ -438,4 +445,4 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase private def normalizeErasedRhs(rhs: Tree, sym: Symbol)(using Context) = if (sym.isEffectivelyErased) dropInlines.transform(rhs) else rhs } -} \ No newline at end of file +} diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 811646b8153d..214872788942 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -66,6 +66,9 @@ object Typer { if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) assert(tree.span.exists, i"position not set for $tree # ${tree.uniqueId} of ${tree.getClass} in ${tree.source}") + /** An attachment for GADT constraints that were inferred for a pattern. */ + val InferredGadtConstraints = new Property.StickyKey[core.GadtConstraint] + /** A context property that indicates the owner of any expressions to be typed in the context * if that owner is different from the context's owner. Typically, a context with a class * as owner would have a local dummy as ExprOwner value. @@ -1598,6 +1601,13 @@ class Typer extends Namer val pat1 = indexPattern(tree).transform(pat) val guard1 = typedExpr(tree.guard, defn.BooleanType) var body1 = ensureNoLocalRefs(typedExpr(tree.body, pt1), pt1, ctx.scope.toList) + if ctx.gadt.nonEmpty then + // Store GADT constraint to later retrieve it (in PostTyper, for now). + // GADT constraints are necessary to correctly check bounds of type app, + // see tests/pos/i12226 and issue #12226. It might be possible that this + // will end up taking too much memory. If it does, we should just limit + // how much GADT constraints we infer - it's always sound to infer less. + pat1.putAttachment(InferredGadtConstraints, ctx.gadt) if (pt1.isValueType) // insert a cast if body does not conform to expected type if we disregard gadt bounds body1 = body1.ensureConforms(pt1)(using originalCtx) assignType(cpy.CaseDef(tree)(pat1, guard1, body1), pat1, body1) diff --git a/tests/pos/i12226.scala b/tests/pos/i12226.scala new file mode 100644 index 000000000000..b22e8b4cd428 --- /dev/null +++ b/tests/pos/i12226.scala @@ -0,0 +1,16 @@ +object Test extends App { + sealed trait P[T] + case class C1[T <: String](c1: T) extends P[T] + case class C2[T](c2: T) extends P[T] + + def test[T](p: P[T], t: T): Unit = p match { + case C1(_) => + // T <: String + val t : T = ??? + val s : String = t + def test = new C1[T](t) + println(1) + + case C2(_) => println(2) + } +}