Skip to content

Commit 70af983

Browse files
committed
Drop curried use scheme
Drop the scheme where we only charge the last arrow of a curried lambda with elements used in the body. On the one hand, this is unsound without compensation measures (like, restricting to reach capabilities, or taking all capture sets of a named curried function as the underlying reference). On the other hand, this should be generalized to all closures and anonymous functions forming the right hand sides of methods.
1 parent fc46e5c commit 70af983

File tree

8 files changed

+39
-18
lines changed

8 files changed

+39
-18
lines changed

Diff for: compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ object ccConfig:
4040
*/
4141
inline val handleEtaExpansionsSpecially = false
4242

43+
/** If enabled we drop inner uses in outer arrows of a curried function */
44+
inline val DropOuterUsesInCurried = false
45+
4346
/** If true, use existential capture set variables */
4447
def useExistentials(using Context) =
4548
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.5`)

Diff for: compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

+6-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ object CheckCaptures:
3333
case NestedInOwner // environment is a temporary one nested in the owner's environment,
3434
// and does not have a different actual owner symbol
3535
// (this happens when doing box adaptation).
36-
case ClosureResult // environment is for the result of a closure
36+
case ClosureResult // environment is for the result of a closure,
37+
// used only under ccConfig.DropOuterUsesInCurried
3738
case Boxed // environment is inside a box (in which case references are not counted)
3839

3940
/** A class describing environments.
@@ -180,7 +181,9 @@ object CheckCaptures:
180181
if ccConfig.useSealed then check.traverse(tp)
181182
end disallowRootCapabilitiesIn
182183

183-
/** Attachment key for bodies of closures, provided they are values */
184+
/** Attachment key for bodies of closures, provided they are values.
185+
* Used only under ccConfig.DropOuterUsesInCurried
186+
*/
184187
val ClosureBodyValue = Property.Key[Unit]
185188

186189
/** A prototype that indicates selection with an immutable value */
@@ -728,7 +731,7 @@ class CheckCaptures extends Recheck, SymTransformer:
728731

729732
override def recheckClosureBlock(mdef: DefDef, expr: Closure, pt: Type)(using Context): Type =
730733
mdef.rhs match
731-
case rhs @ closure(_, _, _) =>
734+
case rhs @ closure(_, _, _) if ccConfig.DropOuterUsesInCurried =>
732735
// In a curried closure `x => y => e` don't leak capabilities retained by
733736
// the second closure `y => e` into the first one. This is an approximation
734737
// of the CC rule which says that a closure contributes captures to its

Diff for: compiler/src/dotty/tools/dotc/cc/Setup.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
294294
case _ => res
295295
)
296296
val fntpe = defn.PolyFunctionOf(mt)
297-
if !encl.isEmpty && resDecomposed.isEmpty then
297+
if !encl.isEmpty && (!ccConfig.DropOuterUsesInCurried || resDecomposed.isEmpty) then
298298
val cs = CaptureSet(encl.map(_.paramRefs.head)*)
299299
CapturingType(fntpe, cs, boxed = false)
300300
else fntpe

Diff for: tests/pos-custom-args/captures/curried-closures.scala renamed to tests/neg-custom-args/captures/curried-closures.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ def Test4(g: OutputStream^) =
3030
val _: (f: OutputStream^) ->{} Int ->{f} Unit = later
3131

3232
val later2 = () => (y: Int) => xs.foreach(x => g.write(x + y))
33-
val _: () ->{} Int ->{g} Unit = later2
33+
val _: () ->{} Int ->{g} Unit = later2 // error, inferred type is () ->{later2} Int ->{g} Unit
3434

Diff for: tests/neg-custom-args/captures/i21620.check

+13
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,23 @@
44
| A pure expression does nothing in statement position
55
|
66
| longer explanation available when compiling with `-explain`
7+
-- [E129] Potential Issue Warning: tests/neg-custom-args/captures/i21620.scala:14:4 ------------------------------------
8+
14 | x
9+
| ^
10+
| A pure expression does nothing in statement position
11+
|
12+
| longer explanation available when compiling with `-explain`
713
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21620.scala:9:31 ----------------------------------------
814
9 | val _: () -> () ->{x} Unit = f // error
915
| ^
1016
| Found: () ->{f} () ->{x} Unit
1117
| Required: () -> () ->{x} Unit
1218
|
1319
| longer explanation available when compiling with `-explain`
20+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21620.scala:20:33 ---------------------------------------
21+
20 | val _: () ->{} () ->{x} Unit = f // error, but could be OK
22+
| ^
23+
| Found: () ->{f} () ->{x} Unit
24+
| Required: () -> () ->{x} Unit
25+
|
26+
| longer explanation available when compiling with `-explain`

Diff for: tests/neg-custom-args/captures/i21620.scala

+11
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,14 @@ def test(x: C^) =
88
() => foo()
99
val _: () -> () ->{x} Unit = f // error
1010
()
11+
12+
def test2(x: C^) =
13+
def foo() =
14+
x
15+
()
16+
val f = () =>
17+
// println() // uncomenting would give an error, but with
18+
// a different way of handling curried functions should be OK
19+
() => foo()
20+
val _: () ->{} () ->{x} Unit = f // error, but could be OK
21+
()

Diff for: tests/neg-custom-args/captures/leaked-curried.check

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
-- Error: tests/neg-custom-args/captures/leaked-curried.scala:14:20 ----------------------------------------------------
22
14 | () => () => io // error
33
| ^^
4-
|(io : Cap^) cannot be referenced here; it is not included in the allowed capture set {} of the self type of class Fuzz
4+
| (io : Cap^) cannot be referenced here; it is not included in the allowed capture set {}
5+
| of an enclosing function literal with expected type () -> () ->{io} (ex$7: caps.Exists) -> Cap^{ex$7}
56
-- Error: tests/neg-custom-args/captures/leaked-curried.scala:17:20 ----------------------------------------------------
67
17 | () => () => io // error
78
| ^^
8-
|(io : Cap^) cannot be referenced here; it is not included in the allowed capture set {} of the self type of class Foo
9+
| (io : Cap^) cannot be referenced here; it is not included in the allowed capture set {}
10+
| of an enclosing function literal with expected type () -> () ->{io} (ex$15: caps.Exists) -> Cap^{ex$15}

Diff for: tests/pos-custom-args/captures/i21620.scala

-11
This file was deleted.

0 commit comments

Comments
 (0)