@@ -297,27 +297,36 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
297
297
*/
298
298
private trait ConstraintAwareTraversal [T ] extends TypeAccumulator [T ]:
299
299
300
+ /** Does `param` have bounds in the current constraint? */
301
+ protected def hasBounds (param : TypeParamRef ): Boolean = entry(param).isInstanceOf [TypeBounds ]
302
+
300
303
override def tyconTypeParams (tp : AppliedType )(using Context ): List [ParamInfo ] =
301
304
def tparams (tycon : Type ): List [ParamInfo ] = tycon match
302
305
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
308
310
case _ => tp.tyconTypeParams
309
311
tparams(tp.tycon)
310
312
311
313
override def applyToPrefix (x : T , tp : NamedType ): T =
312
314
this (x, tp.prefix)
313
315
end ConstraintAwareTraversal
314
316
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 )
316
322
extends TypeTraverser , ConstraintAwareTraversal [Unit ]:
317
323
318
324
var add : Boolean = compiletime.uninitialized
319
325
val seen = util.HashSet [LazyRef ]()
320
326
327
+ override protected def hasBounds (param : TypeParamRef ) =
328
+ (param eq ignoreBinding) || super .hasBounds(param)
329
+
321
330
def update (deps : ReverseDeps , referenced : TypeParamRef ): ReverseDeps =
322
331
val prev = deps.at(referenced)
323
332
val newSet = if add then prev + srcParam else prev - srcParam
@@ -326,12 +335,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
326
335
327
336
def traverse (t : Type ) = t match
328
337
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))
335
343
case tp : LazyRef =>
336
344
if ! seen.contains(tp) then
337
345
seen += tp
@@ -342,8 +350,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
342
350
/** Adjust dependencies to account for the delta of previous entry `prevEntry`
343
351
* and the new bound `entry` for the type parameter `srcParam`.
344
352
*/
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 )
347
355
348
356
/** Adjust reverse dependencies of all type parameters referenced by `bound`
349
357
* @param isLower `bound` is a lower bound
@@ -676,7 +684,14 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
676
684
override def apply (t : Type ): Type =
677
685
if (t eq replacedTypeVar) && t.exists then to else mapOver(t)
678
686
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.
680
695
681
696
def removeParamFrom (ps : List [TypeParamRef ]) =
682
697
ps.filterConserve(param ne _)
@@ -710,22 +725,15 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
710
725
newDepEntry = mapReplacedTypeVarTo(replacement)(newDepEntry)
711
726
case _ =>
712
727
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)
719
729
end replaceParamIn
720
730
721
731
if optimizeReplace then
722
- val co = current.coDeps.at(param)
723
- val contra = current.contraDeps.at(param)
724
732
current.foreachParam { (p, i) =>
725
733
val other = p.paramRefs(i)
726
734
entry(other) match
727
735
case _ : TypeBounds =>
728
- if co .contains(other) || contra .contains(other) then
736
+ if coDepsOfParam .contains(other) || contraDepsOfParam .contains(other) then
729
737
replaceParamIn(other)
730
738
case _ => replaceParamIn(other)
731
739
}
@@ -734,10 +742,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
734
742
val other = p.paramRefs(i)
735
743
if other != param then replaceParamIn(other)
736
744
}
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)
741
746
current.dropDeps(param)
742
747
current.checkWellFormed()
743
748
end replace
0 commit comments