@@ -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
@@ -408,6 +452,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
408
452
}
409
453
410
454
def add (poly : TypeLambda , tvars : List [TypeVar ])(using Context ): This = {
455
+ checkWellFormed() // TODO: drop
411
456
assert(! contains(poly))
412
457
val nparams = poly.paramNames.length
413
458
val entries1 = new Array [Type ](nparams * 2 )
@@ -596,12 +641,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
596
641
else
597
642
assert(replacement.isValueTypeOrLambda)
598
643
599
- val droppedTypeVar = typeVarOfParam(param)
644
+ val replacedTypeVar = typeVarOfParam(param)
645
+ // println(i"replace $param, $replacedTypeVar with $replacement in $this")
600
646
601
- // println(i"replace $param, $droppedTypeVar with $replacement in $this")
602
- val dropTypeVar = new TypeMap :
647
+ def mapReplacedTypeVarTo (to : Type ) = new TypeMap :
603
648
override def apply (t : Type ): Type =
604
- if t.exists && (t eq droppedTypeVar) then param else mapOver(t)
649
+ if (t eq replacedTypeVar) && t.exists then to else mapOver(t)
605
650
606
651
var current = this
607
652
@@ -616,7 +661,29 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
616
661
if other != param then
617
662
val oldEntry = current.entry(other)
618
663
val newEntry = current.ensureNonCyclic(other, oldEntry.substParam(param, replacement))
619
- current = updateEntryNoOrdering(current, other, newEntry, dropTypeVar(oldEntry))
664
+ current = boundsLens.update(this , current, other, newEntry)
665
+ var oldDepEntry = oldEntry
666
+ var newDepEntry = newEntry
667
+ replacedTypeVar match
668
+ case tvar : TypeVar =>
669
+ if tvar.isInstantiated
670
+ then
671
+ // replace is called from TypeVar's instantiateWith,
672
+ // forget about instantiation for old dependencies
673
+ oldDepEntry = mapReplacedTypeVarTo(param)(oldDepEntry)
674
+ else
675
+ // replace is called from unify,
676
+ // assume parameter has been replaced for new dependencies
677
+ // (the actual replacement is done below)
678
+ newDepEntry = mapReplacedTypeVarTo(replacement)(newDepEntry)
679
+ case _ =>
680
+ if oldDepEntry ne newDepEntry then
681
+ if current eq this then
682
+ // We can end up here if oldEntry eq newEntry, so posssibly no new constraint
683
+ // was created, but oldDepEntry ne newDepEntry. In that case we must make
684
+ // sure we have a new constraint before updating dependencies.
685
+ current = newConstraint()
686
+ current.adjustDeps(newDepEntry, oldDepEntry, other)
620
687
}
621
688
622
689
current =
@@ -703,6 +770,26 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
703
770
assert(tvar.origin == param, i " mismatch $tvar, $param" )
704
771
case _ =>
705
772
773
+ def occursAtToplevel (param : TypeParamRef , inst : Type )(using Context ): Boolean =
774
+ def occurs (tp : Type )(using Context ): Boolean = tp match
775
+ case tp : AndOrType =>
776
+ occurs(tp.tp1) || occurs(tp.tp2)
777
+ case tp : TypeParamRef =>
778
+ (tp eq param) || entry(tp).match
779
+ case NoType => false
780
+ case TypeBounds (lo, hi) => (lo eq hi) && occurs(lo)
781
+ case inst => occurs(inst)
782
+ case tp : TypeVar =>
783
+ occurs(tp.underlying)
784
+ case TypeBounds (lo, hi) =>
785
+ occurs(lo) || occurs(hi)
786
+ case _ =>
787
+ val tp1 = tp.dealias
788
+ (tp1 ne tp) && occurs(tp1)
789
+
790
+ occurs(inst)
791
+ end occursAtToplevel
792
+
706
793
// ---------- Exploration --------------------------------------------------------
707
794
708
795
def domainLambdas : List [TypeLambda ] = boundsMap.keys
@@ -755,7 +842,60 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
755
842
756
843
// ---------- Checking -----------------------------------------------
757
844
845
+ /** Depending on Config settngs, check that there are no cycles and that
846
+ * reverse depenecies are correct.
847
+ */
758
848
def checkWellFormed ()(using Context ): this .type =
849
+
850
+ /** Check that each dependency A -> B in coDeps and contraDeps corresponds to
851
+ * a reference to A at the right variance in the entry of B.
852
+ */
853
+ def checkBackward (deps : ReverseDeps , depsName : String , v : Int )(using Context ): Unit =
854
+ deps.foreachBinding { (param, params) =>
855
+ for srcParam <- params do
856
+ assert(contains(srcParam) && occursAtVariance(param, v, in = entry(srcParam)),
857
+ i " wrong $depsName backwards reference $param -> $srcParam in $thisConstraint" )
858
+ }
859
+
860
+ /** A type traverser that checks that all references bound in the constraint
861
+ * are accounted for in coDeps and/or contraDeps.
862
+ */
863
+ def checkForward (srcParam : TypeParamRef )(using Context ) =
864
+ new TypeTraverser with ConstraintAwareTraversal [Unit ]:
865
+ val seen = util.HashSet [LazyRef ]()
866
+ def traverse (t : Type ): Unit = t match
867
+ case param : TypeParamRef if param ne srcParam =>
868
+ def check (deps : ReverseDeps , directDeps : List [TypeParamRef ], depsName : String ) =
869
+ assert(deps.at(param).contains(srcParam) || directDeps.contains(srcParam),
870
+ i " missing $depsName backwards reference $param -> $srcParam in $thisConstraint" )
871
+ entry(param) match
872
+ case _ : TypeBounds =>
873
+ if variance >= 0 then check(contraDeps, upper(param), " contra" )
874
+ if variance <= 0 then check(coDeps, lower(param), " co" )
875
+ case tp =>
876
+ traverse(tp)
877
+ case tp : LazyRef =>
878
+ if ! seen.contains(tp) then
879
+ seen += tp
880
+ traverse(tp.ref)
881
+ case _ => traverseChildren(t)
882
+
883
+ /** Does `param` occur at variance `v` or else at variance 0 in entry `in`? */
884
+ def occursAtVariance (param : TypeParamRef , v : Int , in : Type )(using Context ): Boolean =
885
+ val test = new TypeAccumulator [Boolean ] with ConstraintAwareTraversal [Boolean ]:
886
+ def apply (x : Boolean , t : Type ): Boolean =
887
+ if x then true
888
+ else t match
889
+ case t : TypeParamRef =>
890
+ entry(t) match
891
+ case _ : TypeBounds =>
892
+ t == param && (variance == 0 || variance == v)
893
+ case e =>
894
+ apply(x, e)
895
+ case _ =>
896
+ foldOver(x, t)
897
+ test(false , in)
898
+
759
899
if Config .checkConstraintsNonCyclic then
760
900
domainParams.foreach { param =>
761
901
val inst = entry(param)
@@ -765,37 +905,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
765
905
s " cyclic bound for $param: ${inst.show} in ${this .show}" )
766
906
}
767
907
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
+ checkBackward(coDeps, " co" , - 1 )
909
+ checkBackward(contraDeps, " contra" , + 1 )
910
+ domainParams.foreach(p => if contains(p) then checkForward(p).traverse(entry(p)))
911
+
775
912
this
776
913
end checkWellFormed
777
914
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
915
override def checkClosed ()(using Context ): Unit =
800
916
801
917
def isFreeTypeParamRef (tp : Type ) = tp match
0 commit comments