@@ -18,10 +18,16 @@ import cc.{CapturingType, derivedCapturingType}
18
18
19
19
object OrderingConstraint {
20
20
21
- @ sharable private var id = 0
22
- private def nextId =
23
- id += 1
24
- id
21
+ /** If true, use reverse dependencies in `replace` to avoid checking the bounds
22
+ * of all parameters in the constraint. This can speed things up, but there are some
23
+ * rare corner cases where reverse dependencies miss a parameter. Specifically,
24
+ * if a constraint contains a free reference to TypeParam P and afterwards the
25
+ * same P is added as a bound variable to the constraint, a backwards link would
26
+ * then become necessary at this point but is missing. This causes two CB projects
27
+ * to fail when reverse dependencies are checked (parboiled2 and perspective).
28
+ * In these rare cases `replace` would behave differently when optimized.
29
+ */
30
+ final val optimizeReplace = true
25
31
26
32
type ArrayValuedMap [T ] = SimpleIdentityMap [TypeLambda , Array [T ]]
27
33
@@ -145,10 +151,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
145
151
146
152
type This = OrderingConstraint
147
153
148
- var id = nextId
149
- // if id == 118 then
150
- // new Error(s"at $id").printStackTrace()
151
-
152
154
/** A new constraint with given maps and given set of hard typevars */
153
155
def newConstraint ( // !!! Dotty problem: Making newConstraint `private` causes -Ytest-pickler failure.
154
156
boundsMap : ParamBounds = this .boundsMap,
@@ -294,7 +296,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
294
296
295
297
def update (deps : ReverseDeps , referenced : TypeParamRef ): ReverseDeps =
296
298
val prev = deps.at(referenced)
297
- deps.updated(referenced, if add then prev + srcParam else prev - srcParam)
299
+ val newSet = if add then prev + srcParam else prev - srcParam
300
+ if newSet.isEmpty then deps.remove(referenced)
301
+ else deps.updated(referenced, newSet)
298
302
299
303
def traverse (t : Type ) = t match
300
304
case param : TypeParamRef =>
@@ -651,41 +655,58 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
651
655
652
656
var current = this
653
657
654
- def removeParam (ps : List [TypeParamRef ]) = ps.filterConserve(param ne _)
658
+ def removeParamFrom (ps : List [TypeParamRef ]) =
659
+ ps.filterConserve(param ne _)
660
+
655
661
for lo <- lower(param) do
656
- current = upperLens.map(this , current, lo, removeParam )
662
+ current = upperLens.map(this , current, lo, removeParamFrom )
657
663
for hi <- upper(param) do
658
- current = lowerLens.map(this , current, hi, removeParam)
659
-
660
- current.foreachParam { (p, i) =>
661
- val other = p.paramRefs(i)
662
- if other != param then
663
- val oldEntry = current.entry(other)
664
- val newEntry = current.ensureNonCyclic(other, oldEntry.substParam(param, replacement))
665
- current = boundsLens.update(this , current, other, newEntry)
666
- var oldDepEntry = oldEntry
667
- var newDepEntry = newEntry
668
- replacedTypeVar match
669
- case tvar : TypeVar =>
670
- if tvar.isInstantiated
671
- then
672
- // replace is called from TypeVar's instantiateWith,
673
- // forget about instantiation for old dependencies
674
- oldDepEntry = mapReplacedTypeVarTo(param)(oldDepEntry)
675
- else
676
- // replace is called from unify,
677
- // assume parameter has been replaced for new dependencies
678
- // (the actual replacement is done below)
679
- newDepEntry = mapReplacedTypeVarTo(replacement)(newDepEntry)
680
- case _ =>
681
- if oldDepEntry ne newDepEntry then
682
- if current eq this then
683
- // We can end up here if oldEntry eq newEntry, so posssibly no new constraint
684
- // was created, but oldDepEntry ne newDepEntry. In that case we must make
685
- // sure we have a new constraint before updating dependencies.
686
- current = newConstraint()
687
- current.adjustDeps(newDepEntry, oldDepEntry, other)
688
- }
664
+ current = lowerLens.map(this , current, hi, removeParamFrom)
665
+
666
+ def replaceParamIn (other : TypeParamRef ) =
667
+ val oldEntry = current.entry(other)
668
+ val newEntry = current.ensureNonCyclic(other, oldEntry.substParam(param, replacement))
669
+ current = boundsLens.update(this , current, other, newEntry)
670
+ var oldDepEntry = oldEntry
671
+ var newDepEntry = newEntry
672
+ replacedTypeVar match
673
+ case tvar : TypeVar =>
674
+ if tvar.isInstantiated
675
+ then
676
+ // replace is called from TypeVar's instantiateWith,
677
+ // forget about instantiation for old dependencies
678
+ oldDepEntry = mapReplacedTypeVarTo(param)(oldDepEntry)
679
+ else
680
+ // replace is called from unify,
681
+ // assume parameter has been replaced for new dependencies
682
+ // (the actual replacement is done below)
683
+ newDepEntry = mapReplacedTypeVarTo(replacement)(newDepEntry)
684
+ case _ =>
685
+ if oldDepEntry ne newDepEntry then
686
+ if current eq this then
687
+ // We can end up here if oldEntry eq newEntry, so posssibly no new constraint
688
+ // was created, but oldDepEntry ne newDepEntry. In that case we must make
689
+ // sure we have a new constraint before updating dependencies.
690
+ current = newConstraint()
691
+ current.adjustDeps(newDepEntry, oldDepEntry, other)
692
+ end replaceParamIn
693
+
694
+ if optimizeReplace then
695
+ val co = current.coDeps.at(param)
696
+ val contra = current.contraDeps.at(param)
697
+ current.foreachParam { (p, i) =>
698
+ val other = p.paramRefs(i)
699
+ entry(other) match
700
+ case _ : TypeBounds =>
701
+ if co.contains(other) || contra.contains(other) then
702
+ replaceParamIn(other)
703
+ case _ => replaceParamIn(other)
704
+ }
705
+ else
706
+ current.foreachParam { (p, i) =>
707
+ val other = p.paramRefs(i)
708
+ if other != param then replaceParamIn(other)
709
+ }
689
710
690
711
current =
691
712
if isRemovable(param.binder) then current.remove(param.binder)
0 commit comments