diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 1ecf42a90aaa..844857b609b5 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -507,14 +507,20 @@ class CheckCaptures extends Recheck, SymTransformer: recheckFinish(result, arg, pt) */ - /** If expected type `pt` is boxed, don't propagate free variables. + /** If expected type `pt` is boxed and the tree is a function or a reference, + * don't propagate free variables. * Otherwise, if the result type is boxed, simulate an unboxing by * adding all references in the boxed capture set to the current environment. */ override def recheck(tree: Tree, pt: Type = WildcardType)(using Context): Type = if tree.isTerm && pt.isBoxedCapturing then val saved = curEnv - curEnv = Env(curEnv.owner, nestedInOwner = false, CaptureSet.Var(), isBoxed = true, curEnv) + + tree match + case _: RefTree | closureDef(_) => + curEnv = Env(curEnv.owner, nestedInOwner = false, CaptureSet.Var(), isBoxed = true, curEnv) + case _ => + try super.recheck(tree, pt) finally curEnv = saved else diff --git a/tests/neg-custom-args/captures/i16114.scala b/tests/neg-custom-args/captures/i16114.scala new file mode 100644 index 000000000000..cc491226f9df --- /dev/null +++ b/tests/neg-custom-args/captures/i16114.scala @@ -0,0 +1,46 @@ +trait Cap { def use(): Int; def close(): Unit } +def mkCap(): {*} Cap = ??? + +def expect[T](x: T): x.type = x + +def withCap[T](op: ({*} Cap) => T): T = { + val cap: {*} Cap = mkCap() + val result = op(cap) + cap.close() + result +} + +def main(fs: {*} Cap): Unit = { + def badOp(io: {*} Cap): {} Unit -> Unit = { + val op1: {io} Unit -> Unit = (x: Unit) => // error // limitation + expect[{*} Cap] { + io.use() + fs + } + + val op2: {fs} Unit -> Unit = (x: Unit) => // error // limitation + expect[{*} Cap] { + fs.use() + io + } + + val op3: {io} Unit -> Unit = (x: Unit) => // ok + expect[{*} Cap] { + io.use() + io + } + + val op4: {} Unit -> Unit = (x: Unit) => // ok + expect[{*} Cap](io) + + val op: {} Unit -> Unit = (x: Unit) => // error + expect[{*} Cap] { + io.use() + io + } + op + } + + val leaked: {} Unit -> Unit = withCap(badOp) + leaked(()) +} diff --git a/tests/pos-custom-args/captures/boxmap.scala b/tests/pos-custom-args/captures/boxmap.scala index 5642763b5511..7bd58df80235 100644 --- a/tests/pos-custom-args/captures/boxmap.scala +++ b/tests/pos-custom-args/captures/boxmap.scala @@ -9,7 +9,7 @@ def box[T <: Top](x: T): Box[T] = def map[A <: Top, B <: Top](b: Box[A])(f: A => B): Box[B] = b[Box[B]]((x: A) => box(f(x))) -def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B): (() -> Box[B]) = +def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B): {f} (() -> Box[B]) = () => b[Box[B]]((x: A) => box(f(x))) def test[A <: Top, B <: Top] =