Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions compiler/src/dotty/tools/dotc/cc/Capability.scala
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,13 @@ object Capabilities:
/** An exclusive capability is a capability that derives
* indirectly from a maximal capability without going through
* a read-only capability or a capability classified as SharedCapability first.
* @param required if true, exclusivity can be obtained by setting the mutability
* status of some capture set variable from Ignored to Writer.
*/
final def isExclusive(using Context): Boolean =
final def isExclusive(required: Boolean = false)(using Context): Boolean =
!isReadOnly
&& !classifier.derivesFrom(defn.Caps_SharedCapability)
&& (isTerminalCapability || captureSetOfInfo.isExclusive)
&& (isTerminalCapability || captureSetOfInfo.isExclusive(required))

/** Similar to isExlusive, but also includes capabilties with capture
* set variables in their info whose status is still open.
Expand Down Expand Up @@ -564,8 +566,9 @@ object Capabilities:
case _ => false

def derivesFromCapability(using Context): Boolean = derivesFromCapTrait(defn.Caps_Capability)
def derivesFromMutable(using Context): Boolean = derivesFromCapTrait(defn.Caps_Mutable)
def derivesFromStateful(using Context): Boolean = derivesFromCapTrait(defn.Caps_Stateful)
def derivesFromShared(using Context): Boolean = derivesFromCapTrait(defn.Caps_SharedCapability)
def derivesFromUnscoped(using Context): Boolean = derivesFromCapTrait(defn.Caps_Unscoped)

/** The capture set consisting of exactly this reference */
def singletonCaptureSet(using Context): CaptureSet.Const =
Expand Down Expand Up @@ -924,7 +927,7 @@ object Capabilities:
* Unclassified : No set exists since some parts of tcs are not classified
* ClassifiedAs(clss: All parts of tcss are classified with classes in clss
*/
enum Classifiers:
enum Classifiers derives CanEqual:
case UnknownClassifier
case Unclassified
case ClassifiedAs(clss: List[ClassSymbol])
Expand Down Expand Up @@ -965,7 +968,7 @@ object Capabilities:
/** The place of - and cause for - creating a fresh capability. Used for
* error diagnostics
*/
enum Origin:
enum Origin derives CanEqual:
case InDecl(sym: Symbol)
case TypeArg(tp: Type)
case UnsafeAssumePure
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import tpd.*
import Annotations.Annotation
import CaptureSet.VarState
import Capabilities.*
import Mutability.isMutableType
import Mutability.isStatefulType
import StdNames.nme
import config.Feature
import NameKinds.TryOwnerName
Expand Down Expand Up @@ -395,9 +395,9 @@ extension (tp: Type)
false

def derivesFromCapability(using Context): Boolean = derivesFromCapTrait(defn.Caps_Capability)
def derivesFromMutable(using Context): Boolean = derivesFromCapTrait(defn.Caps_Mutable)
def derivesFromStateful(using Context): Boolean = derivesFromCapTrait(defn.Caps_Stateful)
def derivesFromShared(using Context): Boolean = derivesFromCapTrait(defn.Caps_SharedCapability)
def derivesFromExclusive(using Context): Boolean = derivesFromCapTrait(defn.Caps_ExclusiveCapability)
def derivesFromUnscoped(using Context): Boolean = derivesFromCapTrait(defn.Caps_Unscoped)

/** Drop @retains annotations everywhere */
def dropAllRetains(using Context): Type = // TODO we should drop retains from inferred types before unpickling
Expand Down Expand Up @@ -531,7 +531,7 @@ extension (cls: ClassSymbol)
else defn.AnyClass

def isSeparate(using Context): Boolean =
cls.typeRef.isMutableType
cls.typeRef.isStatefulType || cls.derivesFrom(defn.Caps_Separate)

extension (sym: Symbol)

Expand Down
73 changes: 43 additions & 30 deletions compiler/src/dotty/tools/dotc/cc/CaptureSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ sealed abstract class CaptureSet extends Showable:
def mutability_=(x: Mutability): Unit =
myMut = x

/** Mark this capture set as belonging to a Mutable type. Called when a new
/** Mark this capture set as belonging to a Stateful type. Called when a new
* CapturingType is formed. This is different from the setter `mutability_=`
* in that it be defined with different behaviors:
* in that it can be defined with different behaviors:
*
* - set mutability to Mutable (for normal Vars)
* - set mutability to Writer (for normal Vars)
* - take mutability from the set's sources (for DerivedVars)
* - compute mutability on demand based on mutability of elements (for Consts)
*/
def associateWithMutable()(using Context): CaptureSet
def associateWithStateful()(using Context): CaptureSet

/** Is this capture set constant (i.e. not an unsolved capture variable)?
* Solved capture variables count as constant.
Expand Down Expand Up @@ -148,15 +148,27 @@ sealed abstract class CaptureSet extends Showable:

final def isAlwaysReadOnly(using Context): Boolean = isConst && isReadOnly

final def isExclusive(using Context): Boolean =
elems.exists(_.isExclusive)
/** Is capture set exclusive? If `required` is true, a variable capture set
* is forced to Writer mutability which makes it exclusive. Otherwise a set
* is exclusive if one of its elements is exclusive.
* Possible issue: If required is true, and the set is a constant, with
* multiple elements that each have a variable capture set, then we make
* the set exclusive by updating the first such variable capture set with
* Ignore mutability to have Write mutability. That makes the effect
* order dependent.
*/
def isExclusive(required: Boolean = false)(using Context): Boolean =
if required && !isConst && mutability == Ignored then
mutability = Writer
mutability == Writer
|| elems.exists(_.isExclusive(required))

/** Similar to isExclusive, but also includes capture set variables
* with unknown status.
*/
final def maybeExclusive(using Context): Boolean = reporting.trace(i"mabe exclusive $this"):
if isConst then elems.exists(_.maybeExclusive)
else mutability != ReadOnly
else mutability != Reader

final def keepAlways: Boolean = this.isInstanceOf[EmptyWithProvenance]

Expand Down Expand Up @@ -191,7 +203,7 @@ sealed abstract class CaptureSet extends Showable:
newElems.forall(tryInclude(_, origin))

protected def mutableToReader(origin: CaptureSet)(using Context): Boolean =
if mutability == Mutable then toReader() else true
if mutability == Writer then toReader() else true

/** Add an element to this capture set, assuming it is not already accounted for,
* and omitting any mapping or filtering.
Expand Down Expand Up @@ -315,8 +327,8 @@ sealed abstract class CaptureSet extends Showable:
def adaptMutability(that: CaptureSet)(using Context): CaptureSet | Null =
val m1 = this.mutability
val m2 = that.mutability
if m1 == Mutable && m2 == Reader then this.readOnly
else if m1 == Reader && m2 == Mutable then
if m1 == Writer && m2 == Reader then this.readOnly
else if m1 == Reader && m2 == Writer then
if that.toReader() then this else null
else this

Expand Down Expand Up @@ -541,14 +553,14 @@ object CaptureSet:
type Vars = SimpleIdentitySet[Var]
type Deps = SimpleIdentitySet[CaptureSet]

enum Mutability:
case Mutable, Reader, Ignored
enum Mutability derives CanEqual:
case Writer, Reader, Ignored

def | (that: Mutability): Mutability =
if this == that then this
else if this == Ignored || that == Ignored then Ignored
else if this == Reader || that == Reader then Reader
else Mutable
else Writer

def & (that: Mutability): Mutability =
if this == that then this
Expand All @@ -569,9 +581,9 @@ object CaptureSet:
@sharable // sharable since the set is empty, so mutability won't be set
val empty: CaptureSet.Const = Const(emptyRefs)

/** The empty capture set `{}` of a Mutable type, with Reader status */
@sharable // sharable since the set is empty, so mutability won't be set
val emptyOfMutable: CaptureSet.Const =
/** The empty capture set `{}` of a Stateful type, with Reader status */
@sharable // sharable since the set is empty, so mutability won't be re-set
val emptyOfStateful: CaptureSet.Const =
val cs = Const(emptyRefs)
cs.mutability = Mutability.Reader
cs
Expand Down Expand Up @@ -630,15 +642,15 @@ object CaptureSet:

private var isComplete = true

def associateWithMutable()(using Context): CaptureSet =
if elems.isEmpty then emptyOfMutable
def associateWithStateful()(using Context): CaptureSet =
if elems.isEmpty then emptyOfStateful
else
isComplete = false // delay computation of Mutability status
this

override def mutability(using Context): Mutability =
if !isComplete then
myMut = if maybeExclusive then Mutable else Reader
myMut = if maybeExclusive then Writer else Reader
isComplete = true
myMut

Expand All @@ -652,7 +664,7 @@ object CaptureSet:
else ""

private def capImpliedByCapability(parent: Type)(using Context): Capability =
if parent.derivesFromMutable then GlobalCap.readOnly else GlobalCap
if parent.derivesFromStateful then GlobalCap.readOnly else GlobalCap

/* The same as {cap} but generated implicitly for references of Capability subtypes.
*/
Expand All @@ -665,13 +677,14 @@ object CaptureSet:
* nulls, this provides more lenient checking against compilation units that
* were not yet compiled with capture checking on.
*/
@sharable // sharable since the set is empty, so setMutable is a no-op
@sharable
object Fluid extends Const(emptyRefs):
override def isAlwaysEmpty(using Context) = false
override def addThisElem(elem: Capability)(using Context, VarState) = true
override def toReader()(using Context) = true
override def accountsFor(x: Capability)(using Context)(using VarState): Boolean = true
override def mightAccountFor(x: Capability)(using Context): Boolean = true
override def mutability_=(x: Mutability): Unit = ()
override def toString = "<fluid>"
end Fluid

Expand Down Expand Up @@ -727,8 +740,8 @@ object CaptureSet:
*/
var deps: Deps = SimpleIdentitySet.empty

def associateWithMutable()(using Context): CaptureSet =
mutability = Mutable
def associateWithStateful()(using Context): CaptureSet =
mutability = Writer
this

def isConst(using Context) = solved >= ccState.iterationId
Expand Down Expand Up @@ -846,7 +859,7 @@ object CaptureSet:
if isConst then failWith(MutAdaptFailure(this))
else
mutability = Reader
TypeComparer.logUndoAction(() => mutability = Mutable)
TypeComparer.logUndoAction(() => mutability = Writer)
deps.forall(_.mutableToReader(this))

private def isPartOf(binder: Type)(using Context): Boolean =
Expand Down Expand Up @@ -933,7 +946,7 @@ object CaptureSet:
def markSolved(provisional: Boolean)(using Context): Unit =
solved = if provisional then ccState.iterationId else Int.MaxValue
deps.foreach(_.propagateSolved(provisional))
if mutability == Mutable && !maybeExclusive then mutability = Reader
if mutability == Writer && !maybeExclusive then mutability = Reader


var skippedMaps: Set[TypeMap] = Set.empty
Expand Down Expand Up @@ -1046,7 +1059,7 @@ object CaptureSet:
addAsDependentTo(source)

/** Mutability is same as in source, except for readOnly */
override def associateWithMutable()(using Context): CaptureSet = this
override def associateWithStateful()(using Context): CaptureSet = this

override def mutableToReader(origin: CaptureSet)(using Context): Boolean =
super.mutableToReader(origin)
Expand Down Expand Up @@ -1175,8 +1188,8 @@ object CaptureSet:
super.mutableToReader(origin)
&& {
if (origin eq cs1) || (origin eq cs2) then true
else if cs1.isConst && cs1.mutability == Mutable then cs2.mutableToReader(this)
else if cs2.isConst && cs2.mutability == Mutable then cs1.mutableToReader(this)
else if cs1.isConst && cs1.mutability == Writer then cs2.mutableToReader(this)
else if cs2.isConst && cs2.mutability == Writer then cs1.mutableToReader(this)
else true
}

Expand Down Expand Up @@ -1403,7 +1416,7 @@ object CaptureSet:
else i"$elem cannot be included in $cs"
end IncludeFailure

/** Failure indicating that a read-only capture set of a mutable type cannot be
/** Failure indicating that a read-only capture set of a stateful type cannot be
* widened to an exclusive set.
* @param cs the exclusive set in question
* @param lo the lower type of the orginal type comparison, or NoType if not known
Expand All @@ -1412,7 +1425,7 @@ object CaptureSet:
case class MutAdaptFailure(cs: CaptureSet, lo: Type = NoType, hi: Type = NoType) extends Note:

def render(using Context): String =
def ofType(tp: Type) = if tp.exists then i"of the mutable type $tp" else "of a mutable type"
def ofType(tp: Type) = if tp.exists then i"of the stateful type $tp" else "of a stateful type"
i"""
|
|Note that $cs is an exclusive capture set ${ofType(hi)},
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/cc/CapturingType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object CapturingType:
apply(parent1, refs ++ refs1, boxed)
case _ =>
val refs1 =
if parent.derivesFromMutable then refs.associateWithMutable() else refs
if parent.derivesFromStateful then refs.associateWithStateful() else refs
refs1.adoptClassifier(parent.inheritedClassifier)
AnnotatedType(parent, CaptureAnnotation(refs1, boxed)(defn.RetainsAnnot))

Expand Down
Loading
Loading