Skip to content

Commit

Permalink
Handle local type parameters in markFree
Browse files Browse the repository at this point in the history
These need to be handled like reach capabilities.

Fixes scala#21347
  • Loading branch information
odersky committed Aug 7, 2024
1 parent 02b1b6d commit 618bbc5
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 14 deletions.
30 changes: 16 additions & 14 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -388,23 +388,25 @@ class CheckCaptures extends Recheck, SymTransformer:
// should be included.
val included = cs.filter: c =>
c.stripReach match
case ref: TermRef =>
//if c.isReach then println(i"REACH $c in ${env.owner}")
//assert(!env.owner.isAnonymousFunction)
case ref: NamedType =>
val refSym = ref.symbol
val refOwner = refSym.owner
val isVisible = isVisibleFromEnv(refOwner)
if !isVisible && c.isReach && refSym.is(Param) && refOwner == env.owner then
if refSym.hasAnnotation(defn.UnboxAnnot) then
capt.println(i"exempt: $ref in $refOwner")
else
// Reach capabilities that go out of scope have to be approximated
// by their underlying capture set, which cannot be universal.
// Reach capabilities of @unboxed parameters are exempted.
val cs = CaptureSet.ofInfo(c)
cs.disallowRootCapability: () =>
report.error(em"Local reach capability $c leaks into capture scope of ${env.ownerString}", pos)
checkSubset(cs, env.captured, pos, provenance(env))
if !isVisible
&& (c.isReach || ref.isType)
&& refSym.is(Param)
&& refOwner == env.owner
then
if refSym.hasAnnotation(defn.UnboxAnnot) then
capt.println(i"exempt: $ref in $refOwner")
else
// Reach capabilities that go out of scope have to be approximated
// by their underlying capture set, which cannot be universal.
// Reach capabilities of @unboxed parameters are exempted.
val cs = CaptureSet.ofInfo(c)
cs.disallowRootCapability: () =>
report.error(em"Local reach capability $c leaks into capture scope of ${env.ownerString}", pos)
checkSubset(cs, env.captured, pos, provenance(env))
isVisible
case ref: ThisType => isVisibleFromEnv(ref.cls)
case _ => false
Expand Down
15 changes: 15 additions & 0 deletions tests/neg-custom-args/captures/i21347.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- Error: tests/neg-custom-args/captures/i21347.scala:4:15 -------------------------------------------------------------
4 | ops.foreach: op => // error
| ^
| Local reach capability C leaks into capture scope of method runOps
5 | op()
-- Error: tests/neg-custom-args/captures/i21347.scala:8:14 -------------------------------------------------------------
8 | () => runOps(f :: Nil) // error
| ^^^^^^^^^^^^^^^^
| reference (caps.cap : caps.Capability) is not included in the allowed capture set {}
| of an enclosing function literal with expected type () -> Unit
-- Error: tests/neg-custom-args/captures/i21347.scala:11:15 ------------------------------------------------------------
11 | ops.foreach: op => // error
| ^
| Local reach capability ops* leaks into capture scope of method runOpsAlt
12 | op()
12 changes: 12 additions & 0 deletions tests/neg-custom-args/captures/i21347.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import language.experimental.captureChecking

def runOps[C^](ops: List[() ->{C^} Unit]): Unit =
ops.foreach: op => // error
op()

def boom(f: () => Unit): () -> Unit =
() => runOps(f :: Nil) // error

def runOpsAlt(ops: List[() => Unit]): Unit =
ops.foreach: op => // error
op()

0 comments on commit 618bbc5

Please sign in to comment.