Drop `@experimental` for `scala.caps.Capability`
Closed this issue · 4 comments
scala.annotation.retains and retainsCap are internally used by the capture checker as the representation of capturing types. While capture checking itself is still evolving, the annotations should be considered stable.
Generally, the annotations does not show up in programs that do not enable experimental.captureChecking, even when interacting with modules that do. However, inlining will surface these annotations. While they do not affect the type checker, they cause the experimental check to fail.
An example of this is the scala.util.boundary package, which would greatly benefit from having its Label tracked and capture-checked.
However, boundary.apply requires its body to be inlined into the caller (so that locally-scoped boundary.break calls turn into efficient jumps), which leaks the tracked label with the retainsCap annotation into the caller.
/** Run `body` with freshly generated label as implicit argument. Catch any
* breaks associated with that label and return their results instead of
* `body`'s result.
*/
inline def apply[T](inline body: Label[T]^ ?=> T): T =
val local: Label[T]^ = Label[T]() // Label[T] @retainsCap
try body(using local)
catch case ex: Break[T] @unchecked =>
if ex.label eq local.id then ex.value
else throw exThis would prevent code not enabling capture-checking (which includes the Scala compiler itself) from using boundary.
@natsukagami is this a case of needs-minor-release?
I get the feeling it may be too late to do this in 3.7
@Gedochao yes if we do this (not sure if we need to yet)
So per talk with @odersky I tried making Label[T] extends caps.Capability instead, which makes boundary works without leaking retainsCap.
Earlier I thought it wasn't working due to this code passing cc:
val leak = boundary(l ?=> l)but it seems to just be something to do with inference / Any rather than inlining.
We discussed this in the core meeting and arrived at the following scheme to support non-experimental Capability only.
- Make
scala.capsa package instead of an object. - Make
Capabilitya non-experimental trait incaps:trait Capability extends Any
- Make other classes and values visible from source experimental classes and objects in
caps.@experimental object cap extends Capability @experimental trait Mutable extends Capability @experimental trait SharedCapability extends Capability @experimental trait CapSet extends Any @experimental sealed trait Contains[+C >: CapSet <: CapSet @retainsCap, R <: Singleton] @experimental final class untrackedCaptures extends annotation.StaticAnnotation @experimental final class use extends annotation.StaticAnnotation @experimental final class consume extends annotation.StaticAnnotation @experimentlal object unsafe: extension [T](x: T) def unsafeAssumePure: T = x def unsafeAssumeSeparate(op: Any): op.type = op
The rest of the former caps object just supports the current implementation. Move it into a new experimental object scala.caps.internals.
So, not counting experimentals, we committed to a package scala.caps and a trait Capability in it. We should add wording what the trait is used for independently of capture checking. Something like the following
Base trait for classes that represent capabilities in the 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
Labelclass inscala.util.boundary. It represents a capability in the sense that it gives permission tobreakto the enclosing boundary represented by theLabel. It has a scoped lifetime since breaking to aLabelafter the associated boundary was exited gives a runtime exception.
Capabilityhas a formal meaning when capture checking is turned on using the language import
import language.experimental.captureCheckingBut even without capture checking, extending trait
Capabilitycan be useful for documenting the intended purpose of a class.