Skip to content

Commit

Permalink
simplify: Drop mapping tparams to tvars
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dwijnand committed Oct 29, 2023
1 parent 38559d7 commit 987f9e0
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 20 deletions.
14 changes: 13 additions & 1 deletion compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
17 changes: 1 addition & 16 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
7 changes: 7 additions & 0 deletions tests/pos/i18261.bis.min/Main_0.scala
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions tests/pos/i18261.bis.min/Test_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Test:
def test: Unit =
summon[Foo[Int]]
summon[Foo[Long]]
7 changes: 7 additions & 0 deletions tests/pos/i18261.bis/DFBits_0.scala
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions tests/pos/i18261.bis/Foo_0.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
trait VAL

type Foo[T, M] = M match
case VAL => T
5 changes: 5 additions & 0 deletions tests/pos/i18261.bis/Test_1.scala
Original file line number Diff line number Diff line change
@@ -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]]

0 comments on commit 987f9e0

Please sign in to comment.