Skip to content

Commit ac01ea8

Browse files
committed
Make Capability a sealed trait
- two subtraits: SharedCapability and ExclusiveCapability
1 parent a4eef8a commit ac01ea8

File tree

93 files changed

+277
-195
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+277
-195
lines changed

compiler/src/dotty/tools/dotc/cc/Capability.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ object Capabilities:
492492

493493
def derivesFromCapability(using Context): Boolean = derivesFromCapTrait(defn.Caps_Capability)
494494
def derivesFromMutable(using Context): Boolean = derivesFromCapTrait(defn.Caps_Mutable)
495-
def derivesFromSharable(using Context): Boolean = derivesFromCapTrait(defn.Caps_Sharable)
495+
def derivesFromShared(using Context): Boolean = derivesFromCapTrait(defn.Caps_SharedCapability)
496496

497497
/** The capture set consisting of exactly this reference */
498498
def singletonCaptureSet(using Context): CaptureSet.Const =
@@ -707,7 +707,7 @@ object Capabilities:
707707
case x: ResultCap =>
708708
val result = y match
709709
case y: ResultCap => vs.unify(x, y)
710-
case _ => y.derivesFromSharable
710+
case _ => y.derivesFromShared
711711
if !result then
712712
TypeComparer.addErrorNote(CaptureSet.ExistentialSubsumesFailure(x, y))
713713
result
@@ -717,7 +717,7 @@ object Capabilities:
717717
case _: ResultCap => false
718718
case _: FreshCap if CCState.collapseFresh => true
719719
case _ =>
720-
y.derivesFromSharable
720+
y.derivesFromShared
721721
|| canAddHidden && vs != VarState.HardSeparate && CCState.capIsRoot
722722
case Restricted(x1, cls) =>
723723
y.isKnownClassifiedAs(cls) && x1.maxSubsumes(y, canAddHidden)

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ extension (tp: Type)
378378

379379
def derivesFromCapability(using Context): Boolean = derivesFromCapTrait(defn.Caps_Capability)
380380
def derivesFromMutable(using Context): Boolean = derivesFromCapTrait(defn.Caps_Mutable)
381-
def derivesFromSharedCapability(using Context): Boolean = derivesFromCapTrait(defn.Caps_Sharable)
381+
def derivesFromShared(using Context): Boolean = derivesFromCapTrait(defn.Caps_SharedCapability)
382382

383383
/** Drop @retains annotations everywhere */
384384
def dropAllRetains(using Context): Type = // TODO we should drop retains from inferred types before unpickling

compiler/src/dotty/tools/dotc/cc/SepCheck.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
612612
val badParams = mutable.ListBuffer[Symbol]()
613613
def currentOwner = role.dclSym.orElse(ctx.owner)
614614
for hiddenRef <- refsToCheck.deductSymRefs(role.dclSym).deduct(explicitRefs(tpe)) do
615-
if !hiddenRef.derivesFromSharable then
615+
if !hiddenRef.derivesFromShared then
616616
hiddenRef.pathRoot match
617617
case ref: TermRef =>
618618
val refSym = ref.symbol
@@ -649,7 +649,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
649649
role match
650650
case _: TypeRole.Argument | _: TypeRole.Qualifier =>
651651
for ref <- refsToCheck do
652-
if !ref.derivesFromSharable then
652+
if !ref.derivesFromShared then
653653
consumed.put(ref, pos)
654654
case _ =>
655655
end checkConsumedRefs

compiler/src/dotty/tools/dotc/cc/Setup.scala

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -375,14 +375,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
375375
else fntpe
376376

377377
/** 1. Check that parents of capturing types are not pure.
378-
* 2. Check that types extending caps.Sharable don't have a `cap` in their capture set.
379-
* TODO: Is this enough?
380-
* We need to also track that we cannot get exclusive capabilities in paths
381-
* where some prefix derives from Sharable. Also, can we just
382-
* exclude `cap`, or do we have to extend this to all exclusive capabilties?
383-
* The problem is that we know what is exclusive in general only after capture
384-
* checking, not before.
385-
* But maybe the rules for classification already cover these cases.
386378
*/
387379
def checkRetainsOK(tp: Type): tp.type =
388380
tp match
@@ -393,8 +385,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
393385
// an explicit result type in the override, the inherited capture set
394386
// will be ignored anyway.
395387
fail(em"$parent is a pure type, it makes no sense to add a capture set to it")
396-
else if refs.isUniversal && parent.derivesFromSharedCapability then
397-
fail(em"$tp extends Sharable, so it cannot capture `cap`")
398388
case _ =>
399389
tp
400390

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,12 @@ class Definitions {
10031003
@tu lazy val CapsModule: Symbol = requiredPackage("scala.caps")
10041004
@tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("cap")
10051005
@tu lazy val Caps_Capability: ClassSymbol = requiredClass("scala.caps.Capability")
1006+
@tu lazy val Caps_Classifier: ClassSymbol = requiredClass("scala.caps.Classifier")
1007+
@tu lazy val Caps_SharedCapability: ClassSymbol = requiredClass("scala.caps.SharedCapability")
1008+
@tu lazy val Caps_ExclusiveCapability: ClassSymbol = requiredClass("scala.caps.ExclusiveCapability")
1009+
@tu lazy val Caps_Control: ClassSymbol = requiredClass("scala.caps.Control")
1010+
@tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable")
1011+
@tu lazy val Caps_Read: ClassSymbol = requiredClass("scala.caps.Read")
10061012
@tu lazy val Caps_CapSet: ClassSymbol = requiredClass("scala.caps.CapSet")
10071013
@tu lazy val CapsInternalModule: Symbol = requiredModule("scala.caps.internal")
10081014
@tu lazy val Caps_erasedValue: Symbol = CapsInternalModule.requiredMethod("erasedValue")
@@ -1013,10 +1019,6 @@ class Definitions {
10131019
@tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains")
10141020
@tu lazy val Caps_ContainsModule: Symbol = requiredModule("scala.caps.Contains")
10151021
@tu lazy val Caps_containsImpl: TermSymbol = Caps_ContainsModule.requiredMethod("containsImpl")
1016-
@tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable")
1017-
@tu lazy val Caps_Sharable: ClassSymbol = requiredClass("scala.caps.Sharable")
1018-
@tu lazy val Caps_Control: ClassSymbol = requiredClass("scala.caps.Control")
1019-
@tu lazy val Caps_Classifier: ClassSymbol = requiredClass("scala.caps.Classifier")
10201022

10211023
@tu lazy val PureClass: ClassSymbol = requiredClass("scala.Pure")
10221024

@@ -2110,10 +2112,10 @@ class Definitions {
21102112
@tu lazy val ccExperimental: Set[Symbol] = Set(
21112113
CapsModule, CapsModule.moduleClass, PureClass,
21122114
Caps_Capability, // TODO: Remove when Capability is stabilized
2115+
Caps_Classifier, Caps_SharedCapability, Caps_Control, Caps_ExclusiveCapability, Caps_Mutable, Caps_Read,
21132116
RequiresCapabilityAnnot,
21142117
captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass, UseAnnot,
2115-
Caps_Mutable, Caps_Sharable, Caps_Control, Caps_Classifier, ConsumeAnnot,
2116-
CapsUnsafeModule, CapsUnsafeModule.moduleClass,
2118+
ConsumeAnnot, CapsUnsafeModule, CapsUnsafeModule.moduleClass,
21172119
CapsInternalModule, CapsInternalModule.moduleClass,
21182120
RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot)
21192121

library/src/scala/caps/package.scala

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import annotation.{experimental, compileTimeOnly, retainsCap}
2121
* is turned on.
2222
* But even without capture checking, extending this trait can be useful for documenting the intended purpose
2323
* of a class.
24+
*
25+
* Capability has exactly two subtraits: Shared and Exclusive.
2426
*/
2527
@experimental
26-
trait Capability extends Any
28+
sealed trait Capability extends Any
2729

2830
/** A marker trait for classifier capabilities that can appear in `.only`
2931
* qualifiers. Capability classes directly extending `Classifier` are treated
@@ -36,21 +38,36 @@ trait Classifier
3638
@experimental
3739
object cap extends Capability
3840

39-
/** Marker trait for classes with methods that requires an exclusive reference. */
41+
/** Marker trait for capabilities that can be safely shared in a concurrent context.
42+
* During separation checking, shared capabilities are not taken into account.
43+
*/
4044
@experimental
41-
trait Mutable extends Capability, Classifier
45+
trait SharedCapability extends Capability, Classifier
4246

43-
/** Marker trait for capabilities that can be safely shared in a concurrent context.
44-
* During separation checking, shared capabilities are not taken into account.
45-
*/
4647
@experimental
47-
trait Sharable extends Capability, Classifier
48+
type Shared = SharedCapability
49+
50+
/** Marker trait for exclusive capabilities that are separation-checked
51+
*/
52+
@experimental
53+
trait ExclusiveCapability extends Capability, Classifier
54+
55+
@experimental
56+
type Exclusive = SharedCapability
4857

4958
/** Base trait for capabilities that capture some continuation or return point in
5059
* the stack. Examples are exceptions, labels, Async, CanThrow.
5160
*/
5261
@experimental
53-
trait Control extends Sharable, Classifier
62+
trait Control extends SharedCapability, Classifier
63+
64+
/** Marker trait for classes with methods that require an exclusive reference. */
65+
@experimental
66+
trait Mutable extends ExclusiveCapability, Classifier
67+
68+
/** Marker trait for classes with reader methods, typically extended by Mutable classes */
69+
@experimental
70+
trait Read extends Mutable, Classifier
5471

5572
/** Carrier trait for capture set type parameters */
5673
@experimental

tests/disabled/pos/lazylist.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ object LazyNil extends LazyList[Nothing]:
3434
def map[A, B](xs: {*} LazyList[A], f: {*} A => B): {f, xs} LazyList[B] =
3535
xs.map(f)
3636

37-
class Cap extends caps.Capability
37+
class Cap extends caps.SharedCapability
3838

3939
def test(cap1: Cap, cap2: Cap, cap3: Cap) =
4040
def f[T](x: LazyList[T]): LazyList[T] = if cap1 == cap1 then x else LazyNil

tests/neg-custom-args/captures/branding.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import caps.*
33

44

55
def main() =
6-
trait Channel[T] extends caps.Capability:
6+
trait Channel[T] extends caps.SharedCapability:
77
def send(msg: T): Unit
88
def recv(): T
99

10-
trait Logger extends caps.Capability:
10+
trait Logger extends caps.SharedCapability:
1111
def log(msg: String): Unit
1212

1313
// we can close over anything subsumed by the 'trusted' brand capability, but nothing else
1414
def runSecure[C^ >: {trusted} <: {trusted}](block: () ->{C} Unit): Unit = block()
1515

1616
// This is a 'brand" capability to mark what can be mentioned in trusted code
17-
object trusted extends caps.Capability
17+
object trusted extends caps.SharedCapability
1818

1919
val trustedLogger: Logger^{trusted} = ???
2020
val trustedChannel: Channel[String]^{trusted} = ???

tests/neg-custom-args/captures/branding2.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import caps.*
33

44

55
def main() =
6-
trait Channel[T] extends caps.Capability:
6+
trait Channel[T] extends caps.SharedCapability:
77
def send(msg: T): Unit
88
def recv(): T
99

10-
trait Logger extends caps.Capability:
10+
trait Logger extends caps.SharedCapability:
1111
def log(msg: String): Unit
1212

1313
// we can close over anything subsumed by the 'trusted' brand capability, but nothing else
1414
def runSecure(block: () ->{trusted} Unit): Unit = block()
1515

1616
// This is a 'brand" capability to mark what can be mentioned in trusted code
17-
object trusted extends caps.Capability
17+
object trusted extends caps.SharedCapability
1818

1919
val trustedLogger: Logger^{trusted} = ???
2020
val trustedChannel: Channel[String]^{trusted} = ???

tests/neg-custom-args/captures/byname.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
class Cap extends caps.Capability
1+
class Cap extends caps.SharedCapability
22

33
def test(cap1: Cap, cap2: Cap) =
44
def f() = if cap1 == cap1 then g else g

0 commit comments

Comments
 (0)