Skip to content

Commit ec2b5d6

Browse files
committed
Update nesting level in typer state
1 parent 06336a2 commit ec2b5d6

File tree

3 files changed

+57
-32
lines changed

3 files changed

+57
-32
lines changed

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -511,14 +511,8 @@ trait ConstraintHandling {
511511
// `fixLevels`, this could lead to coarser types. But it has the potential
512512
// to give a better approximation for the current type, since it avoids forming
513513
// a Range in invariant position, which can lead to very coarse types further out.
514-
// TODO: This widening is a side effect that is not undone if a typer state is aborted
515-
// I don't think it's a soundness problem, since all that could happen is that
516-
// the type variable causes earlier instantiations of other type variables
517-
// down the line. But it could produce a hard-to-debug side effect that leads
518-
// to worse types than expected. We should find a more robust way to do this.
519-
// Maybe instantiating `tp` to another freshly created type at nesting level?
520514
constr.println(i"widening nesting level of type variable $tp from ${tp.nestingLevel} to $maxLevel")
521-
tp.nestingLevel = maxLevel
515+
ctx.typerState.setNestingLevel(tp, maxLevel)
522516
true
523517
else false
524518
case _ =>

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

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import config.Config
1010
import config.Printers.constr
1111
import collection.mutable
1212
import java.lang.ref.WeakReference
13-
import util.Stats
13+
import util.{Stats, SimpleIdentityMap}
1414
import Decorators._
1515

1616
import scala.annotation.internal.sharable
@@ -23,24 +23,28 @@ object TyperState {
2323
.setReporter(new ConsoleReporter())
2424
.setCommittable(true)
2525

26-
opaque type Snapshot = (Constraint, TypeVars, TypeVars)
26+
type LevelMap = SimpleIdentityMap[TypeVar, Integer]
27+
28+
opaque type Snapshot = (Constraint, TypeVars, TypeVars, LevelMap)
2729

2830
extension (ts: TyperState)
2931
def snapshot()(using Context): Snapshot =
3032
var previouslyInstantiated: TypeVars = SimpleIdentitySet.empty
3133
for tv <- ts.ownedVars do if tv.inst.exists then previouslyInstantiated += tv
32-
(ts.constraint, ts.ownedVars, previouslyInstantiated)
34+
(ts.constraint, ts.ownedVars, previouslyInstantiated, ts.upLevels)
3335

3436
def resetTo(state: Snapshot)(using Context): Unit =
35-
val (c, tvs, previouslyInstantiated) = state
37+
val (c, tvs, previouslyInstantiated, upLevels) = state
3638
for tv <- tvs do
3739
if tv.inst.exists && !previouslyInstantiated.contains(tv) then
3840
tv.resetInst(ts)
3941
ts.ownedVars = tvs
4042
ts.constraint = c
43+
ts.upLevels = upLevels
4144
}
4245

4346
class TyperState() {
47+
import TyperState.LevelMap
4448

4549
private var myId: Int = _
4650
def id: Int = myId
@@ -89,6 +93,8 @@ class TyperState() {
8993
def ownedVars: TypeVars = myOwnedVars
9094
def ownedVars_=(vs: TypeVars): Unit = myOwnedVars = vs
9195

96+
private var upLevels: LevelMap = _
97+
9298
/** Initializes all fields except reporter, isCommittable, which need to be
9399
* set separately.
94100
*/
@@ -99,20 +105,35 @@ class TyperState() {
99105
this.myConstraint = constraint
100106
this.previousConstraint = constraint
101107
this.myOwnedVars = SimpleIdentitySet.empty
108+
this.upLevels = SimpleIdentityMap.empty
102109
this.isCommitted = false
103110
this
104111

105112
/** A fresh typer state with the same constraint as this one. */
106113
def fresh(reporter: Reporter = StoreReporter(this.reporter, fromTyperState = true),
107114
committable: Boolean = this.isCommittable): TyperState =
108115
util.Stats.record("TyperState.fresh")
109-
TyperState().init(this, this.constraint)
116+
val ts = TyperState().init(this, this.constraint)
110117
.setReporter(reporter)
111118
.setCommittable(committable)
119+
ts.upLevels = upLevels
120+
ts
112121

113122
/** The uninstantiated variables */
114123
def uninstVars: collection.Seq[TypeVar] = constraint.uninstVars
115124

125+
/** The nestingLevel of `tv` in this typer state */
126+
def nestingLevel(tv: TypeVar): Int =
127+
val own = upLevels(tv)
128+
if own == null then tv.initNestingLevel else own.intValue()
129+
130+
/** Set the nestingLevel of `tv` in this typer state
131+
* @pre this level must be smaller than `tv.initNestingLevel`
132+
*/
133+
def setNestingLevel(tv: TypeVar, level: Int) =
134+
assert(level < tv.initNestingLevel)
135+
upLevels = upLevels.updated(tv, level)
136+
116137
/** The closest ancestor of this typer state (including possibly this typer state itself)
117138
* which is not yet committed, or which does not have a parent.
118139
*/
@@ -164,6 +185,12 @@ class TyperState() {
164185
if !ownedVars.isEmpty then ownedVars.foreach(targetState.includeVar)
165186
else
166187
targetState.mergeConstraintWith(this)
188+
189+
upLevels.foreachBinding { (tv, level) =>
190+
if level < targetState.nestingLevel(tv) then
191+
targetState.setNestingLevel(tv, level)
192+
}
193+
167194
targetState.gc()
168195
isCommitted = true
169196
ownedVars = SimpleIdentitySet.empty

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

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4497,27 +4497,11 @@ object Types {
44974497
* is different from the variable's creation state (meaning unrolls are possible)
44984498
* in the current typer state.
44994499
*
4500-
* @param origin The parameter that's tracked by the type variable.
4501-
* @param creatorState The typer state in which the variable was created.
4502-
* @param nestingLevel Symbols with a nestingLevel strictly greater than this
4503-
* will not appear in the instantiation of this type variable.
4504-
* This is enforced in `ConstraintHandling`, dependig on the
4505-
* Config flags setting `checkLevelsOnConstraints` and
4506-
* `checkLevelsOnInstantiation`.
4507-
*
4508-
* Under `checkLevelsOnConstraints` we maintain the invariant that
4509-
* the `nonParamBounds` of a type variable never refer to a type with a
4510-
* greater `nestingLevel` (see `legalBound` for the reason why this
4511-
* cannot be delayed until instantiation). Then, on instantiation,
4512-
* we replace any param in the param bound with a level greater than
4513-
* nestingLevel (see `fullLowerBound`).
4514-
*
4515-
* Under `checkLevelsOnInstantiation`, we avoid incorrect levels only
4516-
* when a type variable is instantiated, see `ConstraintHandling$fixLevels`.
4517-
* Under this mode, the `nestingLevel` of a type variable can be made
4518-
* smaller when fixing the levels for some other type variable instance.
4500+
* @param origin the parameter that's tracked by the type variable.
4501+
* @param creatorState the typer state in which the variable was created.
4502+
* @param initNestingLevel the initial nesting level of the type variable. (c.f. nestingLevel)
45194503
*/
4520-
final class TypeVar private(initOrigin: TypeParamRef, creatorState: TyperState | Null, var nestingLevel: Int) extends CachedProxyType with ValueType {
4504+
final class TypeVar private(initOrigin: TypeParamRef, creatorState: TyperState | Null, val initNestingLevel: Int) extends CachedProxyType with ValueType {
45214505
private var currentOrigin = initOrigin
45224506

45234507
def origin: TypeParamRef = currentOrigin
@@ -4549,6 +4533,26 @@ object Types {
45494533
private[core] var owningState: WeakReference[TyperState] | Null =
45504534
if (creatorState == null) null else new WeakReference(creatorState)
45514535

4536+
/** The nesting level of this type variable in the current typer state. This is usually
4537+
* the same as `initNestingLevel`, but can be decremented by calling `TyperState#setNestingLevel`.
4538+
* Symbols with a nestingLevel strictly greater than this level will not appear in the
4539+
* instantiation of this type variable. This is enforced in `ConstraintHandling`,
4540+
* dependig on the Config flags setting `checkLevelsOnConstraints` and `checkLevelsOnInstantiation`.
4541+
*
4542+
* Under `checkLevelsOnConstraints` we maintain the invariant that
4543+
* the `nonParamBounds` of a type variable never refer to a type with a
4544+
* greater `nestingLevel` (see `legalBound` for the reason why this
4545+
* cannot be delayed until instantiation). Then, on instantiation,
4546+
* we replace any param in the param bound with a level greater than
4547+
* nestingLevel (see `fullLowerBound`).
4548+
*
4549+
* Under `checkLevelsOnInstantiation`, we avoid incorrect levels only
4550+
* when a type variable is instantiated, see `ConstraintHandling$fixLevels`.
4551+
* Under this mode, the `nestingLevel` of a type variable can be made
4552+
* smaller when fixing the levels for some other type variable instance.
4553+
*/
4554+
def nestingLevel(using Context): Int = ctx.typerState.nestingLevel(this)
4555+
45524556
/** The instance type of this variable, or NoType if the variable is currently
45534557
* uninstantiated
45544558
*/

0 commit comments

Comments
 (0)