diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index bbe46c344890..4e2095ce1b65 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -710,7 +710,19 @@ trait ConstraintHandling { * than `maxLevel`. */ def instanceType(param: TypeParamRef, fromBelow: Boolean, widenUnions: Boolean, maxLevel: Int)(using Context): Type = { - val approx = approximation(param, fromBelow, maxLevel).simplified + val addTypeVars = new TypeMap with cc.CaptureSet.IdempotentCaptRefMap: + val constraint = ctx.typerState.constraint + def apply(tp: Type): Type = tp match + case tp: TypeParamRef => constraint.typeVarOfParam(tp).orElse(tp) + case tp: AppliedType if tp.isMatchAlias => + // this case appears necessary to compile + // tests/pos-custom-args/captures/matchtypes.scala + // under -language:experimental.captureChecking + typer.Inferencing.isFullyDefined(tp, typer.ForceDegree.all) + val normed = tp.tryNormalize + if normed.exists then apply(normed) else mapOver(tp) + case _ => mapOver(tp) + val approx = addTypeVars(approximation(param, fromBelow, maxLevel)) if fromBelow then val widened = widenInferred(approx, param, widenUnions) // Widening can add extra constraints, in particular the widened type might diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 5f63f4871c5f..6801ae671b1c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -149,9 +149,6 @@ object TypeOps: case _ => val normed = tp.tryNormalize if normed.exists then simplify(normed, theMap) else tp.map(simplify(_, theMap)) - case tp: TypeParamRef => - val tvar = ctx.typerState.constraint.typeVarOfParam(tp) - if tvar.exists then tvar else tp case _: ThisType | _: BoundType => tp case tp: AliasingBounds => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 7c0f1ac5518b..299e2eb4f168 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1464,22 +1464,7 @@ class TreeUnpickler(reader: TastyReader, val fst = readTpt() val (bound, scrut) = if (nextUnsharedTag == CASEDEF) (EmptyTree, fst) else (fst, readTpt()) - val tpt = MatchTypeTree(bound, scrut, readCases(end)) - // If a match type definition can reduce (e.g. Id in i18261.min) - // then it's important to trigger that reduction - // before a TypeVar is added to the constraint, - // associated to the match type's type parameter. - // Otherwise, if the reduction is triggered with that constraint, - // the reduction will be simplified, - // at which point the TypeVar will replace the type parameter - // and then that TypeVar will be cached - // as the reduction of the match type definition! - // - // We also override the type, as that's what Typer does. - // The difference here is that a match type that reduces to a non-match type - // makes the TypeRef for that definition will have a TypeAlias info instead of a MatchAlias. - tpt.overwriteType(tpt.tpe.normalized) - tpt + MatchTypeTree(bound, scrut, readCases(end)) case TYPEBOUNDStpt => val lo = readTpt() val hi = if currentAddr == end then lo else readTpt() diff --git a/tests/pos/i18261.bis.min/Main_0.scala b/tests/pos/i18261.bis.min/Main_0.scala new file mode 100644 index 000000000000..bf41c376a275 --- /dev/null +++ b/tests/pos/i18261.bis.min/Main_0.scala @@ -0,0 +1,7 @@ +type MT[T, M] = M match { case VAL => T } + +class Foo[A] +object Foo: + given inst[X, Y <: MT[X, VAL]]: Foo[Y] = new Foo[Y] + +trait VAL diff --git a/tests/pos/i18261.bis.min/Test_1.scala b/tests/pos/i18261.bis.min/Test_1.scala new file mode 100644 index 000000000000..24536da598d3 --- /dev/null +++ b/tests/pos/i18261.bis.min/Test_1.scala @@ -0,0 +1,4 @@ +class Test: + def test: Unit = + summon[Foo[Int]] + summon[Foo[Long]] diff --git a/tests/pos/i18261.bis/DFBits_0.scala b/tests/pos/i18261.bis/DFBits_0.scala new file mode 100644 index 000000000000..0313faf04523 --- /dev/null +++ b/tests/pos/i18261.bis/DFBits_0.scala @@ -0,0 +1,7 @@ +trait DFBits[W <: Int] + +trait Candidate[R]: + type OutW <: Int +object Candidate: + given [W <: Int, R <: Foo[DFBits[W], VAL]]: Candidate[R] with + type OutW = W diff --git a/tests/pos/i18261.bis/Foo_0.scala b/tests/pos/i18261.bis/Foo_0.scala new file mode 100644 index 000000000000..47dd67b0984d --- /dev/null +++ b/tests/pos/i18261.bis/Foo_0.scala @@ -0,0 +1,4 @@ +trait VAL + +type Foo[T, M] = M match + case VAL => T diff --git a/tests/pos/i18261.bis/Test_1.scala b/tests/pos/i18261.bis/Test_1.scala new file mode 100644 index 000000000000..3b91847e3326 --- /dev/null +++ b/tests/pos/i18261.bis/Test_1.scala @@ -0,0 +1,5 @@ +def baz[L](lhs: L)(using icL: Candidate[L]): DFBits[Int] = ??? +object Test: + val x: DFBits[8] = ??? + val z: DFBits[Int] = baz(x) + summon[Candidate[z.type]]