Skip to content

Commit 73abb32

Browse files
committed
Fix renaming of constraints
When we replace a type lambda in a constraint by another, the type variables associated with the old lambda changed their origin to point to the new lambda. But that means that the same type variables in predecessor typestates do not point to their associated lambda anymore. To fix this, we have to do the same renaming of type lambdas also in predecessor typestates.
1 parent ce60b14 commit 73abb32

File tree

3 files changed

+32
-30
lines changed

3 files changed

+32
-30
lines changed

compiler/src/dotty/tools/dotc/core/Constraint.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,11 @@ abstract class Constraint extends Showable {
118118
/** A new constraint with all entries coming from `tl` removed. */
119119
def remove(tl: TypeLambda)(using Context): This
120120

121-
/** A new constraint with entry `tl` renamed to a fresh type lambda */
122-
def rename(tl: TypeLambda)(using Context): This
121+
/** A new constraint with entry `from` replaced with `to`
122+
* Rerences to `from` from within other constraint bounds are updated to `to`.
123+
* Type variables are left alone.
124+
*/
125+
def subst(from: TypeLambda, to: TypeLambda)(using Context): This
123126

124127
/** Gives for each instantiated type var that does not yet have its `inst` field
125128
* set, the instance value stored in the constraint. Storing instances in constraints
@@ -150,6 +153,8 @@ abstract class Constraint extends Showable {
150153
def uninstVars: collection.Seq[TypeVar]
151154

152155
/** The weakest constraint that subsumes both this constraint and `other`.
156+
* The constraints should be _compatible_, meaning that a type lambda
157+
* occurring in both constraints is associated with the same typevars in each.
153158
*
154159
* @param otherHasErrors If true, handle incompatible constraints by
155160
* returning an approximate constraint, instead of

compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -485,49 +485,25 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
485485
throw new AssertionError(i"cannot merge $this with $other, mergeEntries($e1, $e2) failed")
486486
}
487487

488-
/** Ensure that constraint `c` does not associate different TypeVars for the
489-
* same type lambda than this constraint. Do this by renaming type lambdas
490-
* in `c` where necessary.
491-
*/
492-
def ensureNotConflicting(c: OrderingConstraint): OrderingConstraint = {
493-
def hasConflictingTypeVarsFor(tl: TypeLambda) =
494-
this.typeVarOfParam(tl.paramRefs(0)) ne c.typeVarOfParam(tl.paramRefs(0))
495-
// Note: Since TypeVars are allocated in bulk for each type lambda, we only
496-
// have to check the first one to find out if some of them are different.
497-
val conflicting = c.domainLambdas.find(tl =>
498-
this.contains(tl) && hasConflictingTypeVarsFor(tl))
499-
conflicting match {
500-
case Some(tl) => ensureNotConflicting(c.rename(tl))
501-
case None => c
502-
}
503-
}
504-
505-
val that = ensureNotConflicting(other.asInstanceOf[OrderingConstraint])
488+
val that = other.asInstanceOf[OrderingConstraint]
506489

507490
new OrderingConstraint(
508491
merge(this.boundsMap, that.boundsMap, mergeEntries),
509492
merge(this.lowerMap, that.lowerMap, mergeParams),
510493
merge(this.upperMap, that.upperMap, mergeParams))
511494
}.showing(i"constraint merge $this with $other = $result", constr)
512495

513-
def rename(tl: TypeLambda)(using Context): OrderingConstraint = {
514-
assert(contains(tl))
515-
val tl1 = ensureFresh(tl)
516-
def swapKey[T](m: ArrayValuedMap[T]) = m.remove(tl).updated(tl1, m(tl))
496+
def subst(from: TypeLambda, to: TypeLambda)(using Context): OrderingConstraint =
497+
def swapKey[T](m: ArrayValuedMap[T]) = m.remove(from).updated(to, m(from))
517498
var current = newConstraint(swapKey(boundsMap), swapKey(lowerMap), swapKey(upperMap))
518-
def subst[T <: Type](x: T): T = x.subst(tl, tl1).asInstanceOf[T]
499+
def subst[T <: Type](x: T): T = x.subst(from, to).asInstanceOf[T]
519500
current.foreachParam {(p, i) =>
520501
current = boundsLens.map(this, current, p, i, subst)
521502
current = lowerLens.map(this, current, p, i, _.map(subst))
522503
current = upperLens.map(this, current, p, i, _.map(subst))
523504
}
524-
current.foreachTypeVar { tvar =>
525-
val TypeParamRef(binder, n) = tvar.origin
526-
if (binder eq tl) tvar.setOrigin(tl1.paramRefs(n))
527-
}
528505
constr.println(i"renamed $this to $current")
529506
current.checkNonCyclic()
530-
}
531507

532508
def instType(tvar: TypeVar): Type = entry(tvar.origin) match
533509
case _: TypeBounds => NoType

compiler/src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,28 @@ class TyperState() {
130130
isCommitted = true
131131
}
132132

133+
/** Ensure that this constraint does not associate different TypeVars for the
134+
* same type lambda than the `other` constraint. Do this by renaming type lambdas
135+
* in this constraint and its predecessors where necessary.
136+
*/
137+
def ensureNotConflicting(other: Constraint)(using Context): Unit =
138+
def hasConflictingTypeVarsFor(tl: TypeLambda) =
139+
constraint.typeVarOfParam(tl.paramRefs(0)) ne other.typeVarOfParam(tl.paramRefs(0))
140+
// Note: Since TypeVars are allocated in bulk for each type lambda, we only
141+
// have to check the first one to find out if some of them are different.
142+
val conflicting = constraint.domainLambdas.find(tl =>
143+
other.contains(tl) && hasConflictingTypeVarsFor(tl))
144+
for tl <- conflicting do
145+
val tl1 = constraint.ensureFresh(tl)
146+
for case (tvar: TypeVar, pref1) <- tl.paramRefs.map(constraint.typeVarOfParam).lazyZip(tl1.paramRefs) do
147+
tvar.setOrigin(pref1)
148+
var ts = this
149+
while ts.constraint.domainLambdas.contains(tl) do
150+
ts.constraint = ts.constraint.subst(tl, tl1)
151+
ts = ts.previous
152+
133153
def mergeConstraintWith(that: TyperState)(using Context): Unit =
154+
that.ensureNotConflicting(constraint)
134155
constraint = constraint & (that.constraint, otherHasErrors = that.reporter.errorsReported)
135156
for tvar <- constraint.uninstVars do
136157
if !isOwnedAnywhere(this, tvar) then ownedVars += tvar

0 commit comments

Comments
 (0)