@@ -90,33 +90,33 @@ object Capabilities:
9090 */
9191 case class Maybe (underlying : Capability ) extends DerivedCapability
9292
93+ /** The readonly capability `x.rd`. We have {x.rd} <: {x}.
94+ *
95+ * Read-only capabilities cannot wrap maybe capabilities
96+ * but they can wrap reach capabilities. We have
97+ * (x?).readOnly = (x.rd)?
98+ */
99+ case class ReadOnly (underlying : CoreCapability | RootCapability | Reach | Restricted )
100+ extends DerivedCapability
101+
93102 /** The restricted capability `x.only[C]`. We have {x.only[C]} <: {x}.
94103 *
95- * Restricted capabilities cannot wrap maybe capabilities
104+ * Restricted capabilities cannot wrap maybe capabilities or read-only capabilities
96105 * but they can wrap reach capabilities. We have
97106 * (x?).restrict[T] = (x.restrict[T])?
98107 * (x.rd).restrict[T] = (x.restrict[T]).rd
99108 */
100109 case class Restricted (underlying : CoreCapability | RootCapability | Reach , cls : ClassSymbol )
101110 extends DerivedCapability
102111
103- /** An extractor for the read-only capability `x.rd`. `x.rd` is represented as
104- * `c.only[caps.Read]`.
105- */
106- object ReadOnly :
107- def apply (underlying : CoreCapability | RootCapability | Reach | Restricted )(using Context ): Restricted =
108- Restricted (underlying.stripRestricted.asInstanceOf , defn.Caps_Read )
109- def unapply (ref : Restricted )(using Context ): Option [CoreCapability | RootCapability | Reach ] =
110- if ref.cls == defn.Caps_Read then Some (ref.underlying) else None
111-
112112 /** If `x` is a capability, its reach capability `x*`. `x*` stands for all
113113 * capabilities reachable through `x`.
114114 * We have `{x} <: {x*} <: dcs(x)}` where the deep capture set `dcs(x)` of `x`
115115 * is the union of all capture sets that appear in covariant position in the
116116 * type of `x`. If `x` and `y` are different variables then `{x*}` and `{y*}`
117117 * are unrelated.
118118 *
119- * Reach capabilities cannot wrap restricted capabilities or maybe capabilities.
119+ * Reach capabilities cannot wrap read-only capabilities or maybe capabilities.
120120 * We have
121121 * (x?).reach = (x.reach)?
122122 * (x.rd).reach = (x.reach).rd
@@ -132,7 +132,7 @@ object Capabilities:
132132 object GlobalCap extends RootCapability :
133133 def descr (using Context ) = " the universal root capability"
134134 override val maybe = Maybe (this )
135- override def readOnly ( using Context ) = ReadOnly (this )
135+ override val readOnly = ReadOnly (this )
136136 override def restrict (cls : ClassSymbol )(using Context ) = Restricted (this , cls)
137137 override def reach = unsupported(" cap.reach" )
138138 override def singletonCaptureSet (using Context ) = CaptureSet .universal
@@ -276,9 +276,10 @@ object Capabilities:
276276 * if separation checks are turned off).
277277 * @pre The capability's origin was not yet set.
278278 */
279- def setOrigin (freshOrigin : FreshCap | GlobalCap .type ): Unit =
279+ def setOrigin (freshOrigin : FreshCap | GlobalCap .type ): this . type =
280280 assert(myOrigin eq GlobalCap )
281281 myOrigin = freshOrigin
282+ this
282283
283284 /** If the current capability was created via a chain of `derivedResult` calls
284285 * from an original ResultCap `r`, that `r`. Otherwise `this`.
@@ -346,21 +347,23 @@ object Capabilities:
346347 case self : Maybe => self
347348 case _ => cached(Maybe (this ))
348349
349- def readOnly ( using Context ) : Restricted | Maybe = this match
350+ def readOnly : ReadOnly | Maybe = this match
350351 case Maybe (ref1) => Maybe (ref1.readOnly)
351- case self @ ReadOnly (_) => self
352+ case self : ReadOnly => self
352353 case self : (CoreCapability | RootCapability | Reach | Restricted ) => cached(ReadOnly (self))
353354
354- def restrict (cls : ClassSymbol )(using Context ): Restricted | Maybe = this match
355+ def restrict (cls : ClassSymbol )(using Context ): Restricted | ReadOnly | Maybe = this match
355356 case Maybe (ref1) => Maybe (ref1.restrict(cls))
357+ case ReadOnly (ref1) => ReadOnly (ref1.restrict(cls).asInstanceOf [Restricted ])
356358 case self @ Restricted (ref1, prevCls) =>
357359 val combinedCls = leastClassifier(prevCls, cls)
358360 if combinedCls == prevCls then self
359361 else cached(Restricted (ref1, combinedCls))
360362 case self : (CoreCapability | RootCapability | Reach ) => cached(Restricted (self, cls))
361363
362- def reach : Reach | Restricted | Maybe = this match
364+ def reach : Reach | Restricted | ReadOnly | Maybe = this match
363365 case Maybe (ref1) => Maybe (ref1.reach)
366+ case ReadOnly (ref1) => ReadOnly (ref1.reach.asInstanceOf [Reach | Restricted ])
364367 case Restricted (ref1, cls) => Restricted (ref1.reach.asInstanceOf [Reach ], cls)
365368 case self : Reach => self
366369 case self : ObjectCapability => cached(Reach (self))
@@ -381,6 +384,7 @@ object Capabilities:
381384 */
382385 final def classifier (using Context ): Symbol = this match
383386 case Restricted (_, cls) => cls
387+ case ReadOnly (ref1) => ref1.classifier
384388 case Maybe (ref1) => ref1.classifier
385389 case self : FreshCap => self.hiddenSet.classifier
386390 case _ => NoSymbol
@@ -399,9 +403,10 @@ object Capabilities:
399403 case Maybe (ref1) => ref1.stripReadOnly.maybe
400404 case _ => this
401405
402- /** Drop restrictions with class `cls` or a superclass of `cls` */
406+ /** Drop restrictions with clss `cls` or a superclass of `cls` */
403407 final def stripRestricted (cls : ClassSymbol )(using Context ): Capability = this match
404408 case Restricted (ref1, cls1) if cls.isSubClass(cls1) => ref1
409+ case ReadOnly (ref1) => ref1.stripRestricted(cls).readOnly
405410 case Maybe (ref1) => ref1.stripRestricted(cls).maybe
406411 case _ => this
407412
@@ -410,6 +415,7 @@ object Capabilities:
410415
411416 final def stripReach (using Context ): Capability = this match
412417 case Reach (ref1) => ref1
418+ case ReadOnly (ref1) => ref1.stripReach.readOnly
413419 case Restricted (ref1, cls) => ref1.stripReach.restrict(cls)
414420 case Maybe (ref1) => ref1.stripReach.maybe
415421 case _ => this
@@ -506,35 +512,44 @@ object Capabilities:
506512
507513 final def isParamPath (using Context ): Boolean = paramPathRoot.exists
508514
509- final def ccOwner (using Context ): Symbol = this match
515+ /** Compute ccOwner or (part of level owner).
516+ * @param mapUnscoped if true, return the nclosing toplevel class for FreshCaps
517+ * classified as Unscoped that don't have a prefix
518+ */
519+ private def computeOwner (mapUnscoped : Boolean )(using Context ): Symbol = this match
510520 case self : ThisType => self.cls
511- case TermRef (prefix : Capability , _) => prefix.ccOwner
521+ case TermRef (prefix : Capability , _) => prefix.computeOwner(mapUnscoped)
512522 case self : NamedType => self.symbol
513- case self : DerivedCapability => self.underlying.ccOwner
523+ case self : DerivedCapability => self.underlying.computeOwner(mapUnscoped)
514524 case self : FreshCap =>
515525 val setOwner = self.hiddenSet.owner
516526 self.prefix match
517527 case prefix : ThisType if setOwner.isTerm && setOwner.owner == prefix.cls =>
518528 setOwner
519- case prefix : Capability => prefix.ccOwner
529+ case prefix : Capability => prefix.computeOwner(mapUnscoped)
530+ case NoPrefix if mapUnscoped && classifier.derivesFrom(defn.Caps_Unscoped ) =>
531+ ctx.owner.topLevelClass
520532 case _ => setOwner
521533 case _ /* : GlobalCap | ResultCap | ParamRef */ => NoSymbol
522534
535+ final def ccOwner (using Context ): Symbol = computeOwner(mapUnscoped = false )
536+
523537 final def visibility (using Context ): Symbol = this match
524- case self : FreshCap => adjustOwner(ccOwner )
538+ case self : FreshCap => adjustOwner(computeOwner(mapUnscoped = true ) )
525539 case _ =>
526- val vis = ccOwner
540+ val vis = computeOwner(mapUnscoped = true )
527541 if vis.is(Param ) then vis.owner else vis
528542
529543 /** The symbol that represents the level closest-enclosing ccOwner.
530544 * Symbols representing levels are
531545 * - class symbols, but not inner (non-static) module classes
532546 * - method symbols, but not accessors or constructors
547+ * For Unscoped FreshCaps the level owner is the top-level class.
533548 */
534549 final def levelOwner (using Context ): Symbol =
535- adjustOwner(ccOwner )
550+ adjustOwner(computeOwner(mapUnscoped = true ) )
536551
537- private def adjustOwner (owner : Symbol )(using Context ): Symbol =
552+ final def adjustOwner (owner : Symbol )(using Context ): Symbol =
538553 if ! owner.exists
539554 || owner.isClass && (! owner.is(Flags .Module ) || owner.isStatic)
540555 || owner.is(Flags .Method , butNot = Flags .Accessor )
@@ -593,6 +608,7 @@ object Capabilities:
593608 def computeHiddenSet (f : Refs => Refs )(using Context ): Refs = this match
594609 case self : FreshCap => f(self.hiddenSet.elems)
595610 case Restricted (elem1, cls) => elem1.computeHiddenSet(f).map(_.restrict(cls))
611+ case ReadOnly (elem1) => elem1.computeHiddenSet(f).map(_.readOnly)
596612 case _ => emptyRefs
597613
598614 /** The transitive classifiers of this capability. */
@@ -610,6 +626,8 @@ object Capabilities:
610626 assert(cls != defn.AnyClass )
611627 if cls == defn.NothingClass then ClassifiedAs (Nil )
612628 else ClassifiedAs (cls :: Nil )
629+ case ReadOnly (ref1) =>
630+ ref1.transClassifiers
613631 case Maybe (ref1) =>
614632 ref1.transClassifiers
615633 case Reach (_) =>
@@ -633,6 +651,8 @@ object Capabilities:
633651 case Restricted (_, cls1) =>
634652 assert(cls != defn.AnyClass )
635653 cls1.isSubClass(cls)
654+ case ReadOnly (ref1) =>
655+ ref1.tryClassifyAs(cls)
636656 case Maybe (ref1) =>
637657 ref1.tryClassifyAs(cls)
638658 case Reach (_) =>
@@ -653,6 +673,7 @@ object Capabilities:
653673 cs.forall(c => leastClassifier(c, cls) == defn.NothingClass )
654674 case _ => false
655675 isEmpty || ref1.isKnownEmpty
676+ case ReadOnly (ref1) => ref1.isKnownEmpty
656677 case Maybe (ref1) => ref1.isKnownEmpty
657678 case _ => false
658679
@@ -713,6 +734,7 @@ object Capabilities:
713734 case _ => false
714735 || viaInfo(y.info)(subsumingRefs(this , _))
715736 case Maybe (y1) => this .stripMaybe.subsumes(y1)
737+ case ReadOnly (y1) => this .stripReadOnly.subsumes(y1)
716738 case Restricted (y1, cls) => this .stripRestricted(cls).subsumes(y1)
717739 case y : TypeRef if y.derivesFrom(defn.Caps_CapSet ) =>
718740 // The upper and lower bounds don't have to be in the form of `CapSet^{...}`.
@@ -796,6 +818,7 @@ object Capabilities:
796818 y.isKnownClassifiedAs(cls) && x1.maxSubsumes(y, canAddHidden)
797819 case _ =>
798820 y match
821+ case ReadOnly (y1) => this .stripReadOnly.maxSubsumes(y1, canAddHidden)
799822 case Restricted (y1, cls) => this .stripRestricted(cls).maxSubsumes(y1, canAddHidden)
800823 case _ => false
801824
@@ -883,6 +906,7 @@ object Capabilities:
883906 case c : DerivedCapability =>
884907 val c1 = c.underlying.toType
885908 c match
909+ case _ : ReadOnly => ReadOnlyCapability (c1)
886910 case Restricted (_, cls) => OnlyCapability (c1, cls)
887911 case _ : Reach => ReachCapability (c1)
888912 case _ : Maybe => MaybeCapability (c1)
@@ -1168,7 +1192,7 @@ object Capabilities:
11681192 case _ =>
11691193 super .mapOver(t)
11701194
1171- class ToResult (localResType : Type , mt : MethodicType , fail : Message => Unit )(using Context ) extends CapMap :
1195+ class ToResult (localResType : Type , mt : MethodicType , sym : Symbol , fail : Message => Unit )(using Context ) extends CapMap :
11721196
11731197 def apply (t : Type ) = t match
11741198 case defn.FunctionNOf (args, res, contextual) if t.typeSymbol.name.isImpureFunction =>
@@ -1183,11 +1207,12 @@ object Capabilities:
11831207 override def mapCapability (c : Capability , deep : Boolean ) = c match
11841208 case c : (FreshCap | GlobalCap .type ) =>
11851209 if variance > 0 then
1186- val res = ResultCap (mt)
11871210 c match
1188- case c : FreshCap => res.setOrigin(c)
1189- case _ =>
1190- res
1211+ case c : FreshCap =>
1212+ if sym.isAnonymousFunction && c.classifier.derivesFrom(defn.Caps_Unscoped )
1213+ then c
1214+ else ResultCap (mt).setOrigin(c)
1215+ case _ => ResultCap (mt)
11911216 else
11921217 if variance == 0 then
11931218 fail(em """ $localResType captures the root capability `cap` in invariant position.
@@ -1227,8 +1252,8 @@ object Capabilities:
12271252 * variable bound by `mt`.
12281253 * Stop at function or method types since these have been mapped before.
12291254 */
1230- def toResult (tp : Type , mt : MethodicType , fail : Message => Unit )(using Context ): Type =
1231- ToResult (tp, mt, fail)(tp)
1255+ def toResult (tp : Type , mt : MethodicType , sym : Symbol , fail : Message => Unit )(using Context ): Type =
1256+ ToResult (tp, mt, sym, fail)(tp)
12321257
12331258 /** Map global roots in function results to result roots. Also,
12341259 * map roots in the types of def methods that are parameterless
@@ -1244,7 +1269,7 @@ object Capabilities:
12441269 else apply(mt))
12451270 case t : MethodType if variance > 0 && t.marksExistentialScope =>
12461271 val t1 = mapOver(t).asInstanceOf [MethodType ]
1247- t1.derivedLambdaType(resType = toResult(t1.resType, t1, fail))
1272+ t1.derivedLambdaType(resType = toResult(t1.resType, t1, sym, fail))
12481273 case CapturingType (parent, refs) =>
12491274 t.derivedCapturingType(this (parent), refs)
12501275 case t : (LazyRef | TypeVar ) =>
@@ -1259,7 +1284,7 @@ object Capabilities:
12591284 m(tp) match
12601285 case tp1 : ExprType if sym.is(Method , butNot = Accessor ) =>
12611286 // Map the result of parameterless `def` methods.
1262- tp1.derivedExprType(toResult(tp1.resType, tp1, fail))
1287+ tp1.derivedExprType(toResult(tp1.resType, tp1, sym, fail))
12631288 case tp1 : PolyType if ! tp1.resType.isInstanceOf [MethodicType ] =>
12641289 // Map also the result type of method with only type parameters.
12651290 // This way, the `^` in the following method will be mapped to a `ResultCap`:
@@ -1269,7 +1294,7 @@ object Capabilities:
12691294 // ```
12701295 // This is more desirable than interpreting `^` as a `Fresh` at the level of `Buffer.empty`
12711296 // in most cases.
1272- tp1.derivedLambdaType(resType = toResult(tp1.resType, tp1, fail))
1297+ tp1.derivedLambdaType(resType = toResult(tp1.resType, tp1, sym, fail))
12731298 case tp1 => tp1
12741299 end toResultInResults
12751300
0 commit comments