Skip to content

Commit d8a79fa

Browse files
committed
Fix the visibility check in markFree
1 parent 62e0641 commit d8a79fa

File tree

3 files changed

+49
-3
lines changed

3 files changed

+49
-3
lines changed

Diff for: compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

+26-3
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,17 @@ class CheckCaptures extends Recheck, SymTransformer:
358358
def markFree(cs: CaptureSet, pos: SrcPos)(using Context): Unit =
359359
if !cs.isAlwaysEmpty then
360360
forallOuterEnvsUpTo(ctx.owner.topLevelClass): env =>
361-
def isVisibleFromEnv(sym: Symbol) =
362-
(env.kind == EnvKind.NestedInOwner || env.owner != sym)
363-
&& env.owner.isContainedIn(sym)
361+
// Whether a symbol is defined inside the owner of the environment?
362+
inline def isContainedInEnv(sym: Symbol) =
363+
if env.kind == EnvKind.NestedInOwner then
364+
sym.isProperlyContainedIn(env.owner)
365+
else
366+
sym.isContainedIn(env.owner)
367+
// A captured reference with the symbol `sym` is visible from the environment
368+
// if `sym` is not defined inside the owner of the environment
369+
inline def isVisibleFromEnv(sym: Symbol) = !isContainedInEnv(sym)
370+
// Only captured references that are visible from the environment
371+
// should be included.
364372
val included = cs.filter:
365373
case ref: TermRef => isVisibleFromEnv(ref.symbol.owner)
366374
case ref: ThisType => isVisibleFromEnv(ref.cls)
@@ -378,6 +386,7 @@ class CheckCaptures extends Recheck, SymTransformer:
378386
// there won't be an apply; need to include call captures now
379387
includeCallCaptures(tree.symbol, tree.srcPos)
380388
else
389+
//debugShowEnvs()
381390
markFree(tree.symbol, tree.srcPos)
382391
super.recheckIdent(tree, pt)
383392

@@ -946,6 +955,19 @@ class CheckCaptures extends Recheck, SymTransformer:
946955
expected
947956
end addOuterRefs
948957

958+
/** A debugging method for showing the envrionments during capture checking. */
959+
private def debugShowEnvs()(using Context): Unit =
960+
def showEnv(env: Env): String = i"Env(${env.owner}, ${env.kind}, ${env.captured})"
961+
val sb = StringBuilder()
962+
@annotation.tailrec def walk(env: Env | Null): Unit =
963+
if env != null then
964+
sb ++= showEnv(env)
965+
sb ++= "\n"
966+
walk(env.outer0)
967+
sb ++= "===== Current Envs ======\n"
968+
walk(curEnv)
969+
sb ++= "===== End ======\n"
970+
println(sb.result())
949971

950972
/** Adapt `actual` type to `expected` type by inserting boxing and unboxing conversions
951973
*
@@ -1085,6 +1107,7 @@ class CheckCaptures extends Recheck, SymTransformer:
10851107
pos)
10861108
}
10871109
if !insertBox then // unboxing
1110+
//debugShowEnvs()
10881111
markFree(criticalSet, pos)
10891112
adaptedType(!boxed)
10901113
else

Diff for: tests/neg-custom-args/captures/i16725.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import language.experimental.captureChecking
2+
@annotation.capability
3+
class IO:
4+
def brewCoffee(): Unit = ???
5+
def usingIO[T](op: IO => T): T = ???
6+
7+
type Wrapper[T] = [R] -> (f: T => R) -> R
8+
def mk[T](x: T): Wrapper[T] = [R] => f => f(x)
9+
def useWrappedIO(wrapper: Wrapper[IO]): () -> Unit =
10+
() =>
11+
wrapper: io => // error
12+
io.brewCoffee()
13+
def main(): Unit =
14+
val escaped = usingIO(io => useWrappedIO(mk(io)))
15+
escaped() // boom

Diff for: tests/neg-custom-args/captures/i20169.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
case class Box[T](x: T):
2+
def foreach(f: T => Unit): Unit = f(x)
3+
4+
def runOps(ops: Box[() => Unit]): () -> Unit =
5+
val applyFn: (() => Unit) -> Unit = f => f()
6+
val fn: () -> Unit = () =>
7+
ops.foreach(applyFn) // error
8+
fn

0 commit comments

Comments
 (0)