@@ -39,8 +39,9 @@ import annotation.internal.sharable
3939 * | +-- SetCapability -----+-- TypeRef
4040 * | +-- TypeParamRef
4141 * |
42- * +-- DerivedCapability -+-- ReadOnly
43- * +-- Reach
42+ * +-- DerivedCapability -+-- Reach
43+ * +-- Only
44+ * +-- ReadOnly
4445 * +-- Maybe
4546 *
4647 * All CoreCapabilities are Types, or, more specifically instances of TypeProxy.
@@ -96,9 +97,18 @@ object Capabilities:
9697 * but they can wrap reach capabilities. We have
9798 * (x?).readOnly = (x.rd)?
9899 */
99- case class ReadOnly (underlying : ObjectCapability | RootCapability | Reach )
100- extends DerivedCapability :
101- assert(! underlying.isInstanceOf [Maybe ])
100+ case class ReadOnly (underlying : ObjectCapability | RootCapability | Reach | Restricted )
101+ extends DerivedCapability
102+
103+ /** The restricted capability `x.only[C]`. We have {x.only[C]} <: {x}.
104+ *
105+ * Restricted capabilities cannot wrap maybe capabilities or read-only capabilities
106+ * but they can wrap reach capabilities. We have
107+ * (x?).restrict[T] = (x.restrict[T])?
108+ * (x.rd).restrict[T] = (x.restrict[T]).rd
109+ */
110+ case class Restricted (underlying : ObjectCapability | RootCapability | Reach , cls : ClassSymbol )
111+ extends DerivedCapability
102112
103113 /** If `x` is a capability, its reach capability `x*`. `x*` stands for all
104114 * capabilities reachable through `x`.
@@ -109,11 +119,11 @@ object Capabilities:
109119 *
110120 * Reach capabilities cannot wrap read-only capabilities or maybe capabilities.
111121 * We have
112- * (x.rd).reach = x*.rd
113- * (x.rd)? = (x*)?
122+ * (x?).reach = (x.reach)?
123+ * (x.rd).reach = (x.reach).rd
124+ * (x.only[T]).reach = (x*).only[T]
114125 */
115- case class Reach (underlying : ObjectCapability ) extends DerivedCapability :
116- assert(! underlying.isInstanceOf [Maybe | ReadOnly ])
126+ case class Reach (underlying : ObjectCapability ) extends DerivedCapability
117127
118128 /** The global root capability referenced as `caps.cap`
119129 * `cap` does not subsume other capabilities, except in arguments of
@@ -124,6 +134,7 @@ object Capabilities:
124134 def descr (using Context ) = " the universal root capability"
125135 override val maybe = Maybe (this )
126136 override val readOnly = ReadOnly (this )
137+ override def restrict (cls : ClassSymbol )(using Context ) = Restricted (this , cls)
127138 override def reach = unsupported(" cap.reach" )
128139 override def singletonCaptureSet (using Context ) = CaptureSet .universal
129140 override def captureSetOfInfo (using Context ) = singletonCaptureSet
@@ -242,7 +253,7 @@ object Capabilities:
242253 /** A trait for references in CaptureSets. These can be NamedTypes, ThisTypes or ParamRefs,
243254 * as well as three kinds of AnnotatedTypes representing readOnly, reach, and maybe capabilities.
244255 * If there are several annotations they come with an order:
245- * `*` first, `.rd` next, `?` last.
256+ * `*` first, `.only` next, `. rd` next, `?` last.
246257 */
247258 trait Capability extends Showable :
248259
@@ -254,7 +265,15 @@ object Capabilities:
254265 protected def cached [C <: DerivedCapability ](newRef : C ): C =
255266 def recur (refs : List [DerivedCapability ]): C = refs match
256267 case ref :: refs1 =>
257- if ref.getClass == newRef.getClass then ref.asInstanceOf [C ] else recur(refs1)
268+ val exists = ref match
269+ case Restricted (_, cls) =>
270+ newRef match
271+ case Restricted (_, newCls) => cls == newCls
272+ case _ => false
273+ case _ =>
274+ ref.getClass == newRef.getClass
275+ if exists then ref.asInstanceOf [C ]
276+ else recur(refs1)
258277 case Nil =>
259278 myDerived = newRef :: myDerived
260279 newRef
@@ -267,11 +286,24 @@ object Capabilities:
267286 def readOnly : ReadOnly | Maybe = this match
268287 case Maybe (ref1) => Maybe (ref1.readOnly)
269288 case self : ReadOnly => self
270- case self : (ObjectCapability | RootCapability | Reach ) => cached(ReadOnly (self))
271-
272- def reach : Reach | ReadOnly | Maybe = this match
289+ case self : (ObjectCapability | RootCapability | Reach | Restricted ) => cached(ReadOnly (self))
290+
291+ def restrict (cls : ClassSymbol )(using Context ): Restricted | ReadOnly | Maybe = this match
292+ case Maybe (ref1) => Maybe (ref1.restrict(cls))
293+ case ReadOnly (ref1) => ReadOnly (ref1.restrict(cls).asInstanceOf [Restricted ])
294+ case self @ Restricted (ref1, prevCls) =>
295+ val combinedCls =
296+ if prevCls.isSubClass(cls) then prevCls
297+ else if cls.isSubClass(prevCls) then cls
298+ else defn.NothingClass
299+ if combinedCls == prevCls then self
300+ else cached(Restricted (ref1, combinedCls))
301+ case self : (ObjectCapability | RootCapability | Reach ) => cached(Restricted (self, cls))
302+
303+ def reach : Reach | Restricted | ReadOnly | Maybe = this match
273304 case Maybe (ref1) => Maybe (ref1.reach)
274- case ReadOnly (ref1) => ReadOnly (ref1.reach.asInstanceOf [Reach ])
305+ case ReadOnly (ref1) => ReadOnly (ref1.reach.asInstanceOf [Reach | Restricted ])
306+ case Restricted (ref1, cls) => Restricted (ref1.reach.asInstanceOf [Reach ], cls)
275307 case self : Reach => self
276308 case self : ObjectCapability => cached(Reach (self))
277309
@@ -285,6 +317,12 @@ object Capabilities:
285317 case tp : SetCapability => tp.captureSetOfInfo.isReadOnly
286318 case _ => this ne stripReadOnly
287319
320+ final def restriction (using Context ): Symbol = this match
321+ case Restricted (_, cls) => cls
322+ case ReadOnly (ref1) => ref1.restriction
323+ case Maybe (ref1) => ref1.restriction
324+ case _ => NoSymbol
325+
288326 /** Is this a reach reference of the form `x*` or a readOnly or maybe variant
289327 * of a reach reference?
290328 */
@@ -299,6 +337,12 @@ object Capabilities:
299337 case Maybe (ref1) => ref1.stripReadOnly.maybe
300338 case _ => this
301339
340+ final def stripRestricted (using Context ): Capability = this match
341+ case Restricted (ref1, _) => ref1
342+ case ReadOnly (ref1) => ref1.stripRestricted.readOnly
343+ case Maybe (ref1) => ref1.stripRestricted.maybe
344+ case _ => this
345+
302346 final def stripReach (using Context ): Capability = this match
303347 case Reach (ref1) => ref1
304348 case ReadOnly (ref1) => ref1.stripReach.readOnly
0 commit comments