Skip to content

Commit d508d9f

Browse files
committed
Less eager removal of type parameters from constraint
Previously, a unified or otherwise instantiated type parameter would be removed from the constraint, i.e. if it was the last parameter of its polytype to be instantiated, the polytype would be dropped. This is a potential problem since it means that the alias `param = instance` is forgetten whereas we might still need it in the same subtype test sequence. The solution is to wait with cleaning up polytypes until an enclosing subtype test has been completely unwound. Also: Generalize constraint solving to GenericTypes (will be needed later for handling existentials).
1 parent f10055a commit d508d9f

File tree

6 files changed

+35
-16
lines changed

6 files changed

+35
-16
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,12 @@ abstract class Constraint extends Showable {
107107
* the type parameter `param` from the domain and replacing all top-level occurrences
108108
* of the parameter elsewhere in the constraint by type `tp`, or a conservative
109109
* approximation of it if that is needed to avoid cycles.
110-
* Occurrences nested inside a refinement or prefix are not affected.
110+
* Occurrences nested inside a refinement or prefix simply substituted instead
111+
* of being approximated.
112+
* If `canRemove` is true, `param` is removed from the domain of the resulting constraint.
113+
* If `canRemove` is false, `param = tp` is remembered as an alias.
111114
*/
112-
def replace(param: PolyParam, tp: Type)(implicit ctx: Context): This
115+
def replace(param: PolyParam, tp: Type, canRemove: Boolean)(implicit ctx: Context): This
113116

114117
/** Narrow one of the bounds of type parameter `param`
115118
* If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure

src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ trait ConstraintHandling {
3232

3333
private var addConstraintInvocations = 0
3434

35+
private var needsCleanup = false
36+
3537
/** If the constraint is frozen we cannot add new bounds to the constraint. */
3638
protected var frozenConstraint = false
3739

@@ -110,6 +112,7 @@ trait ConstraintHandling {
110112
assert(constraint.isLess(p1, p2))
111113
val down = constraint.exclusiveLower(p2, p1)
112114
val up = constraint.exclusiveUpper(p1, p2)
115+
needsCleanup = true
113116
constraint = constraint.unify(p1, p2)
114117
val bounds = constraint.nonParamBounds(p1)
115118
val lo = bounds.lo
@@ -203,7 +206,7 @@ trait ConstraintHandling {
203206
* and propagate all bounds.
204207
* @param tvars See Constraint#add
205208
*/
206-
def addToConstraint(pt: PolyType, tvars: List[TypeVar]): Unit =
209+
def addToConstraint(pt: GenericType, tvars: List[TypeVar]): Unit =
207210
assert {
208211
checkPropagated(i"initialized $pt") {
209212
constraint = constraint.add(pt, tvars)
@@ -313,7 +316,10 @@ trait ConstraintHandling {
313316
val saved = constraint
314317
constraint =
315318
if (addConstraint(param, tp, fromBelow = true) &&
316-
addConstraint(param, tp, fromBelow = false)) constraint.replace(param, tp)
319+
addConstraint(param, tp, fromBelow = false)) {
320+
needsCleanup = true
321+
constraint.replace(param, tp, canRemove = false)
322+
}
317323
else saved
318324
constraint ne saved
319325
}
@@ -337,4 +343,12 @@ trait ConstraintHandling {
337343
}
338344
result
339345
}
346+
347+
/** A new constraint with all polytypes that only have instantiated parameters removed */
348+
protected def cleanup(): Unit =
349+
if (needsCleanup) {
350+
constraint =
351+
(constraint /: constraint.domainPolys.filter(constraint.isRemovable(_)))(_.remove(_))
352+
needsCleanup = false
353+
}
340354
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
349349

350350
def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This = {
351351
val p1Bounds = (nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1)
352-
updateEntry(p1, p1Bounds).replace(p2, p1)
352+
updateEntry(p1, p1Bounds).replace(p2, p1, canRemove = false)
353353
}
354354

355355
def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This = {
@@ -387,7 +387,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
387387
* would not find out where we need to approximate. Occurrences of parameters
388388
* that are not top-level are not affected.
389389
*/
390-
def replace(param: PolyParam, tp: Type)(implicit ctx: Context): OrderingConstraint = {
390+
def replace(param: PolyParam, tp: Type, canRemove: Boolean)(implicit ctx: Context): OrderingConstraint = {
391391
val replacement = tp.dealias.stripTypeVar
392392
if (param == replacement) this
393393
else {
@@ -428,7 +428,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
428428
}
429429

430430
var current =
431-
if (isRemovable(poly, idx)) remove(poly) else updateEntry(param, replacement)
431+
if (canRemove && isRemovable(poly, idx)) remove(poly) else updateEntry(param, replacement)
432432
current.foreachParam {(p, i) =>
433433
current = boundsLens.map(this, current, p, i, replaceParam(_, p, i))
434434
current = lowerLens.map(this, current, p, i, removeParam)

src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
101101
else monitoredIsSubType(tp1, tp2)
102102
recCount = recCount - 1
103103
if (!result) constraint = saved
104-
else if (recCount == 0 && needsGc) {
105-
state.gc()
106-
needsGc = false
107-
}
104+
else if (recCount == 0)
105+
cleanup()
106+
if (needsGc) {
107+
state.gc()
108+
needsGc = false
109+
}
108110
if (Stats.monitored) recordStatistics(result, savedSuccessCount)
109111
result
110112
} catch {

src/dotty/tools/dotc/core/Types.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2866,7 +2866,7 @@ object Types {
28662866
assert(ctx.typerState.constraint contains this) // !!! DEBUG
28672867
if ((ctx.typerState eq owningState) && !ctx.typeComparer.subtypeCheckInProgress)
28682868
inst = tp
2869-
ctx.typerState.constraint = ctx.typerState.constraint.replace(origin, tp)
2869+
ctx.typerState.constraint = ctx.typerState.constraint.replace(origin, tp, canRemove = true)
28702870
tp
28712871
}
28722872

src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,12 @@ object ProtoTypes {
321321
* Also, if `owningTree` is non-empty, add a type variable for each parameter.
322322
* @return The added polytype, and the list of created type variables.
323323
*/
324-
def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = {
324+
def constrained(pt: GenericType, owningTree: untpd.Tree)(implicit ctx: Context): (GenericType, List[TypeVar]) = {
325325
val state = ctx.typerState
326-
assert(!(ctx.typerState.isCommittable && owningTree.isEmpty),
326+
assert(!(ctx.typerState.isCommittable && owningTree.isEmpty && pt.isInstanceOf[PolyType]),
327327
s"inconsistent: no typevars were added to committable constraint ${state.constraint}")
328328

329-
def newTypeVars(pt: PolyType): List[TypeVar] =
329+
def newTypeVars(pt: GenericType): List[TypeVar] =
330330
for (n <- (0 until pt.paramNames.length).toList)
331331
yield new TypeVar(PolyParam(pt, n), state, owningTree, ctx.owner)
332332

@@ -339,7 +339,7 @@ object ProtoTypes {
339339
}
340340

341341
/** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */
342-
def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1
342+
def constrained(pt: GenericType)(implicit ctx: Context): GenericType = constrained(pt, EmptyTree)._1
343343

344344
/** The normalized form of a type
345345
* - unwraps polymorphic types, tracking their parameters in the current constraint

0 commit comments

Comments
 (0)