Skip to content

Commit

Permalink
Refactor VarState
Browse files Browse the repository at this point in the history
 - Drop Frozen enum
 - Make VarState subclasses inner classes of companion object
 - Rename them
 - Give implicit parameter VarState of subCapture method a default value
  • Loading branch information
odersky committed Jan 8, 2025
1 parent 93cb523 commit aa5f411
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 121 deletions.
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import config.Feature
import collection.mutable
import CCState.*
import reporting.Message
import CaptureSet.Frozen
import CaptureSet.VarState

/** Attachment key for capturing type trees */
private val Captures: Key[CaptureSet] = Key()
Expand Down Expand Up @@ -251,7 +251,7 @@ extension (tp: Type)
* the two capture sets are combined.
*/
def capturing(cs: CaptureSet)(using Context): Type =
if (cs.isAlwaysEmpty || cs.isConst && cs.subCaptures(tp.captureSet, Frozen.All).isOK)
if (cs.isAlwaysEmpty || cs.isConst && cs.subCaptures(tp.captureSet, VarState.Separate).isOK)
&& !cs.keepAlways
then tp
else tp match
Expand Down
20 changes: 6 additions & 14 deletions compiler/src/dotty/tools/dotc/cc/CaptureRef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import CCState.*
import Periods.NoRunId
import compiletime.uninitialized
import StdNames.nme
import CaptureSet.VarState

/** A trait for references in CaptureSets. These can be NamedTypes, ThisTypes or ParamRefs,
* as well as three kinds of AnnotatedTypes representing readOnly, reach, and maybe capabilities.
Expand Down Expand Up @@ -134,8 +135,6 @@ trait CaptureRef extends TypeProxy, ValueType:
final def invalidateCaches() =
myCaptureSetRunId = NoRunId

import CaptureSet.{VarState, FrozenAllState}

/** x subsumes x
* x =:= y ==> x subsumes y
* x subsumes y ==> x subsumes y.f
Expand All @@ -150,7 +149,7 @@ trait CaptureRef extends TypeProxy, ValueType:
*
* TODO: Move to CaptureSet
*/
final def subsumes(y: CaptureRef)(using ctx: Context, vs: VarState = FrozenAllState): Boolean =
final def subsumes(y: CaptureRef)(using ctx: Context, vs: VarState = VarState.Separate): Boolean =

def subsumingRefs(x: Type, y: Type): Boolean = x match
case x: CaptureRef => y match
Expand All @@ -166,7 +165,7 @@ trait CaptureRef extends TypeProxy, ValueType:
case _ => false

(this eq y)
|| maxSubsumes(y, canAddHidden = vs.frozen == CaptureSet.Frozen.Vars)
|| maxSubsumes(y, canAddHidden = !vs.isOpen)
|| y.match
case y: TermRef if !y.isCap =>
y.prefix.match
Expand Down Expand Up @@ -223,18 +222,11 @@ trait CaptureRef extends TypeProxy, ValueType:
* the test again with canAddHidden = true as a last effort before we
* fail a comparison.
*/
def maxSubsumes(y: CaptureRef, canAddHidden: Boolean)(using ctx: Context, vs: VarState = FrozenAllState): Boolean =
def maxSubsumes(y: CaptureRef, canAddHidden: Boolean)(using ctx: Context, vs: VarState = VarState.Separate): Boolean =
this.match
case Fresh.Cap(hidden) =>
if vs.ifNotSeen(this)(hidden.elems.exists(_.subsumes(y))) then true
else if !y.stripReadOnly.isCap && hidden.recordElemsState() then
if canAddHidden then
if vs != CaptureSet.FrozenUnrecordedHiddenState then
hidden.elems += y
true
else
false
else false
vs.ifNotSeen(this)(hidden.elems.exists(_.subsumes(y)))
|| !y.stripReadOnly.isCap && canAddHidden && vs.addHidden(hidden, y)
case _ =>
this.isCap && canAddHidden
|| y.match
Expand Down
142 changes: 69 additions & 73 deletions compiler/src/dotty/tools/dotc/cc/CaptureSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ sealed abstract class CaptureSet extends Showable:
* capture set.
*/
protected final def addNewElem(elem: CaptureRef)(using ctx: Context, vs: VarState): CompareResult =
if elem.isMaxCapability || vs.frozen != Frozen.None then
if elem.isMaxCapability || !vs.isOpen then
addThisElem(elem)
else
addThisElem(elem).orElse:
Expand All @@ -168,7 +168,7 @@ sealed abstract class CaptureSet extends Showable:
protected def addThisElem(elem: CaptureRef)(using Context, VarState): CompareResult

protected def addHiddenElem(elem: CaptureRef)(using ctx: Context, vs: VarState): CompareResult =
if elems.exists(_.maxSubsumes(elem, canAddHidden = vs.frozen != Frozen.All))
if elems.exists(_.maxSubsumes(elem, canAddHidden = true))
then CompareResult.OK
else CompareResult.Fail(this :: Nil)

Expand All @@ -177,13 +177,13 @@ sealed abstract class CaptureSet extends Showable:

/** If `cs` is a variable, add this capture set as one of its dependent sets */
protected def addAsDependentTo(cs: CaptureSet)(using Context): this.type =
cs.addDependent(this)(using ctx, UnrecordedState)
cs.addDependent(this)(using ctx, VarState.Unrecorded)
this

/** {x} <:< this where <:< is subcapturing, but treating all variables
* as frozen.
*/
def accountsFor(x: CaptureRef)(using ctx: Context, vs: VarState = FrozenAllState): Boolean =
def accountsFor(x: CaptureRef)(using ctx: Context, vs: VarState = VarState.Separate): Boolean =

/** Like `refs.exists(p)`, but testing fresh cap instances in refs last */
def existsElem(refs: SimpleIdentitySet[CaptureRef], p: CaptureRef => Boolean): Boolean =
Expand All @@ -201,9 +201,9 @@ sealed abstract class CaptureSet extends Showable:
existsElem(elems, _.subsumes(x))
|| !x.isMaxCapability
&& !x.derivesFrom(defn.Caps_CapSet)
&& !(vs.frozen == Frozen.All && x.captureSetOfInfo.containsRootCapability)
// under Frozen.All, don't try to widen to cap since that might succeed with {cap} <: {cap}
&& x.captureSetOfInfo.subCaptures(this, Frozen.All).isOK
&& !(vs == VarState.Separate && x.captureSetOfInfo.containsRootCapability)
// in VarState.Separate, don't try to widen to cap since that might succeed with {cap} <: {cap}
&& x.captureSetOfInfo.subCaptures(this, VarState.Separate).isOK

comparer match
case comparer: ExplainingTypeComparer => comparer.traceIndented(debugInfo)(test)
Expand All @@ -219,7 +219,7 @@ sealed abstract class CaptureSet extends Showable:
*/
def mightAccountFor(x: CaptureRef)(using Context): Boolean =
reporting.trace(i"$this mightAccountFor $x, ${x.captureSetOfInfo}?", show = true):
elems.exists(_.subsumes(x)(using ctx, FrozenUnrecordedHiddenState))
elems.exists(_.subsumes(x)(using ctx, VarState.ClosedUnrecorded))
|| !x.isMaxCapability
&& {
val elems = x.captureSetOfInfo.elems
Expand All @@ -240,15 +240,11 @@ sealed abstract class CaptureSet extends Showable:
* be added when making this test. An attempt to add either
* will result in failure.
*/
final def subCaptures(that: CaptureSet, frozen: Frozen)(using Context): CompareResult =
val state = frozen match
case Frozen.None => VarState()
case Frozen.Vars => FrozenVarState()
case Frozen.All => FrozenAllState
subCaptures(that)(using ctx, state)
final def subCaptures(that: CaptureSet, vs: VarState)(using Context): CompareResult =
subCaptures(that)(using ctx, vs)

/** The subcapturing test, using a given VarState */
private def subCaptures(that: CaptureSet)(using Context, VarState): CompareResult =
def subCaptures(that: CaptureSet)(using ctx: Context, vs: VarState = VarState()): CompareResult =
val result = that.tryInclude(elems, this)
if result.isOK then
addDependent(that)
Expand All @@ -262,16 +258,16 @@ sealed abstract class CaptureSet extends Showable:
* in a frozen state.
*/
def =:= (that: CaptureSet)(using Context): Boolean =
this.subCaptures(that, Frozen.All).isOK
&& that.subCaptures(this, Frozen.All).isOK
this.subCaptures(that, VarState.Separate).isOK
&& that.subCaptures(this, VarState.Separate).isOK

/** The smallest capture set (via <:<) that is a superset of both
* `this` and `that`
*/
def ++ (that: CaptureSet)(using Context): CaptureSet =
if this.subCaptures(that, Frozen.All).isOK then
if this.subCaptures(that, VarState.Separate).isOK then
if that.isAlwaysEmpty && this.keepAlways then this else that
else if that.subCaptures(this, Frozen.All).isOK then this
else if that.subCaptures(this, VarState.Separate).isOK then this
else if this.isConst && that.isConst then Const(this.elems ++ that.elems)
else Union(this, that)

Expand All @@ -286,8 +282,8 @@ sealed abstract class CaptureSet extends Showable:
/** The largest capture set (via <:<) that is a subset of both `this` and `that`
*/
def **(that: CaptureSet)(using Context): CaptureSet =
if this.subCaptures(that, Frozen.Vars).isOK then this
else if that.subCaptures(this, Frozen.Vars).isOK then that
if this.subCaptures(that, VarState.Closed()).isOK then this
else if that.subCaptures(this, VarState.Closed()).isOK then that
else if this.isConst && that.isConst then Const(elemIntersection(this, that))
else Intersection(this, that)

Expand Down Expand Up @@ -556,7 +552,7 @@ object CaptureSet:
else
// id == 108 then assert(false, i"trying to add $elem to $this")
assert(elem.isTrackableRef, elem)
assert(!this.isInstanceOf[HiddenSet] || summon[VarState] == FrozenAllState, summon[VarState])
assert(!this.isInstanceOf[HiddenSet] || summon[VarState] == VarState.Separate, summon[VarState])
elems += elem
if elem.isRootCapability then
rootAddedHandler()
Expand Down Expand Up @@ -652,7 +648,8 @@ object CaptureSet:
.showing(i"solve $this = $result", capt)
//println(i"solving var $this $approx ${approx.isConst} deps = ${deps.toList}")
val newElems = approx.elems -- elems
if tryInclude(newElems, empty)(using ctx, VarState()).isOK then
given VarState()
if tryInclude(newElems, empty).isOK then
markSolved()

/** Mark set as solved and propagate this info to all dependent sets */
Expand Down Expand Up @@ -939,10 +936,6 @@ object CaptureSet:
/** A capture set variable used to record the references hidden by a Fresh.Cap instance */
class HiddenSet(initialHidden: Refs = emptySet)(using @constructorOnly ictx: Context)
extends Var(initialElems = initialHidden):
override def recordElemsState()(using VarState): Boolean =
varState.getElems(this) match
case None => varState.putHidden(this, elems)
case _ => true

/** Apply function `f` to `elems` while setting `elems` to empty for the
* duration. This is used to escape infinite recursions if two Frash.Caps
Expand Down Expand Up @@ -990,7 +983,7 @@ object CaptureSet:
*/
def subCapturesRange(arg1: TypeBounds, arg2: Type)(using Context): Boolean = arg1 match
case TypeBounds(CapturingType(lo, loRefs), CapturingType(hi, hiRefs)) if lo =:= hi =>
given VarState = VarState()
given VarState()
val cs2 = arg2.captureSet
hiRefs.subCaptures(cs2).isOK && cs2.subCaptures(loRefs).isOK
case _ =>
Expand Down Expand Up @@ -1048,19 +1041,11 @@ object CaptureSet:
case _ => this
end CompareResult

/** An enum indicating a Frozen degree for subCapturing tests */
enum Frozen:
case None // operations are performed in a regular VarState
case Vars // operations are performed in a FrozenVarState
case All // operations are performed in FrozenAllState

/** A VarState serves as a snapshot mechanism that can undo
* additions of elements or super sets if an operation fails
*/
class VarState:

def frozen: Frozen = Frozen.None

/** A map from captureset variables to their elements at the time of the snapshot. */
protected val elemsMap: util.EqHashMap[Var, Refs] = new util.EqHashMap

Expand Down Expand Up @@ -1089,11 +1074,19 @@ object CaptureSet:
*/
def putDeps(v: Var, deps: Deps): Boolean = { depsMap(v) = deps; true }

/** Record hidden elements in elemsMap of hidden set `v`,
/** Does this state allow additions of elements to capture set variables? */
def isOpen = true

/** Add element to hidden set, recording it in elemsMap,
* return whether this was allowed. By default, recording is allowed
* but the special state FrozenAllState overrides this.
* but the special state VarState.Separate overrides this.
*/
def putHidden(v: HiddenSet, elems: Refs): Boolean = { elemsMap(v) = elems; true }
def addHidden(hidden: HiddenSet, elem: CaptureRef): Boolean =
elemsMap.get(hidden) match
case None => elemsMap(hidden) = hidden.elems
case _ =>
hidden.elems += elem
true

/** Roll back global state to what was recorded in this VarState */
def rollBack(): Unit =
Expand All @@ -1107,45 +1100,48 @@ object CaptureSet:
if seen.add(ref) then
try pred finally seen -= ref
else false
end VarState

/** A class for states that do not allow to record elements or dependent sets.
* In effect this means that no new elements or dependent sets can be added
* in these states (since the previous state cannot be recorded in a snapshot)
* On the other hand, these states do allow by default Fresh.Cap instances to
* subsume arbitary types, which are then recorded in their hidden sets.
*/
class FrozenVarState extends VarState:
override def frozen = Frozen.Vars
override def putElems(v: Var, refs: Refs) = false
override def putDeps(v: Var, deps: Deps) = false
override def putHidden(v: HiddenSet, elems: Refs): Boolean = { elemsMap(v) = elems; true }
object VarState:

@sharable
/** A frozen state that allows a Fresh.Cap instancce to subsume a
* reference `r` only if `r` is already present in the hidden set of the instance.
* No new references can be added.
*/
object FrozenAllState extends FrozenVarState:
override def frozen = Frozen.All
override def putHidden(v: HiddenSet, elems: Refs): Boolean = false
/** A class for states that do not allow to record elements or dependent sets.
* In effect this means that no new elements or dependent sets can be added
* in these states (since the previous state cannot be recorded in a snapshot)
* On the other hand, these states do allow by default Fresh.Cap instances to
* subsume arbitary types, which are then recorded in their hidden sets.
*/
class Closed extends VarState:
override def putElems(v: Var, refs: Refs) = false
override def putDeps(v: Var, deps: Deps) = false
override def isOpen = false

/** A closed state that allows a Fresh.Cap instance to subsume a
* reference `r` only if `r` is already present in the hidden set of the instance.
* No new references can be added.
*/
@sharable
object Separate extends Closed:
override def addHidden(hidden: HiddenSet, elem: CaptureRef): Boolean = false

@sharable
/** A frozen state that allows a Fresh.Cap instancce to subsume a
* reference `r` only if `r` is already present in the hidden set of the instance.
* No new references can be added.
*/
object FrozenUnrecordedHiddenState extends FrozenVarState
/** A special state that turns off recording of elements. Used only
* in `addSub` to prevent cycles in recordings.
*/
@sharable
private[CaptureSet] object Unrecorded extends VarState:
override def putElems(v: Var, refs: Refs) = true
override def putDeps(v: Var, deps: Deps) = true
override def rollBack(): Unit = ()
override def addHidden(hidden: HiddenSet, elem: CaptureRef): Boolean = true

/** A closed state that turns off recording of hidden elements (but allows
* adding them). Used in `mightAccountFor`.
*/
@sharable
private[CaptureSet] object ClosedUnrecorded extends Closed:
override def addHidden(hidden: HiddenSet, elem: CaptureRef): Boolean = true

@sharable
/** A special state that turns off recording of elements. Used only
* in `addSub` to prevent cycles in recordings.
*/
private object UnrecordedState extends VarState:
override def putElems(v: Var, refs: Refs) = true
override def putDeps(v: Var, deps: Deps) = true
override def rollBack(): Unit = ()
end VarState

@sharable
/** The current VarState, as passed by the implicit context */
def varState(using state: VarState): VarState = state

Expand Down
Loading

0 comments on commit aa5f411

Please sign in to comment.