Skip to content

Commit 4226a33

Browse files
committed
Disable reach capabilities in nested closures appearing without @use
With our current @use scheme, this is unsound. We leave the possibility to re-enable as a Config option which is disabled by default and comes with a warning that enabling it would be unsound.
1 parent fd9e895 commit 4226a33

File tree

4 files changed

+41
-13
lines changed

4 files changed

+41
-13
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ object ccConfig:
2828
inline val allowUnsoundMaps = false
2929

3030
/** If enabled, use a special path in recheckClosure for closures
31-
* that are eta expansions. This can improve some error messages but
32-
* currently leads to unsoundess for handling reach capabilities.
33-
* TODO: The unsoundness needs followin up.
31+
* that are eta expansions. This can improve some error messages.
3432
*/
3533
inline val handleEtaExpansionsSpecially = true
3634

35+
/** Don't require @use for reach capabilities that are accessed
36+
* only in a nested closure. This is unsound without additional
37+
* mitigation measures, as shown by unsound-reach-5.scala.
38+
*/
39+
inline val deferredReaches = false
40+
3741
/** If true, use existential capture set variables */
3842
def useExistentials(using Context) =
3943
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.5`)

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -824,13 +824,15 @@ class CheckCaptures extends Recheck, SymTransformer:
824824
if Synthetics.isExcluded(sym) then sym.info
825825
else
826826
// If rhs ends in a closure or anonymous class, the corresponding symbol
827-
def nestedClosure(rhs: Tree)(using Context): Symbol = rhs match
828-
case Closure(_, meth, _) => meth.symbol
829-
case Apply(fn, _) if fn.symbol.isConstructor && fn.symbol.owner.isAnonymousClass => fn.symbol.owner
830-
case Block(_, expr) => nestedClosure(expr)
831-
case Inlined(_, _, expansion) => nestedClosure(expansion)
832-
case Typed(expr, _) => nestedClosure(expr)
833-
case _ => NoSymbol
827+
def nestedClosure(rhs: Tree)(using Context): Symbol =
828+
if !ccConfig.deferredReaches then NoSymbol
829+
else rhs match
830+
case Closure(_, meth, _) => meth.symbol
831+
case Apply(fn, _) if fn.symbol.isConstructor && fn.symbol.owner.isAnonymousClass => fn.symbol.owner
832+
case Block(_, expr) => nestedClosure(expr)
833+
case Inlined(_, _, expansion) => nestedClosure(expansion)
834+
case Typed(expr, _) => nestedClosure(expr)
835+
case _ => NoSymbol
834836

835837
val saved = curEnv
836838
val localSet = capturedVars(sym)

tests/neg-custom-args/captures/method-uses.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ def test2(xs: List[() => Unit]) =
1818
()
1919

2020
def test3(xs: List[() => Unit]): () ->{xs*} Unit = () =>
21-
println(xs.head) // ok
21+
println(xs.head) // error, ok under deferredReaches
2222

23-
def test4(xs: List[() => Unit]) = () => xs.head // ok
23+
def test4(xs: List[() => Unit]) = () => xs.head // error, ok under deferredReaches
2424

2525
def test5(xs: List[() => Unit]) = new:
26-
println(xs.head) // ok
26+
println(xs.head) // error, ok under deferredReaches
2727

2828
def test6(xs: List[() => Unit]) =
2929
val x= new { println(xs.head) } // error
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class IO
2+
3+
def f(xs: List[() => Unit]): () ->{xs*} Unit = () =>
4+
println(xs.head) // error
5+
6+
def test(io: IO^)(ys: List[() ->{io} Unit]) =
7+
val x = () =>
8+
val z = f(ys)
9+
z()
10+
val _: () -> Unit = x // !!! ys* gets lost
11+
()
12+
13+
def test(io: IO^) =
14+
def ys: List[() ->{io} Unit] = ???
15+
val x = () =>
16+
val z = f(ys)
17+
z()
18+
val _: () -> Unit = x // !!! io gets lost
19+
()
20+
21+
22+

0 commit comments

Comments
 (0)