Skip to content

Commit f177289

Browse files
authored
Changes in preparation to make caps.Capability stable (#22849)
Make `scala.caps` a package instead of an object. The following are still under `scala.caps`, while staying `@experimental`: - The Capability trait, which will be stable in near future - The universal capture reference `cap` -- **now an object**. - The carrier trait for capture set parameters `CapSet` - The `Contains` trait: - Due to `given`s cannot be marked `@experimental`, an experimental `Contains` object was created instead, which contains the `containsImpl` given. - Exclusive capabilities annotations: `Mutable`, `SharedCapability` and `consume` - The _`Exists`_ trait is marked `deprecated`, but is required to build the current version of the compiler. It should be removed in the next version. - The `unsafe` object: - `untrackedCaptures` is moved into `caps.unsafe`. - Deprecated aliases `*` and `Cap` are **removed** The following are moved under the experimental `scala.caps.internal` object: - `capsOf` - `rootCapability`, `reachCapability` and `readOnlyCapability` - `refineOverride` Add documentation for `Capability` and some other experimental annotations. Tests are updated accordingly.
1 parent ee4c68a commit f177289

File tree

15 files changed

+199
-139
lines changed

15 files changed

+199
-139
lines changed

compiler/src/dotty/tools/dotc/ast/untpd.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -521,14 +521,17 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
521521
def scalaUnit(implicit src: SourceFile): Select = scalaDot(tpnme.Unit)
522522
def scalaAny(implicit src: SourceFile): Select = scalaDot(tpnme.Any)
523523

524+
def capsInternalDot(name: Name)(using SourceFile): Select =
525+
Select(Select(scalaDot(nme.caps), nme.internal), name)
526+
524527
def captureRoot(using Context): Select =
525528
Select(scalaDot(nme.caps), nme.CAPTURE_ROOT)
526529

527530
def makeRetaining(parent: Tree, refs: List[Tree], annotName: TypeName)(using Context): Annotated =
528531
Annotated(parent, New(scalaAnnotationDot(annotName), List(refs)))
529532

530533
def makeCapsOf(tp: RefTree)(using Context): Tree =
531-
TypeApply(Select(scalaDot(nme.caps), nme.capsOf), tp :: Nil)
534+
TypeApply(capsInternalDot(nme.capsOf), tp :: Nil)
532535

533536
// Capture set variable `[C^]` becomes: `[C >: CapSet <: CapSet^{cap}]`
534537
def makeCapsBound()(using Context): TypeBoundsTree =

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

+15-8
Original file line numberDiff line numberDiff line change
@@ -993,18 +993,20 @@ class Definitions {
993993
@tu lazy val LabelClass: Symbol = requiredClass("scala.util.boundary.Label")
994994
@tu lazy val BreakClass: Symbol = requiredClass("scala.util.boundary.Break")
995995

996-
@tu lazy val CapsModule: Symbol = requiredModule("scala.caps")
996+
@tu lazy val CapsModule: Symbol = requiredPackage("scala.caps")
997997
@tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("cap")
998998
@tu lazy val Caps_Capability: ClassSymbol = requiredClass("scala.caps.Capability")
999999
@tu lazy val Caps_CapSet: ClassSymbol = requiredClass("scala.caps.CapSet")
1000-
@tu lazy val Caps_reachCapability: TermSymbol = CapsModule.requiredMethod("reachCapability")
1001-
@tu lazy val Caps_readOnlyCapability: TermSymbol = CapsModule.requiredMethod("readOnlyCapability")
1002-
@tu lazy val Caps_capsOf: TermSymbol = CapsModule.requiredMethod("capsOf")
1000+
@tu lazy val CapsInternalModule: Symbol = requiredModule("scala.caps.internal")
1001+
@tu lazy val Caps_reachCapability: TermSymbol = CapsInternalModule.requiredMethod("reachCapability")
1002+
@tu lazy val Caps_readOnlyCapability: TermSymbol = CapsInternalModule.requiredMethod("readOnlyCapability")
1003+
@tu lazy val Caps_capsOf: TermSymbol = CapsInternalModule.requiredMethod("capsOf")
10031004
@tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe")
10041005
@tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure")
10051006
@tu lazy val Caps_unsafeAssumeSeparate: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumeSeparate")
10061007
@tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains")
1007-
@tu lazy val Caps_containsImpl: TermSymbol = CapsModule.requiredMethod("containsImpl")
1008+
@tu lazy val Caps_ContainsModule: Symbol = requiredModule("scala.caps.Contains")
1009+
@tu lazy val Caps_containsImpl: TermSymbol = Caps_ContainsModule.requiredMethod("containsImpl")
10081010
@tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable")
10091011
@tu lazy val Caps_SharedCapability: ClassSymbol = requiredClass("scala.caps.SharedCapability")
10101012

@@ -1066,10 +1068,10 @@ class Definitions {
10661068
@tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable")
10671069
@tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance")
10681070
@tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures")
1069-
@tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.untrackedCaptures")
1071+
@tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.unsafe.untrackedCaptures")
10701072
@tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use")
10711073
@tu lazy val ConsumeAnnot: ClassSymbol = requiredClass("scala.caps.consume")
1072-
@tu lazy val RefineOverrideAnnot: ClassSymbol = requiredClass("scala.caps.refineOverride")
1074+
@tu lazy val RefineOverrideAnnot: ClassSymbol = requiredClass("scala.caps.internal.refineOverride")
10731075
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")
10741076
@tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature")
10751077
@tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter")
@@ -1085,7 +1087,7 @@ class Definitions {
10851087
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
10861088
@tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs")
10871089
@tu lazy val ReachCapabilityAnnot = requiredClass("scala.annotation.internal.reachCapability")
1088-
@tu lazy val RootCapabilityAnnot = requiredClass("scala.caps.rootCapability")
1090+
@tu lazy val RootCapabilityAnnot = requiredClass("scala.caps.internal.rootCapability")
10891091
@tu lazy val ReadOnlyCapabilityAnnot = requiredClass("scala.annotation.internal.readOnlyCapability")
10901092
@tu lazy val RequiresCapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.internal.requiresCapability")
10911093
@tu lazy val RetainsAnnot: ClassSymbol = requiredClass("scala.annotation.retains")
@@ -2096,7 +2098,12 @@ class Definitions {
20962098
*/
20972099
@tu lazy val ccExperimental: Set[Symbol] = Set(
20982100
CapsModule, CapsModule.moduleClass, PureClass,
2101+
Caps_Capability, // TODO: Remove when Capability is stabilized
20992102
RequiresCapabilityAnnot,
2103+
captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass, UseAnnot,
2104+
Caps_Mutable, Caps_SharedCapability, ConsumeAnnot,
2105+
CapsUnsafeModule, CapsUnsafeModule.moduleClass,
2106+
CapsInternalModule, CapsInternalModule.moduleClass,
21002107
RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot)
21012108

21022109
/** Experimental language features defined in `scala.runtime.stdLibPatches.language.experimental`.

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,11 @@ class PlainPrinter(_ctx: Context) extends Printer {
195195
homogenize(tp) match {
196196
case tp: TypeType =>
197197
toTextRHS(tp)
198+
case tp: TermRef if tp.isCap =>
199+
toTextCaptureRef(tp)
198200
case tp: TermRef
199201
if !tp.denotationIsCurrent
200202
&& !homogenizedView // always print underlying when testing picklers
201-
&& !tp.isCap
202203
|| tp.symbol.is(Module)
203204
|| tp.symbol.name == nme.IMPORT =>
204205
toTextRef(tp) ~ ".type"

library/src/scala/caps.scala

-114
This file was deleted.

library/src/scala/caps/package.scala

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package scala
2+
package caps
3+
4+
import annotation.{experimental, compileTimeOnly, retainsCap}
5+
6+
/**
7+
* Base trait for classes that represent capabilities in the
8+
* [object-capability model](https://en.wikipedia.org/wiki/Object-capability_model).
9+
*
10+
* A capability is a value representing a permission, access right, resource or effect.
11+
* Capabilities are typically passed to code as parameters; they should not be global objects.
12+
* Often, they come with access restrictions such as scoped lifetimes or limited sharing.
13+
*
14+
* An example is the [[scala.util.boundary.Label Label]] class in [[scala.util.boundary]].
15+
* It represents a capability in the sense that it gives permission to [[scala.util.boundary.break break]]
16+
* to the enclosing boundary represented by the `Label`. It has a scoped lifetime, since breaking to
17+
* a `Label` after the associated `boundary` was exited gives a runtime exception.
18+
*
19+
* [[Capability]] has a formal meaning when
20+
* [[scala.language.experimental.captureChecking Capture Checking]]
21+
* is turned on.
22+
* But even without capture checking, extending this trait can be useful for documenting the intended purpose
23+
* of a class.
24+
*/
25+
@experimental
26+
trait Capability extends Any
27+
28+
/** The universal capture reference. */
29+
@experimental
30+
object cap extends Capability
31+
32+
/** Marker trait for classes with methods that requires an exclusive reference. */
33+
@experimental
34+
trait Mutable extends Capability
35+
36+
/** Marker trait for capabilities that can be safely shared in a concurrent context.
37+
* During separation checking, shared capabilities are not taken into account.
38+
*/
39+
@experimental
40+
trait SharedCapability extends Capability
41+
42+
/** Carrier trait for capture set type parameters */
43+
@experimental
44+
trait CapSet extends Any
45+
46+
/** A type constraint expressing that the capture set `C` needs to contain
47+
* the capability `R`
48+
*/
49+
@experimental
50+
sealed trait Contains[+C >: CapSet <: CapSet @retainsCap, R <: Singleton]
51+
52+
@experimental
53+
object Contains:
54+
/** The only implementation of `Contains`. The constraint that `{R} <: C` is
55+
* added separately by the capture checker.
56+
*/
57+
@experimental
58+
given containsImpl[C >: CapSet <: CapSet @retainsCap, R <: Singleton]: Contains[C, R]()
59+
60+
/** An annotation on parameters `x` stating that the method's body makes
61+
* use of the reach capability `x*`. Consequently, when calling the method
62+
* we need to charge the deep capture set of the actual argiment to the
63+
* environment.
64+
*
65+
* Note: This should go into annotations. For now it is here, so that we
66+
* can experiment with it quickly between minor releases
67+
*/
68+
@experimental
69+
final class use extends annotation.StaticAnnotation
70+
71+
/** An annotations on parameters and update methods.
72+
* On a parameter it states that any capabilties passed in the argument
73+
* are no longer available afterwards, unless they are of class `SharableCapabilitty`.
74+
* On an update method, it states that the `this` of the enclosing class is
75+
* consumed, which means that any capabilities of the method prefix are
76+
* no longer available afterwards.
77+
*/
78+
@experimental
79+
final class consume extends annotation.StaticAnnotation
80+
81+
/** A trait that used to allow expressing existential types. Replaced by
82+
* root.Result instances.
83+
*/
84+
@experimental
85+
@deprecated
86+
sealed trait Exists extends Capability
87+
88+
@experimental
89+
object internal:
90+
91+
/** A wrapper indicating a type variable in a capture argument list of a
92+
* @retains annotation. E.g. `^{x, Y^}` is represented as `@retains(x, capsOf[Y])`.
93+
*/
94+
@compileTimeOnly("Should be be used only internally by the Scala compiler")
95+
def capsOf[CS >: CapSet <: CapSet @retainsCap]: Any = ???
96+
97+
/** Reach capabilities x* which appear as terms in @retains annotations are encoded
98+
* as `caps.reachCapability(x)`. When converted to CaptureRef types in capture sets
99+
* they are represented as `x.type @annotation.internal.reachCapability`.
100+
*/
101+
extension (x: Any) def reachCapability: Any = x
102+
103+
/** Read-only capabilities x.rd which appear as terms in @retains annotations are encoded
104+
* as `caps.readOnlyCapability(x)`. When converted to CaptureRef types in capture sets
105+
* they are represented as `x.type @annotation.internal.readOnlyCapability`.
106+
*/
107+
extension (x: Any) def readOnlyCapability: Any = x
108+
109+
/** An internal annotation placed on a refinement created by capture checking.
110+
* Refinements with this annotation unconditionally override any
111+
* info from the parent type, so no intersection needs to be formed.
112+
* This could be useful for tracked parameters as well.
113+
*/
114+
final class refineOverride extends annotation.StaticAnnotation
115+
116+
/** An annotation used internally for root capability wrappers of `cap` that
117+
* represent either Fresh or Result capabilities.
118+
* A capability is encoded as `caps.cap @rootCapability(...)` where
119+
* `rootCapability(...)` is a special kind of annotation of type `root.Annot`
120+
* that contains either a hidden set for Fresh instances or a method type binder
121+
* for Result instances.
122+
*/
123+
final class rootCapability extends annotation.StaticAnnotation
124+
125+
@experimental
126+
object unsafe:
127+
/**
128+
* Marks the constructor parameter as untracked.
129+
* The capture set of this parameter will not be included in
130+
* the capture set of the constructed object.
131+
*
132+
* @note This should go into annotations. For now it is here, so that we
133+
* can experiment with it quickly between minor releases
134+
*/
135+
final class untrackedCaptures extends annotation.StaticAnnotation
136+
137+
extension [T](x: T)
138+
/** A specific cast operation to remove a capture set.
139+
* If argument is of type `T^C`, assume it is of type `T` instead.
140+
* Calls to this method are treated specially by the capture checker.
141+
*/
142+
def unsafeAssumePure: T = x
143+
144+
/** A wrapper around code for which separation checks are suppressed.
145+
*/
146+
def unsafeAssumeSeparate(op: Any): op.type = op
147+
148+
end unsafe

scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import scala.language.implicitConversions
2424
import scala.runtime.Statics
2525
import language.experimental.captureChecking
2626
import annotation.unchecked.uncheckedCaptures
27-
import caps.{cap, untrackedCaptures}
28-
import caps.unsafe.unsafeAssumeSeparate
27+
import caps.cap
28+
import caps.unsafe.{unsafeAssumeSeparate, untrackedCaptures}
2929

3030
/** This class implements an immutable linked list. We call it "lazy"
3131
* because it computes its elements only when they are needed.

tests/disabled/neg-custom-args/captures/capt-wf.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// No longer valid
22
class C
3-
type Cap = C @retains(caps.*)
4-
type Top = Any @retains(caps.*)
3+
type Cap = C @retains(caps.cap)
4+
type Top = Any @retains(caps.cap)
55

66
type T = (x: Cap) => List[String @retains(x)] => Unit // error
77
val x: (x: Cap) => Array[String @retains(x)] = ??? // error

tests/disabled/neg-custom-args/captures/try2.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import annotation.ability
55
@ability erased val canThrow: * = ???
66

77
class CanThrow[E <: Exception] extends Retains[canThrow.type]
8-
type Top = Any @retains(caps.*)
8+
type Top = Any @retains(caps.cap)
99

1010
infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R
1111

0 commit comments

Comments
 (0)