@@ -79,10 +79,11 @@ class TyperState() {
7979
8080 private var isCommitted : Boolean = _
8181
82- /** The set of uninstantiated type variables which have this state as their owning state
83- * NOTE: It could be that a variable in `ownedVars` is already instantiated. This is because
84- * the link between ownedVars and variable instantiation in TypeVar#setInst is made up
85- * from a weak reference and weak references can have spurious nulls.
82+ /** The set of uninstantiated type variables which have this state as their owning state.
83+ *
84+ * Invariant:
85+ * if `tstate.isCommittable` then
86+ * `tstate.ownedVars.contains(tvar)` iff `tvar.owningState.get eq tstate`
8687 */
8788 private var myOwnedVars : TypeVars = _
8889 def ownedVars : TypeVars = myOwnedVars
@@ -138,6 +139,7 @@ class TyperState() {
138139 def commit ()(using Context ): Unit = {
139140 Stats .record(" typerState.commit" )
140141 assert(isCommittable)
142+ setCommittable(false )
141143 val targetState = ctx.typerState
142144 if constraint ne targetState.constraint then
143145 Stats .record(" typerState.commit.new constraint" )
@@ -157,12 +159,7 @@ class TyperState() {
157159 * in this constraint and its predecessors where necessary.
158160 */
159161 def ensureNotConflicting (other : Constraint )(using Context ): Unit =
160- def hasConflictingTypeVarsFor (tl : TypeLambda ) =
161- constraint.typeVarOfParam(tl.paramRefs(0 )) ne other.typeVarOfParam(tl.paramRefs(0 ))
162- // Note: Since TypeVars are allocated in bulk for each type lambda, we only
163- // have to check the first one to find out if some of them are different.
164- val conflicting = constraint.domainLambdas.find(tl =>
165- other.contains(tl) && hasConflictingTypeVarsFor(tl))
162+ val conflicting = constraint.domainLambdas.filter(constraint.hasConflictingTypeVarsFor(_, other))
166163 for tl <- conflicting do
167164 val tl1 = constraint.ensureFresh(tl)
168165 for case (tvar : TypeVar , pref1) <- tl.paramRefs.map(constraint.typeVarOfParam).lazyZip(tl1.paramRefs) do
@@ -172,15 +169,29 @@ class TyperState() {
172169 ts.constraint = ts.constraint.subst(tl, tl1)
173170 ts = ts.previous
174171
172+ /** Integrate the constraints from `that` into this TyperState.
173+ *
174+ * @pre If `that` is committable, it must not contain any type variable which
175+ * does not exist in `this` (in other words, all its type variables must
176+ * be owned by a common parent of `this` and `that`).
177+ */
175178 def mergeConstraintWith (that : TyperState )(using Context ): Unit =
176179 that.ensureNotConflicting(constraint)
177180 constraint = constraint & (that.constraint, otherHasErrors = that.reporter.errorsReported)
178181 for tvar <- constraint.uninstVars do
179- if ! isOwnedAnywhere(this , tvar) then ownedVars += tvar
182+ if ! isOwnedAnywhere(this , tvar) then includeVar( tvar)
180183 for tl <- constraint.domainLambdas do
181184 if constraint.isRemovable(tl) then constraint = constraint.remove(tl)
182185
186+ /** Take ownership of `tvar`.
187+ *
188+ * @pre `tvar` is not owned by a committable TyperState. This ensures
189+ * each tvar can only be instantiated by one TyperState.
190+ */
183191 private def includeVar (tvar : TypeVar )(using Context ): Unit =
192+ val oldState = tvar.owningState.get
193+ assert(oldState == null || ! oldState.isCommittable,
194+ i " $this attempted to take ownership of $tvar which is already owned by committable $oldState" )
184195 tvar.owningState = new WeakReference (this )
185196 ownedVars += tvar
186197
@@ -196,12 +207,13 @@ class TyperState() {
196207 Stats .record(" typerState.gc" )
197208 val toCollect = new mutable.ListBuffer [TypeLambda ]
198209 for tvar <- ownedVars do
199- if ! tvar.inst.exists then // See comment of `ownedVars` for why this test is necessary
200- val inst = constraint.instType(tvar)
201- if inst.exists then
202- tvar.setInst(inst)
203- val tl = tvar.origin.binder
204- if constraint.isRemovable(tl) then toCollect += tl
210+ assert(tvar.owningState.get eq this , s " Inconsistent state in $this: it owns $tvar whose owningState is ${tvar.owningState.get}" )
211+ assert(! tvar.inst.exists, s " Inconsistent state in $this: it owns $tvar which is already instantiated " )
212+ val inst = constraint.instType(tvar)
213+ if inst.exists then
214+ tvar.setInst(inst)
215+ val tl = tvar.origin.binder
216+ if constraint.isRemovable(tl) then toCollect += tl
205217 for tl <- toCollect do
206218 constraint = constraint.remove(tl)
207219
0 commit comments