Skip to content

Commit f77436b

Browse files
committed
only create boxed environment for function values and references
1 parent fff8c0d commit f77436b

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
@@ -495,14 +495,28 @@ class CheckCaptures extends Recheck, SymTransformer:
495495
recheckFinish(result, arg, pt)
496496
*/
497497

498+
extension (tree: Tree)
499+
private def isFunctionLiteral(using Context): Boolean = tree match
500+
case Block((defTree @ DefDef(_, _, _, _)) :: Nil, Closure(_, meth, _)) =>
501+
val defSym = defTree.symbol
502+
val methSym = meth.symbol
503+
defSym.eq(methSym)
504+
case _ =>
505+
false
506+
507+
private def isIdent: Boolean = tree match
508+
case Ident(_) => true
509+
case _ => false
510+
498511
/** If expected type `pt` is boxed, don't propagate free variables.
499512
* Otherwise, if the result type is boxed, simulate an unboxing by
500513
* adding all references in the boxed capture set to the current environment.
501514
*/
502515
override def recheck(tree: Tree, pt: Type = WildcardType)(using Context): Type =
503516
if tree.isTerm && pt.isBoxedCapturing then
504517
val saved = curEnv
505-
curEnv = Env(curEnv.owner, CaptureSet.Var(), isBoxed = true, curEnv)
518+
if tree.isIdent || tree.isFunctionLiteral then
519+
curEnv = Env(curEnv.owner, CaptureSet.Var(), isBoxed = true, curEnv)
506520
try super.recheck(tree, pt)
507521
finally curEnv = saved
508522
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)