@@ -297,27 +297,36 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
297297 */
298298 private trait ConstraintAwareTraversal [T ] extends TypeAccumulator [T ]:
299299
300+ /** Does `param` have bounds in the current constraint? */
301+ protected def hasBounds (param : TypeParamRef ): Boolean = entry(param).isInstanceOf [TypeBounds ]
302+
300303 override def tyconTypeParams (tp : AppliedType )(using Context ): List [ParamInfo ] =
301304 def tparams (tycon : Type ): List [ParamInfo ] = tycon match
302305 case tycon : TypeVar if ! tycon.inst.exists => tparams(tycon.origin)
303- case tycon : TypeParamRef =>
304- entry(tycon) match
305- case _ : TypeBounds => tp.tyconTypeParams
306- case tycon1 if tycon1.typeParams.nonEmpty => tycon1.typeParams
307- case _ => tp.tyconTypeParams
306+ case tycon : TypeParamRef if ! hasBounds(tycon) =>
307+ val entryParams = entry(tycon).typeParams
308+ if entryParams.nonEmpty then entryParams
309+ else tp.tyconTypeParams
308310 case _ => tp.tyconTypeParams
309311 tparams(tp.tycon)
310312
311313 override def applyToPrefix (x : T , tp : NamedType ): T =
312314 this (x, tp.prefix)
313315 end ConstraintAwareTraversal
314316
315- private class Adjuster (srcParam : TypeParamRef )(using Context )
317+ /** A type traverser that adjust dependencies originating from a given type
318+ * @param ignoreBinding if not null, a parameter that is assumed to be still uninstantiated.
319+ * This is necessary to handle replacements.
320+ */
321+ private class Adjuster (srcParam : TypeParamRef , ignoreBinding : TypeParamRef | Null )(using Context )
316322 extends TypeTraverser , ConstraintAwareTraversal [Unit ]:
317323
318324 var add : Boolean = compiletime.uninitialized
319325 val seen = util.HashSet [LazyRef ]()
320326
327+ override protected def hasBounds (param : TypeParamRef ) =
328+ (param eq ignoreBinding) || super .hasBounds(param)
329+
321330 def update (deps : ReverseDeps , referenced : TypeParamRef ): ReverseDeps =
322331 val prev = deps.at(referenced)
323332 val newSet = if add then prev + srcParam else prev - srcParam
@@ -326,12 +335,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
326335
327336 def traverse (t : Type ) = t match
328337 case param : TypeParamRef =>
329- entry(param) match
330- case _ : TypeBounds =>
331- if variance >= 0 then coDeps = update(coDeps, param)
332- if variance <= 0 then contraDeps = update(contraDeps, param)
333- case tp =>
334- traverse(tp)
338+ if hasBounds(param) then
339+ if variance >= 0 then coDeps = update(coDeps, param)
340+ if variance <= 0 then contraDeps = update(contraDeps, param)
341+ else
342+ traverse(entry(param))
335343 case tp : LazyRef =>
336344 if ! seen.contains(tp) then
337345 seen += tp
@@ -342,8 +350,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
342350 /** Adjust dependencies to account for the delta of previous entry `prevEntry`
343351 * and the new bound `entry` for the type parameter `srcParam`.
344352 */
345- def adjustDeps (entry : Type | Null , prevEntry : Type | Null , srcParam : TypeParamRef )(using Context ): this .type =
346- val adjuster = new Adjuster (srcParam)
353+ def adjustDeps (entry : Type | Null , prevEntry : Type | Null , srcParam : TypeParamRef , ignoreBinding : TypeParamRef | Null = null )(using Context ): this .type =
354+ val adjuster = new Adjuster (srcParam, ignoreBinding )
347355
348356 /** Adjust reverse dependencies of all type parameters referenced by `bound`
349357 * @param isLower `bound` is a lower bound
@@ -676,7 +684,14 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
676684 override def apply (t : Type ): Type =
677685 if (t eq replacedTypeVar) && t.exists then to else mapOver(t)
678686
679- var current = this
687+ val coDepsOfParam = coDeps.at(param)
688+ val contraDepsOfParam = contraDeps.at(param)
689+
690+ var current = updateEntry(this , param, replacement)
691+ // Need to update param early to avoid infinite recursion on instantiation.
692+ // See i16311.scala for a test case. On the other hand, for the purposes of
693+ // dependency adjustment, we need to pretend that `param` is still unbound.
694+ // We achieve that by passing a `ignoreBinding = param` to `adjustDeps` below.
680695
681696 def removeParamFrom (ps : List [TypeParamRef ]) =
682697 ps.filterConserve(param ne _)
@@ -710,22 +725,15 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
710725 newDepEntry = mapReplacedTypeVarTo(replacement)(newDepEntry)
711726 case _ =>
712727 if oldDepEntry ne newDepEntry then
713- if current eq this then
714- // We can end up here if oldEntry eq newEntry, so posssibly no new constraint
715- // was created, but oldDepEntry ne newDepEntry. In that case we must make
716- // sure we have a new constraint before updating dependencies.
717- current = newConstraint()
718- current.adjustDeps(newDepEntry, oldDepEntry, other)
728+ current.adjustDeps(newDepEntry, oldDepEntry, other, ignoreBinding = param)
719729 end replaceParamIn
720730
721731 if optimizeReplace then
722- val co = current.coDeps.at(param)
723- val contra = current.contraDeps.at(param)
724732 current.foreachParam { (p, i) =>
725733 val other = p.paramRefs(i)
726734 entry(other) match
727735 case _ : TypeBounds =>
728- if co .contains(other) || contra .contains(other) then
736+ if coDepsOfParam .contains(other) || contraDepsOfParam .contains(other) then
729737 replaceParamIn(other)
730738 case _ => replaceParamIn(other)
731739 }
@@ -734,10 +742,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
734742 val other = p.paramRefs(i)
735743 if other != param then replaceParamIn(other)
736744 }
737-
738- current =
739- if isRemovable(param.binder) then current.remove(param.binder)
740- else updateEntry(current, param, replacement)
745+ if isRemovable(param.binder) then current = current.remove(param.binder)
741746 current.dropDeps(param)
742747 current.checkWellFormed()
743748 end replace
0 commit comments