Skip to content

Commit b9d2efc

Browse files
committed
Require explicit types also for static capturing definitions
Require explicit types also for static definitions that have capture sets in their (result-) types.
1 parent f78648b commit b9d2efc

File tree

6 files changed

+82
-9
lines changed

6 files changed

+82
-9
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,7 +1679,7 @@ object CaptureSet:
16791679
// might happen during construction of lambdas, assume `{cap}` in this case so that
16801680
// `ref` will not seem subsumed by other capabilities in a `++`.
16811681
universal
1682-
case c: TermRef if isAssumedPure(c) =>
1682+
case c: TermRef if isAssumedPure(c.symbol) =>
16831683
CaptureSet.empty
16841684
case c: CoreCapability =>
16851685
ofType(c.underlying, followResult = ccConfig.useSpanCapset)
@@ -1741,13 +1741,13 @@ object CaptureSet:
17411741
collect(CaptureSet.empty, tp)
17421742
}
17431743

1744-
/** Is `ref` assumed to be pure even though it would have a non-empty capture
1744+
/** Is `sym` assumed to be pure even though it would have a non-empty capture
17451745
* set by the normal rules?
17461746
*/
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)
1747+
def isAssumedPure(sym: Symbol)(using Context): Boolean =
1748+
sym.name == nme.DOLLAR_VALUES // sym is an enum $values array, which for backwards
1749+
&& sym.owner.is(Module) // compatible reasons is an array, but should really be an IArray.
1750+
&& sym.owner.companionClass.is(Enum)
17511751

17521752
type AssumedContains = immutable.Map[TypeRef, SimpleIdentitySet[Capability]]
17531753
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
@@ -1385,8 +1385,9 @@ class CheckCaptures extends Recheck, SymTransformer:
13851385
// does not interfere with normal rechecking by constraining capture set variables.
13861386
}
13871387
// Test point (2) of doc comment above
1388-
if sym.owner.isClass && !sym.owner.isStaticOwner
1388+
if sym.owner.isClass
13891389
&& contributesFreshToClass(sym)
1390+
&& !CaptureSet.isAssumedPure(sym)
13901391
then
13911392
todoAtPostCheck += { () =>
13921393
val cls = sym.owner.asClass

tests/neg-custom-args/captures/indirect-avoid.check

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,11 @@
2727
| Note that capability nll is not included in capture set {ll}.
2828
|
2929
| longer explanation available when compiling with `-explain`
30+
-- Error: tests/neg-custom-args/captures/indirect-avoid.scala:24:8 -----------------------------------------------------
31+
24 | val x1 = filterImpl1(LL()) // error
32+
| ^
33+
|value x1 needs an explicit type because it captures a root capability in its type Test.LL^{cap.rd}.
34+
|Fields capturing a root capability need to be given an explicit type unless the capability is already
35+
|subsumed by the computed capability of the enclosing class.
36+
|
37+
|where: cap is a fresh root capability created in value x1 when instantiating method filterImpl1's type (ll: Test.LL^): Test.LL^{cap².rd, ll}

tests/neg-custom-args/captures/indirect-avoid.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object Test:
2121
val nll: LL^{cl} = ???
2222
nll
2323

24-
val x1 = filterImpl1(LL())
24+
val x1 = filterImpl1(LL()) // error
2525
val _: LL^{cap.rd}= x1
2626
val _: LL = x1 // error
2727

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/withFile.scala:14:38 -------------------------------------
2+
14 | private val later1 = usingLogFile { f => () => f.write() } // error
3+
| ^^^^^^^^^^^^^^^^^^^^
4+
|Capability f outlives its scope: it leaks into outer capture set 's1 which is owned by value later1.
5+
|The leakage occurred when trying to match the following types:
6+
|
7+
|Found: (f: Test2.File^'s2) ->'s3 () ->{f} Unit
8+
|Required: Test2.File^ => () ->'s1 Unit
9+
|
10+
|where: => refers to a fresh root capability created in value later1 when checking argument to parameter op of method usingLogFile
11+
| ^ refers to the universal root capability
12+
|
13+
| longer explanation available when compiling with `-explain`
14+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/withFile.scala:15:38 -------------------------------------
15+
15 | private val later2 = usingLogFile { f => Box(f) } // error
16+
| ^^^^^^^^^^^
17+
|Capability cap outlives its scope: it leaks into outer capture set 's4 which is owned by value later2.
18+
|The leakage occurred when trying to match the following types:
19+
|
20+
|Found: (f: Test2.File^'s5) ->'s6 Test2.Box[Test2.File^'s7]^'s8
21+
|Required: Test2.File^ => Test2.Box[Test2.File^'s4]^'s9
22+
|
23+
|where: => refers to a fresh root capability created in value later2 when checking argument to parameter op of method usingLogFile
24+
| ^ refers to the universal root capability
25+
| cap is a root capability associated with the result type of (f: Test2.File^): Test2.Box[Test2.File^'s4]^'s9
26+
|
27+
| longer explanation available when compiling with `-explain`
28+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/withFile.scala:17:4 --------------------------------------
29+
17 | f => () => f.write() // error
30+
| ^^^^^^^^^^^^^^^^^^^^
31+
|Found: (f: Test2.File^'s10) ->'s11 () ->{f} Unit
32+
|Required: Test2.File^ => () =>² Unit
33+
|
34+
|Note that capability f is not included in capture set {cap}
35+
|because (f : Test2.File^'s10) is not visible from cap in value later3.
36+
|
37+
|where: => refers to a fresh root capability created in value later3 when checking argument to parameter op of method usingLogFile
38+
| =>² and cap refer to a fresh root capability created in value later3
39+
| ^ refers to the universal root capability
40+
|
41+
| longer explanation available when compiling with `-explain`
42+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/withFile.scala:19:4 --------------------------------------
43+
19 | f => Box(f) // error
44+
| ^^^^^^^^^^^
45+
|Found: (f: Test2.File^'s12) ->'s13 Test2.Box[Test2.File^'s14]^'s15
46+
|Required: Test2.File^ => Test2.Box[Test2.File^²]
47+
|
48+
|Note that capability cap is not included in capture set {cap²}
49+
|because cap is not visible from cap² in value later4.
50+
|
51+
|where: => refers to a fresh root capability created in value later4 when checking argument to parameter op of method usingLogFile
52+
| ^ refers to the universal root capability
53+
| ^² and cap² refer to a fresh root capability created in value later4
54+
| cap is a root capability associated with the result type of (f: Test2.File^'s12): Test2.Box[Test2.File^'s14]^'s15
55+
|
56+
| longer explanation available when compiling with `-explain`
57+
-- Error: tests/neg-custom-args/captures/withFile.scala:16:20 ----------------------------------------------------------
58+
16 | private val later3 = usingLogFile[() => Unit]: // error
59+
| ^
60+
| value later3 needs an explicit type because it captures a root capability in its type () => Unit.
61+
| Fields capturing a root capability need to be given an explicit type unless the capability is already
62+
| subsumed by the computed capability of the enclosing class.
63+
|
64+
| where: => refers to a fresh root capability in the type of value later3

tests/neg-custom-args/captures/withFile.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object Test2:
1313

1414
private val later1 = usingLogFile { f => () => f.write() } // error
1515
private val later2 = usingLogFile { f => Box(f) } // error
16-
private val later3 = usingLogFile[() => Unit]:
16+
private val later3 = usingLogFile[() => Unit]: // error
1717
f => () => f.write() // error
1818
private val later4 = usingLogFile[Box[File^]]:
1919
f => Box(f) // error

0 commit comments

Comments
 (0)