Skip to content

Commit 4b69577

Browse files
committed
Implement subsumption rules for restricted capabilities
1 parent e1965f6 commit 4b69577

File tree

3 files changed

+33
-7
lines changed

3 files changed

+33
-7
lines changed

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

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,15 +336,20 @@ object Capabilities:
336336
case Maybe(ref1) => ref1.stripReadOnly.maybe
337337
case _ => this
338338

339-
final def stripRestricted(using Context): Capability = this match
340-
case Restricted(ref1, _) => ref1
341-
case ReadOnly(ref1) => ref1.stripRestricted.readOnly
342-
case Maybe(ref1) => ref1.stripRestricted.maybe
339+
/** Drop restrictions with clss `cls` or a superclass of `cls` */
340+
final def stripRestricted(cls: ClassSymbol)(using Context): Capability = this match
341+
case Restricted(ref1, cls1) if cls.isSubClass(cls1) => ref1
342+
case ReadOnly(ref1) => ref1.stripRestricted(cls).readOnly
343+
case Maybe(ref1) => ref1.stripRestricted(cls).maybe
343344
case _ => this
344345

346+
final def stripRestricted(using Context): Capability =
347+
stripRestricted(defn.NothingClass)
348+
345349
final def stripReach(using Context): Capability = this match
346350
case Reach(ref1) => ref1
347351
case ReadOnly(ref1) => ref1.stripReach.readOnly
352+
case Restricted(ref1, cls) => ref1.stripReach.restrict(cls)
348353
case Maybe(ref1) => ref1.stripReach.maybe
349354
case _ => this
350355

@@ -541,6 +546,22 @@ object Capabilities:
541546
self.classifier.isSubClass(cls)
542547
&& captureSetOfInfo.tryClassifyAs(cls)
543548

549+
def isKnownClassifiedAs(cls: ClassSymbol)(using Context): Boolean =
550+
transClassifiers match
551+
case ClassifiedAs(cs) => cs.forall(_.isSubClass(cls))
552+
case _ => false
553+
554+
def isKnownEmpty(using Context): Boolean = this match
555+
case Restricted(ref1, cls) =>
556+
val isEmpty = ref1.transClassifiers match
557+
case ClassifiedAs(cs) =>
558+
cs.forall(c => leastClassifier(c, cls) == defn.NothingClass)
559+
case _ => false
560+
isEmpty || ref1.isKnownEmpty
561+
case ReadOnly(ref1) => ref1.isKnownEmpty
562+
case Maybe(ref1) => ref1.isKnownEmpty
563+
case _ => false
564+
544565
def invalidateCaches() =
545566
captureSetValid = invalid
546567
classifiersValid = invalid
@@ -597,6 +618,7 @@ object Capabilities:
597618
|| viaInfo(y.info)(subsumingRefs(this, _))
598619
case Maybe(y1) => this.stripMaybe.subsumes(y1)
599620
case ReadOnly(y1) => this.stripReadOnly.subsumes(y1)
621+
case Restricted(y1, cls) => this.stripRestricted(cls).subsumes(y1)
600622
case y: TypeRef if y.derivesFrom(defn.Caps_CapSet) =>
601623
// The upper and lower bounds don't have to be in the form of `CapSet^{...}`.
602624
// They can be other capture set variables, which are bounded by `CapSet`,
@@ -611,6 +633,7 @@ object Capabilities:
611633
case _ => false
612634
|| this.match
613635
case Reach(x1) => x1.subsumes(y.stripReach)
636+
case Restricted(x1, cls) => y.isKnownClassifiedAs(cls) && x1.subsumes(y)
614637
case x: TermRef => viaInfo(x.info)(subsumingRefs(_, y))
615638
case x: TypeRef if assumedContainsOf(x).contains(y) => true
616639
case x: TypeRef if x.derivesFrom(defn.Caps_CapSet) =>
@@ -652,7 +675,7 @@ object Capabilities:
652675
vs.ifNotSeen(this)(x.hiddenSet.elems.exists(_.subsumes(y)))
653676
|| levelOK
654677
&& ( y.tryClassifyAs(x.hiddenSet.classifier)
655-
|| { capt.println(i"$y is not classified as $x"); false }
678+
|| { capt.println(i"$y cannot be classified as $x"); false }
656679
)
657680
&& canAddHidden
658681
&& vs.addHidden(x.hiddenSet, y)
@@ -674,6 +697,7 @@ object Capabilities:
674697
case _ =>
675698
y match
676699
case ReadOnly(y1) => this.stripReadOnly.maxSubsumes(y1, canAddHidden)
700+
case Restricted(y1, cls) => this.stripRestricted(cls).maxSubsumes(y1, canAddHidden)
677701
case _ => false
678702

679703
/** `x covers y` if we should retain `y` when computing the overlap of
@@ -718,6 +742,7 @@ object Capabilities:
718742
val c1 = c.underlying.toType
719743
c match
720744
case _: ReadOnly => ReadOnlyCapability(c1)
745+
case Restricted(_, cls) => OnlyCapability(c1, cls)
721746
case _: Reach => ReachCapability(c1)
722747
case _: Maybe => MaybeCapability(c1)
723748
case _ => c1

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ extension (tp: Type)
8181
case ReadOnlyCapability(tp1) =>
8282
tp1.toCapability.readOnly
8383
case OnlyCapability(tp1, cls) =>
84-
tp1.toCapability.restrict(cls) // for now
84+
tp1.toCapability.restrict(cls)
8585
case ref: TermRef if ref.isCapRef =>
8686
GlobalCap
8787
case ref: Capability if ref.isTrackableRef =>
@@ -290,7 +290,7 @@ extension (tp: Type)
290290
def forceBoxStatus(boxed: Boolean)(using Context): Type = tp.widenDealias match
291291
case tp @ CapturingType(parent, refs) if tp.isBoxed != boxed =>
292292
val refs1 = tp match
293-
case ref: Capability if ref.isTracked || ref.isReach || ref.isReadOnly =>
293+
case ref: Capability if ref.isTracked || ref.isInstanceOf[DerivedCapability] =>
294294
ref.singletonCaptureSet
295295
case _ => refs
296296
CapturingType(parent, refs1, boxed)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ sealed abstract class CaptureSet extends Showable:
210210

211211
protected def addIfHiddenOrFail(elem: Capability)(using ctx: Context, vs: VarState): Boolean =
212212
elems.exists(_.maxSubsumes(elem, canAddHidden = true))
213+
|| elem.isKnownEmpty
213214
|| failWith(IncludeFailure(this, elem))
214215

215216
/** If this is a variable, add `cs` as a dependent set */

0 commit comments

Comments
 (0)