Skip to content

Commit 76af6e3

Browse files
Backport "Avoid the TypeVar.inst trap" to LTS (#21139)
Backports #20160 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents 1e31b17 + e2471f7 commit 76af6e3

File tree

5 files changed

+45
-21
lines changed

5 files changed

+45
-21
lines changed

compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala

+6-6
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
313313

314314
override def tyconTypeParams(tp: AppliedType)(using Context): List[ParamInfo] =
315315
def tparams(tycon: Type): List[ParamInfo] = tycon match
316-
case tycon: TypeVar if !tycon.inst.exists => tparams(tycon.origin)
316+
case tycon: TypeVar if !tycon.isPermanentlyInstantiated => tparams(tycon.origin)
317317
case tycon: TypeParamRef if !hasBounds(tycon) =>
318318
val entryParams = entry(tycon).typeParams
319319
if entryParams.nonEmpty then entryParams
@@ -713,7 +713,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
713713
var newDepEntry = newEntry
714714
replacedTypeVar match
715715
case tvar: TypeVar =>
716-
if tvar.inst.exists // `isInstantiated` would use ctx.typerState.constraint rather than the current constraint
716+
if tvar.isPermanentlyInstantiated // `isInstantiated` would use ctx.typerState.constraint rather than the current constraint
717717
then
718718
// If the type variable has been instantiated, we need to forget about
719719
// the instantiation for old dependencies.
@@ -770,7 +770,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
770770
@tailrec def allRemovable(last: Int): Boolean =
771771
if (last < 0) true
772772
else typeVar(entries, last) match {
773-
case tv: TypeVar => tv.inst.exists && allRemovable(last - 1)
773+
case tv: TypeVar => tv.isPermanentlyInstantiated && allRemovable(last - 1)
774774
case _ => false
775775
}
776776
allRemovable(paramCount(entries) - 1)
@@ -876,7 +876,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
876876
val limit = paramCount(entries)
877877
while i < limit do
878878
typeVar(entries, i) match
879-
case tv: TypeVar if !tv.inst.exists => op(tv)
879+
case tv: TypeVar if !tv.isPermanentlyInstantiated => op(tv)
880880
case _ =>
881881
i += 1
882882
}
@@ -885,12 +885,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
885885

886886
/** The uninstantiated typevars of this constraint */
887887
def uninstVars: collection.Seq[TypeVar] = {
888-
if (myUninstVars == null || myUninstVars.uncheckedNN.exists(_.inst.exists)) {
888+
if (myUninstVars == null || myUninstVars.uncheckedNN.exists(_.isPermanentlyInstantiated)) {
889889
myUninstVars = new mutable.ArrayBuffer[TypeVar]
890890
boundsMap.foreachBinding { (poly, entries) =>
891891
for (i <- 0 until paramCount(entries))
892892
typeVar(entries, i) match {
893-
case tv: TypeVar if !tv.inst.exists && isBounds(entries(i)) => myUninstVars.uncheckedNN += tv
893+
case tv: TypeVar if !tv.isPermanentlyInstantiated && isBounds(entries(i)) => myUninstVars.uncheckedNN += tv
894894
case _ =>
895895
}
896896
}

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1596,7 +1596,7 @@ object SymDenotations {
15961596
case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo)
15971597
case tp: RecType => hasSkolems(tp.parent)
15981598
case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi)
1599-
case tp: TypeVar => hasSkolems(tp.inst)
1599+
case tp: TypeVar => hasSkolems(tp.permanentInst)
16001600
case tp: ExprType => hasSkolems(tp.resType)
16011601
case tp: AppliedType => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems)
16021602
case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType)

compiler/src/dotty/tools/dotc/core/TyperState.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ class TyperState() {
230230
val tvars = tl.paramRefs.map(other.typeVarOfParam(_)).collect { case tv: TypeVar => tv }
231231
if this.isCommittable then
232232
tvars.foreach(tvar =>
233-
if !tvar.inst.exists && !isOwnedAnywhere(this, tvar) then includeVar(tvar))
233+
if !tvar.isPermanentlyInstantiated && !isOwnedAnywhere(this, tvar) then includeVar(tvar))
234234
typeComparer.addToConstraint(tl, tvars)
235235
}) &&
236236
// Integrate the additional constraints on type variables from `other`
@@ -286,10 +286,10 @@ class TyperState() {
286286
for tvar <- ownedVars do
287287
val tvarState = tvar.owningState.nn.get
288288
assert(tvarState eqn this, s"Inconsistent state in $this: it owns $tvar whose owningState is ${tvarState}")
289-
assert(!tvar.inst.exists, s"Inconsistent state in $this: it owns $tvar which is already instantiated")
289+
assert(!tvar.isPermanentlyInstantiated, s"Inconsistent state in $this: it owns $tvar which is already instantiated")
290290
val inst = constraint.instType(tvar)
291291
if inst.exists then
292-
tvar.setInst(inst)
292+
tvar.setPermanentInst(inst)
293293
val tl = tvar.origin.binder
294294
if constraint.isRemovable(tl) then toCollect += tl
295295
for tl <- toCollect do

compiler/src/dotty/tools/dotc/core/Types.scala

+20-11
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ object Types extends TypeUtils {
137137
case t: AppliedType =>
138138
t.fold(false, (x, tp) => x || test(tp, theAcc))
139139
case t: TypeVar =>
140-
!t.inst.exists || test(t.inst, theAcc)
140+
!t.isPermanentlyInstantiated || test(t.permanentInst, theAcc)
141141
case t: LazyRef =>
142142
!t.completed || test(t.ref, theAcc)
143143
case _ =>
@@ -4792,20 +4792,24 @@ object Types extends TypeUtils {
47924792
def setOrigin(p: TypeParamRef) = currentOrigin = p
47934793

47944794
/** The permanent instance type of the variable, or NoType is none is given yet */
4795-
private var myInst: Type = NoType
4795+
private var inst: Type = NoType
47964796

4797-
private[core] def inst: Type = myInst
4798-
private[core] def setInst(tp: Type): Unit =
4799-
myInst = tp
4797+
/** The permanent instance type that's stored in the type variable, so it cannot be retracted
4798+
* anymore, or NoType if the variable can still be further constrained or a provisional
4799+
* instance type in the constraint can be retracted.
4800+
*/
4801+
private[core] def permanentInst = inst
4802+
private[core] def setPermanentInst(tp: Type): Unit =
4803+
inst = tp
48004804
if tp.exists && owningState != null then
48014805
val owningState1 = owningState.uncheckedNN.get
48024806
if owningState1 != null then
48034807
owningState1.ownedVars -= this
48044808
owningState = null // no longer needed; null out to avoid a memory leak
48054809

48064810
private[core] def resetInst(ts: TyperState): Unit =
4807-
assert(myInst.exists)
4808-
myInst = NoType
4811+
assert(inst.exists)
4812+
inst = NoType
48094813
owningState = new WeakReference(ts)
48104814

48114815
/** The state owning the variable. This is at first `creatorState`, but it can
@@ -4843,18 +4847,23 @@ object Types extends TypeUtils {
48434847
/** Is the variable already instantiated? */
48444848
def isInstantiated(using Context): Boolean = instanceOpt.exists
48454849

4850+
/** Is the variable already instantiated so that the instance cannot be
4851+
* retracted anymore?
4852+
*/
4853+
def isPermanentlyInstantiated: Boolean = inst.exists
4854+
48464855
/** Instantiate variable with given type */
48474856
def instantiateWith(tp: Type)(using Context): Type = {
48484857
assert(tp ne this, i"self instantiation of $origin, constraint = ${ctx.typerState.constraint}")
4849-
assert(!myInst.exists, i"$origin is already instantiated to $myInst but we attempted to instantiate it to $tp")
4858+
assert(!inst.exists, i"$origin is already instantiated to $inst but we attempted to instantiate it to $tp")
48504859
typr.println(i"instantiating $this with $tp")
48514860

48524861
if Config.checkConstraintsSatisfiable then
48534862
assert(currentEntry.bounds.contains(tp),
48544863
i"$origin is constrained to be $currentEntry but attempted to instantiate it to $tp")
48554864

48564865
if ((ctx.typerState eq owningState.nn.get.uncheckedNN) && !TypeComparer.subtypeCheckInProgress)
4857-
setInst(tp)
4866+
setPermanentInst(tp)
48584867
ctx.typerState.constraint = ctx.typerState.constraint.replace(origin, tp)
48594868
tp
48604869
}
@@ -4868,8 +4877,8 @@ object Types extends TypeUtils {
48684877
*/
48694878
def instantiate(fromBelow: Boolean)(using Context): Type =
48704879
val tp = TypeComparer.instanceType(origin, fromBelow, widenUnions, nestingLevel)
4871-
if myInst.exists then // The line above might have triggered instantiation of the current type variable
4872-
myInst
4880+
if inst.exists then // The line above might have triggered instantiation of the current type variable
4881+
inst
48734882
else
48744883
instantiateWith(tp)
48754884

tests/pos/i20154.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
sealed abstract class Kyo[+T, -S]
2+
opaque type <[+T, -S] >: T = T | Kyo[T, S]
3+
4+
abstract class Effect[+E]:
5+
type Command[_]
6+
7+
case class Recurse[Command[_], Result[_], E <: Effect[E], T, S, S2](
8+
h: ResultHandler[Command, Result, E, S],
9+
v: T < (E & S & S2)
10+
)
11+
12+
abstract class ResultHandler[Command[_], Result[_], E <: Effect[E], S]:
13+
opaque type Handle[T, S2] >: (Result[T] < (S & S2)) = Result[T] < (S & S2) | Recurse[Command, Result, E, T, S, S2]
14+
15+
def handle[T, S2](h: ResultHandler[Command, Result, E, S], v: T < (E & S & S2)): Handle[T, S2] = Recurse(h, v)

0 commit comments

Comments
 (0)