@@ -18,6 +18,11 @@ 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
25
+
21
26
type ArrayValuedMap [T ] = SimpleIdentityMap [TypeLambda , Array [T ]]
22
27
23
28
/** The type of `OrderingConstraint#boundsMap` */
@@ -140,6 +145,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
140
145
141
146
type This = OrderingConstraint
142
147
148
+ var id = nextId
149
+ // if id == 118 then
150
+ // new Error(s"at $id").printStackTrace()
151
+
143
152
/** A new constraint with given maps and given set of hard typevars */
144
153
def newConstraint ( // !!! Dotty problem: Making newConstraint `private` causes -Ytest-pickler failure.
145
154
boundsMap : ParamBounds = this .boundsMap,
@@ -245,8 +254,40 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
245
254
// .showing(i"outer depends on $tv with ${tvdeps.toList}%, % = $result")
246
255
if co then test(coDeps, upperLens) else test(contraDeps, lowerLens)
247
256
257
+ /** Modify traversals in two respects:
258
+ * - when encountering an application C[Ts], where C is a type variable or parameter
259
+ * that has an instantiation in this constraint, assume the type parameters of
260
+ * the instantiation instead of the type parameters of C when traversing the
261
+ * arguments Ts. That can make a difference for the variance in which an argument
262
+ * is traversed. Example constraint:
263
+ *
264
+ * constrainded types: C[X], A
265
+ * A >: C[B]
266
+ * C := Option
267
+ *
268
+ * Here, B is traversed with variance +1 instead of 0. Test case: pos/t3152.scala
269
+ *
270
+ * - When typing a prefx, don't avoid negative variances. This matters only for the
271
+ * corner case where a parameter is instantiated to Nothing (see comment in
272
+ * TypeAccumulator#applyToPrefix). When determining instantiation directions
273
+ * (which is what dependency variances are for), it can be ignored.
274
+ */
275
+ private trait ConstraintAwareTraversal [T ] extends TypeAccumulator [T ]:
276
+ override def tyconTypeParams (tp : AppliedType )(using Context ): List [ParamInfo ] =
277
+ def tparams (tycon : Type ): List [ParamInfo ] = tycon match
278
+ case tycon : TypeVar if ! tycon.isInstantiated => tparams(tycon.origin)
279
+ case tycon : TypeParamRef =>
280
+ entry(tycon) match
281
+ case _ : TypeBounds => tp.tyconTypeParams
282
+ case tycon1 if tycon1.typeParams.nonEmpty => tycon1.typeParams
283
+ case _ => tp.tyconTypeParams
284
+ case _ => tp.tyconTypeParams
285
+ tparams(tp.tycon)
286
+ override def applyToPrefix (x : T , tp : NamedType ): T =
287
+ this (x, tp.prefix)
288
+
248
289
private class Adjuster (srcParam : TypeParamRef )(using Context )
249
- extends TypeTraverser , ConstraintAwareTraversal :
290
+ extends TypeTraverser , ConstraintAwareTraversal [ Unit ] :
250
291
251
292
var add : Boolean = compiletime.uninitialized
252
293
val seen = util.HashSet [LazyRef ]()
@@ -257,9 +298,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
257
298
258
299
def traverse (t : Type ) = t match
259
300
case param : TypeParamRef =>
260
- if contains(param) then
261
- if variance >= 0 then coDeps = update(coDeps, param)
262
- if variance <= 0 then contraDeps = update(contraDeps, param)
301
+ entry(param) match
302
+ case _ : TypeBounds =>
303
+ if variance >= 0 then coDeps = update(coDeps, param)
304
+ if variance <= 0 then contraDeps = update(contraDeps, param)
305
+ case tp =>
306
+ traverse(tp)
263
307
case tp : LazyRef =>
264
308
if ! seen.contains(tp) then
265
309
seen += tp
@@ -287,39 +331,40 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
287
331
* and/or prefix of `bound`, just add the new parts of `bound`.
288
332
* @param isLower `bound` and `prevBound` are lower bounds
289
333
*/
290
- def adjustDelta (bound : Type , prevBound : Type , isLower : Boolean ): Boolean =
291
- if bound eq prevBound then true
334
+ def adjustDelta (bound : Type , prevBound : Type , isLower : Boolean , baseCase : => Boolean ): Boolean =
335
+ if bound eq prevBound then
336
+ baseCase
292
337
else bound match
293
338
case bound : AndOrType =>
294
- adjustDelta(bound.tp1, prevBound, isLower) && {
339
+ adjustDelta(bound.tp1, prevBound, isLower, baseCase ) && {
295
340
adjustReferenced(bound.tp2, isLower, add = true )
296
341
true
297
342
}
298
343
case _ => false
299
344
300
- /** Adjust dependencies to account for the delta of previous bound `prevBound `
301
- * and new bound `bound `.
302
- * @param isLower `bound` and `prevBound` are lower bounds
345
+ /** Adjust dependencies to account for the delta of previous bounds `prevBounds `
346
+ * and new bounds `bounds `.
347
+ * @param add true if the bounds are added, false if they are removed
303
348
*/
304
- def adjustBounds (bound : Type , prevBound : Type , isLower : Boolean ) =
305
- if ! adjustDelta(bound, prevBound, isLower) then
306
- adjustReferenced(prevBound, isLower, add = false )
307
- adjustReferenced(bound, isLower, add = true )
349
+ def adjustBounds (bounds : TypeBounds , add : Boolean ) =
350
+ adjustReferenced(bounds.lo, isLower = true , add)
351
+ adjustReferenced(bounds.hi, isLower = false , add)
308
352
309
353
entry match
310
- case TypeBounds (lo, hi) =>
354
+ case entry @ TypeBounds (lo, hi) =>
311
355
prevEntry match
312
- case TypeBounds (plo, phi) =>
313
- adjustBounds(lo, plo, isLower = true )
314
- adjustBounds(hi, phi, isLower = false )
356
+ case prevEntry @ TypeBounds (plo, phi) =>
357
+ if ! adjustDelta(lo, plo, isLower = true ,
358
+ adjustDelta(hi, phi, isLower = false , true ))
359
+ then
360
+ adjustBounds(prevEntry, add = false )
361
+ adjustBounds(entry, add = true )
315
362
case _ =>
316
- adjustReferenced(lo, isLower = true , add = true )
317
- adjustReferenced(hi, isLower = false , add = true )
363
+ adjustBounds(entry, add = true )
318
364
case _ =>
319
365
prevEntry match
320
- case TypeBounds (plo, phi) =>
321
- adjustReferenced(plo, isLower = true , add = false )
322
- adjustReferenced(phi, isLower = false , add = false )
366
+ case prevEntry : TypeBounds =>
367
+ adjustBounds(prevEntry, add = false )
323
368
case _ =>
324
369
dropDeps(srcParam)
325
370
this
@@ -408,6 +453,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
408
453
}
409
454
410
455
def add (poly : TypeLambda , tvars : List [TypeVar ])(using Context ): This = {
456
+ checkWellFormed() // TODO: drop
411
457
assert(! contains(poly))
412
458
val nparams = poly.paramNames.length
413
459
val entries1 = new Array [Type ](nparams * 2 )
@@ -563,7 +609,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
563
609
boundsLens.update(this , current, param, newEntry).adjustDeps(newEntry, oldEntry, param)
564
610
565
611
private def updateEntry (current : This , param : TypeParamRef , newEntry : Type )(using Context ): This = {
566
- // println(i"update $param to $tp in $current")
612
+ // println(i"update $param to $newEntry in $current")
567
613
if Config .checkNoWildcardsInConstraint then assert(! newEntry.containsWildcardTypes)
568
614
var current1 = updateEntryNoOrdering(current, param, newEntry, current.entry(param))
569
615
newEntry match {
@@ -596,12 +642,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
596
642
else
597
643
assert(replacement.isValueTypeOrLambda)
598
644
599
- val droppedTypeVar = typeVarOfParam(param)
645
+ val replacedTypeVar = typeVarOfParam(param)
646
+ // println(i"replace $param, $replacedTypeVar with $replacement in $this")
600
647
601
- // println(i"replace $param, $droppedTypeVar with $replacement in $this")
602
- val dropTypeVar = new TypeMap :
648
+ def mapReplacedTypeVarTo (to : Type ) = new TypeMap :
603
649
override def apply (t : Type ): Type =
604
- if t.exists && (t eq droppedTypeVar) then param else mapOver(t)
650
+ if (t eq replacedTypeVar) && t.exists then to else mapOver(t)
605
651
606
652
var current = this
607
653
@@ -616,7 +662,29 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
616
662
if other != param then
617
663
val oldEntry = current.entry(other)
618
664
val newEntry = current.ensureNonCyclic(other, oldEntry.substParam(param, replacement))
619
- current = updateEntryNoOrdering(current, other, newEntry, dropTypeVar(oldEntry))
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)
620
688
}
621
689
622
690
current =
@@ -703,6 +771,26 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
703
771
assert(tvar.origin == param, i " mismatch $tvar, $param" )
704
772
case _ =>
705
773
774
+ def occursAtToplevel (param : TypeParamRef , inst : Type )(using Context ): Boolean =
775
+ def occurs (tp : Type )(using Context ): Boolean = tp match
776
+ case tp : AndOrType =>
777
+ occurs(tp.tp1) || occurs(tp.tp2)
778
+ case tp : TypeParamRef =>
779
+ (tp eq param) || entry(tp).match
780
+ case NoType => false
781
+ case TypeBounds (lo, hi) => (lo eq hi) && occurs(lo)
782
+ case inst => occurs(inst)
783
+ case tp : TypeVar =>
784
+ occurs(tp.underlying)
785
+ case TypeBounds (lo, hi) =>
786
+ occurs(lo) || occurs(hi)
787
+ case _ =>
788
+ val tp1 = tp.dealias
789
+ (tp1 ne tp) && occurs(tp1)
790
+
791
+ occurs(inst)
792
+ end occursAtToplevel
793
+
706
794
// ---------- Exploration --------------------------------------------------------
707
795
708
796
def domainLambdas : List [TypeLambda ] = boundsMap.keys
@@ -755,7 +843,60 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
755
843
756
844
// ---------- Checking -----------------------------------------------
757
845
846
+ /** Depending on Config settngs, check that there are no cycles and that
847
+ * reverse depenecies are correct.
848
+ */
758
849
def checkWellFormed ()(using Context ): this .type =
850
+
851
+ /** Check that each dependency A -> B in coDeps and contraDeps corresponds to
852
+ * a reference to A at the right variance in the entry of B.
853
+ */
854
+ def checkBackward (deps : ReverseDeps , depsName : String , v : Int )(using Context ): Unit =
855
+ deps.foreachBinding { (param, params) =>
856
+ for srcParam <- params do
857
+ assert(contains(srcParam) && occursAtVariance(param, v, in = entry(srcParam)),
858
+ i " wrong $depsName backwards reference $param -> $srcParam in $thisConstraint" )
859
+ }
860
+
861
+ /** A type traverser that checks that all references bound in the constraint
862
+ * are accounted for in coDeps and/or contraDeps.
863
+ */
864
+ def checkForward (srcParam : TypeParamRef )(using Context ) =
865
+ new TypeTraverser with ConstraintAwareTraversal [Unit ]:
866
+ val seen = util.HashSet [LazyRef ]()
867
+ def traverse (t : Type ): Unit = t match
868
+ case param : TypeParamRef if param ne srcParam =>
869
+ def check (deps : ReverseDeps , directDeps : List [TypeParamRef ], depsName : String ) =
870
+ assert(deps.at(param).contains(srcParam) || directDeps.contains(srcParam),
871
+ i " missing $depsName backwards reference $param -> $srcParam in $thisConstraint" )
872
+ entry(param) match
873
+ case _ : TypeBounds =>
874
+ if variance >= 0 then check(contraDeps, upper(param), " contra" )
875
+ if variance <= 0 then check(coDeps, lower(param), " co" )
876
+ case tp =>
877
+ traverse(tp)
878
+ case tp : LazyRef =>
879
+ if ! seen.contains(tp) then
880
+ seen += tp
881
+ traverse(tp.ref)
882
+ case _ => traverseChildren(t)
883
+
884
+ /** Does `param` occur at variance `v` or else at variance 0 in entry `in`? */
885
+ def occursAtVariance (param : TypeParamRef , v : Int , in : Type )(using Context ): Boolean =
886
+ val test = new TypeAccumulator [Boolean ] with ConstraintAwareTraversal [Boolean ]:
887
+ def apply (x : Boolean , t : Type ): Boolean =
888
+ if x then true
889
+ else t match
890
+ case t : TypeParamRef =>
891
+ entry(t) match
892
+ case _ : TypeBounds =>
893
+ t == param && (variance == 0 || variance == v)
894
+ case e =>
895
+ apply(x, e)
896
+ case _ =>
897
+ foldOver(x, t)
898
+ test(false , in)
899
+
759
900
if Config .checkConstraintsNonCyclic then
760
901
domainParams.foreach { param =>
761
902
val inst = entry(param)
@@ -764,38 +905,14 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
764
905
assert(! occursAtToplevel(param, inst),
765
906
s " cyclic bound for $param: ${inst.show} in ${this .show}" )
766
907
}
767
- if Config .checkConstraintDeps then
768
- def checkDeps (deps : ReverseDeps ) = ()/*
769
- deps.foreachBinding { (tv, tvs) =>
770
- for tv1 <- tvs do
771
- assert(!tv1.instanceOpt.exists, i"$this")
772
- }*/
773
- checkDeps(coDeps)
774
- checkDeps(contraDeps)
908
+ if Config .checkConstraintDeps || ctx.settings.YcheckConstraintDeps .value then
909
+ checkBackward(coDeps, " co" , - 1 )
910
+ checkBackward(contraDeps, " contra" , + 1 )
911
+ domainParams.foreach(p => if contains(p) then checkForward(p).traverse(entry(p)))
912
+
775
913
this
776
914
end checkWellFormed
777
915
778
- def occursAtToplevel (param : TypeParamRef , inst : Type )(using Context ): Boolean =
779
-
780
- def occurs (tp : Type )(using Context ): Boolean = tp match
781
- case tp : AndOrType =>
782
- occurs(tp.tp1) || occurs(tp.tp2)
783
- case tp : TypeParamRef =>
784
- (tp eq param) || entry(tp).match
785
- case NoType => false
786
- case TypeBounds (lo, hi) => (lo eq hi) && occurs(lo)
787
- case inst => occurs(inst)
788
- case tp : TypeVar =>
789
- occurs(tp.underlying)
790
- case TypeBounds (lo, hi) =>
791
- occurs(lo) || occurs(hi)
792
- case _ =>
793
- val tp1 = tp.dealias
794
- (tp1 ne tp) && occurs(tp1)
795
-
796
- occurs(inst)
797
- end occursAtToplevel
798
-
799
916
override def checkClosed ()(using Context ): Unit =
800
917
801
918
def isFreeTypeParamRef (tp : Type ) = tp match
0 commit comments