Skip to content

Commit 34f155a

Browse files
committed
Make <:< all-or-nothing
Don't allow capture variables to be changed if `<:<` returns false.
1 parent b58205b commit 34f155a

File tree

1 file changed

+32
-8
lines changed

1 file changed

+32
-8
lines changed

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

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ sealed abstract class CaptureSet extends Showable:
3838
* in all their supersets.
3939
* @return true iff elements were added
4040
*/
41-
protected def addNewElems(newElems: Refs)(using Context): Boolean
41+
protected def addNewElems(newElems: Refs)(using Context, VarState): Boolean
4242

4343
/** If this is a variable, add `cs` as a super set */
4444
protected def addSuper(cs: CaptureSet): this.type
@@ -52,17 +52,25 @@ sealed abstract class CaptureSet extends Showable:
5252
* capture set. Inclusion is via `addElems`.
5353
* @return true iff elements were added
5454
*/
55-
protected def tryInclude(elems: Refs)(using Context): Boolean =
55+
protected def tryInclude(elems: Refs)(using Context, VarState): Boolean =
5656
val unaccounted = elems.filter(!accountsFor(_))
5757
unaccounted.isEmpty || addNewElems(unaccounted)
5858

59-
/** {x} <:< this where <:< is subcapturing */
59+
/** {x} <:< this where <:< is subcapturing, but treating all variables
60+
* as frozen.
61+
*/
6062
def accountsFor(x: CaptureRef)(using Context) =
6163
elems.contains(x) || !x.isRootCapability && x.captureSetOfInfo <:< this
6264

6365
/** The subcapturing test */
6466
def <:< (that: CaptureSet)(using Context): Boolean =
65-
that.tryInclude(elems) && { addSuper(that); true }
67+
given VarState = new VarState
68+
val result = that.tryInclude(elems)
69+
if result then addSuper(that) else abort()
70+
result
71+
72+
private def abort()(using state: VarState): Unit =
73+
state.keysIterator.foreach(_.reset())
6674

6775
/** The smallest capture set (via <:<) that is a superset of both
6876
* `this` and `that`
@@ -134,7 +142,7 @@ object CaptureSet:
134142
def isConst = true
135143
def isEmpty: Boolean = elems.isEmpty
136144

137-
def addNewElems(elems: Refs)(using Context): Boolean = false
145+
def addNewElems(elems: Refs)(using Context, VarState): Boolean = false
138146
def addSuper(cs: CaptureSet) = this
139147

140148
override def toString = elems.toString
@@ -150,8 +158,20 @@ object CaptureSet:
150158
def isConst = false
151159
def isEmpty = false
152160

153-
def addNewElems(newElems: Refs)(using Context): Boolean =
154-
deps.forall(_.tryInclude(newElems)) && { elems ++= newElems; true }
161+
private def recordState()(using VarState) = varState.get(this) match
162+
case None => varState(this) = elems
163+
case _ =>
164+
165+
def reset()(using state: VarState): Unit =
166+
elems = state(this)
167+
168+
def addNewElems(newElems: Refs)(using Context, VarState): Boolean =
169+
deps.forall(_.tryInclude(newElems))
170+
&& {
171+
recordState()
172+
elems ++= newElems
173+
true
174+
}
155175

156176
def addSuper(cs: CaptureSet) = { deps += cs; this }
157177

@@ -164,12 +184,16 @@ object CaptureSet:
164184
override def accountsFor(x: CaptureRef)(using Context): Boolean =
165185
f(x).elems.forall(super.accountsFor)
166186

167-
override def addNewElems(newElems: Refs)(using Context): Boolean =
187+
override def addNewElems(newElems: Refs)(using Context, VarState): Boolean =
168188
super.addNewElems(mapRefs(newElems, f).elems)
169189

170190
override def toString = s"Mapped$id$elems"
171191
end Mapped
172192

193+
type VarState = util.EqHashMap[Var, Refs]
194+
195+
def varState(using state: VarState): VarState = state
196+
173197
def mapRefs(xs: Refs, f: CaptureRef => CaptureSet)(using Context): CaptureSet =
174198
(empty /: xs)((cs, x) => cs ++ f(x))
175199

0 commit comments

Comments
 (0)