Skip to content

Commit fdf23b7

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 5af15bc commit fdf23b7

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
@@ -1297,6 +1297,20 @@ class CheckCaptures extends Recheck, SymTransformer:
12971297
checker.traverse(tree.knownType)
12981298
end healTypeParam
12991299

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