Skip to content

Commit b76bc3e

Browse files
committed
Drop @capability annotations
Replace with references that inherit trait `Capability`.
1 parent 0c1394f commit b76bc3e

Some content is hidden

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

62 files changed

+382
-130
lines changed

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,23 @@ extension (tp: Type)
203203
case _ =>
204204
false
205205

206+
/** Does type derive from caps.Capability?, which means it references of this
207+
* type are maximal capabilities?
208+
*/
209+
def derivesFromCapability(using Context): Boolean = tp.dealias match
210+
case tp: (TypeRef | AppliedType) =>
211+
val sym = tp.typeSymbol
212+
if sym.isClass then sym.derivesFrom(defn.Caps_Capability)
213+
else tp.superType.derivesFromCapability
214+
case tp: TypeProxy =>
215+
tp.superType.derivesFromCapability
216+
case tp: AndType =>
217+
tp.tp1.derivesFromCapability || tp.tp2.derivesFromCapability
218+
case tp: OrType =>
219+
tp.tp1.derivesFromCapability && tp.tp2.derivesFromCapability
220+
case _ =>
221+
false
222+
206223
/** Drop @retains annotations everywhere */
207224
def dropAllRetains(using Context): Type = // TODO we should drop retains from inferred types before unpickling
208225
val tm = new TypeMap:
@@ -408,7 +425,7 @@ extension (sym: Symbol)
408425
/** The owner of the current level. Qualifying owners are
409426
* - methods other than constructors and anonymous functions
410427
* - anonymous functions, provided they either define a local
411-
* root of type caps.Cap, or they are the rhs of a val definition.
428+
* root of type caps.Capability, or they are the rhs of a val definition.
412429
* - classes, if they are not staticOwners
413430
* - _root_
414431
*/

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,8 @@ object CaptureSet:
10511051
case tp: TermParamRef =>
10521052
tp.captureSet
10531053
case tp: TypeRef =>
1054-
if tp.typeSymbol == defn.Caps_Cap then universal else empty
1054+
if tp.derivesFromCapability then universal // TODO: maybe return another value that indicates that the underltinf ref is maximal?
1055+
else empty
10551056
case _: TypeParamRef =>
10561057
empty
10571058
case CapturingType(parent, refs) =>

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -537,8 +537,8 @@ class CheckCaptures extends Recheck, SymTransformer:
537537
*/
538538
def addParamArgRefinements(core: Type, initCs: CaptureSet): (Type, CaptureSet) =
539539
var refined: Type = core
540-
var allCaptures: CaptureSet = if setup.isCapabilityClassRef(core)
541-
then CaptureSet.universal else initCs
540+
var allCaptures: CaptureSet =
541+
if core.derivesFromCapability then CaptureSet.universal else initCs
542542
for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do
543543
val getter = cls.info.member(getterName).suchThat(_.is(ParamAccessor)).symbol
544544
if getter.termRef.isTracked && !getter.is(Private) then
@@ -572,8 +572,10 @@ class CheckCaptures extends Recheck, SymTransformer:
572572
val TypeApply(fn, args) = tree
573573
val polyType = atPhase(thisPhase.prev):
574574
fn.tpe.widen.asInstanceOf[TypeLambda]
575+
def isExempt(sym: Symbol) =
576+
sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
575577
for case (arg: TypeTree, formal, pname) <- args.lazyZip(polyType.paramRefs).lazyZip((polyType.paramNames)) do
576-
if !tree.symbol.isTypeTestOrCast then
578+
if !isExempt(tree.symbol) then
577579
def where = if fn.symbol.exists then i" in an argument of ${fn.symbol}" else ""
578580
disallowRootCapabilitiesIn(arg.knownType, NoSymbol,
579581
i"Sealed type variable $pname", "be instantiated to",
@@ -1305,7 +1307,7 @@ class CheckCaptures extends Recheck, SymTransformer:
13051307
case ref: TermParamRef
13061308
if !allowed.contains(ref) && !seen.contains(ref) =>
13071309
seen += ref
1308-
if ref.underlying.isRef(defn.Caps_Cap) then
1310+
if ref.underlying.isRef(defn.Caps_Capability) then
13091311
report.error(i"escaping local reference $ref", tree.srcPos)
13101312
else
13111313
val widened = ref.captureSetOfInfo

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
7777
def isCapabilityClassRef(tp: Type)(using Context): Boolean = tp.dealiasKeepAnnots match
7878
case _: TypeRef | _: AppliedType =>
7979
val sym = tp.classSymbol
80-
def checkSym: Boolean =
81-
sym.hasAnnotation(defn.CapabilityAnnot)
82-
|| sym.info.parents.exists(hasUniversalCapability)
80+
def checkSym: Boolean = sym.info.parents.exists(hasUniversalCapability)
8381
sym.isClass && capabilityClassMap.getOrElseUpdate(sym, checkSym)
8482
case _ => false
8583

@@ -594,7 +592,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
594592
if sym.isClass then
595593
!sym.isPureClass
596594
else
597-
sym != defn.Caps_Cap && instanceCanBeImpure(tp.superType)
595+
sym != defn.Caps_Capability && instanceCanBeImpure(tp.superType)
598596
case tp: (RefinedOrRecType | MatchType) =>
599597
instanceCanBeImpure(tp.underlying)
600598
case tp: AndType =>

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ class Definitions {
991991

992992
@tu lazy val CapsModule: Symbol = requiredModule("scala.caps")
993993
@tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("cap")
994-
@tu lazy val Caps_Cap: TypeSymbol = CapsModule.requiredType("Cap")
994+
@tu lazy val Caps_Capability: TypeSymbol = CapsModule.requiredType("Capability")
995995
@tu lazy val Caps_reachCapability: TermSymbol = CapsModule.requiredMethod("reachCapability")
996996
@tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe")
997997
@tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure")
@@ -1014,7 +1014,6 @@ class Definitions {
10141014
@tu lazy val BeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BeanProperty")
10151015
@tu lazy val BooleanBeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BooleanBeanProperty")
10161016
@tu lazy val BodyAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Body")
1017-
@tu lazy val CapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.capability")
10181017
@tu lazy val ChildAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Child")
10191018
@tu lazy val ContextResultCountAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ContextResultCount")
10201019
@tu lazy val ProvisionalSuperClassAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ProvisionalSuperClass")
@@ -2033,7 +2032,7 @@ class Definitions {
20332032
*/
20342033
@tu lazy val ccExperimental: Set[Symbol] = Set(
20352034
CapsModule, CapsModule.moduleClass, PureClass,
2036-
CapabilityAnnot, RequiresCapabilityAnnot,
2035+
RequiresCapabilityAnnot,
20372036
RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot)
20382037

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

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,7 +3014,8 @@ object Types extends TypeUtils {
30143014
name == nme.CAPTURE_ROOT && symbol == defn.captureRoot
30153015

30163016
override def isMaxCapability(using Context): Boolean =
3017-
widen.derivesFrom(defn.Caps_Cap) && symbol.isStableMember
3017+
import cc.*
3018+
this.derivesFromCapability && symbol.isStableMember
30183019

30193020
override def normalizedRef(using Context): CaptureRef =
30203021
if isTrackableRef then symbol.termRef else this
@@ -4815,7 +4816,9 @@ object Types extends TypeUtils {
48154816
def kindString: String = "Term"
48164817
def copyBoundType(bt: BT): Type = bt.paramRefs(paramNum)
48174818
override def isTrackableRef(using Context) = true
4818-
override def isMaxCapability(using Context) = widen.derivesFrom(defn.Caps_Cap)
4819+
override def isMaxCapability(using Context) =
4820+
import cc.*
4821+
this.derivesFromCapability
48194822
}
48204823

48214824
private final class TermParamRefImpl(binder: TermLambda, paramNum: Int) extends TermParamRef(binder, paramNum)

library/src/scala/CanThrow.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import annotation.{implicitNotFound, experimental, capability}
66
* experimental.saferExceptions feature, a `throw Ex()` expression will require
77
* a given of class `CanThrow[Ex]` to be available.
88
*/
9-
@experimental @capability
9+
@experimental
1010
@implicitNotFound("The capability to throw exception ${E} is missing.\nThe capability can be provided by one of the following:\n - Adding a using clause `(using CanThrow[${E}])` to the definition of the enclosing method\n - Adding `throws ${E}` clause after the result type of the enclosing method\n - Wrapping this piece of code with a `try` block that catches ${E}")
11-
erased class CanThrow[-E <: Exception]
11+
erased class CanThrow[-E <: Exception] extends caps.Capability
1212

1313
@experimental
1414
object unsafeExceptions:

library/src/scala/annotation/capability.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ import annotation.experimental
1111
* THere, the capture set of any instance of `CanThrow` is assumed to be
1212
* `{*}`.
1313
*/
14-
@experimental final class capability extends StaticAnnotation
14+
@experimental
15+
@deprecated("To make a class a capability, let it derive from the `Capability` trait instead")
16+
final class capability extends StaticAnnotation

library/src/scala/caps.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ import annotation.experimental
44

55
@experimental object caps:
66

7-
class Cap // should be @erased
7+
trait Capability // should be @erased
8+
9+
/** The universal capture reference */
10+
val cap: Capability = new Capability() {}
811

912
/** The universal capture reference (deprecated) */
1013
@deprecated("Use `cap` instead")
11-
val `*`: Cap = cap
14+
val `*`: Capability = cap
1215

13-
/** The universal capture reference */
14-
val cap: Cap = Cap()
16+
@deprecated("Use `Capability` instead")
17+
type Cap = Capability
1518

1619
/** Reach capabilities x* which appear as terms in @retains annotations are encoded
1720
* as `caps.reachCapability(x)`. When converted to CaptureRef types in capture sets

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-
@annotation.capability class Cap
37+
class Cap extends caps.Capability
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

0 commit comments

Comments
 (0)