Skip to content

Commit 5517193

Browse files
Backport "changes to scala.caps in preparation to make Capability stable" to 3.7.0 (#22967)
Backports #22849 and #22956.
2 parents d112546 + 2ee0c7e commit 5517193

File tree

24 files changed

+209
-122
lines changed

24 files changed

+209
-122
lines changed

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

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

521+
def capsInternalDot(name: Name)(using SourceFile): Select =
522+
Select(Select(scalaDot(nme.caps), nme.internal), name)
523+
521524
def captureRoot(using Context): Select =
522525
Select(scalaDot(nme.caps), nme.CAPTURE_ROOT)
523526

524527
def makeRetaining(parent: Tree, refs: List[Tree], annotName: TypeName)(using Context): Annotated =
525528
Annotated(parent, New(scalaAnnotationDot(annotName), List(refs)))
526529

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

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

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

+12-5
Original file line numberDiff line numberDiff line change
@@ -993,17 +993,19 @@ 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: TypeSymbol = CapsModule.requiredType("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_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_capsOf: TermSymbol = CapsInternalModule.requiredMethod("capsOf")
10021003
@tu lazy val Caps_Exists: ClassSymbol = requiredClass("scala.caps.Exists")
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_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains")
1006-
@tu lazy val Caps_containsImpl: TermSymbol = CapsModule.requiredMethod("containsImpl")
1007+
@tu lazy val Caps_ContainsModule: Symbol = requiredModule("scala.caps.Contains")
1008+
@tu lazy val Caps_containsImpl: TermSymbol = Caps_ContainsModule.requiredMethod("containsImpl")
10071009

10081010
/** The same as CaptureSet.universal but generated implicitly for references of Capability subtypes */
10091011
@tu lazy val universalCSImpliedByCapability = CaptureSet(captureRoot.termRef)
@@ -1063,7 +1065,7 @@ class Definitions {
10631065
@tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable")
10641066
@tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance")
10651067
@tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures")
1066-
@tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.untrackedCaptures")
1068+
@tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.unsafe.untrackedCaptures")
10671069
@tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use")
10681070
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")
10691071
@tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature")
@@ -2087,7 +2089,12 @@ class Definitions {
20872089
*/
20882090
@tu lazy val ccExperimental: Set[Symbol] = Set(
20892091
CapsModule, CapsModule.moduleClass, PureClass,
2092+
Caps_Capability, // TODO: Remove when Capability is stabilized
20902093
RequiresCapabilityAnnot,
2094+
captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass, UseAnnot,
2095+
Caps_Exists,
2096+
CapsUnsafeModule, CapsUnsafeModule.moduleClass,
2097+
CapsInternalModule, CapsInternalModule.moduleClass,
20912098
RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot)
20922099

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

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

+13-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ object SymbolLoaders {
7878
* and give them `completer` as type.
7979
*/
8080
def enterPackage(owner: Symbol, pname: TermName, completer: (TermSymbol, ClassSymbol) => PackageLoader)(using Context): Symbol = {
81-
val preExisting = owner.info.decls lookup pname
81+
val preExisting = owner.info.decls.lookup(pname)
8282
if (preExisting != NoSymbol)
8383
// Some jars (often, obfuscated ones) include a package and
8484
// object with the same name. Rather than render them unusable,
@@ -95,6 +95,18 @@ object SymbolLoaders {
9595
s"Resolving package/object name conflict in favor of object ${preExisting.fullName}. The package will be inaccessible.")
9696
return NoSymbol
9797
}
98+
else if pname == nme.caps && owner == defn.ScalaPackageClass then
99+
// `scala.caps`` was an object until 3.6, it is a package from 3.7. Without special handling
100+
// this would cause a TypeError to be thrown below if a build has several versions of the
101+
// Scala standard library on the classpath. This was the case for 29 projects in OpenCB.
102+
// These projects should be updated. But until that's the case we issue a warning instead
103+
// of a hard failure.
104+
report.warning(
105+
em"""$owner contains object and package with same name: $pname.
106+
|This indicates that there are several versions of the Scala standard library on the classpath.
107+
|The build should be reconfigured so that only one version of the standard library is on the classpath.""")
108+
owner.info.decls.openForMutations.unlink(preExisting)
109+
owner.info.decls.openForMutations.unlink(preExisting.moduleClass)
98110
else
99111
throw TypeError(
100112
em"""$owner contains object and package with same name: $pname

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,11 @@ class PlainPrinter(_ctx: Context) extends Printer {
187187
homogenize(tp) match {
188188
case tp: TypeType =>
189189
toTextRHS(tp)
190+
case tp: TermRef if tp.isRootCapability =>
191+
toTextCaptureRef(tp)
190192
case tp: TermRef
191193
if !tp.denotationIsCurrent
192194
&& !homogenizedView // always print underlying when testing picklers
193-
&& !tp.isRootCapability
194195
|| tp.symbol.is(Module)
195196
|| tp.symbol.name == nme.IMPORT =>
196197
toTextRef(tp) ~ ".type"

library/src/scala/caps.scala

-69
This file was deleted.

library/src/scala/caps/package.scala

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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+
/** Carrier trait for capture set type parameters */
33+
@experimental
34+
trait CapSet extends Any
35+
36+
/** A type constraint expressing that the capture set `C` needs to contain
37+
* the capability `R`
38+
*/
39+
@experimental
40+
sealed trait Contains[+C >: CapSet <: CapSet @retainsCap, R <: Singleton]
41+
42+
@experimental
43+
object Contains:
44+
/** The only implementation of `Contains`. The constraint that `{R} <: C` is
45+
* added separately by the capture checker.
46+
*/
47+
@experimental
48+
given containsImpl[C >: CapSet <: CapSet @retainsCap, R <: Singleton]: Contains[C, R]()
49+
50+
/** An annotation on parameters `x` stating that the method's body makes
51+
* use of the reach capability `x*`. Consequently, when calling the method
52+
* we need to charge the deep capture set of the actual argiment to the
53+
* environment.
54+
*
55+
* Note: This should go into annotations. For now it is here, so that we
56+
* can experiment with it quickly between minor releases
57+
*/
58+
@experimental
59+
final class use extends annotation.StaticAnnotation
60+
61+
/** A trait to allow expressing existential types such as
62+
*
63+
* (x: Exists) => A ->{x} B
64+
*/
65+
@experimental
66+
sealed trait Exists extends Capability
67+
68+
@experimental
69+
object internal:
70+
71+
/** A wrapper indicating a type variable in a capture argument list of a
72+
* @retains annotation. E.g. `^{x, Y^}` is represented as `@retains(x, capsOf[Y])`.
73+
*/
74+
@compileTimeOnly("Should be be used only internally by the Scala compiler")
75+
def capsOf[CS >: CapSet <: CapSet @retainsCap]: Any = ???
76+
77+
/** Reach capabilities x* which appear as terms in @retains annotations are encoded
78+
* as `caps.reachCapability(x)`. When converted to CaptureRef types in capture sets
79+
* they are represented as `x.type @annotation.internal.reachCapability`.
80+
*/
81+
extension (x: Any) def reachCapability: Any = x
82+
83+
@experimental
84+
object unsafe:
85+
/**
86+
* Marks the constructor parameter as untracked.
87+
* The capture set of this parameter will not be included in
88+
* the capture set of the constructed object.
89+
*
90+
* @note This should go into annotations. For now it is here, so that we
91+
* can experiment with it quickly between minor releases
92+
*/
93+
final class untrackedCaptures extends annotation.StaticAnnotation
94+
95+
extension [T](x: T)
96+
/** A specific cast operation to remove a capture set.
97+
* If argument is of type `T^C`, assume it is of type `T` instead.
98+
* Calls to this method are treated specially by the capture checker.
99+
*/
100+
def unsafeAssumePure: T = x
101+
102+
/** A wrapper around code for which separation checks are suppressed.
103+
*/
104+
def unsafeAssumeSeparate(op: Any): op.type = op
105+
106+
end unsafe

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import scala.language.implicitConversions
2424
import scala.runtime.Statics
2525
import language.experimental.captureChecking
2626
import annotation.unchecked.uncheckedCaptures
27-
import caps.untrackedCaptures
27+
import caps.unsafe.untrackedCaptures
2828

2929
/** This class implements an immutable linked list. We call it "lazy"
3030
* 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

tests/neg-custom-args/captures/capt1.check

+6-6
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@
3636
-- Error: tests/neg-custom-args/captures/capt1.scala:34:16 -------------------------------------------------------------
3737
34 | val z2 = h[() -> Cap](() => x) // error // error
3838
| ^^^^^^^^^
39-
| Type variable X of method h cannot be instantiated to () -> (ex$15: caps.Exists) -> C^{ex$15} since
40-
| the part C^{ex$15} of that type captures the root capability `cap`.
39+
| Type variable X of method h cannot be instantiated to () -> (ex$15: scala.caps.Exists) -> C^{ex$15} since
40+
| the part C^{ex$15} of that type captures the root capability `cap`.
4141
-- Error: tests/neg-custom-args/captures/capt1.scala:34:30 -------------------------------------------------------------
4242
34 | val z2 = h[() -> Cap](() => x) // error // error
4343
| ^
44-
| reference (x : C^) is not included in the allowed capture set {}
45-
| of an enclosing function literal with expected type () -> (ex$15: caps.Exists) -> C^{ex$15}
44+
| reference (x : C^) is not included in the allowed capture set {}
45+
| of an enclosing function literal with expected type () -> (ex$15: scala.caps.Exists) -> C^{ex$15}
4646
-- Error: tests/neg-custom-args/captures/capt1.scala:36:13 -------------------------------------------------------------
4747
36 | val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error
4848
| ^^^^^^^^^^^^^^^^^^^^^^^
49-
| Type variable X of method h cannot be instantiated to box () ->{x} (ex$20: caps.Exists) -> C^{ex$20} since
50-
| the part C^{ex$20} of that type captures the root capability `cap`.
49+
|Type variable X of method h cannot be instantiated to box () ->{x} (ex$20: scala.caps.Exists) -> C^{ex$20} since
50+
|the part C^{ex$20} of that type captures the root capability `cap`.
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
-- [E057] Type Mismatch Error: tests/neg-custom-args/captures/cc-poly-1.scala:12:6 -------------------------------------
22
12 | f[Any](D()) // error
33
| ^
4-
| Type argument Any does not conform to upper bound caps.CapSet^
4+
| Type argument Any does not conform to upper bound scala.caps.CapSet^
55
|
66
| longer explanation available when compiling with `-explain`
77
-- [E057] Type Mismatch Error: tests/neg-custom-args/captures/cc-poly-1.scala:13:6 -------------------------------------
88
13 | f[String](D()) // error
99
| ^
10-
| Type argument String does not conform to upper bound caps.CapSet^
10+
| Type argument String does not conform to upper bound scala.caps.CapSet^
1111
|
1212
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/cc-this2.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
-- Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:3:8 --------------------------------------------------------
33
3 | this: D^ => // error
44
| ^^
5-
|reference (caps.cap : caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class C
5+
| reference cap captured by this self type is not included in the allowed capture set {} of pure base class class C
66
-- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:2:6 -----------------------------------
77
2 |class D extends C: // error
88
| ^

tests/neg-custom-args/captures/exception-definitions.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Error: tests/neg-custom-args/captures/exception-definitions.scala:3:8 -----------------------------------------------
22
3 | self: Err^ => // error
33
| ^^^^
4-
|reference (caps.cap : caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class Throwable
4+
|reference cap captured by this self type is not included in the allowed capture set {} of pure base class class Throwable
55
-- Error: tests/neg-custom-args/captures/exception-definitions.scala:7:12 ----------------------------------------------
66
7 | val x = c // error
77
| ^

0 commit comments

Comments
 (0)