@@ -18,6 +18,11 @@ import cc.{CapturingType, derivedCapturingType}
1818
1919object OrderingConstraint {
2020
21+ @ sharable private var id = 0
22+ private def nextId =
23+ id += 1
24+ id
25+
2126 type ArrayValuedMap [T ] = SimpleIdentityMap [TypeLambda , Array [T ]]
2227
2328 /** The type of `OrderingConstraint#boundsMap` */
@@ -140,6 +145,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
140145
141146 type This = OrderingConstraint
142147
148+ var id = nextId
149+ // if id == 118 then
150+ // new Error(s"at $id").printStackTrace()
151+
143152 /** A new constraint with given maps and given set of hard typevars */
144153 def newConstraint ( // !!! Dotty problem: Making newConstraint `private` causes -Ytest-pickler failure.
145154 boundsMap : ParamBounds = this .boundsMap,
@@ -245,8 +254,40 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
245254 // .showing(i"outer depends on $tv with ${tvdeps.toList}%, % = $result")
246255 if co then test(coDeps, upperLens) else test(contraDeps, lowerLens)
247256
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+
248289 private class Adjuster (srcParam : TypeParamRef )(using Context )
249- extends TypeTraverser , ConstraintAwareTraversal :
290+ extends TypeTraverser , ConstraintAwareTraversal [ Unit ] :
250291
251292 var add : Boolean = compiletime.uninitialized
252293 val seen = util.HashSet [LazyRef ]()
@@ -257,9 +298,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
257298
258299 def traverse (t : Type ) = t match
259300 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)
263307 case tp : LazyRef =>
264308 if ! seen.contains(tp) then
265309 seen += tp
@@ -408,6 +452,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
408452 }
409453
410454 def add (poly : TypeLambda , tvars : List [TypeVar ])(using Context ): This = {
455+ checkWellFormed() // TODO: drop
411456 assert(! contains(poly))
412457 val nparams = poly.paramNames.length
413458 val entries1 = new Array [Type ](nparams * 2 )
@@ -596,12 +641,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
596641 else
597642 assert(replacement.isValueTypeOrLambda)
598643
599- val droppedTypeVar = typeVarOfParam(param)
644+ val replacedTypeVar = typeVarOfParam(param)
645+ // println(i"replace $param, $replacedTypeVar with $replacement in $this")
600646
601- // println(i"replace $param, $droppedTypeVar with $replacement in $this")
602- val dropTypeVar = new TypeMap :
647+ def mapReplacedTypeVarTo (to : Type ) = new TypeMap :
603648 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)
605650
606651 var current = this
607652
@@ -616,7 +661,29 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
616661 if other != param then
617662 val oldEntry = current.entry(other)
618663 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)
620687 }
621688
622689 current =
@@ -703,6 +770,26 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
703770 assert(tvar.origin == param, i " mismatch $tvar, $param" )
704771 case _ =>
705772
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+
706793// ---------- Exploration --------------------------------------------------------
707794
708795 def domainLambdas : List [TypeLambda ] = boundsMap.keys
@@ -755,7 +842,60 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
755842
756843// ---------- Checking -----------------------------------------------
757844
845+ /** Depending on Config settngs, check that there are no cycles and that
846+ * reverse depenecies are correct.
847+ */
758848 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+
759899 if Config .checkConstraintsNonCyclic then
760900 domainParams.foreach { param =>
761901 val inst = entry(param)
@@ -765,37 +905,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
765905 s " cyclic bound for $param: ${inst.show} in ${this .show}" )
766906 }
767907 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+
775912 this
776913 end checkWellFormed
777914
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-
799915 override def checkClosed ()(using Context ): Unit =
800916
801917 def isFreeTypeParamRef (tp : Type ) = tp match
0 commit comments