From b32c2420cef385f3b9af4b2bab3124b98b3a0108 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Thu, 20 Mar 2025 18:13:34 +0100 Subject: [PATCH 01/11] Make caps a package with non-experimental Capability trait, add caps.internal The following are still under `scala.caps`, while staying `@experimental`: - The universal capture reference `cap` - The carrier trait for capture set parameters `CapSet` - The `Contains` trait and the `use` annotation - Deprecated aliases `*` and `Cap` - Exclusive capabilities annotations: `Mutable`, `SharedCapability` and `consume` - The _`Exists`_ trait: ideally this should go into `internal`, but I could not get the non-bootstrapped compiler to compile afterwards - The `unsafe` object The following are moved under the experimental `scala.caps.internal` object: - `containsImpl` - `capsOf` - `rootCapability`, `reachCapability` and `readOnlyCapability` - `untrackedCaptures` and `refineOverride` --- .../dotty/tools/dotc/core/Definitions.scala | 19 +-- .../scala/{caps.scala => caps/package.scala} | 135 ++++++++++-------- 2 files changed, 83 insertions(+), 71 deletions(-) rename library/src/scala/{caps.scala => caps/package.scala} (50%) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index c1939d6f8fa6..341bf99ec920 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -993,18 +993,19 @@ class Definitions { @tu lazy val LabelClass: Symbol = requiredClass("scala.util.boundary.Label") @tu lazy val BreakClass: Symbol = requiredClass("scala.util.boundary.Break") - @tu lazy val CapsModule: Symbol = requiredModule("scala.caps") + @tu lazy val CapsModule: Symbol = requiredPackage("scala.caps") @tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("cap") @tu lazy val Caps_Capability: ClassSymbol = requiredClass("scala.caps.Capability") @tu lazy val Caps_CapSet: ClassSymbol = requiredClass("scala.caps.CapSet") - @tu lazy val Caps_reachCapability: TermSymbol = CapsModule.requiredMethod("reachCapability") - @tu lazy val Caps_readOnlyCapability: TermSymbol = CapsModule.requiredMethod("readOnlyCapability") - @tu lazy val Caps_capsOf: TermSymbol = CapsModule.requiredMethod("capsOf") + @tu lazy val CapsInternalModule: Symbol = requiredModule("scala.caps.internal") + @tu lazy val Caps_reachCapability: TermSymbol = CapsInternalModule.requiredMethod("reachCapability") + @tu lazy val Caps_readOnlyCapability: TermSymbol = CapsInternalModule.requiredMethod("readOnlyCapability") + @tu lazy val Caps_capsOf: TermSymbol = CapsInternalModule.requiredMethod("capsOf") @tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe") @tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure") @tu lazy val Caps_unsafeAssumeSeparate: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumeSeparate") @tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains") - @tu lazy val Caps_containsImpl: TermSymbol = CapsModule.requiredMethod("containsImpl") + @tu lazy val Caps_containsImpl: TermSymbol = CapsInternalModule.requiredMethod("containsImpl") @tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable") @tu lazy val Caps_SharedCapability: ClassSymbol = requiredClass("scala.caps.SharedCapability") @@ -1066,10 +1067,10 @@ class Definitions { @tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable") @tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance") @tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures") - @tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.untrackedCaptures") + @tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.internal.untrackedCaptures") @tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use") @tu lazy val ConsumeAnnot: ClassSymbol = requiredClass("scala.caps.consume") - @tu lazy val RefineOverrideAnnot: ClassSymbol = requiredClass("scala.caps.refineOverride") + @tu lazy val RefineOverrideAnnot: ClassSymbol = requiredClass("scala.caps.internal.refineOverride") @tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile") @tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature") @tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter") @@ -1084,8 +1085,8 @@ class Definitions { @tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface") @tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName") @tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs") - @tu lazy val ReachCapabilityAnnot = requiredClass("scala.annotation.internal.reachCapability") - @tu lazy val RootCapabilityAnnot = requiredClass("scala.caps.rootCapability") + @tu lazy val ReachCapabilityAnnot = requiredClass("scala.annotation.reachCapability") + @tu lazy val RootCapabilityAnnot = requiredClass("scala.caps.internal.rootCapability") @tu lazy val ReadOnlyCapabilityAnnot = requiredClass("scala.annotation.internal.readOnlyCapability") @tu lazy val RequiresCapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.internal.requiresCapability") @tu lazy val RetainsAnnot: ClassSymbol = requiredClass("scala.annotation.retains") diff --git a/library/src/scala/caps.scala b/library/src/scala/caps/package.scala similarity index 50% rename from library/src/scala/caps.scala rename to library/src/scala/caps/package.scala index d53a9b128e5b..9ce68f9fee81 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps/package.scala @@ -1,32 +1,68 @@ package scala +package caps import annotation.{experimental, compileTimeOnly, retainsCap} -@experimental object caps: - - trait Capability extends Any - - /** The universal capture reference */ - val cap: Capability = new Capability() {} - - /** The universal capture reference (deprecated) */ - @deprecated("Use `cap` instead") - val `*`: Capability = cap - - @deprecated("Use `Capability` instead") - type Cap = Capability - - trait Mutable extends Capability - - trait SharedCapability extends Capability - - /** Carrier trait for capture set type parameters */ - trait CapSet extends Any - - /** A type constraint expressing that the capture set `C` needs to contain - * the capability `R` - */ - sealed trait Contains[+C >: CapSet <: CapSet @retainsCap, R <: Singleton] +trait Capability extends Any + +/** The universal capture reference */ +@experimental +val cap: Capability = new Capability() {} + +/** The universal capture reference (deprecated) */ +@deprecated("Use `cap` instead") +@experimental +val `*`: Capability = cap + +@deprecated("Use `Capability` instead") +@experimental +type Cap = Capability + +@experimental +trait Mutable extends Capability + +@experimental +trait SharedCapability extends Capability + +/** Carrier trait for capture set type parameters */ +@experimental +trait CapSet extends Any + +/** A type constraint expressing that the capture set `C` needs to contain + * the capability `R` + */ +@experimental +sealed trait Contains[+C >: CapSet <: CapSet @retainsCap, R <: Singleton] + +/** An annotation on parameters `x` stating that the method's body makes + * use of the reach capability `x*`. Consequently, when calling the method + * we need to charge the deep capture set of the actual argiment to the + * environment. + * + * Note: This should go into annotations. For now it is here, so that we + * can experiment with it quickly between minor releases + */ +@experimental +final class use extends annotation.StaticAnnotation + +/** An annotations on parameters and update methods. + * On a parameter it states that any capabilties passed in the argument + * are no longer available afterwards, unless they are of class `SharableCapabilitty`. + * On an update method, it states that the `this` of the enclosing class is + * consumed, which means that any capabilities of the method prefix are + * no longer available afterwards. + */ +@experimental +final class consume extends annotation.StaticAnnotation + +/** A trait that used to allow expressing existential types. Replaced by +* root.Result instances. +*/ +@deprecated +sealed trait Exists extends Capability + +@experimental +object internal: /** The only implementation of `Contains`. The constraint that `{R} <: C` is * added separately by the capture checker. @@ -51,36 +87,11 @@ import annotation.{experimental, compileTimeOnly, retainsCap} */ extension (x: Any) def readOnlyCapability: Any = x - /** A trait that used to allow expressing existential types. Replaced by - * root.Result instances. - */ - @deprecated - sealed trait Exists extends Capability - /** This should go into annotations. For now it is here, so that we * can experiment with it quickly between minor releases */ final class untrackedCaptures extends annotation.StaticAnnotation - /** An annotation on parameters `x` stating that the method's body makes - * use of the reach capability `x*`. Consequently, when calling the method - * we need to charge the deep capture set of the actual argiment to the - * environment. - * - * Note: This should go into annotations. For now it is here, so that we - * can experiment with it quickly between minor releases - */ - final class use extends annotation.StaticAnnotation - - /** An annotations on parameters and update methods. - * On a parameter it states that any capabilties passed in the argument - * are no longer available afterwards, unless they are of class `SharableCapabilitty`. - * On an update method, it states that the `this` of the enclosing class is - * consumed, which means that any capabilities of the method prefix are - * no longer available afterwards. - */ - final class consume extends annotation.StaticAnnotation - /** An internal annotation placed on a refinement created by capture checking. * Refinements with this annotation unconditionally override any * info from the parent type, so no intersection needs to be formed. @@ -97,18 +108,18 @@ import annotation.{experimental, compileTimeOnly, retainsCap} */ final class rootCapability extends annotation.StaticAnnotation - object unsafe: - - extension [T](x: T) - /** A specific cast operation to remove a capture set. - * If argument is of type `T^C`, assume it is of type `T` instead. - * Calls to this method are treated specially by the capture checker. - */ - def unsafeAssumePure: T = x +@experimental +object unsafe: - /** A wrapper around code for which separation checks are suppressed. + extension [T](x: T) + /** A specific cast operation to remove a capture set. + * If argument is of type `T^C`, assume it is of type `T` instead. + * Calls to this method are treated specially by the capture checker. */ - def unsafeAssumeSeparate(op: Any): op.type = op + def unsafeAssumePure: T = x + + /** A wrapper around code for which separation checks are suppressed. + */ + def unsafeAssumeSeparate(op: Any): op.type = op - end unsafe -end caps \ No newline at end of file +end unsafe From 77b70c665e0237bfc581eb083ce91c9c3b931947 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Thu, 20 Mar 2025 19:37:35 +0100 Subject: [PATCH 02/11] Add documentation for `Capability` and some other experimental annotations --- library/src/scala/caps/package.scala | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/library/src/scala/caps/package.scala b/library/src/scala/caps/package.scala index 9ce68f9fee81..9a97b18c0406 100644 --- a/library/src/scala/caps/package.scala +++ b/library/src/scala/caps/package.scala @@ -3,9 +3,28 @@ package caps import annotation.{experimental, compileTimeOnly, retainsCap} +/** + * Base trait for classes that represent capabilities in the + * [object-capability model](https://en.wikipedia.org/wiki/Object-capability_model). + * + * A capability is a value representing a permission, access right, resource or effect. + * Capabilities are typically passed to code as parameters; they should not be global objects. + * Often, they come with access restrictions such as scoped lifetimes or limited sharing. + * + * An example is the [[scala.util.boundary.Label Label]] class in [[scala.util.boundary]]. + * It represents a capability in the sense that it gives permission to [[scala.util.boundary.break break]] + * to the enclosing boundary represented by the `Label`. It has a scoped lifetime, since breaking to + * a `Label` after the associated `boundary` was exited gives a runtime exception. + * + * [[Capability]] has a formal meaning when + * [[scala.language.experimental.captureChecking Capture Checking]] + * is turned on. + * But even without capture checking, extending this trait can be useful for documenting the intended purpose + * of a class. + */ trait Capability extends Any -/** The universal capture reference */ +/** The universal capture reference. */ @experimental val cap: Capability = new Capability() {} @@ -18,9 +37,13 @@ val `*`: Capability = cap @experimental type Cap = Capability +/** Marker trait for classes with methods that requires an exclusive reference. */ @experimental trait Mutable extends Capability +/** Marker trait for capabilities that can be safely shared in a concurrent context. + * During separation checking, shared capabilities are not taken into account. + */ @experimental trait SharedCapability extends Capability From 0d75c91f3da9405d29ff04832bd8408101f27ca7 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Thu, 20 Mar 2025 19:58:38 +0100 Subject: [PATCH 03/11] Rename capsOf in makeCapsOf --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 5 ++++- compiler/src/dotty/tools/dotc/core/Definitions.scala | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e89dc2c1cdb5..145c61584fcc 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -521,6 +521,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def scalaUnit(implicit src: SourceFile): Select = scalaDot(tpnme.Unit) def scalaAny(implicit src: SourceFile): Select = scalaDot(tpnme.Any) + def capsInternalDot(name: Name)(using SourceFile): Select = + Select(Select(scalaDot(nme.caps), nme.internal), name) + def captureRoot(using Context): Select = Select(scalaDot(nme.caps), nme.CAPTURE_ROOT) @@ -528,7 +531,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { Annotated(parent, New(scalaAnnotationDot(annotName), List(refs))) def makeCapsOf(tp: RefTree)(using Context): Tree = - TypeApply(Select(scalaDot(nme.caps), nme.capsOf), tp :: Nil) + TypeApply(capsInternalDot(nme.capsOf), tp :: Nil) // Capture set variable `[C^]` becomes: `[C >: CapSet <: CapSet^{cap}]` def makeCapsBound()(using Context): TypeBoundsTree = diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 341bf99ec920..3b8625488766 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1085,7 +1085,7 @@ class Definitions { @tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface") @tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName") @tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs") - @tu lazy val ReachCapabilityAnnot = requiredClass("scala.annotation.reachCapability") + @tu lazy val ReachCapabilityAnnot = requiredClass("scala.annotation.internal.reachCapability") @tu lazy val RootCapabilityAnnot = requiredClass("scala.caps.internal.rootCapability") @tu lazy val ReadOnlyCapabilityAnnot = requiredClass("scala.annotation.internal.readOnlyCapability") @tu lazy val RequiresCapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.internal.requiresCapability") From 4b5489153a72a9836760915492eb5be255b89255 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Thu, 20 Mar 2025 20:06:43 +0100 Subject: [PATCH 04/11] Add all newly @experimental annotated items to cc exception list --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3b8625488766..2b23d0d87ad5 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -2098,6 +2098,10 @@ class Definitions { @tu lazy val ccExperimental: Set[Symbol] = Set( CapsModule, CapsModule.moduleClass, PureClass, RequiresCapabilityAnnot, + captureRoot, Caps_CapSet, Caps_ContainsTrait, UseAnnot, + Caps_Mutable, Caps_SharedCapability, ConsumeAnnot, + CapsUnsafeModule, CapsUnsafeModule.moduleClass, + CapsInternalModule, CapsInternalModule.moduleClass, RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot) /** Experimental language features defined in `scala.runtime.stdLibPatches.language.experimental`. From 65c8e25634c6a950732bd16ff54b11f3ebde2e54 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Thu, 20 Mar 2025 20:30:08 +0100 Subject: [PATCH 05/11] Move untrackedCaptures to unsafe --- .../dotty/tools/dotc/core/Definitions.scala | 7 ++--- library/src/scala/caps/package.scala | 27 ++++++++++++------- .../captures/untracked-captures.scala | 3 ++- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 2b23d0d87ad5..48c8c6ebde10 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1005,7 +1005,8 @@ class Definitions { @tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure") @tu lazy val Caps_unsafeAssumeSeparate: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumeSeparate") @tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains") - @tu lazy val Caps_containsImpl: TermSymbol = CapsInternalModule.requiredMethod("containsImpl") + @tu lazy val Caps_ContainsModule: Symbol = requiredModule("scala.caps.Contains") + @tu lazy val Caps_containsImpl: TermSymbol = Caps_ContainsModule.requiredMethod("containsImpl") @tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable") @tu lazy val Caps_SharedCapability: ClassSymbol = requiredClass("scala.caps.SharedCapability") @@ -1067,7 +1068,7 @@ class Definitions { @tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable") @tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance") @tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures") - @tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.internal.untrackedCaptures") + @tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.unsafe.untrackedCaptures") @tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use") @tu lazy val ConsumeAnnot: ClassSymbol = requiredClass("scala.caps.consume") @tu lazy val RefineOverrideAnnot: ClassSymbol = requiredClass("scala.caps.internal.refineOverride") @@ -2098,7 +2099,7 @@ class Definitions { @tu lazy val ccExperimental: Set[Symbol] = Set( CapsModule, CapsModule.moduleClass, PureClass, RequiresCapabilityAnnot, - captureRoot, Caps_CapSet, Caps_ContainsTrait, UseAnnot, + captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass, UseAnnot, Caps_Mutable, Caps_SharedCapability, ConsumeAnnot, CapsUnsafeModule, CapsUnsafeModule.moduleClass, CapsInternalModule, CapsInternalModule.moduleClass, diff --git a/library/src/scala/caps/package.scala b/library/src/scala/caps/package.scala index 9a97b18c0406..5e6d0c90a86e 100644 --- a/library/src/scala/caps/package.scala +++ b/library/src/scala/caps/package.scala @@ -57,6 +57,14 @@ trait CapSet extends Any @experimental sealed trait Contains[+C >: CapSet <: CapSet @retainsCap, R <: Singleton] +@experimental +object Contains: + /** The only implementation of `Contains`. The constraint that `{R} <: C` is + * added separately by the capture checker. + */ + @experimental + given containsImpl[C >: CapSet <: CapSet @retainsCap, R <: Singleton]: Contains[C, R]() + /** An annotation on parameters `x` stating that the method's body makes * use of the reach capability `x*`. Consequently, when calling the method * we need to charge the deep capture set of the actual argiment to the @@ -87,11 +95,6 @@ sealed trait Exists extends Capability @experimental object internal: - /** The only implementation of `Contains`. The constraint that `{R} <: C` is - * added separately by the capture checker. - */ - given containsImpl[C >: CapSet <: CapSet @retainsCap, R <: Singleton]: Contains[C, R]() - /** A wrapper indicating a type variable in a capture argument list of a * @retains annotation. E.g. `^{x, Y^}` is represented as `@retains(x, capsOf[Y])`. */ @@ -110,11 +113,6 @@ object internal: */ extension (x: Any) def readOnlyCapability: Any = x - /** This should go into annotations. For now it is here, so that we - * can experiment with it quickly between minor releases - */ - final class untrackedCaptures extends annotation.StaticAnnotation - /** An internal annotation placed on a refinement created by capture checking. * Refinements with this annotation unconditionally override any * info from the parent type, so no intersection needs to be formed. @@ -133,6 +131,15 @@ object internal: @experimental object unsafe: + /** + * Marks the constructor parameter as untracked. + * The capture set of this parameter will not be included in + * the capture set of the constructed object. + * + * @note This should go into annotations. For now it is here, so that we + * can experiment with it quickly between minor releases + */ + final class untrackedCaptures extends annotation.StaticAnnotation extension [T](x: T) /** A specific cast operation to remove a capture set. diff --git a/tests/pos-custom-args/captures/untracked-captures.scala b/tests/pos-custom-args/captures/untracked-captures.scala index 7a090a5dd24f..f02dee607c01 100644 --- a/tests/pos-custom-args/captures/untracked-captures.scala +++ b/tests/pos-custom-args/captures/untracked-captures.scala @@ -1,4 +1,5 @@ -import caps.untrackedCaptures +import caps.unsafe.untrackedCaptures + class LL[+A] private (@untrackedCaptures lazyState: () => LL.State[A]^): private val res = lazyState() From 95b98b1ac181262a4f950f6ed12c528e6b4ee236 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Thu, 20 Mar 2025 20:32:39 +0100 Subject: [PATCH 06/11] Update error messages For some reason the errors use the `scala.caps` prefix instead of just `caps`. Might need to look into? --- tests/neg-custom-args/captures/cc-poly-1.check | 4 ++-- tests/neg-custom-args/captures/cc-this2.check | 2 +- tests/neg-custom-args/captures/exception-definitions.check | 2 +- tests/neg-custom-args/captures/i21313.check | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/neg-custom-args/captures/cc-poly-1.check b/tests/neg-custom-args/captures/cc-poly-1.check index ea486f55a61f..0ac35b532916 100644 --- a/tests/neg-custom-args/captures/cc-poly-1.check +++ b/tests/neg-custom-args/captures/cc-poly-1.check @@ -1,12 +1,12 @@ -- [E057] Type Mismatch Error: tests/neg-custom-args/captures/cc-poly-1.scala:12:6 ------------------------------------- 12 | f[Any](D()) // error | ^ - | Type argument Any does not conform to upper bound caps.CapSet^ + | Type argument Any does not conform to upper bound scala.caps.CapSet^ | | longer explanation available when compiling with `-explain` -- [E057] Type Mismatch Error: tests/neg-custom-args/captures/cc-poly-1.scala:13:6 ------------------------------------- 13 | f[String](D()) // error | ^ - | Type argument String does not conform to upper bound caps.CapSet^ + | Type argument String does not conform to upper bound scala.caps.CapSet^ | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-this2.check b/tests/neg-custom-args/captures/cc-this2.check index 6cb3010d6174..29fb266b1054 100644 --- a/tests/neg-custom-args/captures/cc-this2.check +++ b/tests/neg-custom-args/captures/cc-this2.check @@ -2,7 +2,7 @@ -- Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:3:8 -------------------------------------------------------- 3 | this: D^ => // error | ^^ - |reference (caps.cap : caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class C + |reference (scala.caps.cap : scala.caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class C -- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:2:6 ----------------------------------- 2 |class D extends C: // error | ^ diff --git a/tests/neg-custom-args/captures/exception-definitions.check b/tests/neg-custom-args/captures/exception-definitions.check index 3f2b15f312b9..960fb77e62a1 100644 --- a/tests/neg-custom-args/captures/exception-definitions.check +++ b/tests/neg-custom-args/captures/exception-definitions.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/exception-definitions.scala:3:8 ----------------------------------------------- 3 | self: Err^ => // error | ^^^^ - |reference (caps.cap : caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class Throwable + |reference (scala.caps.cap : scala.caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class Throwable -- Error: tests/neg-custom-args/captures/exception-definitions.scala:7:12 ---------------------------------------------- 7 | val x = c // error | ^ diff --git a/tests/neg-custom-args/captures/i21313.check b/tests/neg-custom-args/captures/i21313.check index 37b944a97d68..f76f4bc6871e 100644 --- a/tests/neg-custom-args/captures/i21313.check +++ b/tests/neg-custom-args/captures/i21313.check @@ -5,7 +5,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21313.scala:15:12 --------------------------------------- 15 | ac1.await(src2) // error | ^^^^ - | Found: (src2 : Source[Int, caps.CapSet^{ac2}]^?) - | Required: Source[Int, caps.CapSet^{ac1}]^ + | Found: (src2 : Source[Int, scala.caps.CapSet^{ac2}]^?) + | Required: Source[Int, scala.caps.CapSet^{ac1}]^ | | longer explanation available when compiling with `-explain` From 2674d4777bebc436fbb715bc9676f186ad49b05a Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Fri, 21 Mar 2025 13:00:14 +0100 Subject: [PATCH 07/11] Keep Capability experimental --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 1 + library/src/scala/caps/package.scala | 2 ++ 2 files changed, 3 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 48c8c6ebde10..b88a3e6e986a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -2098,6 +2098,7 @@ class Definitions { */ @tu lazy val ccExperimental: Set[Symbol] = Set( CapsModule, CapsModule.moduleClass, PureClass, + Caps_Capability, // TODO: Remove when Capability is stabilized RequiresCapabilityAnnot, captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass, UseAnnot, Caps_Mutable, Caps_SharedCapability, ConsumeAnnot, diff --git a/library/src/scala/caps/package.scala b/library/src/scala/caps/package.scala index 5e6d0c90a86e..a5a737731ff7 100644 --- a/library/src/scala/caps/package.scala +++ b/library/src/scala/caps/package.scala @@ -22,6 +22,7 @@ import annotation.{experimental, compileTimeOnly, retainsCap} * But even without capture checking, extending this trait can be useful for documenting the intended purpose * of a class. */ +@experimental trait Capability extends Any /** The universal capture reference. */ @@ -89,6 +90,7 @@ final class consume extends annotation.StaticAnnotation /** A trait that used to allow expressing existential types. Replaced by * root.Result instances. */ +@experimental @deprecated sealed trait Exists extends Capability From dfe630933607b7eea80622d15d7ba6dbf1792142 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Fri, 21 Mar 2025 13:23:56 +0100 Subject: [PATCH 08/11] Fix untrackedCaptures import in scala2-library-cc --- .../src/scala/collection/immutable/LazyListIterable.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala b/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala index e9383f6e4c8c..45186e7c13b2 100644 --- a/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala +++ b/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala @@ -24,8 +24,8 @@ import scala.language.implicitConversions import scala.runtime.Statics import language.experimental.captureChecking import annotation.unchecked.uncheckedCaptures -import caps.{cap, untrackedCaptures} -import caps.unsafe.unsafeAssumeSeparate +import caps.cap +import caps.unsafe.{unsafeAssumeSeparate, untrackedCaptures} /** This class implements an immutable linked list. We call it "lazy" * because it computes its elements only when they are needed. From 97bfef969875c263a5d3f0c4e471bf736bc3f0bf Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Fri, 21 Mar 2025 14:50:30 +0100 Subject: [PATCH 09/11] Update list of experimental references --- .../stdlibExperimentalDefinitions.scala | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 0715bedea201..900564858e66 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -32,8 +32,23 @@ val experimentalDefinitionInLibrary = Set( "scala.annotation.retainsCap", "scala.annotation.retainsArg", "scala.Pure", - "scala.caps", - "scala.caps$", + "scala.caps.CapSet", + "scala.caps.Capability", + "scala.caps.Contains", + "scala.caps.Contains$", + "scala.caps.Contains$.containsImpl", + "scala.caps.Exists", + "scala.caps.Mutable", + "scala.caps.SharedCapability", + "scala.caps.consume", + "scala.caps.internal", + "scala.caps.internal$", + "scala.caps.package$package$.*", + "scala.caps.package$package$.Cap", + "scala.caps.package$package$.cap", + "scala.caps.unsafe", + "scala.caps.unsafe$", + "scala.caps.use", //// New feature: into "scala.annotation.into", From 698810da5237be39f204d372c026fb167e966e83 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Fri, 21 Mar 2025 19:39:58 +0100 Subject: [PATCH 10/11] Make `cap` an object instead of a val, change how it is printed --- .../src/dotty/tools/dotc/printing/PlainPrinter.scala | 3 ++- library/src/scala/caps/package.scala | 11 +---------- tests/neg-custom-args/captures/cc-this2.check | 2 +- .../captures/exception-definitions.check | 2 +- .../stdlibExperimentalDefinitions.scala | 5 ++--- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 41278ca27159..0dcb06ae8c87 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -195,10 +195,11 @@ class PlainPrinter(_ctx: Context) extends Printer { homogenize(tp) match { case tp: TypeType => toTextRHS(tp) + case tp: TermRef if tp.isCap => + toTextCaptureRef(tp) case tp: TermRef if !tp.denotationIsCurrent && !homogenizedView // always print underlying when testing picklers - && !tp.isCap || tp.symbol.is(Module) || tp.symbol.name == nme.IMPORT => toTextRef(tp) ~ ".type" diff --git a/library/src/scala/caps/package.scala b/library/src/scala/caps/package.scala index a5a737731ff7..7f8b184c1c95 100644 --- a/library/src/scala/caps/package.scala +++ b/library/src/scala/caps/package.scala @@ -27,16 +27,7 @@ trait Capability extends Any /** The universal capture reference. */ @experimental -val cap: Capability = new Capability() {} - -/** The universal capture reference (deprecated) */ -@deprecated("Use `cap` instead") -@experimental -val `*`: Capability = cap - -@deprecated("Use `Capability` instead") -@experimental -type Cap = Capability +object cap extends Capability /** Marker trait for classes with methods that requires an exclusive reference. */ @experimental diff --git a/tests/neg-custom-args/captures/cc-this2.check b/tests/neg-custom-args/captures/cc-this2.check index 29fb266b1054..dc61fe2e0396 100644 --- a/tests/neg-custom-args/captures/cc-this2.check +++ b/tests/neg-custom-args/captures/cc-this2.check @@ -2,7 +2,7 @@ -- Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:3:8 -------------------------------------------------------- 3 | this: D^ => // error | ^^ - |reference (scala.caps.cap : scala.caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class C + | reference cap captured by this self type is not included in the allowed capture set {} of pure base class class C -- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:2:6 ----------------------------------- 2 |class D extends C: // error | ^ diff --git a/tests/neg-custom-args/captures/exception-definitions.check b/tests/neg-custom-args/captures/exception-definitions.check index 960fb77e62a1..67f0f3b72cbb 100644 --- a/tests/neg-custom-args/captures/exception-definitions.check +++ b/tests/neg-custom-args/captures/exception-definitions.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/exception-definitions.scala:3:8 ----------------------------------------------- 3 | self: Err^ => // error | ^^^^ - |reference (scala.caps.cap : scala.caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class Throwable + |reference cap captured by this self type is not included in the allowed capture set {} of pure base class class Throwable -- Error: tests/neg-custom-args/captures/exception-definitions.scala:7:12 ---------------------------------------------- 7 | val x = c // error | ^ diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 900564858e66..36075f0a2cee 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -43,9 +43,8 @@ val experimentalDefinitionInLibrary = Set( "scala.caps.consume", "scala.caps.internal", "scala.caps.internal$", - "scala.caps.package$package$.*", - "scala.caps.package$package$.Cap", - "scala.caps.package$package$.cap", + "scala.caps.cap", + "scala.caps.cap$", "scala.caps.unsafe", "scala.caps.unsafe$", "scala.caps.use", From e07db84ad72a2ffa5ef103e362a390eb398f7c73 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Mon, 24 Mar 2025 13:50:17 +0100 Subject: [PATCH 11/11] Rename caps.* to caps.cap --- tests/disabled/neg-custom-args/captures/capt-wf.scala | 4 ++-- tests/disabled/neg-custom-args/captures/try2.scala | 2 +- tests/pos/caps-universal.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/disabled/neg-custom-args/captures/capt-wf.scala b/tests/disabled/neg-custom-args/captures/capt-wf.scala index bfe349747776..302202064ac0 100644 --- a/tests/disabled/neg-custom-args/captures/capt-wf.scala +++ b/tests/disabled/neg-custom-args/captures/capt-wf.scala @@ -1,7 +1,7 @@ // No longer valid class C -type Cap = C @retains(caps.*) -type Top = Any @retains(caps.*) +type Cap = C @retains(caps.cap) +type Top = Any @retains(caps.cap) type T = (x: Cap) => List[String @retains(x)] => Unit // error val x: (x: Cap) => Array[String @retains(x)] = ??? // error diff --git a/tests/disabled/neg-custom-args/captures/try2.scala b/tests/disabled/neg-custom-args/captures/try2.scala index 876dc1ec12f1..43e17d8c9eef 100644 --- a/tests/disabled/neg-custom-args/captures/try2.scala +++ b/tests/disabled/neg-custom-args/captures/try2.scala @@ -5,7 +5,7 @@ import annotation.ability @ability erased val canThrow: * = ??? class CanThrow[E <: Exception] extends Retains[canThrow.type] -type Top = Any @retains(caps.*) +type Top = Any @retains(caps.cap) infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R diff --git a/tests/pos/caps-universal.scala b/tests/pos/caps-universal.scala index 014955caaa87..3451866ed8f3 100644 --- a/tests/pos/caps-universal.scala +++ b/tests/pos/caps-universal.scala @@ -3,7 +3,7 @@ import annotation.retains val id: Int -> Int = (x: Int) => x val foo: Int => Int = id -val bar: (Int -> Int) @retains(caps.*) = foo +val bar: (Int -> Int) @retains(caps.cap) = foo