@@ -10,8 +10,6 @@ import printing.Texts._
1010import config .Config
1111import config .Printers .constr
1212import reflect .ClassTag
13- import Constraint .ReverseDeps
14- import Substituters .SubstParamMap
1513import annotation .tailrec
1614import annotation .internal .sharable
1715import cc .{CapturingType , derivedCapturingType }
@@ -25,20 +23,27 @@ object OrderingConstraint {
2523 * same P is added as a bound variable to the constraint, a backwards link would
2624 * then become necessary at this point but is missing. This causes two CB projects
2725 * to fail when reverse dependencies are checked (parboiled2 and perspective).
28- * In these rare cases `replace` would behave differently when optimized.
26+ * In these rare cases `replace` could behave differently when optimized. However,
27+ * no deviation was found in the two projects. It is not clear what the "right"
28+ * behavior of `replace` should be in these cases. Normally, PolyTypes added
29+ * to constraints are supposed to be fresh, so that would mean that the behavior
30+ * with optimizeReplace = true would be correct. But the previous behavior without
31+ * reverse dependency checking corresponds to `optimizeReplace = false`. This behavior
32+ * makes sense if we assume that the added polytype was simply added too late, so we
33+ * want to establish the link between newly bound variable and pre-existing reference.
2934 */
30- final val optimizeReplace = true
35+ private final val optimizeReplace = true
3136
32- type ArrayValuedMap [T ] = SimpleIdentityMap [TypeLambda , Array [T ]]
37+ private type ArrayValuedMap [T ] = SimpleIdentityMap [TypeLambda , Array [T ]]
3338
3439 /** The type of `OrderingConstraint#boundsMap` */
35- type ParamBounds = ArrayValuedMap [Type ]
40+ private type ParamBounds = ArrayValuedMap [Type ]
3641
3742 /** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */
38- type ParamOrdering = ArrayValuedMap [List [TypeParamRef ]]
43+ private type ParamOrdering = ArrayValuedMap [List [TypeParamRef ]]
3944
4045 /** A lens for updating a single entry array in one of the three constraint maps */
41- abstract class ConstraintLens [T <: AnyRef : ClassTag ] {
46+ private abstract class ConstraintLens [T <: AnyRef : ClassTag ] {
4247 def entries (c : OrderingConstraint , poly : TypeLambda ): Array [T ] | Null
4348 def updateEntries (c : OrderingConstraint , poly : TypeLambda , entries : Array [T ])(using Context ): OrderingConstraint
4449 def initial : T
@@ -91,23 +96,23 @@ object OrderingConstraint {
9196 map(prev, current, param.binder, param.paramNum, f)
9297 }
9398
94- val boundsLens : ConstraintLens [Type ] = new ConstraintLens [Type ] {
99+ private val boundsLens : ConstraintLens [Type ] = new ConstraintLens [Type ] {
95100 def entries (c : OrderingConstraint , poly : TypeLambda ): Array [Type ] | Null =
96101 c.boundsMap(poly)
97102 def updateEntries (c : OrderingConstraint , poly : TypeLambda , entries : Array [Type ])(using Context ): OrderingConstraint =
98103 c.newConstraint(boundsMap = c.boundsMap.updated(poly, entries))
99104 def initial = NoType
100105 }
101106
102- val lowerLens : ConstraintLens [List [TypeParamRef ]] = new ConstraintLens [List [TypeParamRef ]] {
107+ private val lowerLens : ConstraintLens [List [TypeParamRef ]] = new ConstraintLens [List [TypeParamRef ]] {
103108 def entries (c : OrderingConstraint , poly : TypeLambda ): Array [List [TypeParamRef ]] | Null =
104109 c.lowerMap(poly)
105110 def updateEntries (c : OrderingConstraint , poly : TypeLambda , entries : Array [List [TypeParamRef ]])(using Context ): OrderingConstraint =
106111 c.newConstraint(lowerMap = c.lowerMap.updated(poly, entries))
107112 def initial = Nil
108113 }
109114
110- val upperLens : ConstraintLens [List [TypeParamRef ]] = new ConstraintLens [List [TypeParamRef ]] {
115+ private val upperLens : ConstraintLens [List [TypeParamRef ]] = new ConstraintLens [List [TypeParamRef ]] {
111116 def entries (c : OrderingConstraint , poly : TypeLambda ): Array [List [TypeParamRef ]] | Null =
112117 c.upperMap(poly)
113118 def updateEntries (c : OrderingConstraint , poly : TypeLambda , entries : Array [List [TypeParamRef ]])(using Context ): OrderingConstraint =
@@ -237,13 +242,30 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
237242
238243// ------------- Type parameter dependencies ----------------------------------------
239244
240- var coDeps, contraDeps : ReverseDeps = SimpleIdentityMap .empty
245+ private type ReverseDeps = SimpleIdentityMap [ TypeParamRef , SimpleIdentitySet [ TypeParamRef ]]
241246
242- extension (deps : ReverseDeps ) def at (param : TypeParamRef ): SimpleIdentitySet [TypeParamRef ] =
247+ /** A map that associates type parameters of this constraint with all other type
248+ * parameters that refer to them in their bounds covariantly, such that, if the
249+ * type parameter is instantiated to a larger type, the constraint would be narrowed
250+ * (i.e. solution set changes other than simply being made larger).
251+ */
252+ private var coDeps : ReverseDeps = SimpleIdentityMap .empty
253+
254+ /** A map that associates type parameters of this constraint with all other type
255+ * parameters that refer to them in their bounds covariantly, such that, if the
256+ * type parameter is instantiated to a smaller type, the constraint would be narrowed.
257+ * (i.e. solution set changes other than simply being made larger).
258+ */
259+ private var contraDeps : ReverseDeps = SimpleIdentityMap .empty
260+
261+ /** Null-safe indexing */
262+ extension (deps : ReverseDeps ) def at (param : TypeParamRef ): SimpleIdentitySet [TypeParamRef ] =
243263 val result = deps(param)
244- if null == result then SimpleIdentitySet .empty else result
264+ if null == result // swapped operand order important since `==` is overloaded in `SimpleIdentitySet`
265+ then SimpleIdentitySet .empty
266+ else result
245267
246- def dependsOn (tv : TypeVar , except : TypeVars , co : Boolean )(using Context ): Boolean =
268+ override def dependsOn (tv : TypeVar , except : TypeVars , co : Boolean )(using Context ): Boolean =
247269 def origin (tv : TypeVar ) =
248270 assert(! tv.isInstantiated)
249271 tv.origin
@@ -253,7 +275,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
253275 def test (deps : ReverseDeps , lens : ConstraintLens [List [TypeParamRef ]]) =
254276 deps.at(param).exists(qualifies)
255277 || lens(this , tv.origin.binder, tv.origin.paramNum).exists(qualifies)
256- // .showing(i"outer depends on $tv with ${tvdeps.toList}%, % = $result")
257278 if co then test(coDeps, upperLens) else test(contraDeps, lowerLens)
258279
259280 /** Modify traversals in two respects:
@@ -271,10 +292,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
271292 *
272293 * - When typing a prefx, don't avoid negative variances. This matters only for the
273294 * corner case where a parameter is instantiated to Nothing (see comment in
274- * TypeAccumulator#applyToPrefix). When determining instantiation directions
275- * (which is what dependency variances are for), it can be ignored.
295+ * TypeAccumulator#applyToPrefix). When determining instantiation directions in
296+ * interpolations (which is what dependency variances are for), it can be ignored.
276297 */
277298 private trait ConstraintAwareTraversal [T ] extends TypeAccumulator [T ]:
299+
278300 override def tyconTypeParams (tp : AppliedType )(using Context ): List [ParamInfo ] =
279301 def tparams (tycon : Type ): List [ParamInfo ] = tycon match
280302 case tycon : TypeVar if ! tycon.isInstantiated => tparams(tycon.origin)
@@ -285,14 +307,16 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
285307 case _ => tp.tyconTypeParams
286308 case _ => tp.tyconTypeParams
287309 tparams(tp.tycon)
310+
288311 override def applyToPrefix (x : T , tp : NamedType ): T =
289312 this (x, tp.prefix)
313+ end ConstraintAwareTraversal
290314
291315 private class Adjuster (srcParam : TypeParamRef )(using Context )
292316 extends TypeTraverser , ConstraintAwareTraversal [Unit ]:
293317
294318 var add : Boolean = compiletime.uninitialized
295- val seen = util.HashSet [LazyRef ]()
319+ private val seen = util.HashSet [LazyRef ]()
296320
297321 def update (deps : ReverseDeps , referenced : TypeParamRef ): ReverseDeps =
298322 val prev = deps.at(referenced)
@@ -316,7 +340,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
316340 end Adjuster
317341
318342 /** Adjust dependencies to account for the delta of previous entry `prevEntry`
319- * and new bound `entry` for the type parameter `srcParam`.
343+ * and the new bound `entry` for the type parameter `srcParam`.
320344 */
321345 def adjustDeps (entry : Type | Null , prevEntry : Type | Null , srcParam : TypeParamRef )(using Context ): this .type =
322346 val adjuster = new Adjuster (srcParam)
@@ -332,8 +356,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
332356
333357 /** Use an optimized strategy to adjust dependencies to account for the delta
334358 * of previous bound `prevBound` and new bound `bound`: If `prevBound` is some
335- * and/or prefix of `bound`, just add the new parts of `bound`.
359+ * and/or prefix of `bound`, and `baseCase` is true, just add the new parts of `bound`.
336360 * @param isLower `bound` and `prevBound` are lower bounds
361+ * @return true iff the delta strategy succeeded, false if it failed in which case
362+ * the constraint is left unchanged.
337363 */
338364 def adjustDelta (bound : Type , prevBound : Type , isLower : Boolean , baseCase : => Boolean ): Boolean =
339365 if bound eq prevBound then
@@ -346,9 +372,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
346372 }
347373 case _ => false
348374
349- /** Adjust dependencies to account for the delta of previous bounds `prevBounds`
350- * and new bounds `bounds`.
351- * @param add true if the bounds are added, false if they are removed
375+ /** Add or remove depenencies referenced in `bounds`.
376+ * @param add if true, dependecies are added, otherwise they are removed
352377 */
353378 def adjustBounds (bounds : TypeBounds , add : Boolean ) =
354379 adjustReferenced(bounds.lo, isLower = true , add)
@@ -370,7 +395,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
370395 case prevEntry : TypeBounds =>
371396 adjustBounds(prevEntry, add = false )
372397 case _ =>
373- dropDeps(srcParam)
398+ dropDeps(srcParam) // srcParam is instantiated, so its dependencies can be dropped
374399 this
375400 end adjustDeps
376401
@@ -390,7 +415,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
390415 coDeps = coDeps.remove(param)
391416 contraDeps = contraDeps.remove(param)
392417
393- /** A string representing the two depenecy maps */
418+ /** A string representing the two dependency maps */
394419 def depsToString (using Context ): String =
395420 def depsStr (deps : ReverseDeps ): String =
396421 def depStr (param : TypeParamRef ) = i " $param --> ${deps.at(param).toList}%, % "
@@ -457,7 +482,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
457482 }
458483
459484 def add (poly : TypeLambda , tvars : List [TypeVar ])(using Context ): This = {
460- checkWellFormed() // TODO: drop
461485 assert(! contains(poly))
462486 val nparams = poly.paramNames.length
463487 val entries1 = new Array [Type ](nparams * 2 )
@@ -609,13 +633,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
609633 case _ =>
610634 Nil
611635
612- private def updateEntryNoOrdering (current : This , param : TypeParamRef , newEntry : Type , oldEntry : Type )(using Context ): This =
613- boundsLens.update(this , current, param, newEntry).adjustDeps(newEntry, oldEntry, param)
614-
615636 private def updateEntry (current : This , param : TypeParamRef , newEntry : Type )(using Context ): This = {
616- // println(i"update $param to $newEntry in $current")
617637 if Config .checkNoWildcardsInConstraint then assert(! newEntry.containsWildcardTypes)
618- var current1 = updateEntryNoOrdering(current, param, newEntry, current.entry(param))
638+ val oldEntry = current.entry(param)
639+ var current1 = boundsLens.update(this , current, param, newEntry)
640+ .adjustDeps(newEntry, oldEntry, param)
619641 newEntry match {
620642 case TypeBounds (lo, hi) =>
621643 for p <- dependentParams(lo, isUpper = false ) do
@@ -647,7 +669,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
647669 assert(replacement.isValueTypeOrLambda)
648670
649671 val replacedTypeVar = typeVarOfParam(param)
650- // println(i"replace $param, $replacedTypeVar with $replacement in $this")
672+ // println(i"replace $param with $replacement in $this")
651673
652674 def mapReplacedTypeVarTo (to : Type ) = new TypeMap :
653675 override def apply (t : Type ): Type =
@@ -673,13 +695,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
673695 case tvar : TypeVar =>
674696 if tvar.isInstantiated
675697 then
676- // replace is called from TypeVar's instantiateWith,
677- // forget about instantiation for old dependencies
698+ // That's the case if replace is called from TypeVar's instantiateWith.
699+ // Forget about instantiation for old dependencies.
678700 oldDepEntry = mapReplacedTypeVarTo(param)(oldDepEntry)
679701 else
680- // replace is called from unify,
681- // assume parameter has been replaced for new dependencies
682- // (the actual replacement is done below)
702+ // That's the case if replace is called from unify.
703+ // Assume parameter has been replaced for new dependencies
704+ // (the actual replacement is done below).
683705 newDepEntry = mapReplacedTypeVarTo(replacement)(newDepEntry)
684706 case _ =>
685707 if oldDepEntry ne newDepEntry then
@@ -864,9 +886,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
864886
865887// ---------- Checking -----------------------------------------------
866888
867- /** Depending on Config settngs, check that there are no cycles and that
868- * reverse depenecies are correct.
869- */
870889 def checkWellFormed ()(using Context ): this .type =
871890
872891 /** Check that each dependency A -> B in coDeps and contraDeps corresponds to
0 commit comments