Skip to content

Commit ee60b21

Browse files
committed
Fix compute overlap logic for consumes
- Make it peak-based instead of footprint-based - Don't flag rd/rd conflicts between consumed and re-used
1 parent f547582 commit ee60b21

File tree

3 files changed

+32
-21
lines changed

3 files changed

+32
-21
lines changed

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

+21-4
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ object SepCheck:
9797
var refs: Array[CaptureRef] = new Array(4)
9898
var locs: Array[SrcPos] = new Array(4)
9999
var size = 0
100+
var peaks: Refs = emptyRefs
100101

101102
private def double[T <: AnyRef : ClassTag](xs: Array[T]): Array[T] =
102103
val xs1 = new Array[T](xs.length * 2)
@@ -114,31 +115,44 @@ object SepCheck:
114115
while i < size && (refs(i) ne ref) do i += 1
115116
if i < size then locs(i) else null
116117

118+
def clashing(ref: CaptureRef)(using Context): SrcPos | Null =
119+
val refPeaks = ref.peaks
120+
if !peaks.sharedWith(refPeaks).isEmpty then
121+
var i = 0
122+
while i < size && refs(i).peaks.sharedWith(refPeaks).isEmpty do
123+
i += 1
124+
assert(i < size)
125+
locs(i)
126+
else null
127+
117128
/** If `ref` is not yet in the set, add it with given source position */
118-
def put(ref: CaptureRef, loc: SrcPos): Unit =
129+
def put(ref: CaptureRef, loc: SrcPos)(using Context): Unit =
119130
if get(ref) == null then
120131
ensureCapacity(1)
121132
refs(size) = ref
122133
locs(size) = loc
123134
size += 1
135+
peaks = peaks ++ ref.peaks
124136

125137
/** Add all references with their associated positions from `that` which
126138
* are not yet in the set.
127139
*/
128-
def ++= (that: ConsumedSet): Unit =
140+
def ++= (that: ConsumedSet)(using Context): Unit =
129141
for i <- 0 until that.size do put(that.refs(i), that.locs(i))
130142

131143
/** Run `op` and return any new references it created in a separate `ConsumedSet`.
132144
* The current mutable set is reset to its state before `op` was run.
133145
*/
134146
def segment(op: => Unit): ConsumedSet =
135147
val start = size
148+
val savedPeaks = peaks
136149
try
137150
op
138151
if size == start then EmptyConsumedSet
139152
else ConstConsumedSet(refs.slice(start, size), locs.slice(start, size))
140153
finally
141154
size = start
155+
peaks = savedPeaks
142156
end MutConsumedSet
143157

144158
val EmptyConsumedSet = ConstConsumedSet(Array(), Array())
@@ -265,6 +279,9 @@ object SepCheck:
265279

266280
end extension
267281

282+
extension (ref: CaptureRef)
283+
def peaks(using Context): Refs = SimpleIdentitySet(ref).peaks
284+
268285
class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
269286
import checker.*
270287
import SepCheck.*
@@ -553,7 +570,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
553570
sepUseError(tree, null, used, defsShadow)
554571

555572
for ref <- used do
556-
val pos = consumed.get(ref.stripReadOnly)
573+
val pos = consumed.clashing(ref)
557574
if pos != null then consumeError(ref, pos, tree.srcPos)
558575
end checkUse
559576

@@ -632,7 +649,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
632649
case _: TypeRole.Argument | _: TypeRole.Qualifier =>
633650
for ref <- refsToCheck do
634651
if !ref.derivesFromSharedCapability then
635-
consumed.put(ref.stripReadOnly, pos)
652+
consumed.put(ref, pos)
636653
case _ =>
637654
end checkConsumedRefs
638655

tests/neg-custom-args/captures/sep-consume.check

+7-13
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,25 @@
44
| Separation failure: Illegal access to (x : Ref^), which was passed to a
55
| @consume parameter or was used as a prefix to a @consume method on line 18
66
| and therefore is no longer available.
7-
-- Error: tests/neg-custom-args/captures/sep-consume.scala:20:2 --------------------------------------------------------
8-
20 | x.get // error
9-
| ^
10-
| Separation failure: Illegal access to x.rd, which was passed to a
11-
| @consume parameter or was used as a prefix to a @consume method on line 18
12-
| and therefore is no longer available.
137
-- Error: tests/neg-custom-args/captures/sep-consume.scala:21:16 -------------------------------------------------------
148
21 | par(rx, () => x.put(42)) // error
159
| ^
1610
| Separation failure: Illegal access to (x : Ref^), which was passed to a
1711
| @consume parameter or was used as a prefix to a @consume method on line 18
1812
| and therefore is no longer available.
19-
-- Error: tests/neg-custom-args/captures/sep-consume.scala:22:16 -------------------------------------------------------
20-
22 | par(rx, () => x.get) // error
21-
| ^
22-
| Separation failure: Illegal access to x.rd, which was passed to a
23-
| @consume parameter or was used as a prefix to a @consume method on line 18
24-
| and therefore is no longer available.
2513
-- Error: tests/neg-custom-args/captures/sep-consume.scala:26:16 -------------------------------------------------------
2614
26 | def foo = bad(f) // error
2715
| ^
2816
| Separation failure: argument to @consume parameter with type (f : () ->{x.rd} Unit) refers to non-local value f
2917
-- Error: tests/neg-custom-args/captures/sep-consume.scala:34:12 -------------------------------------------------------
30-
34 | println(p.fst.get) // errorSep
18+
34 | println(p.fst.get) // error
3119
| ^^^^^
3220
| Separation failure: Illegal access to p.fst*, which was passed to a
3321
| @consume parameter or was used as a prefix to a @consume method on line 33
3422
| and therefore is no longer available.
23+
-- Error: tests/neg-custom-args/captures/sep-consume.scala:40:12 -------------------------------------------------------
24+
40 | println(p.fst.get) // error
25+
| ^^^^^
26+
| Separation failure: Illegal access to p.fst*, which was passed to a
27+
| @consume parameter or was used as a prefix to a @consume method on line 39
28+
| and therefore is no longer available.

tests/neg-custom-args/captures/sep-consume.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ def test2(@consume x: Ref^): Unit =
1717
val f: () ->{x.rd} Unit = () => x.get
1818
val rx: () => Unit = bad(f) // hides x.rd in the resulting `cap`
1919
x.put(42) // error
20-
x.get // error
20+
x.get // ok rd/rd
2121
par(rx, () => x.put(42)) // error
22-
par(rx, () => x.get) // error
22+
par(rx, () => x.get) // ok rd/rd
2323

2424
def test3(@consume x: Ref^): Unit =
2525
val f: () ->{x.rd} Unit = () => x.get
@@ -31,11 +31,11 @@ def test4(@consume @use p: Pair[Ref^, Ref^]): Unit =
3131
val x: Ref^{p.fst*} = p.fst
3232
val y: Ref^{p.snd*} = p.snd
3333
badp(Pair(x, y))
34-
println(p.fst.get) // errorSep
34+
println(p.fst.get) // error
3535

3636
def badp(@consume p: Pair[Ref^, Ref^]): Unit = ()
3737

3838
def test5(@consume @use p: Pair[Ref^, Ref^]): Unit =
3939
badp(p) // ok
40-
println(p.fst.get) // ok, but should be error
40+
println(p.fst.get) // error
4141

0 commit comments

Comments
 (0)