Skip to content

Commit 185f177

Browse files
committed
Allow for embedded CapSet^{refs} entries in capture sets
1 parent dd4a60a commit 185f177

File tree

3 files changed

+49
-44
lines changed

3 files changed

+49
-44
lines changed

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def depFun(args: List[Type], resultType: Type, isContextual: Boolean, paramNames
6464
mt.toFunctionType(alwaysDependent = true)
6565

6666
/** An exception thrown if a @retains argument is not syntactically a CaptureRef */
67-
class IllegalCaptureRef(tpe: Type) extends Exception(tpe.toString)
67+
class IllegalCaptureRef(tpe: Type)(using Context) extends Exception(tpe.show)
6868

6969
/** Capture checking state, which is known to other capture checking components */
7070
class CCState:
@@ -127,15 +127,21 @@ class NoCommonRoot(rs: Symbol*)(using Context) extends Exception(
127127

128128
extension (tree: Tree)
129129

130-
/** Map tree with CaptureRef type to its type, throw IllegalCaptureRef otherwise */
131-
def toCaptureRef(using Context): CaptureRef = tree match
130+
/** Map tree with CaptureRef type to its type,
131+
* map CapSet^{refs} to the `refs` references,
132+
* throw IllegalCaptureRef otherwise
133+
*/
134+
def toCaptureRefs(using Context): List[CaptureRef] = tree match
132135
case ReachCapabilityApply(arg) =>
133-
arg.toCaptureRef.reach
136+
arg.toCaptureRefs.map(_.reach)
134137
case CapsOfApply(arg) =>
135-
arg.toCaptureRef
136-
case _ => tree.tpe match
138+
arg.toCaptureRefs
139+
case _ => tree.tpe.dealiasKeepAnnots match
137140
case ref: CaptureRef if ref.isTrackableRef =>
138-
ref
141+
ref :: Nil
142+
case AnnotatedType(parent, ann)
143+
if ann.symbol.isRetains && parent.derivesFrom(defn.Caps_CapSet) =>
144+
ann.tree.toCaptureSet.elems.toList
139145
case tpe =>
140146
throw IllegalCaptureRef(tpe) // if this was compiled from cc syntax, problem should have been reported at Typer
141147

@@ -146,7 +152,7 @@ extension (tree: Tree)
146152
tree.getAttachment(Captures) match
147153
case Some(refs) => refs
148154
case None =>
149-
val refs = CaptureSet(tree.retainedElems.map(_.toCaptureRef)*)
155+
val refs = CaptureSet(tree.retainedElems.flatMap(_.toCaptureRefs)*)
150156
//.showing(i"toCaptureSet $tree --> $result", capt)
151157
tree.putAttachment(Captures, refs)
152158
refs

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

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -729,25 +729,28 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
729729
var retained = ann.retainedElems.toArray
730730
for i <- 0 until retained.length do
731731
val refTree = retained(i)
732-
val ref = refTree.toCaptureRef
733-
734-
def pos =
735-
if refTree.span.exists then refTree.srcPos
736-
else if ann.span.exists then ann.srcPos
737-
else tpt.srcPos
738-
739-
def check(others: CaptureSet, dom: Type | CaptureSet): Unit =
740-
if others.accountsFor(ref) then
741-
report.warning(em"redundant capture: $dom already accounts for $ref", pos)
742-
743-
if ref.captureSetOfInfo.elems.isEmpty && !ref.derivesFrom(defn.Caps_Capability) then
744-
report.error(em"$ref cannot be tracked since its capture set is empty", pos)
745-
check(parent.captureSet, parent)
746-
747-
val others =
748-
for j <- 0 until retained.length if j != i yield retained(j).toCaptureRef
749-
val remaining = CaptureSet(others*)
750-
check(remaining, remaining)
732+
for ref <- refTree.toCaptureRefs do
733+
def pos =
734+
if refTree.span.exists then refTree.srcPos
735+
else if ann.span.exists then ann.srcPos
736+
else tpt.srcPos
737+
738+
def check(others: CaptureSet, dom: Type | CaptureSet): Unit =
739+
if others.accountsFor(ref) then
740+
report.warning(em"redundant capture: $dom already accounts for $ref", pos)
741+
742+
if ref.captureSetOfInfo.elems.isEmpty && !ref.derivesFrom(defn.Caps_Capability) then
743+
report.error(em"$ref cannot be tracked since its capture set is empty", pos)
744+
check(parent.captureSet, parent)
745+
746+
val others =
747+
for
748+
j <- 0 until retained.length if j != i
749+
r <- retained(j).toCaptureRefs
750+
yield r
751+
val remaining = CaptureSet(others*)
752+
check(remaining, remaining)
753+
end for
751754
end for
752755
end checkWellformedPost
753756

tests/pos/cc-poly-source-capability.scala

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import caps.{CapSet, Capability}
44

55
@experimental object Test:
66

7-
class Label extends Capability
7+
class Async extends Capability
8+
9+
def listener(async: Async): Listener^{async} = ???
810

911
class Listener
1012

@@ -15,22 +17,16 @@ import caps.{CapSet, Capability}
1517

1618
def allListeners: Set[Listener^{X^}] = listeners
1719

18-
def test1(lbl1: Label, lbl2: Label) =
19-
val src = Source[CapSet^{lbl1, lbl2}]
20-
def l1: Listener^{lbl1} = ???
21-
val l2: Listener^{lbl2} = ???
22-
src.register{l1}
23-
src.register{l2}
24-
val ls = src.allListeners
25-
val _: Set[Listener^{lbl1, lbl2}] = ls
26-
27-
def test2(lbls: List[Label]) =
28-
def makeListener(lbl: Label): Listener^{lbl} = ???
29-
val listeners = lbls.map(makeListener)
30-
val src = Source[CapSet^{lbls*}]
31-
for l <- listeners do
32-
src.register(l)
20+
def test1(async1: Async, others: List[Async]) =
21+
val src = Source[CapSet^{async1, others*}]
22+
val lst1 = listener(async1)
23+
val lsts = others.map(listener)
24+
val _: List[Listener^{others*}] = lsts
25+
src.register{lst1}
26+
src.register(listener(async1))
27+
lsts.foreach(src.register)
28+
others.map(listener).foreach(src.register)
3329
val ls = src.allListeners
34-
val _: Set[Listener^{lbls*}] = ls
30+
val _: Set[Listener^{async1, others*}] = ls
3531

3632

0 commit comments

Comments
 (0)