Skip to content

Commit 1065bd1

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 96928a2 commit 1065bd1

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
@@ -1301,6 +1301,20 @@ class CheckCaptures extends Recheck, SymTransformer:
13011301
checker.traverse(tree.knownType)
13021302
end healTypeParam
13031303

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