Skip to content

Commit 0c6744d

Browse files
authored
Create boxed environments only for references and function values (#16136)
Fixes #16114. According to the [comment](#16114 (comment)) in the issue, only create boxed environment when the rechecked tree is a reference or a function value. This PR also fixes a testcase, as explained in this [commit](25daaa8).
2 parents 96d4ccd + a25677e commit 0c6744d

File tree

3 files changed

+55
-3
lines changed

3 files changed

+55
-3
lines changed

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

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

510-
/** If expected type `pt` is boxed, don't propagate free variables.
510+
/** If expected type `pt` is boxed and the tree is a function or a reference,
511+
* don't propagate free variables.
511512
* Otherwise, if the result type is boxed, simulate an unboxing by
512513
* adding all references in the boxed capture set to the current environment.
513514
*/
514515
override def recheck(tree: Tree, pt: Type = WildcardType)(using Context): Type =
515516
if tree.isTerm && pt.isBoxedCapturing then
516517
val saved = curEnv
517-
curEnv = Env(curEnv.owner, nestedInOwner = false, CaptureSet.Var(), isBoxed = true, curEnv)
518+
519+
tree match
520+
case _: RefTree | closureDef(_) =>
521+
curEnv = Env(curEnv.owner, nestedInOwner = false, CaptureSet.Var(), isBoxed = true, curEnv)
522+
case _ =>
523+
518524
try super.recheck(tree, pt)
519525
finally curEnv = saved
520526
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+
}

tests/pos-custom-args/captures/boxmap.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def box[T <: Top](x: T): Box[T] =
99
def map[A <: Top, B <: Top](b: Box[A])(f: A => B): Box[B] =
1010
b[Box[B]]((x: A) => box(f(x)))
1111

12-
def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B): (() -> Box[B]) =
12+
def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B): {f} (() -> Box[B]) =
1313
() => b[Box[B]]((x: A) => box(f(x)))
1414

1515
def test[A <: Top, B <: Top] =

0 commit comments

Comments
 (0)