Skip to content

Commit 37b93cc

Browse files
committed
Also mark free identifiers referring to global symbols
1 parent 6a79065 commit 37b93cc

File tree

4 files changed

+28
-3
lines changed

4 files changed

+28
-3
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import compiletime.uninitialized
2121
import Capabilities.*
2222
import Names.Name
2323
import NameKinds.CapsetName
24+
import StdNames.nme
2425

2526
/** A class for capture sets. Capture sets can be constants or variables.
2627
* Capture sets support inclusion constraints <:< where <:< is subcapturing.
@@ -1678,6 +1679,8 @@ object CaptureSet:
16781679
// might happen during construction of lambdas, assume `{cap}` in this case so that
16791680
// `ref` will not seem subsumed by other capabilities in a `++`.
16801681
universal
1682+
case c: TermRef if isAssumedPure(c) =>
1683+
CaptureSet.empty
16811684
case c: CoreCapability =>
16821685
ofType(c.underlying, followResult = ccConfig.useSpanCapset)
16831686

@@ -1723,7 +1726,7 @@ object CaptureSet:
17231726
/** The deep capture set of a type is the union of all covariant occurrences of
17241727
* capture sets. Nested existential sets are approximated with `cap`.
17251728
*/
1726-
def ofTypeDeeply(tp: Type, includeTypevars: Boolean = false, includeBoxed: Boolean = true)(using Context): CaptureSet =
1729+
def ofTypeDeeply(tp: Type, includeTypevars: Boolean = false, includeBoxed: Boolean = true)(using Context): CaptureSet = {
17271730
val collect = new DeepTypeAccumulator[CaptureSet]:
17281731

17291732
def capturingCase(acc: CaptureSet, parent: Type, refs: CaptureSet, boxed: Boolean) =
@@ -1736,6 +1739,15 @@ object CaptureSet:
17361739
else this(acc, upperBound)
17371740

17381741
collect(CaptureSet.empty, tp)
1742+
}
1743+
1744+
/** Is `ref` assumed to be pure even though it would have a non-empty capture
1745+
* set by the normal rules?
1746+
*/
1747+
def isAssumedPure(ref: TermRef)(using Context): Boolean =
1748+
ref.name == nme.DOLLAR_VALUES // ref is an enum $values array, which for backwards
1749+
&& ref.symbol.owner.is(Module) // compatible reasons is an array, but should really be an IArray.
1750+
&& ref.symbol.owner.companionClass.is(Enum)
17391751

17401752
type AssumedContains = immutable.Map[TypeRef, SimpleIdentitySet[Capability]]
17411753
val AssumedContains: Property.Key[AssumedContains] = Property.Key()

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ class CheckCaptures extends Recheck, SymTransformer:
732732
// For fields it's not a problem since `c` would already have been
733733
// charged for the prefix `p` in `p.x`.
734734
markFree(sym.info.captureSet, tree)
735-
if sym.exists && !sym.isStatic then
735+
if sym.exists then
736736
markPathFree(sym.termRef, pt, tree)
737737
mapResultRoots(super.recheckIdent(tree, pt), tree.symbol)
738738

@@ -1269,6 +1269,7 @@ class CheckCaptures extends Recheck, SymTransformer:
12691269
&& !(sym.is(ModuleVal) && sym.owner.is(Package))
12701270
// global modules that don't derive from capability can have captures
12711271
// only if their fields have them, and then the field was already reported.
1272+
&& !CaptureSet.isAssumedPure(sym.termRef)
12721273
then
12731274
def where =
12741275
if sym.effectiveOwner.is(Package) then "top-level definition"

tests/neg-custom-args/captures/class-caps.check

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,15 @@
2828
| value console has a non-empty capture set but will not be added as
2929
| a capability to computed capture sets since it is globally accessible
3030
| as a member of static object Test2. Global values cannot be capabilities.
31+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/class-caps.scala:37:46 -----------------------------------
32+
37 | def addWritesToConsole: (Int, Int) -> Int = (a, b) => // error
33+
| ^
34+
| Found: (a: Int, b: Int) ->{Test2.console} Int
35+
| Required: (Int, Int) -> Int
36+
|
37+
| Note that capability Test2.console is not included in capture set {}.
38+
|
39+
38 | log(s"adding a ($a) to b ($b)")(using console)
40+
39 | a + b
41+
|
42+
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/class-caps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ object Test2:
3434

3535
val console: Console^ = Console() // provide capability locally
3636

37-
def addWritesToConsole: (Int, Int) -> Int = (a, b) => // ok since `console` is static (maybe flag this?)
37+
def addWritesToConsole: (Int, Int) -> Int = (a, b) => // error
3838
log(s"adding a ($a) to b ($b)")(using console)
3939
a + b

0 commit comments

Comments
 (0)