Skip to content

Commit 08321d4

Browse files
committed
only create boxed environment for function values and references
1 parent 92f6b8c commit 08321d4

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,14 +507,28 @@ class CheckCaptures extends Recheck, SymTransformer:
507507
recheckFinish(result, arg, pt)
508508
*/
509509

510+
extension (tree: Tree)
511+
private def isFunctionLiteral(using Context): Boolean = tree match
512+
case Block((defTree @ DefDef(_, _, _, _)) :: Nil, Closure(_, meth, _)) =>
513+
val defSym = defTree.symbol
514+
val methSym = meth.symbol
515+
defSym.eq(methSym)
516+
case _ =>
517+
false
518+
519+
private def isIdent: Boolean = tree match
520+
case Ident(_) => true
521+
case _ => false
522+
510523
/** If expected type `pt` is boxed, don't propagate free variables.
511524
* Otherwise, if the result type is boxed, simulate an unboxing by
512525
* adding all references in the boxed capture set to the current environment.
513526
*/
514527
override def recheck(tree: Tree, pt: Type = WildcardType)(using Context): Type =
515528
if tree.isTerm && pt.isBoxedCapturing then
516529
val saved = curEnv
517-
curEnv = Env(curEnv.owner, nestedInOwner = false, CaptureSet.Var(), isBoxed = true, curEnv)
530+
if tree.isIdent || tree.isFunctionLiteral then
531+
curEnv = Env(curEnv.owner, nestedInOwner = false, CaptureSet.Var(), isBoxed = true, curEnv)
518532
try super.recheck(tree, pt)
519533
finally curEnv = saved
520534
else
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
trait Cap { def use(): Int; def close(): Unit }
2+
def mkCap(): {*} Cap = ???
3+
4+
def expect[T](x: T): x.type = x
5+
6+
def withCap[T](op: ({*} Cap) => T): T = {
7+
val cap: {*} Cap = mkCap()
8+
val result = op(cap)
9+
cap.close()
10+
result
11+
}
12+
13+
def main(fs: {*} Cap): Unit = {
14+
def badOp(io: {*} Cap): {} Unit -> Unit = {
15+
val op1: {io} Unit -> Unit = (x: Unit) => // error // limitation
16+
expect[{*} Cap] {
17+
io.use()
18+
fs
19+
}
20+
21+
val op2: {fs} Unit -> Unit = (x: Unit) => // error // limitation
22+
expect[{*} Cap] {
23+
fs.use()
24+
io
25+
}
26+
27+
val op3: {io} Unit -> Unit = (x: Unit) => // ok
28+
expect[{*} Cap] {
29+
io.use()
30+
io
31+
}
32+
33+
val op4: {} Unit -> Unit = (x: Unit) => // ok
34+
expect[{*} Cap](io)
35+
36+
val op: {} Unit -> Unit = (x: Unit) => // error
37+
expect[{*} Cap] {
38+
io.use()
39+
io
40+
}
41+
op
42+
}
43+
44+
val leaked: {} Unit -> Unit = withCap(badOp)
45+
leaked(())
46+
}

0 commit comments

Comments
 (0)