Skip to content

Commit c7a5350

Browse files
committed
Fix leaking problem detected in review
1 parent 482dc78 commit c7a5350

File tree

3 files changed

+39
-21
lines changed

3 files changed

+39
-21
lines changed

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

+21-21
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ object CheckCaptures:
154154
traverseChildren(t)
155155
check.traverse(tp)
156156

157-
/** Attachment key for boxed curried closures */
158-
val BoxedClosure = Property.Key[Type]
157+
/** Attachment key for bodies of closures, provided they are values */
158+
val ClosureBodyValue = Property.Key[Unit]
159159

160160
class CheckCaptures extends Recheck, SymTransformer:
161161
thisPhase =>
@@ -243,18 +243,20 @@ class CheckCaptures extends Recheck, SymTransformer:
243243
if sym.ownersIterator.exists(_.isTerm) then CaptureSet.Var()
244244
else CaptureSet.empty)
245245

246-
/** For all nested environments up to `limit` or a closed environment perform `op` */
246+
/** For all nested environments up to `limit` or a closed environment perform `op`,
247+
* but skip environmenrts directly enclosing environments of kind ClosureResult.
248+
*/
247249
def forallOuterEnvsUpTo(limit: Symbol)(op: Env => Unit)(using Context): Unit =
248-
def recur(env: Env): Unit =
250+
def recur(env: Env, skip: Boolean): Unit =
249251
if env.isOpen && env.owner != limit then
250-
op(env)
252+
if !skip then op(env)
251253
if !env.isOutermost then
252254
var nextEnv = env.outer
253255
if env.owner.isConstructor then
254256
if nextEnv.owner != limit && !nextEnv.isOutermost then
255257
nextEnv = nextEnv.outer
256-
recur(nextEnv)
257-
recur(curEnv)
258+
recur(nextEnv, skip = env.kind == EnvKind.ClosureResult)
259+
recur(curEnv, skip = false)
258260

259261
/** Include `sym` in the capture sets of all enclosing environments nested in the
260262
* the environment in which `sym` is defined.
@@ -495,8 +497,7 @@ class CheckCaptures extends Recheck, SymTransformer:
495497
// the second closure `y => e` into the first one. This is an approximation
496498
// of the CC rule which says that a closure contributes captures to its
497499
// environment only if a let-bound reference to the closure is used.
498-
capt.println(i"boxing $rhs")
499-
rhs.putAttachment(BoxedClosure, ())
500+
mdef.rhs.putAttachment(ClosureBodyValue, ())
500501
case _ =>
501502
case _ =>
502503
super.recheckBlock(block, pt)
@@ -595,20 +596,19 @@ class CheckCaptures extends Recheck, SymTransformer:
595596
* adding all references in the boxed capture set to the current environment.
596597
*/
597598
override def recheck(tree: Tree, pt: Type = WildcardType)(using Context): Type =
598-
if tree.isTerm && (pt.isBoxedCapturing || tree.hasAttachment(BoxedClosure)) then
599-
val saved = curEnv
600-
601-
tree match
602-
case _: RefTree | closureDef(_) =>
603-
curEnv = Env(curEnv.owner, EnvKind.Boxed, CaptureSet.Var(), curEnv)
604-
case _ =>
605-
599+
val saved = curEnv
600+
tree match
601+
case _: RefTree | closureDef(_) if pt.isBoxedCapturing =>
602+
curEnv = Env(curEnv.owner, EnvKind.Boxed, CaptureSet.Var(), curEnv)
603+
case _ if tree.hasAttachment(ClosureBodyValue) =>
604+
curEnv = Env(curEnv.owner, EnvKind.ClosureResult, CaptureSet.Var(), curEnv)
605+
case _ =>
606+
val res =
606607
try super.recheck(tree, pt)
607608
finally curEnv = saved
608-
else
609-
val res = super.recheck(tree, pt)
610-
if tree.isTerm then markFree(res.boxedCaptureSet, tree.srcPos)
611-
res
609+
if tree.isTerm && !pt.isBoxedCapturing then
610+
markFree(res.boxedCaptureSet, tree.srcPos)
611+
res
612612

613613
/** If `tree` is a reference or an application where the result type refers
614614
* to an enclosing class or method parameter of the reference, check that the result type
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Error: tests/neg-custom-args/captures/leaked-curried.scala:12:52 ----------------------------------------------------
2+
12 | val get: () ->{} () ->{io} Cap^ = () => () => io // error
3+
| ^^
4+
|(io : Cap^) cannot be referenced here; it is not included in the allowed capture set {} of pure base class trait Pure
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait Cap:
2+
def use(): Unit
3+
4+
def withCap[sealed T](op: (x: Cap^) => T): T = ???
5+
6+
trait Box:
7+
val get: () ->{} () ->{cap} Cap^
8+
9+
def main(): Unit =
10+
val leaked = withCap: (io: Cap^) =>
11+
class Foo extends Box, Pure:
12+
val get: () ->{} () ->{io} Cap^ = () => () => io // error
13+
new Foo
14+
val bad = leaked.get()().use() // using a leaked capability

0 commit comments

Comments
 (0)