@@ -12,7 +12,7 @@ import ast.{tpd, untpd, Trees}
12
12
import Trees .*
13
13
import typer .RefChecks .{checkAllOverrides , checkSelfAgainstParents , OverridingPairsChecker }
14
14
import typer .Checking .{checkBounds , checkAppliedTypesIn }
15
- import util .{SimpleIdentitySet , EqHashMap , SrcPos }
15
+ import util .{SimpleIdentitySet , EqHashMap , SrcPos , Property }
16
16
import transform .SymUtils .*
17
17
import transform .{Recheck , PreRecheck }
18
18
import Recheck .*
@@ -145,6 +145,9 @@ object CheckCaptures:
145
145
traverseChildren(t)
146
146
check.traverse(tp)
147
147
148
+ /** Attachment key for boxed curried closures */
149
+ val BoxedClosure = Property .Key [Type ]
150
+
148
151
class CheckCaptures extends Recheck , SymTransformer :
149
152
thisPhase =>
150
153
@@ -231,29 +234,15 @@ class CheckCaptures extends Recheck, SymTransformer:
231
234
if sym.ownersIterator.exists(_.isTerm) then CaptureSet .Var ()
232
235
else CaptureSet .empty)
233
236
234
- /**
235
- class anon1 extends Function1:
236
- def apply: Function1 =
237
- use(x)
238
- class anon2 extends Function1:
239
- use(y)
240
- def apply: Function1 = ...
241
- anon2()
242
- anon1()
243
- */
244
- /** For all nested environments up to `limit` perform `op` */
237
+ /** For all nested environments up to `limit` or a closed environment perform `op` */
245
238
def forallOuterEnvsUpTo (limit : Symbol )(op : Env => Unit )(using Context ): Unit =
246
- def stopsPropagation (env : Env ) =
247
- val sym = env.owner
248
- env.isOutermost
249
- || false && sym.is(Method ) && sym.owner.isTerm && ! sym.isConstructor
250
239
def recur (env : Env ): Unit =
251
240
if env.isOpen && env.owner != limit then
252
241
op(env)
253
- if ! stopsPropagation( env) then
242
+ if ! env.isOutermost then
254
243
var nextEnv = env.outer
255
244
if env.owner.isConstructor then
256
- if nextEnv.owner != limit && ! stopsPropagation( nextEnv) then
245
+ if nextEnv.owner != limit && ! nextEnv.isOutermost then
257
246
nextEnv = nextEnv.outer
258
247
recur(nextEnv)
259
248
recur(curEnv)
@@ -491,6 +480,15 @@ class CheckCaptures extends Recheck, SymTransformer:
491
480
recheckDef(mdef, meth)
492
481
meth.updateInfoBetween(preRecheckPhase, thisPhase, completer)
493
482
case _ =>
483
+ mdef.rhs match
484
+ case rhs @ closure(_, _, _) =>
485
+ // In a curried closure `x => y => e` don't leak capabilities retained by
486
+ // the second closure `y => e` into the first one. This is an approximation
487
+ // of the CC rule which says that a closure contributes captures to its
488
+ // environment only if a let-bound reference to the closure is used.
489
+ capt.println(i " boxing $rhs" )
490
+ rhs.putAttachment(BoxedClosure , ())
491
+ case _ =>
494
492
case _ =>
495
493
super .recheckBlock(block, pt)
496
494
@@ -588,7 +586,7 @@ class CheckCaptures extends Recheck, SymTransformer:
588
586
* adding all references in the boxed capture set to the current environment.
589
587
*/
590
588
override def recheck (tree : Tree , pt : Type = WildcardType )(using Context ): Type =
591
- if tree.isTerm && pt.isBoxedCapturing then
589
+ if tree.isTerm && ( pt.isBoxedCapturing || tree.hasAttachment( BoxedClosure )) then
592
590
val saved = curEnv
593
591
594
592
tree match
0 commit comments