Skip to content

Commit b5fe6d2

Browse files
committed
Coarse restriction to disallow local roots in external types
This needs to be refined further for class members, similar to how we check that private types cannot escape from a class API.
1 parent d85db26 commit b5fe6d2

File tree

8 files changed

+83
-4
lines changed

8 files changed

+83
-4
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,6 +1299,20 @@ class CheckCaptures extends Recheck, SymTransformer:
12991299
checker.traverse(tree.knownType)
13001300
end healTypeParam
13011301

1302+
def checkNoLocalRootIn(sym: Symbol, info: Type, pos: SrcPos)(using Context): Unit =
1303+
val check = new TypeTraverser:
1304+
def traverse(tp: Type) = tp match
1305+
case tp: TermRef if tp.isLocalRootCapability =>
1306+
if tp.localRootOwner == sym then
1307+
report.error(i"local root $tp cannot appear in type of $sym", pos)
1308+
case tp: ClassInfo =>
1309+
traverseChildren(tp)
1310+
for mbr <- tp.decls do
1311+
if !mbr.is(Private) then checkNoLocalRootIn(sym, mbr.info, mbr.srcPos)
1312+
case _ =>
1313+
traverseChildren(tp)
1314+
check.traverse(info)
1315+
13021316
/** Perform the following kinds of checks
13031317
* - Check all explicitly written capturing types for well-formedness using `checkWellFormedPost`.
13041318
* - Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
@@ -1322,6 +1336,8 @@ class CheckCaptures extends Recheck, SymTransformer:
13221336
checkBounds(normArgs, tl)
13231337
args.lazyZip(tl.paramNames).foreach(healTypeParam(_, _, fun.symbol))
13241338
case _ =>
1339+
case _: ValOrDefDef | _: TypeDef =>
1340+
checkNoLocalRootIn(tree.symbol, tree.symbol.info, tree.symbol.srcPos)
13251341
case _ =>
13261342
end check
13271343
end checker

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class File:
55
def write(x: String): Unit = ???
66

77
class Service:
8-
var file: File^{cap[Service]} = uninitialized
8+
var file: File^{cap[Service]} = uninitialized // error
99
def log = file.write("log")
1010

1111
def withFile[T](op: (l: caps.Cap) ?-> (f: File^{l}) => T): T =
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Error: tests/neg-custom-args/captures/localcaps.scala:4:12 ----------------------------------------------------------
2+
4 | def x: C^{cap[d]} = ??? // error
3+
| ^^^^^^
4+
| `d` does not name an outer definition that represents a capture level
5+
-- Error: tests/neg-custom-args/captures/localcaps.scala:9:47 ----------------------------------------------------------
6+
9 | private val z2 = identity((x: Int) => (c: C^{cap[z2]}) => x) // error
7+
| ^^^^^^^
8+
| `z2` does not name an outer definition that represents a capture level
9+
-- Error: tests/neg-custom-args/captures/localcaps.scala:6:6 -----------------------------------------------------------
10+
6 | def y: C^{cap[C]} = ??? // error
11+
| ^
12+
| local root (cap[C] : caps.Cap) cannot appear in type of class C

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class C:
33

44
def x: C^{cap[d]} = ??? // error
55

6-
def y: C^{cap[C]} = ??? // ok
6+
def y: C^{cap[C]} = ??? // error
77
private val z = (c0: caps.Cap) => (x: Int) => (c: C^{cap[C]}) => x // ok
88

99
private val z2 = identity((x: Int) => (c: C^{cap[z2]}) => x) // error

tests/neg-custom-args/captures/pairs.check

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,11 @@
1212
| Required: Cap^ ->{d} Unit
1313
|
1414
| longer explanation available when compiling with `-explain`
15+
-- Error: tests/neg-custom-args/captures/pairs.scala:6:8 ---------------------------------------------------------------
16+
6 | def fst: Cap^{cap[Pair]} ->{x} Unit = x // error
17+
| ^
18+
| local root (cap[Pair] : caps.Cap) cannot appear in type of class Pair
19+
-- Error: tests/neg-custom-args/captures/pairs.scala:7:8 ---------------------------------------------------------------
20+
7 | def snd: Cap^{cap[Pair]} ->{y} Unit = y // error
21+
| ^
22+
| local root (cap[Pair] : caps.Cap) cannot appear in type of class Pair

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
object Monomorphic2:
44

55
class Pair(x: Cap => Unit, y: Cap => Unit):
6-
def fst: Cap^{cap[Pair]} ->{x} Unit = x
7-
def snd: Cap^{cap[Pair]} ->{y} Unit = y
6+
def fst: Cap^{cap[Pair]} ->{x} Unit = x // error
7+
def snd: Cap^{cap[Pair]} ->{y} Unit = y // error
88

99
def test(c: Cap, d: Cap) =
1010
def f(x: Cap): Unit = if c == x then ()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import language.experimental.captureChecking
2+
trait Cap:
3+
def use: Int = 42
4+
5+
def usingCap[sealed T](op: Cap^ => T): T = ???
6+
7+
def badTest(): Unit =
8+
def bad(b: Boolean)(c: Cap^): Cap^{cap[bad]} = // error
9+
if b then c
10+
else
11+
val leaked = usingCap[Cap^{cap[bad]}](bad(true))
12+
leaked.use // boom
13+
c
14+
15+
usingCap[Unit]: c0 =>
16+
bad(false)(c0)
17+
18+
class Bad:
19+
def foo: Cap^{cap[Bad]} = ??? // error
20+
private def bar: Cap^{cap[Bad]} = ??? // ok
21+
22+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
abstract class C1[A1]:
2+
def set(x: A1): Unit
3+
def get: A1
4+
5+
trait Co[+A]:
6+
def get: A
7+
8+
class C2[sealed A2] extends C1[A2], Co[A2]: // ok
9+
private var x: A2 = ???
10+
def set(x: A2): Unit =
11+
this.x = x
12+
def get: A2 = x
13+
14+
class C3[A3] extends C2[A3] // error
15+
16+
abstract class C4[sealed A4] extends Co[A4] // ok
17+
18+
abstract class C5[sealed +A5] extends Co[A5] // ok
19+
20+
abstract class C6[A6] extends C5[A6] // error
21+

0 commit comments

Comments
 (0)