Skip to content

Commit e97ebe0

Browse files
committed
Change dependency tracking to relation on TypeParams
1 parent 8796e4a commit e97ebe0

File tree

3 files changed

+107
-111
lines changed

3 files changed

+107
-111
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package core
44

55
import Types._, Contexts._
66
import printing.Showable
7-
import util.SimpleIdentityMap
7+
import util.{SimpleIdentitySet, SimpleIdentityMap}
88

99
/** Constraint over undetermined type parameters. Constraints are built
1010
* over values of the following types:
@@ -170,13 +170,13 @@ abstract class Constraint extends Showable {
170170
* refer to them in their bounds covariantly, such that, if the type variable
171171
* is isntantiated to a larger type, the constraint would be narrowed.
172172
*/
173-
def coDeps: Constraint.TypeVarDeps
173+
def coDeps: Constraint.ReverseDeps
174174

175175
/** A map that associates type variables with all other type variables that
176176
* refer to them in their bounds covariantly, such that, if the type variable
177177
* is isntantiated to a smaller type, the constraint would be narrowed.
178178
*/
179-
def contraDeps: Constraint.TypeVarDeps
179+
def contraDeps: Constraint.ReverseDeps
180180

181181
/** A string showing the `coDeps` and `contraDeps` maps */
182182
def depsToString(using Context): String
@@ -207,7 +207,7 @@ abstract class Constraint extends Showable {
207207
}
208208

209209
object Constraint:
210-
type TypeVarDeps = SimpleIdentityMap[TypeVar, TypeVars]
210+
type ReverseDeps = SimpleIdentityMap[TypeParamRef, SimpleIdentitySet[TypeParamRef]]
211211

212212

213213
/** When calling `Constraint#addLess(p1, p2, ...)`, the caller might end up

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

Lines changed: 84 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import printing.Texts._
1010
import config.Config
1111
import config.Printers.constr
1212
import reflect.ClassTag
13-
import Constraint.TypeVarDeps
13+
import Constraint.ReverseDeps
1414
import NameKinds.DepParamName
1515
import annotation.tailrec
1616
import annotation.internal.sharable
@@ -225,122 +225,117 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
225225

226226
// ------------- TypeVar dependencies -----------------------------------
227227

228-
var coDeps, contraDeps: TypeVarDeps = SimpleIdentityMap.empty
228+
var coDeps, contraDeps: ReverseDeps = SimpleIdentityMap.empty
229229

230230
def dependsOn(tv: TypeVar, except: TypeVars, co: Boolean)(using Context): Boolean =
231-
def test(deps: TypeVarDeps, lens: ConstraintLens[List[TypeParamRef]]) =
232-
val tvdeps = deps(tv)
233-
null != tvdeps && tvdeps.exists(!except.contains(_))
234-
|| lens(this, tv.origin.binder, tv.origin.paramNum).exists(
235-
p => !except.contains(typeVarOfParam(p)))
231+
def origin(tv: TypeVar) =
232+
assert(!tv.isInstantiated)
233+
tv.origin
234+
val param = origin(tv)
235+
val excluded = except.map(origin)
236+
def test(deps: ReverseDeps, lens: ConstraintLens[List[TypeParamRef]]) =
237+
val depending = deps(param)
238+
null != depending && depending.exists(!excluded.contains(_))
239+
|| lens(this, tv.origin.binder, tv.origin.paramNum).exists(!excluded.contains(_))
236240
//.showing(i"outer depends on $tv with ${tvdeps.toList}%, % = $result")
237241
if co then test(coDeps, upperLens) else test(contraDeps, lowerLens)
238242

239-
private class Adjuster(tvar: TypeVar)(using Context) extends TypeTraverser:
243+
private class Adjuster(srcParam: TypeParamRef)(using Context) extends TypeTraverser:
240244
var add: Boolean = compiletime.uninitialized
241245

242-
def update(deps: TypeVarDeps, referenced: TypeVar): TypeVarDeps =
246+
def update(deps: ReverseDeps, referenced: TypeParamRef): ReverseDeps =
243247
val entry = deps(referenced)
244248
val prev = if null == entry then SimpleIdentitySet.empty else entry
245-
val now = if add then prev + tvar else prev - tvar
249+
val now = if add then prev + srcParam else prev - srcParam
246250
deps.updated(referenced, now)
247251

248252
def traverse(t: Type) = t match
249-
case tv: TypeVar =>
250-
val inst = tv.instanceOpt
251-
if inst.exists then traverse(inst)
252-
else
253-
if variance >= 0 then coDeps = update(coDeps, tv)
254-
if variance <= 0 then contraDeps = update(contraDeps, tv)
255253
case param: TypeParamRef =>
256-
traverse(typeVarOfParam(param))
254+
if contains(param) then
255+
if variance >= 0 then coDeps = update(coDeps, param)
256+
if variance <= 0 then contraDeps = update(contraDeps, param)
257257
case tp: LazyRef if !tp.completed =>
258-
case _ =>
259-
traverseChildren(t)
258+
case _ => traverseChildren(t)
259+
end Adjuster
260260

261261
/** Adjust dependencies to account for the delta of previous entry `prevEntry`
262262
* and new bound `entry` for the type variable `tvar`.
263263
*/
264-
def adjustDeps(entry: Type | Null, prevEntry: Type | Null, tvar: Type | Null)(using Context): this.type =
265-
tvar match
266-
case tvar: TypeVar =>
267-
val adjuster = new Adjuster(tvar)
268-
269-
/** Adjust reverse depemdencies of all type variables referenced by `bound`
270-
* @param isLower `bound` is a lower bound
271-
* @param add if true, add referenced variables to dependencoes, otherwise drop them.
272-
*/
273-
def adjustReferenced(bound: Type, isLower: Boolean, add: Boolean) =
274-
adjuster.variance = if isLower then 1 else -1
275-
adjuster.add = add
276-
adjuster.traverse(bound)
277-
278-
/** Use an optimized strategy to adjust dependencies to account for the delta
279-
* of previous bound `prevBound` and new bound `bound`: If `prevBound` is some
280-
* and/or prefix of `bound`, just add the new parts of `bound`.
281-
* @param isLower `bound` and `prevBound` are lower bounds
282-
*/
283-
def adjustDelta(bound: Type, prevBound: Type, isLower: Boolean): Boolean =
284-
if bound eq prevBound then true
285-
else bound match
286-
case bound: AndOrType =>
287-
adjustDelta(bound.tp1, prevBound, isLower) && {
288-
adjustReferenced(bound.tp2, isLower, add = true)
289-
true
290-
}
291-
case _ => false
292-
293-
/** Adjust dependencies to account for the delta of previous bound `prevBound`
294-
* and new bound `bound`.
295-
* @param isLower `bound` and `prevBound` are lower bounds
296-
*/
297-
def adjustBounds(bound: Type, prevBound: Type, isLower: Boolean) =
298-
if !adjustDelta(bound, prevBound, isLower) then
299-
adjustReferenced(prevBound, isLower, add = false)
300-
adjustReferenced(bound, isLower, add = true)
301-
302-
entry match
303-
case TypeBounds(lo, hi) =>
304-
prevEntry match
305-
case TypeBounds(plo, phi) =>
306-
adjustBounds(lo, plo, isLower = true)
307-
adjustBounds(hi, phi, isLower = false)
308-
case _ =>
309-
adjustReferenced(lo, isLower = true, add = true)
310-
adjustReferenced(hi, isLower = false, add = true)
264+
def adjustDeps(entry: Type | Null, prevEntry: Type | Null, srcParam: TypeParamRef)(using Context): this.type =
265+
val adjuster = new Adjuster(srcParam)
266+
267+
/** Adjust reverse depemdencies of all type parameters referenced by `bound`
268+
* @param isLower `bound` is a lower bound
269+
* @param add if true, add referenced variables to dependencoes, otherwise drop them.
270+
*/
271+
def adjustReferenced(bound: Type, isLower: Boolean, add: Boolean) =
272+
adjuster.variance = if isLower then 1 else -1
273+
adjuster.add = add
274+
adjuster.traverse(bound)
275+
276+
/** Use an optimized strategy to adjust dependencies to account for the delta
277+
* of previous bound `prevBound` and new bound `bound`: If `prevBound` is some
278+
* and/or prefix of `bound`, just add the new parts of `bound`.
279+
* @param isLower `bound` and `prevBound` are lower bounds
280+
*/
281+
def adjustDelta(bound: Type, prevBound: Type, isLower: Boolean): Boolean =
282+
if bound eq prevBound then true
283+
else bound match
284+
case bound: AndOrType =>
285+
adjustDelta(bound.tp1, prevBound, isLower) && {
286+
adjustReferenced(bound.tp2, isLower, add = true)
287+
true
288+
}
289+
case _ => false
290+
291+
/** Adjust dependencies to account for the delta of previous bound `prevBound`
292+
* and new bound `bound`.
293+
* @param isLower `bound` and `prevBound` are lower bounds
294+
*/
295+
def adjustBounds(bound: Type, prevBound: Type, isLower: Boolean) =
296+
if !adjustDelta(bound, prevBound, isLower) then
297+
adjustReferenced(prevBound, isLower, add = false)
298+
adjustReferenced(bound, isLower, add = true)
299+
300+
entry match
301+
case TypeBounds(lo, hi) =>
302+
prevEntry match
303+
case TypeBounds(plo, phi) =>
304+
adjustBounds(lo, plo, isLower = true)
305+
adjustBounds(hi, phi, isLower = false)
311306
case _ =>
312-
prevEntry match
313-
case TypeBounds(plo, phi) =>
314-
adjustReferenced(plo, isLower = true, add = false)
315-
adjustReferenced(phi, isLower = false, add = false)
316-
case _ =>
317-
dropDeps(tvar)
307+
adjustReferenced(lo, isLower = true, add = true)
308+
adjustReferenced(hi, isLower = false, add = true)
318309
case _ =>
310+
prevEntry match
311+
case TypeBounds(plo, phi) =>
312+
adjustReferenced(plo, isLower = true, add = false)
313+
adjustReferenced(phi, isLower = false, add = false)
314+
case _ =>
315+
dropDeps(srcParam)
319316
this
320317
end adjustDeps
321318

322319
/** Adjust dependencies to account for adding or dropping `entries` to the
323320
* constraint.
324321
* @param add if true, entries is added, otherwise it is dropped
325322
*/
326-
def adjustDeps(entries: Array[Type], add: Boolean)(using Context): this.type =
323+
def adjustDeps(poly: TypeLambda, entries: Array[Type], add: Boolean)(using Context): this.type =
327324
for n <- 0 until paramCount(entries) do
328325
if add
329-
then adjustDeps(entries(n), NoType, typeVar(entries, n))
330-
else adjustDeps(NoType, entries(n), typeVar(entries, n))
326+
then adjustDeps(entries(n), NoType, poly.paramRefs(n))
327+
else adjustDeps(NoType, entries(n), poly.paramRefs(n))
331328
this
332329

333330
/** If `tp` is a type variable, remove all its reverse dependencies */
334-
def dropDeps(tp: Type)(using Context): Unit = tp match
335-
case tv: TypeVar =>
336-
coDeps = coDeps.remove(tv)
337-
contraDeps = contraDeps.remove(tv)
338-
case _ =>
331+
def dropDeps(param: TypeParamRef)(using Context): Unit =
332+
coDeps = coDeps.remove(param)
333+
contraDeps = contraDeps.remove(param)
339334

340335
/** A string representing the two depenecy maps */
341336
def depsToString(using Context): String =
342-
def depsStr(deps: SimpleIdentityMap[TypeVar, TypeVars]): String =
343-
def depStr(tv: TypeVar) = i"$tv --> ${deps(tv).nn.toList}%, %"
337+
def depsStr(deps: ReverseDeps): String =
338+
def depStr(param: TypeParamRef) = i"$param --> ${deps(param).nn.toList}%, %"
344339
if deps.isEmpty then "" else i"\n ${deps.toList.map((k, v) => depStr(k))}%\n %"
345340
i"co-deps:${depsStr(coDeps)}\ncontra-deps:${depsStr(contraDeps)}\n"
346341

@@ -411,7 +406,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
411406
tvars.copyToArray(entries1, nparams)
412407
newConstraint(boundsMap = this.boundsMap.updated(poly, entries1))
413408
.init(poly)
414-
.adjustDeps(entries1, add = true)
409+
.adjustDeps(poly, entries1, add = true)
415410
}
416411

417412
/** Split dependent parameters off the bounds for parameters in `poly`.
@@ -558,7 +553,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
558553
private def updateEntry(current: This, param: TypeParamRef, tp: Type)(using Context): This = {
559554
if Config.checkNoWildcardsInConstraint then assert(!tp.containsWildcardTypes)
560555
var current1 = boundsLens.update(this, current, param, tp)
561-
current1.adjustDeps(tp, current.entry(param), typeVarOfParam(param))
556+
current1.adjustDeps(tp, current.entry(param), param)
562557
tp match {
563558
case TypeBounds(lo, hi) =>
564559
for p <- dependentParams(lo, isUpper = false) do
@@ -601,12 +596,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
601596
current = boundsLens.map(this, current, p, i,
602597
entry =>
603598
val newEntry = replaceParam(entry, p, i)
604-
adjustDeps(newEntry, entry, typeVar(this.boundsMap(p).nn, i))
599+
adjustDeps(newEntry, entry, p.paramRefs(i))
605600
newEntry)
606601
current = lowerLens.map(this, current, p, i, removeParam)
607602
current = upperLens.map(this, current, p, i, removeParam)
608603
}
609-
current.dropDeps(typeVarOfParam(param))
604+
current.dropDeps(param)
610605
current.checkWellFormed()
611606
end replace
612607

@@ -620,7 +615,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
620615
}
621616
val hardVars1 = pt.paramRefs.foldLeft(hardVars)((hvs, param) => hvs - typeVarOfParam(param))
622617
newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap), hardVars1)
623-
.adjustDeps(boundsMap(pt).nn, add = false)
618+
.adjustDeps(pt, boundsMap(pt).nn, add = false)
624619
.checkWellFormed()
625620
}
626621

@@ -749,11 +744,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
749744
s"cyclic bound for $param: ${inst.show} in ${this.show}")
750745
}
751746
if Config.checkConstraintDeps then
752-
def checkDeps(deps: TypeVarDeps) =
747+
def checkDeps(deps: ReverseDeps) = ()/*
753748
deps.foreachBinding { (tv, tvs) =>
754749
for tv1 <- tvs do
755750
assert(!tv1.instanceOpt.exists, i"$this")
756-
}
751+
}*/
757752
checkDeps(coDeps)
758753
checkDeps(contraDeps)
759754
this

compiler/src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -623,24 +623,25 @@ trait Inferencing { this: Typer =>
623623
for tvar <- qualifying do
624624
if !tvar.isInstantiated && constraint.contains(tvar) && tvar.nestingLevel >= ctx.nestingLevel then
625625
constrainIfDependentParamRef(tvar, tree)
626-
// Needs to be checked again, since previous interpolations could already have
627-
// instantiated `tvar` through unification.
628-
val v = vs(tvar)
629-
if v == null then
630-
toInstantiate += ((tvar, 0))
631-
else if v.intValue != 0 then
632-
toInstantiate += ((tvar, v.intValue))
633-
else comparing(cmp =>
634-
if !cmp.levelOK(tvar.nestingLevel, ctx.nestingLevel) then
635-
// Invariant: The type of a tree whose enclosing scope is level
636-
// N only contains type variables of level <= N.
637-
typr.println(i"instantiate nonvariant $tvar of level ${tvar.nestingLevel} to a type variable of level <= ${ctx.nestingLevel}, $constraint")
638-
cmp.atLevel(ctx.nestingLevel, tvar.origin)
639-
else
640-
typr.println(i"no interpolation for nonvariant $tvar in $state")
641-
)
626+
if !tvar.isInstantiated then
627+
// Needs to be checked again, since previous interpolations could already have
628+
// instantiated `tvar` through unification.
629+
val v = vs(tvar)
630+
if v == null then
631+
toInstantiate += ((tvar, 0))
632+
else if v.intValue != 0 then
633+
toInstantiate += ((tvar, v.intValue))
634+
else comparing(cmp =>
635+
if !cmp.levelOK(tvar.nestingLevel, ctx.nestingLevel) then
636+
// Invariant: The type of a tree whose enclosing scope is level
637+
// N only contains type variables of level <= N.
638+
typr.println(i"instantiate nonvariant $tvar of level ${tvar.nestingLevel} to a type variable of level <= ${ctx.nestingLevel}, $constraint")
639+
cmp.atLevel(ctx.nestingLevel, tvar.origin)
640+
else
641+
typr.println(i"no interpolation for nonvariant $tvar in $state")
642+
)
642643

643-
def typeVarsIn(xs: collection.Seq[(TypeVar, Int)]): TypeVars =
644+
def typeVarsIn(xs: List[(TypeVar, Int)]): TypeVars =
644645
xs.foldLeft(SimpleIdentitySet.empty: TypeVars)((tvs, tvi) => tvs + tvi._1)
645646

646647
def filterByDeps(tvs0: List[(TypeVar, Int)]): List[(TypeVar, Int)] = {
@@ -654,7 +655,7 @@ trait Inferencing { this: Typer =>
654655
else if v == 0 && !belowOK then
655656
step((tvar, -1) :: tvs1)
656657
else if v == -1 && !aboveOK || v == 1 && !belowOK then
657-
println(i"drop $tvar, $v in $tp, $pt, qualifying = ${qualifying.toList}, tvs0 = ${tvs0.toList}%, %, excluded = ${excluded.toList}, $constraint")
658+
typr.println(i"drop $tvar, $v in $tp, $pt, qualifying = ${qualifying.toList}, tvs0 = ${tvs0.toList}%, %, excluded = ${excluded.toList}, $constraint")
658659
step(tvs1)
659660
else
660661
tvs.derivedCons(hd, step(tvs1))

0 commit comments

Comments
 (0)