Skip to content

Commit 9d7fd0e

Browse files
committed
Change retains annotation from using term arguments to using type arguments
1 parent a5a7644 commit 9d7fd0e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+366
-341
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2261,9 +2261,9 @@ object desugar {
22612261
AppliedTypeTree(ref(defn.SeqType), t),
22622262
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil))
22632263
else if op.name == nme.CC_REACH then
2264-
Apply(ref(defn.Caps_reachCapability), t :: Nil)
2264+
Annotated(t, New(ref(defn.ReachCapabilityAnnot.typeRef), Nil :: Nil))
22652265
else if op.name == nme.CC_READONLY then
2266-
Apply(ref(defn.Caps_readOnlyCapability), t :: Nil)
2266+
Annotated(t, New(ref(defn.ReadOnlyCapabilityAnnot.typeRef), Nil :: Nil))
22672267
else
22682268
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
22692269
Select(t, op.name)

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
386386
/** Property key for contextual Apply trees of the form `fn given arg` */
387387
val KindOfApply: Property.StickyKey[ApplyKind] = Property.StickyKey()
388388

389+
val RetainsAnnot: Property.StickyKey[Unit] = Property.StickyKey()
390+
389391
// ------ Creation methods for untyped only -----------------
390392

391393
def Ident(name: Name)(implicit src: SourceFile): Ident = new Ident(name)
@@ -528,10 +530,16 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
528530
Select(scalaDot(nme.caps), nme.CAPTURE_ROOT)
529531

530532
def makeRetaining(parent: Tree, refs: List[Tree], annotName: TypeName)(using Context): Annotated =
531-
Annotated(parent, New(scalaAnnotationDot(annotName), List(refs)))
532-
533-
def makeCapsOf(tp: RefTree)(using Context): Tree =
534-
TypeApply(capsInternalDot(nme.capsOf), tp :: Nil)
533+
var annot: Tree = scalaAnnotationDot(annotName)
534+
if annotName == tpnme.retainsCap then
535+
annot = New(annot, Nil)
536+
else
537+
val trefs =
538+
if refs.isEmpty then ref(defn.NothingType)
539+
else refs.map(SingletonTypeTree).reduce[Tree]((a, b) => makeOrType(a, b))
540+
annot = New(AppliedTypeTree(annot, trefs :: Nil), Nil)
541+
annot.putAttachment(RetainsAnnot, ())
542+
Annotated(parent, annot)
535543

536544
def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef =
537545
DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs)
@@ -555,6 +563,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
555563
def makeAndType(left: Tree, right: Tree)(using Context): AppliedTypeTree =
556564
AppliedTypeTree(ref(defn.andType.typeRef), left :: right :: Nil)
557565

566+
def makeOrType(left: Tree, right: Tree)(using Context): AppliedTypeTree =
567+
AppliedTypeTree(ref(defn.orType.typeRef), left :: right :: Nil)
568+
558569
def makeParameter(pname: TermName, tpe: Tree, mods: Modifiers, isBackquoted: Boolean = false)(using Context): ValDef = {
559570
val vdef = ValDef(pname, tpe, EmptyTree)
560571
if (isBackquoted) vdef.pushAttachment(Backquoted, ())

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

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,14 @@ case class CaptureAnnotation(refs: CaptureSet, boxed: Boolean)(cls: Symbol) exte
3939

4040
/** Reconstitute annotation tree from capture set */
4141
override def tree(using Context) =
42-
val elems = refs.elems.toList.map {
43-
case c: TermRef => ref(c)
44-
case c: TermParamRef => untpd.Ident(c.paramName).withType(c)
45-
case c: ThisType => This(c.cls)
46-
case c: RootCapability => ref(defn.captureRoot)
47-
// TODO: Will crash if the type is an annotated type, for example `cap.rd`
48-
}
49-
val arg = repeated(elems, TypeTree(defn.AnyType))
50-
New(symbol.typeRef, arg :: Nil)
42+
if symbol == defn.RetainsCapAnnot then
43+
New(symbol.typeRef, Nil)
44+
else
45+
val elems = refs.elems.toList
46+
val trefs =
47+
if elems.isEmpty then defn.NothingType
48+
else elems.reduce[Type]((a, b) => OrType(a, b, soft = false))
49+
New(AppliedType(symbol.typeRef, trefs :: Nil), Nil)
5150

5251
override def symbol(using Context) = cls
5352

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

Lines changed: 91 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,23 @@ extension (tree: Tree)
5454
* map CapSet^{refs} to the `refs` references,
5555
* throw IllegalCaptureRef otherwise
5656
*/
57-
def toCapabilities(using Context): List[Capability] = tree match
58-
case ReachCapabilityApply(arg) =>
59-
arg.toCapabilities.map(_.reach)
60-
case ReadOnlyCapabilityApply(arg) =>
61-
arg.toCapabilities.map(_.readOnly)
62-
case CapsOfApply(arg) =>
63-
arg.toCapabilities
64-
case _ => tree.tpe.dealiasKeepAnnots match
65-
case ref: TermRef if ref.isCapRef =>
66-
GlobalCap :: Nil
67-
case ref: Capability if ref.isTrackableRef =>
68-
ref :: Nil
69-
case AnnotatedType(parent, ann)
70-
if ann.symbol.isRetains && parent.derivesFrom(defn.Caps_CapSet) =>
71-
ann.tree.toCaptureSet.elems.toList
72-
case tpe =>
73-
throw IllegalCaptureRef(tpe) // if this was compiled from cc syntax, problem should have been reported at Typer
57+
// def toCapabilities(using Context): List[Capability] = tree match
58+
// case ReachCapabilityApply(arg) =>
59+
// arg.toCapabilities.map(_.reach)
60+
// case ReadOnlyCapabilityApply(arg) =>
61+
// arg.toCapabilities.map(_.readOnly)
62+
// case CapsOfApply(arg) =>
63+
// arg.toCapabilities
64+
// case _ => tree.tpe.dealiasKeepAnnots match
65+
// case ref: TermRef if ref.isCapRef =>
66+
// GlobalCap :: Nil
67+
// case ref: Capability if ref.isTrackableRef =>
68+
// ref :: Nil
69+
// case AnnotatedType(parent, ann)
70+
// if ann.symbol.isRetains && parent.derivesFrom(defn.Caps_CapSet) =>
71+
// ann.tree.toCaptureSet.elems.toList
72+
// case tpe =>
73+
// throw IllegalCaptureRef(tpe) // if this was compiled from cc syntax, problem should have been reported at Typer
7474

7575
/** Convert a @retains or @retainsByName annotation tree to the capture set it represents.
7676
* For efficience, the result is cached as an Attachment on the tree.
@@ -79,28 +79,41 @@ extension (tree: Tree)
7979
tree.getAttachment(Captures) match
8080
case Some(refs) => refs
8181
case None =>
82-
val refs = CaptureSet(tree.retainedElems.flatMap(_.toCapabilities)*)
83-
//.showing(i"toCaptureSet $tree --> $result", capt)
82+
val refs = CaptureSet(tree.retainedSet.retainedElements*)
8483
tree.putAttachment(Captures, refs)
8584
refs
86-
87-
/** The arguments of a @retains, @retainsCap or @retainsByName annotation */
88-
def retainedElems(using Context): List[Tree] = tree match
89-
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) =>
90-
elems
91-
case _ =>
92-
if tree.symbol.maybeOwner == defn.RetainsCapAnnot
93-
then ref(defn.captureRoot) :: Nil
94-
else Nil
85+
/** The type representing the capture set of retains annotation.
86+
*/
87+
def retainedSet(using Context): Type =
88+
tree match
89+
case Apply(TypeApply(_, refs :: Nil), _) => refs.tpe
90+
case _ =>
91+
if tree.symbol.maybeOwner == defn.RetainsCapAnnot
92+
then ref(defn.captureRoot) else NoType
9593

9694
extension (tp: Type)
9795

96+
def retainedElementsRaw(using Context): List[Type] = tp match
97+
case ReachCapability(tp1) =>
98+
tp1.reach :: Nil
99+
case ReadOnlyCapability(tp1) =>
100+
tp1.readOnly :: Nil
101+
case OrType(tp1, tp2) =>
102+
tp1.retainedElementsRaw ++ tp2.retainedElementsRaw
103+
case tp =>
104+
// Nothing is a special type to represent the empty set
105+
if tp.isNothingType then Nil
106+
else tp :: Nil // should be checked by wellformedness
107+
108+
def retainedElements(using Context): List[Capability] =
109+
retainedElementsRaw.map:
110+
case tp: CaptureRef => tp
111+
case tp => throw IllegalCaptureRef(tp)
112+
98113
/** Is this type a Capability that can be tracked?
99114
* This is true for
100115
* - all ThisTypes and all TermParamRef,
101116
* - stable TermRefs with NoPrefix or ThisTypes as prefixes,
102-
* - the root capability `caps.cap`
103-
* - abstract or parameter TypeRefs that derive from caps.CapSet
104117
* - annotated types that represent reach or maybe capabilities
105118
*/
106119
final def isTrackableRef(using Context): Boolean = tp match
@@ -418,7 +431,7 @@ extension (cls: ClassSymbol)
418431
|| bc.is(CaptureChecked)
419432
&& bc.givenSelfType.dealiasKeepAnnots.match
420433
case CapturingType(_, refs) => refs.isAlwaysEmpty
421-
case RetainingType(_, refs) => refs.isEmpty
434+
case RetainingType(_, refs) => refs.retainedElements.isEmpty
422435
case selfType =>
423436
isCaptureChecking // At Setup we have not processed self types yet, so
424437
// unless a self type is explicitly given, we can't tell
@@ -533,7 +546,7 @@ class CleanupRetains(using Context) extends TypeMap:
533546
def apply(tp: Type): Type =
534547
tp match
535548
case AnnotatedType(tp, annot) if annot.symbol == defn.RetainsAnnot || annot.symbol == defn.RetainsByNameAnnot =>
536-
RetainingType(tp, Nil, byName = annot.symbol == defn.RetainsByNameAnnot)
549+
RetainingType(tp, defn.NothingType, byName = annot.symbol == defn.RetainsByNameAnnot)
537550
case _ => mapOver(tp)
538551

539552
/** A typemap that follows aliases and keeps their transformed results if
@@ -549,29 +562,57 @@ trait FollowAliasesMap(using Context) extends TypeMap:
549562
else t
550563
else mapOver(t)
551564

552-
/** An extractor for `caps.reachCapability(ref)`, which is used to express a reach
553-
* capability as a tree in a @retains annotation.
554-
*/
555-
object ReachCapabilityApply:
556-
def unapply(tree: Apply)(using Context): Option[Tree] = tree match
557-
case Apply(reach, arg :: Nil) if reach.symbol == defn.Caps_reachCapability => Some(arg)
565+
// /** An extractor for `caps.reachCapability(ref)`, which is used to express a reach
566+
// * capability as a tree in a @retains annotation.
567+
// */
568+
// object ReachCapabilityApply:
569+
// def unapply(tree: Apply)(using Context): Option[Tree] = tree match
570+
// case Apply(reach, arg :: Nil) if reach.symbol == defn.Caps_reachCapability => Some(arg)
571+
// case _ => None
572+
573+
// /** An extractor for `caps.readOnlyCapability(ref)`, which is used to express a read-only
574+
// * capability as a tree in a @retains annotation.
575+
// */
576+
// object ReadOnlyCapabilityApply:
577+
// def unapply(tree: Apply)(using Context): Option[Tree] = tree match
578+
// case Apply(ro, arg :: Nil) if ro.symbol == defn.Caps_readOnlyCapability => Some(arg)
579+
// case _ => None
580+
581+
abstract class AnnotatedCapability(annotCls: Context ?=> ClassSymbol):
582+
def apply(tp: Type)(using Context): AnnotatedType =
583+
assert(tp.isTrackableRef, i"not a trackable ref: $tp")
584+
tp match
585+
case AnnotatedType(_, annot) =>
586+
assert(!unwrappable.contains(annot.symbol), i"illegal combination of derived capabilities: $annotCls over ${annot.symbol}")
587+
case _ =>
588+
tp match
589+
case tp: Capability => tp.derivedRef(annotCls)
590+
case _ => AnnotatedType(tp, Annotation(annotCls, util.Spans.NoSpan))
591+
592+
def unapply(tree: AnnotatedType)(using Context): Option[Capability] = tree match
593+
case AnnotatedType(parent: Capability, ann) if ann.hasSymbol(annotCls) => Some(parent)
558594
case _ => None
559595

560-
/** An extractor for `caps.readOnlyCapability(ref)`, which is used to express a read-only
561-
* capability as a tree in a @retains annotation.
562-
*/
563-
object ReadOnlyCapabilityApply:
564-
def unapply(tree: Apply)(using Context): Option[Tree] = tree match
565-
case Apply(ro, arg :: Nil) if ro.symbol == defn.Caps_readOnlyCapability => Some(arg)
596+
protected def unwrappable(using Context): Set[Symbol]
597+
end AnnotatedCapability
598+
599+
object QualifiedCapability:
600+
def unapply(tree: AnnotatedType)(using Context): Option[Capability] = tree match
601+
case AnnotatedType(parent: Capability, ann)
602+
if defn.capabilityQualifierAnnots.contains(ann.symbol) => Some(parent)
566603
case _ => None
567604

568-
/** An extractor for `caps.capsOf[X]`, which is used to express a generic capture set
569-
* as a tree in a @retains annotation.
605+
/** An extractor for `ref @readOnlyCapability`, which is used to express
606+
* the read-only capability `ref.rd` as a type.
570607
*/
571-
object CapsOfApply:
572-
def unapply(tree: TypeApply)(using Context): Option[Tree] = tree match
573-
case TypeApply(capsOf, arg :: Nil) if capsOf.symbol == defn.Caps_capsOf => Some(arg)
574-
case _ => None
608+
object ReadOnlyCapability extends AnnotatedCapability(defn.ReadOnlyCapabilityAnnot):
609+
protected def unwrappable(using Context) = Set()
610+
611+
/** An extractor for `ref @annotation.internal.reachCapability`, which is used to express
612+
* the reach capability `ref*` as a type.
613+
*/
614+
object ReachCapability extends AnnotatedCapability(defn.ReachCapabilityAnnot):
615+
protected def unwrappable(using Context) = Set(defn.ReadOnlyCapabilityAnnot)
575616

576617
/** An extractor for all kinds of function types as well as method and poly types.
577618
* It includes aliases of function types such as `=>`. TODO: Can we do without?

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

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,24 @@ object CheckCaptures:
100100
* This check is performed at Typer.
101101
*/
102102
def checkWellformed(parent: Tree, ann: Tree)(using Context): Unit =
103-
def check(elem: Tree, pos: SrcPos): Unit = elem.tpe match
103+
def check(elem: Type, pos: SrcPos): Unit = elem match
104104
case ref: Capability =>
105105
if !ref.isTrackableRef && !ref.isCapRef then
106106
report.error(em"$elem cannot be tracked since it is not a parameter or local value", pos)
107107
case tpe =>
108108
report.error(em"$elem: $tpe is not a legal element of a capture set", pos)
109-
for elem <- ann.retainedElems do
109+
for elem <- ann.retainedSet.retainedElementsRaw do
110110
elem match
111-
case CapsOfApply(arg) =>
112-
def isLegalCapsOfArg =
113-
arg.symbol.isType && arg.symbol.info.derivesFrom(defn.Caps_CapSet)
114-
if !isLegalCapsOfArg then
115-
report.error(
116-
em"""$arg is not a legal prefix for `^` here,
117-
|is must be a type parameter or abstract type with a caps.CapSet upper bound.""",
118-
elem.srcPos)
119-
case ReachCapabilityApply(arg) => check(arg, elem.srcPos)
120-
case ReadOnlyCapabilityApply(arg) => check(arg, elem.srcPos)
121-
case _ => check(elem, elem.srcPos)
111+
case ref: TypeRef =>
112+
val refSym = ref.symbol
113+
if refSym.isType && !refSym.info.derivesFrom(defn.Caps_CapSet) then
114+
report.error(em"$elem is not a legal element of a capture set", ann.srcPos)
115+
case ReachCapability(ref) =>
116+
check(ref, ann.srcPos)
117+
case ReadOnlyCapability(ref) =>
118+
check(ref, ann.srcPos)
119+
case _ =>
120+
check(elem, ann.srcPos)
122121

123122
/** Under the sealed policy, report an error if some part of `tp` contains the
124123
* root capability in its capture set or if it refers to a type parameter that

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,20 @@ import Decorators.i
1212
*/
1313
object RetainingType:
1414

15-
def apply(tp: Type, refs: List[Tree], byName: Boolean = false)(using Context): Type =
15+
def apply(tp: Type, typeElems: Type, byName: Boolean = false)(using Context): Type =
1616
val annotCls = if byName then defn.RetainsByNameAnnot else defn.RetainsAnnot
17-
val annotTree =
18-
New(annotCls.typeRef,
19-
Typed(
20-
SeqLiteral(refs, TypeTree(defn.AnyType)),
21-
TypeTree(defn.RepeatedParamClass.typeRef.appliedTo(defn.AnyType))) :: Nil)
17+
val annotTree = New(AppliedType(annotCls.typeRef, typeElems :: Nil), Nil)
2218
AnnotatedType(tp, Annotation(annotTree))
2319

24-
def unapply(tp: AnnotatedType)(using Context): Option[(Type, List[Tree])] =
20+
def unapply(tp: AnnotatedType)(using Context): Option[(Type, Type)] =
2521
val sym = tp.annot.symbol
2622
if sym.isRetainsLike then
2723
tp.annot match
2824
case _: CaptureAnnotation =>
2925
assert(ctx.mode.is(Mode.IgnoreCaptures), s"bad retains $tp at ${ctx.phase}")
3026
None
3127
case ann =>
32-
Some((tp.parent, ann.tree.retainedElems))
28+
Some((tp.parent, ann.tree.retainedSet))
3329
else
3430
None
3531
end RetainingType

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

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
801801
case CapturingType(_, refs) =>
802802
!refs.isAlwaysEmpty
803803
case RetainingType(parent, refs) =>
804-
!refs.isEmpty
804+
!refs.retainedElements.isEmpty
805805
case tp: (TypeRef | AppliedType) =>
806806
val sym = tp.typeSymbol
807807
if sym.isClass
@@ -847,7 +847,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
847847
&& !refs.isUniversal // if refs is {cap}, an added variable would not change anything
848848
case RetainingType(parent, refs) =>
849849
needsVariable(parent)
850-
&& !refs.tpes.exists:
850+
&& !refs.retainedElements.exists:
851851
case ref: TermRef => ref.isCapRef
852852
case _ => false
853853
case AnnotatedType(parent, _) =>
@@ -942,19 +942,13 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
942942
* @param tpt the tree for which an error or warning should be reported
943943
*/
944944
private def checkWellformed(parent: Type, ann: Tree, tpt: Tree)(using Context): Unit =
945-
capt.println(i"checkWF post $parent ${ann.retainedElems} in $tpt")
946-
var retained = ann.retainedElems.toArray
947-
for i <- 0 until retained.length do
948-
val refTree = retained(i)
949-
val refs =
950-
try refTree.toCapabilities
951-
catch case ex: IllegalCaptureRef =>
952-
report.error(em"Illegal capture reference: ${ex.getMessage}", refTree.srcPos)
953-
Nil
954-
for ref <- refs do
945+
capt.println(i"checkWF post $parent ${ann.retainedSet} in $tpt")
946+
try
947+
var retained = ann.retainedSet.retainedElements.toArray
948+
for i <- 0 until retained.length do
949+
val ref = retained(i)
955950
def pos =
956-
if refTree.span.exists then refTree.srcPos
957-
else if ann.span.exists then ann.srcPos
951+
if ann.span.exists then ann.srcPos
958952
else tpt.srcPos
959953

960954
def check(others: CaptureSet, dom: Type | CaptureSet): Unit =
@@ -971,13 +965,14 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
971965
val others =
972966
for
973967
j <- 0 until retained.length if j != i
974-
r <- retained(j).toCapabilities
968+
r = retained(j)
975969
if !r.isTerminalCapability
976970
yield r
977971
val remaining = CaptureSet(others*)
978972
check(remaining, remaining)
979973
end for
980-
end for
974+
catch case ex: IllegalCaptureRef =>
975+
report.error(em"Illegal capture reference: ${ex.getMessage}", tpt.srcPos)
981976
end checkWellformed
982977

983978
/** Check well formed at post check time. We need to wait until after

0 commit comments

Comments
 (0)