Skip to content

Commit 81cf8d8

Browse files
committed
Make capture sets of expressions deriving Capability explicit
When an expression has a type that derives from caps.Capability, add an explicit capture set. Also: Address other review comments
1 parent 999be5e commit 81cf8d8

21 files changed

+92
-50
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ 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?
206+
/** Tests whether the type derives from `caps.Capability`, which means
207+
* references of this type are maximal capabilities.
208208
*/
209209
def derivesFromCapability(using Context): Boolean = tp.dealias match
210210
case tp: (TypeRef | AppliedType) =>

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@ sealed abstract class CaptureSet extends Showable:
154154
(x eq y)
155155
|| x.isRootCapability
156156
|| y.match
157-
case y: TermRef => y.prefix eq x
157+
case y: TermRef =>
158+
(y.prefix eq x)
159+
|| y.info.match
160+
case y1: CaptureRef => x.subsumes(y1)
161+
case _ => false
158162
case MaybeCapability(y1) => x.stripMaybe.subsumes(y1)
159163
case _ => false
160164
|| x.match

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

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,7 @@ class CheckCaptures extends Recheck, SymTransformer:
10371037
val arrow = if covariant then "~~>" else "<~~"
10381038
i"adapting $actual $arrow $expected"
10391039

1040-
def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true) {
1040+
def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true):
10411041
if expected.isInstanceOf[WildcardType] then actual
10421042
else
10431043
// Decompose the actual type into the inner shape type, the capture set and the box status
@@ -1117,7 +1117,22 @@ class CheckCaptures extends Recheck, SymTransformer:
11171117
adaptedType(!boxed)
11181118
else
11191119
adaptedType(boxed)
1120-
}
1120+
end adapt
1121+
1122+
/** If result derives from caps.Capability, yet is not a capturing type itself,
1123+
* make its capture set explicit.
1124+
*/
1125+
def makeCaptureSetExplicit(result: Type) = result match
1126+
case CapturingType(_, _) => result
1127+
case _ =>
1128+
if result.derivesFromCapability then
1129+
val cap: CaptureRef = actual match
1130+
case ref: CaptureRef if ref.isTracked =>
1131+
ref
1132+
case _ =>
1133+
defn.captureRoot.termRef // TODO: skolemize?
1134+
CapturingType(result, cap.singletonCaptureSet)
1135+
else result
11211136

11221137
if expected == LhsProto || expected.isSingleton && actual.isSingleton then
11231138
actual
@@ -1133,10 +1148,12 @@ class CheckCaptures extends Recheck, SymTransformer:
11331148
case _ =>
11341149
case _ =>
11351150
val adapted = adapt(actualw.withReachCaptures(actual), expected, covariant = true)
1136-
if adapted ne actualw then
1137-
capt.println(i"adapt boxed $actual vs $expected ===> $adapted")
1138-
adapted
1139-
else actual
1151+
makeCaptureSetExplicit:
1152+
if adapted ne actualw then
1153+
capt.println(i"adapt boxed $actual vs $expected ===> $adapted")
1154+
adapted
1155+
else
1156+
actual
11401157
end adaptBoxed
11411158

11421159
/** Check overrides again, taking capture sets into account.

library/src/scala/caps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import annotation.experimental
44

55
@experimental object caps:
66

7-
trait Capability // should be @erased
7+
trait Capability extends Any
88

99
/** The universal capture reference */
1010
val cap: Capability = new Capability() {}
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:64:8 -------------------------
2-
63 | Result:
3-
64 | Future: // error, type mismatch
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:62:8 -------------------------
2+
61 | Result:
3+
62 | Future: // error, type mismatch
44
| ^
55
| Found: Result.Ok[box Future[box T^?]^{fr, contextual$1}]
66
| Required: Result[Future[T], Nothing]
7-
65 | fr.await.ok
7+
63 | fr.await.ok
88
|--------------------------------------------------------------------------------------------------------------------
99
|Inline stack trace
1010
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
11-
|This location contains code that was inlined from effect-swaps-explicit.scala:41
12-
41 | boundary(Ok(body))
11+
|This location contains code that was inlined from effect-swaps-explicit.scala:39
12+
39 | boundary(Ok(body))
1313
| ^^^^^^^^
1414
--------------------------------------------------------------------------------------------------------------------
1515
|
1616
| longer explanation available when compiling with `-explain`
17-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:74:10 ------------------------
18-
74 | Future: fut ?=> // error: type mismatch
17+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:72:10 ------------------------
18+
72 | Future: fut ?=> // error: type mismatch
1919
| ^
2020
| Found: Future[box T^?]^{fr, lbl}
2121
| Required: Future[box T^?]^?
22-
75 | fr.await.ok
22+
73 | fr.await.ok
2323
|
2424
| longer explanation available when compiling with `-explain`
25-
-- Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:68:15 ---------------------------------------------
26-
68 | Result.make: //lbl ?=> // error, escaping label from Result
25+
-- Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:66:15 ---------------------------------------------
26+
66 | Result.make: //lbl ?=> // error, escaping label from Result
2727
| ^^^^^^^^^^^
2828
|local reference contextual$9 from (using contextual$9: boundary.Label[Result[box Future[box T^?]^{fr, contextual$9}, box E^?]]^):
2929
| box Future[box T^?]^{fr, contextual$9} leaks into outer capture set of type parameter T of method make in object Result

tests/neg-custom-args/captures/effect-swaps-explicit.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import annotation.capability
2-
31
object boundary:
42

53
final class Label[-T] // extends caps.Capability
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:64:8 ----------------------------------
2-
63 | Result:
3-
64 | Future: // error, type mismatch
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:62:8 ----------------------------------
2+
61 | Result:
3+
62 | Future: // error, type mismatch
44
| ^
55
| Found: Result.Ok[box Future[box T^?]^{fr, contextual$1}]
66
| Required: Result[Future[T], Nothing]
7-
65 | fr.await.ok
7+
63 | fr.await.ok
88
|--------------------------------------------------------------------------------------------------------------------
99
|Inline stack trace
1010
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
11-
|This location contains code that was inlined from effect-swaps.scala:41
12-
41 | boundary(Ok(body))
11+
|This location contains code that was inlined from effect-swaps.scala:39
12+
39 | boundary(Ok(body))
1313
| ^^^^^^^^
1414
--------------------------------------------------------------------------------------------------------------------
1515
|
1616
| longer explanation available when compiling with `-explain`
17-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:74:10 ---------------------------------
18-
74 | Future: fut ?=> // error: type mismatch
17+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:72:10 ---------------------------------
18+
72 | Future: fut ?=> // error: type mismatch
1919
| ^
2020
| Found: Future[box T^?]^{fr, lbl}
2121
| Required: Future[box T^?]^?
22-
75 | fr.await.ok
22+
73 | fr.await.ok
2323
|
2424
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/effect-swaps.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import annotation.capability
2-
31
object boundary:
42

53
final class Label[-T] extends caps.Capability
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/extending-cap-classes.scala:7:15 -------------------------
2+
7 | val x2: C1 = new C2 // error
3+
| ^^^^^^
4+
| Found: C2^
5+
| Required: C1
6+
|
7+
| longer explanation available when compiling with `-explain`
8+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/extending-cap-classes.scala:8:15 -------------------------
9+
8 | val x3: C1 = new C3 // error
10+
| ^^^^^^
11+
| Found: C3^
12+
| Required: C1
13+
|
14+
| longer explanation available when compiling with `-explain`
15+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/extending-cap-classes.scala:13:15 ------------------------
16+
13 | val z2: C1 = y2 // error
17+
| ^^
18+
| Found: (y2 : C2)^{y2}
19+
| Required: C1
20+
|
21+
| longer explanation available when compiling with `-explain`
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class C1
2+
class C2 extends C1, caps.Capability
3+
class C3 extends C2
4+
5+
def test =
6+
val x1: C1 = new C1
7+
val x2: C1 = new C2 // error
8+
val x3: C1 = new C3 // error
9+
10+
val y2: C2 = new C2
11+
val y3: C3 = new C3
12+
13+
val z2: C1 = y2 // error
14+
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:23:14 ------------------------------------------------------
2-
23 | val later = usingLogFile { f => () => f.write(0) } // error
1+
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:22:14 ------------------------------------------------------
2+
22 | val later = usingLogFile { f => () => f.write(0) } // error
33
| ^^^^^^^^^^^^
44
| local reference f leaks into outer capture set of type parameter T of method usingLogFile in object Test2
5-
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:28:23 ------------------------------------------------------
6-
28 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error
5+
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:27:23 ------------------------------------------------------
6+
27 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error
77
| ^^^^^^^^^^^^
88
| local reference f leaks into outer capture set of type parameter T of method usingLogFile in object Test2
9-
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:44:16 ------------------------------------------------------
10-
44 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error
9+
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:43:16 ------------------------------------------------------
10+
43 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error
1111
| ^^^^^^^^^
1212
| local reference f leaks into outer capture set of type parameter T of method usingFile in object Test3

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import java.io.*
2-
import annotation.capability
32

43
object Test1:
54

tests/pos-custom-args/captures/capt-capability.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import annotation.capability
21
import caps.Capability
32

43
def f1(c: Capability): () ->{c} c.type = () => c // ok

tests/pos-custom-args/captures/filevar-tracked.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import language.experimental.captureChecking
22
import language.experimental.modularity
3-
import annotation.capability
43
import compiletime.uninitialized
54

65
object test1:

tests/pos-custom-args/captures/i19751.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import language.experimental.captureChecking
2-
import annotation.capability
32
import caps.cap
43

54
trait Ptr[A]

tests/pos-custom-args/captures/logger-tracked.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import annotation.capability
21
import language.experimental.saferExceptions
32
import language.experimental.modularity
43

tests/pos-custom-args/captures/logger.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import annotation.capability
21
import language.experimental.saferExceptions
32
import language.experimental.modularity
43

tests/pos-custom-args/captures/null-logger.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import annotation.capability
21
import annotation.constructorOnly
32

43
class FileSystem extends caps.Capability

tests/pos-custom-args/captures/try3.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import language.experimental.erasedDefinitions
2-
import annotation.capability
32
import java.io.IOException
43

54
class CanThrow[-E] extends caps.Capability

tests/pos-with-compiler-cc/dotc/core/Definitions.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,6 @@ class Definitions {
985985
@tu lazy val BeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BeanProperty")
986986
@tu lazy val BooleanBeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BooleanBeanProperty")
987987
@tu lazy val BodyAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Body")
988-
@tu lazy val CapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.capability")
989988
@tu lazy val ChildAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Child")
990989
@tu lazy val ContextResultCountAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ContextResultCount")
991990
@tu lazy val ProvisionalSuperClassAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ProvisionalSuperClass")

tests/pos/i20237.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import language.experimental.captureChecking
2-
import scala.annotation.capability
32

43
class Cap extends caps.Capability:
54
def use[T](body: Cap ?=> T) = body(using this)

0 commit comments

Comments
 (0)