Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/cc/Capability.scala
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,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
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
53 changes: 27 additions & 26 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 @@ -191,7 +191,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 +315,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 @@ -542,13 +542,13 @@ object CaptureSet:
type Deps = SimpleIdentitySet[CaptureSet]

enum Mutability:
case Mutable, Reader, Ignored
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 +569,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 +630,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 +652,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 +665,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 +728,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 +847,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 +934,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 +1047,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 +1176,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 +1404,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 +1413,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
33 changes: 16 additions & 17 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ class CheckCaptures extends Recheck, SymTransformer:
if nextEnv != null && !nextEnv.owner.isStaticOwner then
if nextEnv.owner != env.owner
&& env.owner.isReadOnlyMember
&& env.owner.owner.derivesFrom(defn.Caps_Mutable)
&& env.owner.owner.derivesFrom(defn.Caps_Stateful)
then
checkReadOnlyMethod(included, env.owner)
recur(included, nextEnv, env)
Expand Down Expand Up @@ -864,6 +864,7 @@ class CheckCaptures extends Recheck, SymTransformer:
&& !resultType.isBoxedCapturing
&& !tree.fun.symbol.isConstructor
&& !resultType.captureSet.containsResultCapability
&& !resultType.captureSet.elems.exists(_.derivesFromUnscoped)
&& qualCaptures.mightSubcapture(refs)
&& argCaptures.forall(_.mightSubcapture(refs)) =>
val callCaptures = argCaptures.foldLeft(qualCaptures)(_ ++ _)
Expand Down Expand Up @@ -961,7 +962,7 @@ class CheckCaptures extends Recheck, SymTransformer:
/** The additional capture set implied by the capture sets of its fields. This
* is either empty or, if some fields have a terminal capability in their span
* capture sets, it consists of a single fresh cap that subsumes all these terminal
* capabiltities. Class parameters are not counted. If the type is a mutable type,
* capabiltities. Class parameters are not counted. If the type externds Separate,
* we add a fresh cap in any case -- this is because we can currently hide
* mutability in array vals, an example is neg-customargs/captures/matrix.scala.
*/
Expand All @@ -980,8 +981,6 @@ class CheckCaptures extends Recheck, SymTransformer:
def impliedClassifiers(cls: Symbol): List[ClassSymbol] = cls match
case cls: ClassSymbol =>
var fieldClassifiers = knownFields(cls).flatMap(classifiersOfFreshInType)
if cls.typeRef.isMutableType then
fieldClassifiers = cls.classifier :: fieldClassifiers
val parentClassifiers =
cls.parentSyms.map(impliedClassifiers).filter(_.nonEmpty)
if fieldClassifiers.isEmpty && parentClassifiers.isEmpty
Expand Down Expand Up @@ -1166,10 +1165,10 @@ class CheckCaptures extends Recheck, SymTransformer:
tree.tpt.nuType, NoSymbol, i"Mutable $sym", "have type", addendum, sym.srcPos)
if ccConfig.noUnsafeMutableFields
&& sym.owner.isClass
&& !sym.owner.derivesFrom(defn.Caps_Mutable)
&& !sym.owner.derivesFrom(defn.Caps_Stateful)
&& !sym.hasAnnotation(defn.UntrackedCapturesAnnot) then
report.error(
em"""Mutable $sym is defined in a class that does not extend `Mutable`.
em"""Mutable $sym is defined in a class that does not extend `Stateful`.
|The variable needs to be annotated with `untrackedCaptures` to allow this.""",
tree.namePos)

Expand Down Expand Up @@ -1867,7 +1866,7 @@ class CheckCaptures extends Recheck, SymTransformer:
if needsAdaptation && !insertBox then // we are unboxing
val criticalSet = // the set with which we unbox
if covariant then
if expected.expectsReadOnly && actual.derivesFromMutable
if expected.expectsReadOnly && actual.derivesFromStateful
then captures.readOnly
else captures
else expected.captureSet // contravarant: we unbox with captures of epected type
Expand Down Expand Up @@ -2242,18 +2241,18 @@ class CheckCaptures extends Recheck, SymTransformer:
end for
end checkEscapingUses

/** Check all parent class constructors of classes extending Mutable
* either also extend Mutable or are read-only.
/** Check all parent class constructors of classes extending Stateful
* either also extend Stateful or are read-only.
*
* A parent class constructor is _read-only_ if the following conditions are met
* 1. The class does not retain any exclusive capabilities from its environment.
* 2. The constructor does not take arguments that retain exclusive capabilities.
* 3. The class does not does not have fields that retain exclusive universal capabilities.
*/
def checkMutableInheritance(cls: ClassSymbol, parents: List[Tree])(using Context): Unit =
if cls.derivesFrom(defn.Caps_Mutable) then
def checkStatefulInheritance(cls: ClassSymbol, parents: List[Tree])(using Context): Unit =
if cls.derivesFrom(defn.Caps_Stateful) then
for parent <- parents do
if !parent.tpe.derivesFromMutable then
if !parent.tpe.derivesFromStateful then
val pcls = parent.nuType.classSymbol
val parentIsExclusive =
if parent.isType then
Expand All @@ -2263,17 +2262,17 @@ class CheckCaptures extends Recheck, SymTransformer:
else parent.nuType.captureSet.isExclusive
if parentIsExclusive then
report.error(
em"""illegal inheritance: $cls which extends `Mutable` is not allowed to also extend $pcls
|since $pcls retains exclusive capabilities but does not extend `Mutable`.""",
em"""illegal inheritance: $cls which extends `Stateful` is not allowed to also extend $pcls
|since $pcls retains exclusive capabilities but does not extend `Stateful`.""",
parent.srcPos)

/** Checks to run after the rechecking pass:
* - Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
* - Check that no uses refer to reach capabilities of parameters of enclosing
* methods or classes.
* - Run the separation checker under language.experimental.separationChecking
* - Check that classes extending Mutable do not extend other classes that do
* not extend Mutable yet retain exclusive capabilities
* - Check that classes extending Stateful do not extend other classes that do
* not extend Stateful yet retain exclusive capabilities
*/
def postCheck(unit: tpd.Tree)(using Context): Unit =
val checker = new TreeTraverser:
Expand All @@ -2298,7 +2297,7 @@ class CheckCaptures extends Recheck, SymTransformer:
args.lazyZip(tl.paramNames).foreach(checkTypeParam(_, _, fun.symbol))
case _ =>
case TypeDef(_, impl: Template) =>
checkMutableInheritance(tree.symbol.asClass, impl.parents)
checkStatefulInheritance(tree.symbol.asClass, impl.parents)
case _ =>
end checker

Expand Down
Loading
Loading