Skip to content

Commit c0cfd0f

Browse files
authored
Charge also dcs of local reaches to capture set of enclosing method (#21443)
Fixes #21442
2 parents dd37503 + 98a41c2 commit c0cfd0f

File tree

9 files changed

+49
-16
lines changed

9 files changed

+49
-16
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ class CheckCaptures extends Recheck, SymTransformer:
394394
val isVisible = isVisibleFromEnv(refOwner)
395395
if !isVisible
396396
&& (c.isReach || ref.isType)
397-
&& refSym.is(Param)
397+
&& (!ccConfig.useSealed || refSym.is(Param))
398398
&& refOwner == env.owner
399399
then
400400
if refSym.hasAnnotation(defn.UnboxAnnot) then

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

+4
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@
88
| ^^^^^^^^^^^^^^^^^^^
99
| The expression's type Res is not allowed to capture the root capability `cap` in its part box IO^.
1010
| This usually means that a capability persists longer than its allowed lifetime.
11+
-- Error: tests/neg-custom-args/captures/i21401.scala:18:21 ------------------------------------------------------------
12+
18 | val y: IO^{x*} = x.unbox // error
13+
| ^^^^^^^
14+
| Local reach capability x* leaks into capture scope of method test2

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ def test2() =
1515
val a = usingIO[IO^](x => x) // error: The expression's type IO^ is not allowed to capture the root capability `cap`
1616
val leaked: [R, X <: Boxed[IO^] -> R] -> (op: X) -> R = usingIO[Res](mkRes) // error: The expression's type Res is not allowed to capture the root capability `cap` in its part box IO^
1717
val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x)
18-
val y: IO^{x*} = x.unbox
18+
val y: IO^{x*} = x.unbox // error
1919
y.println("boom")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- Error: tests/neg-custom-args/captures/i21442.scala:9:13 -------------------------------------------------------------
2+
9 | val io = x.unbox // error: local reach capability {x*} leaks
3+
| ^^^^^^^
4+
| Local reach capability x* leaks into capture scope of method foo
5+
-- Error: tests/neg-custom-args/captures/i21442.scala:17:14 ------------------------------------------------------------
6+
17 | val io = x1.unbox // error
7+
| ^^^^^^^^
8+
| Local reach capability x1* leaks into capture scope of method bar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import language.experimental.captureChecking
2+
trait IO:
3+
def use(): Unit
4+
case class Boxed[+T](unbox: T)
5+
6+
// `foo` is a function that unboxes its parameter
7+
// and uses the capability boxed inside the parameter.
8+
def foo(x: Boxed[IO^]): Unit =
9+
val io = x.unbox // error: local reach capability {x*} leaks
10+
io.use()
11+
12+
// `bar` is a function that does the same thing in a
13+
// slightly different way.
14+
// But, no type error reported.
15+
def bar(x: Boxed[IO^]): Unit =
16+
val x1: Boxed[IO^] = x
17+
val io = x1.unbox // error
18+
io.use()

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

+10-12
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,10 @@
2525
| ^^^^^^^^^^^^
2626
| The expression's type box () => Unit is not allowed to capture the root capability `cap`.
2727
| This usually means that a capability persists longer than its allowed lifetime.
28-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:53:2 ---------------------------------------
29-
53 | val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error
30-
| ^
31-
| Found: box () => Unit
32-
| Required: () => Unit
33-
|
34-
| Note that box () => Unit cannot be box-converted to () => Unit
35-
| since at least one of their capture sets contains the root capability `cap`
36-
54 | usingFile: f =>
37-
55 | id(() => f.write())
38-
|
39-
| longer explanation available when compiling with `-explain`
28+
-- Error: tests/neg-custom-args/captures/reaches.scala:55:6 ------------------------------------------------------------
29+
55 | id(() => f.write()) // error
30+
| ^^^^^^^^^^^^^^^^^^^
31+
| Local reach capability id* leaks into capture scope of method test
4032
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:62:27 --------------------------------------
4133
62 | val f1: File^{id*} = id(f) // error, since now id(f): File^
4234
| ^^^^^
@@ -52,3 +44,9 @@
5244
79 | ps.map((x, y) => compose1(x, y)) // error // error
5345
| ^
5446
| Local reach capability ps* leaks into capture scope of method mapCompose
47+
-- [E057] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:53:51 --------------------------------------
48+
53 | val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error
49+
| ^
50+
| Type argument () -> Unit does not conform to lower bound () => Unit
51+
|
52+
| longer explanation available when compiling with `-explain`

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class Id[-A, +B >: A]():
5252
def test =
5353
val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error
5454
usingFile: f =>
55-
id(() => f.write())
55+
id(() => f.write()) // error
5656

5757
def attack2 =
5858
val id: File^ -> File^ = x => x

tests/neg-custom-args/captures/unsound-reach.check

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
-- Error: tests/neg-custom-args/captures/unsound-reach.scala:18:21 -----------------------------------------------------
2+
18 | boom.use(f): (f1: File^{backdoor*}) => // error
3+
| ^
4+
| Local reach capability backdoor* leaks into capture scope of method bad
5+
19 | escaped = f1
16
-- [E164] Declaration Error: tests/neg-custom-args/captures/unsound-reach.scala:10:8 -----------------------------------
27
10 | def use(x: File^)(op: File^ => Unit): Unit = op(x) // error, was OK using sealed checking
38
| ^

tests/neg-custom-args/captures/unsound-reach.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ def bad(): Unit =
1515

1616
var escaped: File^{backdoor*} = null
1717
withFile("hello.txt"): f =>
18-
boom.use(f): (f1: File^{backdoor*}) => // was error before existentials
18+
boom.use(f): (f1: File^{backdoor*}) => // error
1919
escaped = f1
2020

0 commit comments

Comments
 (0)