From 987f9e00688b3711eb58693adc9a07267551d459 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 29 Oct 2023 12:28:42 +0000 Subject: [PATCH] simplify: Drop mapping tparams to tvars Mapping type params back to type vars is dangerous for match types because it can lead to one use-site's type var being cached as the reduction of the match type in a match alias defintion. It looks like mapping type parameters to type vars is only important when getting the instance type of a type parameter, because the bounds could make references to other parameters, including as type arguments. So, as long as we map then, we can drop this mapping from general simplification. There is, however, something going on with capture checking, which I documented, but perhaps it's due to some underlying bug. The situation occurs because boxed A *: T does seem to match case x *: _. So I kept the fully-defined forcing and normalization case from simplify. --- .../tools/dotc/core/ConstraintHandling.scala | 14 +++++++++++++- .../src/dotty/tools/dotc/core/TypeOps.scala | 3 --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 17 +---------------- tests/pos/i18261.bis.min/Main_0.scala | 7 +++++++ tests/pos/i18261.bis.min/Test_1.scala | 4 ++++ tests/pos/i18261.bis/DFBits_0.scala | 7 +++++++ tests/pos/i18261.bis/Foo_0.scala | 4 ++++ tests/pos/i18261.bis/Test_1.scala | 5 +++++ 8 files changed, 41 insertions(+), 20 deletions(-) create mode 100644 tests/pos/i18261.bis.min/Main_0.scala create mode 100644 tests/pos/i18261.bis.min/Test_1.scala create mode 100644 tests/pos/i18261.bis/DFBits_0.scala create mode 100644 tests/pos/i18261.bis/Foo_0.scala create mode 100644 tests/pos/i18261.bis/Test_1.scala 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]]