@@ -10,8 +10,6 @@ import printing.Texts._
10
10
import config .Config
11
11
import config .Printers .constr
12
12
import reflect .ClassTag
13
- import Constraint .ReverseDeps
14
- import Substituters .SubstParamMap
15
13
import annotation .tailrec
16
14
import annotation .internal .sharable
17
15
import cc .{CapturingType , derivedCapturingType }
@@ -25,20 +23,27 @@ object OrderingConstraint {
25
23
* same P is added as a bound variable to the constraint, a backwards link would
26
24
* then become necessary at this point but is missing. This causes two CB projects
27
25
* 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.
29
34
*/
30
- final val optimizeReplace = true
35
+ private final val optimizeReplace = true
31
36
32
- type ArrayValuedMap [T ] = SimpleIdentityMap [TypeLambda , Array [T ]]
37
+ private type ArrayValuedMap [T ] = SimpleIdentityMap [TypeLambda , Array [T ]]
33
38
34
39
/** The type of `OrderingConstraint#boundsMap` */
35
- type ParamBounds = ArrayValuedMap [Type ]
40
+ private type ParamBounds = ArrayValuedMap [Type ]
36
41
37
42
/** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */
38
- type ParamOrdering = ArrayValuedMap [List [TypeParamRef ]]
43
+ private type ParamOrdering = ArrayValuedMap [List [TypeParamRef ]]
39
44
40
45
/** 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 ] {
42
47
def entries (c : OrderingConstraint , poly : TypeLambda ): Array [T ] | Null
43
48
def updateEntries (c : OrderingConstraint , poly : TypeLambda , entries : Array [T ])(using Context ): OrderingConstraint
44
49
def initial : T
@@ -91,23 +96,23 @@ object OrderingConstraint {
91
96
map(prev, current, param.binder, param.paramNum, f)
92
97
}
93
98
94
- val boundsLens : ConstraintLens [Type ] = new ConstraintLens [Type ] {
99
+ private val boundsLens : ConstraintLens [Type ] = new ConstraintLens [Type ] {
95
100
def entries (c : OrderingConstraint , poly : TypeLambda ): Array [Type ] | Null =
96
101
c.boundsMap(poly)
97
102
def updateEntries (c : OrderingConstraint , poly : TypeLambda , entries : Array [Type ])(using Context ): OrderingConstraint =
98
103
c.newConstraint(boundsMap = c.boundsMap.updated(poly, entries))
99
104
def initial = NoType
100
105
}
101
106
102
- val lowerLens : ConstraintLens [List [TypeParamRef ]] = new ConstraintLens [List [TypeParamRef ]] {
107
+ private val lowerLens : ConstraintLens [List [TypeParamRef ]] = new ConstraintLens [List [TypeParamRef ]] {
103
108
def entries (c : OrderingConstraint , poly : TypeLambda ): Array [List [TypeParamRef ]] | Null =
104
109
c.lowerMap(poly)
105
110
def updateEntries (c : OrderingConstraint , poly : TypeLambda , entries : Array [List [TypeParamRef ]])(using Context ): OrderingConstraint =
106
111
c.newConstraint(lowerMap = c.lowerMap.updated(poly, entries))
107
112
def initial = Nil
108
113
}
109
114
110
- val upperLens : ConstraintLens [List [TypeParamRef ]] = new ConstraintLens [List [TypeParamRef ]] {
115
+ private val upperLens : ConstraintLens [List [TypeParamRef ]] = new ConstraintLens [List [TypeParamRef ]] {
111
116
def entries (c : OrderingConstraint , poly : TypeLambda ): Array [List [TypeParamRef ]] | Null =
112
117
c.upperMap(poly)
113
118
def updateEntries (c : OrderingConstraint , poly : TypeLambda , entries : Array [List [TypeParamRef ]])(using Context ): OrderingConstraint =
@@ -237,13 +242,30 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
237
242
238
243
// ------------- Type parameter dependencies ----------------------------------------
239
244
240
- var coDeps, contraDeps : ReverseDeps = SimpleIdentityMap .empty
245
+ private type ReverseDeps = SimpleIdentityMap [ TypeParamRef , SimpleIdentitySet [ TypeParamRef ]]
241
246
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 ] =
243
263
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
245
267
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 =
247
269
def origin (tv : TypeVar ) =
248
270
assert(! tv.isInstantiated)
249
271
tv.origin
@@ -253,7 +275,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
253
275
def test (deps : ReverseDeps , lens : ConstraintLens [List [TypeParamRef ]]) =
254
276
deps.at(param).exists(qualifies)
255
277
|| lens(this , tv.origin.binder, tv.origin.paramNum).exists(qualifies)
256
- // .showing(i"outer depends on $tv with ${tvdeps.toList}%, % = $result")
257
278
if co then test(coDeps, upperLens) else test(contraDeps, lowerLens)
258
279
259
280
/** Modify traversals in two respects:
@@ -271,10 +292,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
271
292
*
272
293
* - When typing a prefx, don't avoid negative variances. This matters only for the
273
294
* 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.
276
297
*/
277
298
private trait ConstraintAwareTraversal [T ] extends TypeAccumulator [T ]:
299
+
278
300
override def tyconTypeParams (tp : AppliedType )(using Context ): List [ParamInfo ] =
279
301
def tparams (tycon : Type ): List [ParamInfo ] = tycon match
280
302
case tycon : TypeVar if ! tycon.isInstantiated => tparams(tycon.origin)
@@ -285,14 +307,16 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
285
307
case _ => tp.tyconTypeParams
286
308
case _ => tp.tyconTypeParams
287
309
tparams(tp.tycon)
310
+
288
311
override def applyToPrefix (x : T , tp : NamedType ): T =
289
312
this (x, tp.prefix)
313
+ end ConstraintAwareTraversal
290
314
291
315
private class Adjuster (srcParam : TypeParamRef )(using Context )
292
316
extends TypeTraverser , ConstraintAwareTraversal [Unit ]:
293
317
294
318
var add : Boolean = compiletime.uninitialized
295
- val seen = util.HashSet [LazyRef ]()
319
+ private val seen = util.HashSet [LazyRef ]()
296
320
297
321
def update (deps : ReverseDeps , referenced : TypeParamRef ): ReverseDeps =
298
322
val prev = deps.at(referenced)
@@ -316,7 +340,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
316
340
end Adjuster
317
341
318
342
/** 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`.
320
344
*/
321
345
def adjustDeps (entry : Type | Null , prevEntry : Type | Null , srcParam : TypeParamRef )(using Context ): this .type =
322
346
val adjuster = new Adjuster (srcParam)
@@ -332,8 +356,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
332
356
333
357
/** Use an optimized strategy to adjust dependencies to account for the delta
334
358
* 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`.
336
360
* @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.
337
363
*/
338
364
def adjustDelta (bound : Type , prevBound : Type , isLower : Boolean , baseCase : => Boolean ): Boolean =
339
365
if bound eq prevBound then
@@ -346,9 +372,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
346
372
}
347
373
case _ => false
348
374
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
352
377
*/
353
378
def adjustBounds (bounds : TypeBounds , add : Boolean ) =
354
379
adjustReferenced(bounds.lo, isLower = true , add)
@@ -370,7 +395,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
370
395
case prevEntry : TypeBounds =>
371
396
adjustBounds(prevEntry, add = false )
372
397
case _ =>
373
- dropDeps(srcParam)
398
+ dropDeps(srcParam) // srcParam is instantiated, so its dependencies can be dropped
374
399
this
375
400
end adjustDeps
376
401
@@ -390,7 +415,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
390
415
coDeps = coDeps.remove(param)
391
416
contraDeps = contraDeps.remove(param)
392
417
393
- /** A string representing the two depenecy maps */
418
+ /** A string representing the two dependency maps */
394
419
def depsToString (using Context ): String =
395
420
def depsStr (deps : ReverseDeps ): String =
396
421
def depStr (param : TypeParamRef ) = i " $param --> ${deps.at(param).toList}%, % "
@@ -457,7 +482,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
457
482
}
458
483
459
484
def add (poly : TypeLambda , tvars : List [TypeVar ])(using Context ): This = {
460
- checkWellFormed() // TODO: drop
461
485
assert(! contains(poly))
462
486
val nparams = poly.paramNames.length
463
487
val entries1 = new Array [Type ](nparams * 2 )
@@ -609,13 +633,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
609
633
case _ =>
610
634
Nil
611
635
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
-
615
636
private def updateEntry (current : This , param : TypeParamRef , newEntry : Type )(using Context ): This = {
616
- // println(i"update $param to $newEntry in $current")
617
637
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)
619
641
newEntry match {
620
642
case TypeBounds (lo, hi) =>
621
643
for p <- dependentParams(lo, isUpper = false ) do
@@ -647,7 +669,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
647
669
assert(replacement.isValueTypeOrLambda)
648
670
649
671
val replacedTypeVar = typeVarOfParam(param)
650
- // println(i"replace $param, $replacedTypeVar with $replacement in $this")
672
+ // println(i"replace $param with $replacement in $this")
651
673
652
674
def mapReplacedTypeVarTo (to : Type ) = new TypeMap :
653
675
override def apply (t : Type ): Type =
@@ -673,13 +695,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
673
695
case tvar : TypeVar =>
674
696
if tvar.isInstantiated
675
697
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.
678
700
oldDepEntry = mapReplacedTypeVarTo(param)(oldDepEntry)
679
701
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).
683
705
newDepEntry = mapReplacedTypeVarTo(replacement)(newDepEntry)
684
706
case _ =>
685
707
if oldDepEntry ne newDepEntry then
@@ -864,9 +886,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
864
886
865
887
// ---------- Checking -----------------------------------------------
866
888
867
- /** Depending on Config settngs, check that there are no cycles and that
868
- * reverse depenecies are correct.
869
- */
870
889
def checkWellFormed ()(using Context ): this .type =
871
890
872
891
/** Check that each dependency A -> B in coDeps and contraDeps corresponds to
0 commit comments