Skip to content

Commit 1d48e30

Browse files
committed
Add Unscoped classifier
- FreshCaps with Unscoped classifier are assumed to be owned by the enclosing toplevel class - FreshCaps with unscoped classifier are not turned into ResultCaps in the return types of anonymous functions.
1 parent 5ccea40 commit 1d48e30

File tree

4 files changed

+42
-12
lines changed

4 files changed

+42
-12
lines changed

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,10 @@ object Capabilities:
276276
* if separation checks are turned off).
277277
* @pre The capability's origin was not yet set.
278278
*/
279-
def setOrigin(freshOrigin: FreshCap | GlobalCap.type): Unit =
279+
def setOrigin(freshOrigin: FreshCap | GlobalCap.type): this.type =
280280
assert(myOrigin eq GlobalCap)
281281
myOrigin = freshOrigin
282+
this
282283

283284
/** If the current capability was created via a chain of `derivedResult` calls
284285
* from an original ResultCap `r`, that `r`. Otherwise `this`.
@@ -517,6 +518,8 @@ object Capabilities:
517518
case prefix: ThisType if setOwner.isTerm && setOwner.owner == prefix.cls =>
518519
setOwner
519520
case prefix: Capability => prefix.ccOwner
521+
case NoPrefix if classifier.derivesFrom(defn.Caps_Unscoped) =>
522+
ctx.owner.topLevelClass
520523
case _ => setOwner
521524
case _ /* : GlobalCap | ResultCap | ParamRef */ => NoSymbol
522525

@@ -1168,7 +1171,7 @@ object Capabilities:
11681171
case _ =>
11691172
super.mapOver(t)
11701173

1171-
class ToResult(localResType: Type, mt: MethodicType, fail: Message => Unit)(using Context) extends CapMap:
1174+
class ToResult(localResType: Type, mt: MethodicType, sym: Symbol, fail: Message => Unit)(using Context) extends CapMap:
11721175

11731176
def apply(t: Type) = t match
11741177
case defn.FunctionNOf(args, res, contextual) if t.typeSymbol.name.isImpureFunction =>
@@ -1183,11 +1186,12 @@ object Capabilities:
11831186
override def mapCapability(c: Capability, deep: Boolean) = c match
11841187
case c: (FreshCap | GlobalCap.type) =>
11851188
if variance > 0 then
1186-
val res = ResultCap(mt)
11871189
c match
1188-
case c: FreshCap => res.setOrigin(c)
1189-
case _ =>
1190-
res
1190+
case c: FreshCap =>
1191+
if sym.isAnonymousFunction && c.classifier.derivesFrom(defn.Caps_Unscoped)
1192+
then c
1193+
else ResultCap(mt).setOrigin(c)
1194+
case _ => ResultCap(mt)
11911195
else
11921196
if variance == 0 then
11931197
fail(em"""$localResType captures the root capability `cap` in invariant position.
@@ -1227,8 +1231,8 @@ object Capabilities:
12271231
* variable bound by `mt`.
12281232
* Stop at function or method types since these have been mapped before.
12291233
*/
1230-
def toResult(tp: Type, mt: MethodicType, fail: Message => Unit)(using Context): Type =
1231-
ToResult(tp, mt, fail)(tp)
1234+
def toResult(tp: Type, mt: MethodicType, sym: Symbol, fail: Message => Unit)(using Context): Type =
1235+
ToResult(tp, mt, sym, fail)(tp)
12321236

12331237
/** Map global roots in function results to result roots. Also,
12341238
* map roots in the types of def methods that are parameterless
@@ -1244,7 +1248,7 @@ object Capabilities:
12441248
else apply(mt))
12451249
case t: MethodType if variance > 0 && t.marksExistentialScope =>
12461250
val t1 = mapOver(t).asInstanceOf[MethodType]
1247-
t1.derivedLambdaType(resType = toResult(t1.resType, t1, fail))
1251+
t1.derivedLambdaType(resType = toResult(t1.resType, t1, sym, fail))
12481252
case CapturingType(parent, refs) =>
12491253
t.derivedCapturingType(this(parent), refs)
12501254
case t: (LazyRef | TypeVar) =>
@@ -1259,7 +1263,7 @@ object Capabilities:
12591263
m(tp) match
12601264
case tp1: ExprType if sym.is(Method, butNot = Accessor) =>
12611265
// Map the result of parameterless `def` methods.
1262-
tp1.derivedExprType(toResult(tp1.resType, tp1, fail))
1266+
tp1.derivedExprType(toResult(tp1.resType, tp1, sym, fail))
12631267
case tp1: PolyType if !tp1.resType.isInstanceOf[MethodicType] =>
12641268
// Map also the result type of method with only type parameters.
12651269
// This way, the `^` in the following method will be mapped to a `ResultCap`:
@@ -1269,7 +1273,7 @@ object Capabilities:
12691273
// ```
12701274
// This is more desirable than interpreting `^` as a `Fresh` at the level of `Buffer.empty`
12711275
// in most cases.
1272-
tp1.derivedLambdaType(resType = toResult(tp1.resType, tp1, fail))
1276+
tp1.derivedLambdaType(resType = toResult(tp1.resType, tp1, sym, fail))
12731277
case tp1 => tp1
12741278
end toResultInResults
12751279

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,7 @@ class Definitions {
10201020
@tu lazy val Caps_Control: ClassSymbol = requiredClass("scala.caps.Control")
10211021
@tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable")
10221022
@tu lazy val Caps_Read: ClassSymbol = requiredClass("scala.caps.Read")
1023+
@tu lazy val Caps_Unscoped: ClassSymbol = requiredClass("scala.caps.Unscoped")
10231024
@tu lazy val Caps_CapSet: ClassSymbol = requiredClass("scala.caps.CapSet")
10241025
@tu lazy val CapsInternalModule: Symbol = requiredModule("scala.caps.internal")
10251026
@tu lazy val Caps_erasedValue: Symbol = CapsInternalModule.requiredMethod("erasedValue")
@@ -2096,7 +2097,7 @@ class Definitions {
20962097
@tu lazy val ccExperimental: Set[Symbol] = Set(
20972098
CapsModule, CapsModule.moduleClass, PureClass,
20982099
/* Caps_Classifier, Caps_SharedCapability, Caps_Control, -- already stable */
2099-
Caps_ExclusiveCapability, Caps_Mutable, Caps_Read,
2100+
Caps_ExclusiveCapability, Caps_Mutable, Caps_Read, Caps_Unscoped,
21002101
RequiresCapabilityAnnot,
21012102
captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass,
21022103
ConsumeAnnot, UseAnnot, ReserveAnnot,

library/src/scala/caps/package.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ trait Mutable extends ExclusiveCapability
8686
@experimental
8787
trait Read extends Mutable, Classifier
8888

89+
/** Marker trait for classes that are not subject to scoping restrictions
90+
* of captured capabilities.
91+
*/
92+
@experimental
93+
trait Unscoped extends ExclusiveCapability, Classifier
94+
8995
/** Carrier trait for capture set type parameters */
9096
@experimental
9197
trait CapSet extends Any
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import caps.*
2+
3+
class Ref[T](init: T) extends Mutable, Unscoped:
4+
var x = init
5+
def get: T = x
6+
update def put(y: T): Unit = x = y
7+
8+
class File:
9+
def read(): String = ???
10+
11+
def withFile[T](op: (f: File^) => T): T =
12+
op(new File)
13+
14+
def Test =
15+
withFile: f =>
16+
val r = Ref(f.read())
17+
r
18+
19+

0 commit comments

Comments
 (0)