Skip to content

Commit 03a29ca

Browse files
committed
Include call captures for parameterless methods
1 parent e19328c commit 03a29ca

File tree

8 files changed

+33
-28
lines changed

8 files changed

+33
-28
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,8 +1513,8 @@ object Types {
15131513
}
15141514

15151515
def captureSet(using Context): CaptureSet = CaptureSet.ofType(this)
1516-
def noCaptures(using Context): Boolean =
1517-
ctx.mode.is(Mode.RelaxedCapturing) || allCaptures.isEmpty
1516+
def noCaptures(using Context): Boolean = // ^^^ drop
1517+
ctx.mode.is(Mode.RelaxedCapturing) || !ctx.settings.Ycc.value || allCaptures.isEmpty
15181518

15191519
def allCaptures(using Context): CaptureSet = this match // ^^^^ optimize, relate with boxedCaptures?
15201520
case tp: CapturingType => tp.refs
@@ -2709,7 +2709,7 @@ object Types {
27092709
* References to term parameters of classes cannot be tracked individually.
27102710
* They are subsumed in the capture sets of the enclosing class.
27112711
*/
2712-
def canBeTracked(using Context) =
2712+
def canBeTracked(using Context) = // ^^^ exclude methods
27132713
(prefix eq NoPrefix) || symbol.hasAnnotation(defn.AbilityAnnot) || isRootCapability
27142714

27152715
override def isRootCapability(using Context): Boolean =

compiler/src/dotty/tools/dotc/transform/Recheck.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ abstract class Recheck extends Phase, IdentityDenotTransformer:
5959
def reinferResult(info: Type)(using Context): Type = info match
6060
case info: MethodOrPoly =>
6161
info.derivedLambdaType(resType = reinferResult(info.resType))
62+
case info: ExprType =>
63+
info.derivedExprType(resType = reinferResult(info.resType))
6264
case _ =>
6365
reinfer(info)
6466

@@ -317,7 +319,7 @@ abstract class Recheck extends Phase, IdentityDenotTransformer:
317319
|| expected.isRepeatedParam
318320
&& actual <:< expected.translateFromRepeated(toArray = tree.tpe.isRef(defn.ArrayClass))
319321
if !isCompatible then
320-
err.typeMismatch(tree.withType(tpe), pt)
322+
err.typeMismatch(tree.withType(tpe), expected)
321323

322324
def checkUnit(unit: CompilationUnit)(using Context): Unit =
323325
recheck(unit.tpdTree)

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

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,22 @@ class CheckCaptures extends Recheck:
9595
val ref = sym.termRef
9696
def recur(env: Env): Unit =
9797
if env.isOpen && env.owner != sym.enclosure then
98-
capt.println(i"Mark $sym free in ${env.owner}")
98+
capt.println(i"Mark $sym with cs ${ref.captureSet} free in ${env.owner}")
9999
checkElem(ref, env.captured, pos)
100100
recur(env.outer)
101101
if ref.isTracked then recur(curEnv)
102102

103+
def includeCallCaptures(sym: Symbol, pos: SrcPos)(using Context): Unit =
104+
if curEnv.isOpen then
105+
val ownEnclosure = ctx.owner.enclosingMethodOrClass
106+
var targetSet = capturedVars(sym)
107+
if !targetSet.isEmpty && sym.enclosure == ownEnclosure then
108+
targetSet = targetSet.filter {
109+
case ref: TermRef => ref.symbol.enclosure != ownEnclosure
110+
case _ => true
111+
}
112+
checkSubset(targetSet, curEnv.captured, pos)
113+
103114
def assertSub(cs1: CaptureSet, cs2: CaptureSet)(using Context) =
104115
assert((cs1 <:< cs2) == CompareResult.OK, i"$cs1 is not a subset of $cs2")
105116

@@ -121,6 +132,7 @@ class CheckCaptures extends Recheck:
121132

122133
override def recheckIdent(tree: Ident)(using Context): Type =
123134
markFree(tree.symbol, tree.srcPos)
135+
if tree.symbol.is(Method) then includeCallCaptures(tree.symbol, tree.srcPos)
124136
super.recheckIdent(tree)
125137

126138
override def recheckDefDef(tree: DefDef, sym: Symbol)(using Context): Type =
@@ -138,17 +150,8 @@ class CheckCaptures extends Recheck:
138150
finally curEnv = saved
139151

140152
override def recheckApply(tree: Apply, pt: Type)(using Context): Type =
141-
if curEnv.isOpen then
142-
val ownEnclosure = ctx.owner.enclosingMethodOrClass
143-
var targetSet = capturedVars(tree.symbol)
144-
if !targetSet.isEmpty && tree.symbol.enclosure == ownEnclosure then
145-
targetSet = targetSet.filter {
146-
case ref: TermRef => ref.symbol.enclosure != ownEnclosure
147-
case _ => true
148-
}
149-
150-
checkSubset(targetSet, curEnv.captured, tree.srcPos)
151153
val sym = tree.symbol
154+
includeCallCaptures(sym, tree.srcPos)
152155
val cs = if sym.isConstructor then capturedVars(sym.owner) else CaptureSet.empty
153156
super.recheckApply(tree, pt).capturing(cs)
154157

tests/neg-custom-args/captures/capt-wf.scala renamed to tests/disabled/neg-custom-args/captures/capt-wf.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// No longer valid
12
class C
23
type Cap = C retains *
34
type Top = Any retains *
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boxmap.scala:15:2 ----------------------------------------
2-
15 | () => b[Box[B]]((x: A) => box(f(x))) // error
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boxmap.scala:14:5 ----------------------------------------
2+
14 | () => b[Box[B]]((x: A) => box(f(x))) // error
33
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4-
| Found: {f} {b} () => Box[B]
5-
| Required: {B} () => Box[B]
6-
|
7-
| where: B is a type in method lazymap with bounds <: Top
4+
| Found: {f} () => Box[B]
5+
| Required: () => Box[B]
86

97
longer explanation available when compiling with `-explain`

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
type Top = Any retains *
2-
class Cap extends Retains[*]
32

43
infix type ==> [A, B] = (A => B) retains *
54

6-
type Box[+T <: Top] = ([K <: Top] => (T ==> K) => K) retains T
5+
type Box[+T <: Top] = ([K <: Top] => (T ==> K) => K)
76

87
def box[T <: Top](x: T): Box[T] =
98
[K <: Top] => (k: T ==> K) => k(x)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sealed trait IO:
33

44
def test1 =
55
val IO : IO retains * = new IO {}
6-
def foo = IO.puts("hello")
6+
def foo = {IO; IO.puts("hello") }
77
val x : () => Unit = () => foo // error: Found: (() => Unit) retains IO; Required: () => Unit
88

99
def test2 =
@@ -19,3 +19,4 @@ def test3 =
1919
def puts(msg: Any, io: Capability[IO]) = println(msg)
2020
def foo() = puts("hello", IO)
2121
val x : () => Unit = () => foo() // error: Found: (() => Unit) retains IO; Required: () => Unit
22+

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import java.io.IOException
22

3-
class CanThrow[E] extends Retains[*]
4-
type Top = Any retains *
3+
class CT[E]
4+
type CanThrow[E] = {*} CT[E]
5+
type Top = {*} Any
56

67
def handle[E <: Exception, T <: Top](op: CanThrow[E] ?=> T)(handler: E => T): T =
78
val x: CanThrow[E] = ???
@@ -13,9 +14,9 @@ def raise[E <: Exception](ex: E)(using CanThrow[E]): Nothing =
1314

1415
@main def Test: Int =
1516
def f(a: Boolean) =
16-
handle { // error
17+
handle {
1718
if !a then raise(IOException())
18-
(b: Boolean) =>
19+
(b: Boolean) => // error
1920
if !b then raise(IOException())
2021
0
2122
} {

0 commit comments

Comments
 (0)