Skip to content

Commit ba63208

Browse files
committed
Distinguish maximal from root capabilities
A maximal capability is one that derives from `caps.Cap`. Also: drop the given caps.Cap. It's not clear why there needs to be a given for it.
1 parent 7bdeb0b commit ba63208

File tree

7 files changed

+17
-12
lines changed

7 files changed

+17
-12
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ sealed abstract class CaptureSet extends Showable:
115115
* capture set.
116116
*/
117117
protected final def addNewElem(elem: CaptureRef)(using Context, VarState): CompareResult =
118-
if elem.isRootCapability || summon[VarState] == FrozenState then
118+
if elem.isMaxCapability || summon[VarState] == FrozenState then
119119
addThisElem(elem)
120120
else
121121
addThisElem(elem).orElse:
@@ -167,11 +167,11 @@ sealed abstract class CaptureSet extends Showable:
167167
if comparer.isInstanceOf[ExplainingTypeComparer] then // !!! DEBUG
168168
reporting.trace.force(i"$this accountsFor $x, ${x.captureSetOfInfo}?", show = true):
169169
elems.exists(_.subsumes(x))
170-
|| !x.isRootCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK
170+
|| !x.isMaxCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK
171171
else
172172
reporting.trace(i"$this accountsFor $x, ${x.captureSetOfInfo}?", show = true):
173173
elems.exists(_.subsumes(x))
174-
|| !x.isRootCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK
174+
|| !x.isMaxCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK
175175

176176
/** A more optimistic version of accountsFor, which does not take variable supersets
177177
* of the `x` reference into account. A set might account for `x` if it accounts
@@ -183,7 +183,7 @@ sealed abstract class CaptureSet extends Showable:
183183
def mightAccountFor(x: CaptureRef)(using Context): Boolean =
184184
reporting.trace(i"$this mightAccountFor $x, ${x.captureSetOfInfo}?", show = true) {
185185
elems.exists(_.subsumes(x))
186-
|| !x.isRootCapability
186+
|| !x.isMaxCapability
187187
&& {
188188
val elems = x.captureSetOfInfo.elems
189189
!elems.isEmpty && elems.forall(mightAccountFor)
@@ -1032,7 +1032,7 @@ object CaptureSet:
10321032

10331033
/** The capture set of the type underlying CaptureRef */
10341034
def ofInfo(ref: CaptureRef)(using Context): CaptureSet = ref match
1035-
case ref: TermRef if ref.isRootCapability => ref.singletonCaptureSet
1035+
case ref: (TermRef | TermParamRef) if ref.isMaxCapability => ref.singletonCaptureSet
10361036
case ReachCapability(ref1) => deepCaptureSet(ref1.widen)
10371037
.showing(i"Deep capture set of $ref: ${ref1.widen} = $result", capt)
10381038
case _ => ofType(ref.underlying, followResult = true)

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2259,7 +2259,7 @@ object Types extends TypeUtils {
22592259
* set of the underlying type is not always empty.
22602260
*/
22612261
final def isTracked(using Context): Boolean =
2262-
isTrackableRef && (isRootCapability || !captureSetOfInfo.isAlwaysEmpty)
2262+
isTrackableRef && (isMaxCapability || !captureSetOfInfo.isAlwaysEmpty)
22632263

22642264
/** Is this a reach reference of the form `x*`? */
22652265
def isReach(using Context): Boolean = false // overridden in AnnotatedType
@@ -2273,6 +2273,9 @@ object Types extends TypeUtils {
22732273
/** Is this reference the generic root capability `cap` ? */
22742274
def isRootCapability(using Context): Boolean = false
22752275

2276+
/** Is this reference capability that does not derive from another capability ? */
2277+
def isMaxCapability(using Context): Boolean = false
2278+
22762279
/** Normalize reference so that it can be compared with `eq` for equality */
22772280
def normalizedRef(using Context): CaptureRef = this
22782281

@@ -3010,6 +3013,9 @@ object Types extends TypeUtils {
30103013
override def isRootCapability(using Context): Boolean =
30113014
name == nme.CAPTURE_ROOT && symbol == defn.captureRoot
30123015

3016+
override def isMaxCapability(using Context): Boolean =
3017+
widen.derivesFrom(defn.Caps_Cap) && symbol.isStableMember
3018+
30133019
override def normalizedRef(using Context): CaptureRef =
30143020
if isTrackableRef then symbol.termRef else this
30153021
}
@@ -4809,6 +4815,7 @@ object Types extends TypeUtils {
48094815
def kindString: String = "Term"
48104816
def copyBoundType(bt: BT): Type = bt.paramRefs(paramNum)
48114817
override def isTrackableRef(using Context) = true
4818+
override def isMaxCapability(using Context) = widen.derivesFrom(defn.Caps_Cap)
48124819
}
48134820

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

library/src/scala/caps.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import annotation.experimental
1313
/** The universal capture reference */
1414
val cap: Cap = Cap()
1515

16-
given Cap = cap
17-
1816
/** Reach capabilities x* which appear as terms in @retains annotations are encoded
1917
* as `caps.reachCapability(x)`. When converted to CaptureRef types in capture sets
2018
* they are represented as `x.type @annotation.internal.reachCapability`.

tests/neg-custom-args/captures/filevar.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Service:
99
def log = file.write("log")
1010

1111
def withFile[T](op: (l: caps.Cap) ?-> (f: File^{l}) => T): T =
12-
op(new File)
12+
op(using caps.cap)(new File)
1313

1414
def test =
1515
withFile: f =>

tests/neg-custom-args/captures/i15923.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x)
55
def bar() = {
66
def withCap[X](op: (lcap: caps.Cap) ?-> Cap^{lcap} => X): X = {
77
val cap: Cap = new Cap { def use() = { println("cap is used"); 0 } }
8-
val result = op(cap)
8+
val result = op(using caps.cap)(cap)
99
result
1010
}
1111

tests/neg-custom-args/captures/stack-alloc.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def withFreshPooled[T](op: (lcap: caps.Cap) ?-> Pooled^{lcap} => T): T =
99
if nextFree >= stack.size then stack.append(new Pooled)
1010
val pooled = stack(nextFree)
1111
nextFree = nextFree + 1
12-
val ret = op(pooled)
12+
val ret = op(using caps.cap)(pooled)
1313
nextFree = nextFree - 1
1414
ret
1515

tests/neg-custom-args/captures/usingLogFile.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object Test1:
55

66
def usingLogFile[T](op: (local: caps.Cap) ?-> FileOutputStream => T): T =
77
val logFile = FileOutputStream("log")
8-
val result = op(logFile)
8+
val result = op(using caps.cap)(logFile)
99
logFile.close()
1010
result
1111

0 commit comments

Comments
 (0)