@@ -258,9 +258,11 @@ object Capabilities:
258258 trait Capability extends Showable :
259259
260260 private var myCaptureSet : CaptureSet | Null = uninitialized
261- private var myCaptureSetValid : Validity = invalid
261+ private var captureSetValid : Validity = invalid
262262 private var mySingletonCaptureSet : CaptureSet .Const | Null = null
263263 private var myDerived : List [DerivedCapability ] = Nil
264+ private var myClassifiers : Classifiers = UnknownClassifier
265+ private var classifiersValid : Validity = invalid
264266
265267 protected def cached [C <: DerivedCapability ](newRef : C ): C =
266268 def recur (refs : List [DerivedCapability ]): C = refs match
@@ -292,10 +294,7 @@ object Capabilities:
292294 case Maybe (ref1) => Maybe (ref1.restrict(cls))
293295 case ReadOnly (ref1) => ReadOnly (ref1.restrict(cls).asInstanceOf [Restricted ])
294296 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
297+ val combinedCls = leastClassifier(prevCls, cls)
299298 if combinedCls == prevCls then self
300299 else cached(Restricted (ref1, combinedCls))
301300 case self : (ObjectCapability | RootCapability | Reach ) => cached(Restricted (self, cls))
@@ -469,7 +468,7 @@ object Capabilities:
469468
470469 def derivesFromCapability (using Context ): Boolean = derivesFromCapTrait(defn.Caps_Capability )
471470 def derivesFromMutable (using Context ): Boolean = derivesFromCapTrait(defn.Caps_Mutable )
472- def derivesFromSharedCapability (using Context ): Boolean = derivesFromCapTrait(defn.Caps_SharedCapability )
471+ def derivesFromSharable (using Context ): Boolean = derivesFromCapTrait(defn.Caps_Sharable )
473472
474473 /** The capture set consisting of exactly this reference */
475474 def singletonCaptureSet (using Context ): CaptureSet .Const =
@@ -479,7 +478,7 @@ object Capabilities:
479478
480479 /** The capture set of the type underlying this reference */
481480 def captureSetOfInfo (using Context ): CaptureSet =
482- if myCaptureSetValid == currentId then myCaptureSet.nn
481+ if captureSetValid == currentId then myCaptureSet.nn
483482 else if myCaptureSet.asInstanceOf [AnyRef ] eq CaptureSet .Pending then CaptureSet .empty
484483 else
485484 myCaptureSet = CaptureSet .Pending
@@ -491,11 +490,60 @@ object Capabilities:
491490 myCaptureSet = null
492491 else
493492 myCaptureSet = computed
494- myCaptureSetValid = currentId
493+ captureSetValid = currentId
495494 computed
496495
496+ /** The transitive classifiers of this capability. */
497+ def transClassifiers (using Context ): Classifiers =
498+ def toClassifiers (cls : ClassSymbol ): Classifiers =
499+ if cls == defn.AnyClass then Unclassified
500+ else ClassifiedAs (cls :: Nil )
501+ if classifiersValid != currentId then
502+ myClassifiers = this match
503+ case self : FreshCap =>
504+ toClassifiers(self.hiddenSet.classifier)
505+ case self : RootCapability =>
506+ Unclassified
507+ case Restricted (_, cls) =>
508+ assert(cls != defn.AnyClass )
509+ if cls == defn.NothingClass then ClassifiedAs (Nil )
510+ else ClassifiedAs (cls :: Nil )
511+ case ReadOnly (ref1) =>
512+ ref1.transClassifiers
513+ case Maybe (ref1) =>
514+ ref1.transClassifiers
515+ case Reach (_) =>
516+ captureSetOfInfo.transClassifiers
517+ case self : CoreCapability =>
518+ joinClassifiers(toClassifiers(self.classifier), captureSetOfInfo.transClassifiers)
519+ if myClassifiers != UnknownClassifier then
520+ classifiersValid == currentId
521+ myClassifiers
522+ end transClassifiers
523+
524+ def tryClassifyAs (cls : ClassSymbol )(using Context ): Boolean =
525+ cls == defn.AnyClass
526+ || this .match
527+ case self : FreshCap =>
528+ self.hiddenSet.tryClassifyAs(cls)
529+ case self : RootCapability =>
530+ true
531+ case Restricted (_, cls1) =>
532+ assert(cls != defn.AnyClass )
533+ cls1.isSubClass(cls)
534+ case ReadOnly (ref1) =>
535+ ref1.tryClassifyAs(cls)
536+ case Maybe (ref1) =>
537+ ref1.tryClassifyAs(cls)
538+ case Reach (_) =>
539+ captureSetOfInfo.tryClassifyAs(cls)
540+ case self : CoreCapability =>
541+ self.classifier.isSubClass(cls)
542+ && captureSetOfInfo.tryClassifyAs(cls)
543+
497544 def invalidateCaches () =
498- myCaptureSetValid = invalid
545+ captureSetValid = invalid
546+ classifiersValid = invalid
499547
500548 /** x subsumes x
501549 * x =:= y ==> x subsumes y
@@ -603,12 +651,15 @@ object Capabilities:
603651
604652 vs.ifNotSeen(this )(x.hiddenSet.elems.exists(_.subsumes(y)))
605653 || levelOK
654+ && ( y.tryClassifyAs(x.hiddenSet.classifier)
655+ || { capt.println(i " $y is not classified as $x" ); false }
656+ )
606657 && canAddHidden
607658 && vs.addHidden(x.hiddenSet, y)
608659 case x : ResultCap =>
609660 val result = y match
610661 case y : ResultCap => vs.unify(x, y)
611- case _ => y.derivesFromSharedCapability
662+ case _ => y.derivesFromSharable
612663 if ! result then
613664 TypeComparer .addErrorNote(CaptureSet .ExistentialSubsumesFailure (x, y))
614665 result
@@ -618,7 +669,7 @@ object Capabilities:
618669 case _ : ResultCap => false
619670 case _ : FreshCap if CCState .collapseFresh => true
620671 case _ =>
621- y.derivesFromSharedCapability
672+ y.derivesFromSharable
622673 || canAddHidden && vs != VarState .HardSeparate && CCState .capIsRoot
623674 case _ =>
624675 y match
@@ -674,6 +725,39 @@ object Capabilities:
674725 def toText (printer : Printer ): Text = printer.toTextCapability(this )
675726 end Capability
676727
728+ /** Result type of `transClassifiers`. Interprete as follows:
729+ * UnknownClassifier: No list could be computed since some capture sets
730+ * are still unsolved variables
731+ * Unclassified : No set exists since some parts of tcs are not classified
732+ * ClassifiedAs(clss: All parts of tcss are classified with classes in clss
733+ */
734+ enum Classifiers :
735+ case UnknownClassifier
736+ case Unclassified
737+ case ClassifiedAs (clss : List [ClassSymbol ])
738+
739+ export Classifiers .{UnknownClassifier , Unclassified , ClassifiedAs }
740+
741+ /** The least classifier between `cls1` and `cls2`, which are either
742+ * AnyClass, NothingClass, or a class directly extending caps.Classifier.
743+ * @return if oen of cls1, cls2 is a subclass of the other, the subclass
744+ * otherwise NothingClass (which is a subclass of all classes)
745+ */
746+ def leastClassifier (cls1 : ClassSymbol , cls2 : ClassSymbol )(using Context ): ClassSymbol =
747+ if cls1.isSubClass(cls2) then cls1
748+ else if cls2.isSubClass(cls1) then cls2
749+ else defn.NothingClass
750+
751+ def joinClassifiers (cs1 : Classifiers , cs2 : Classifiers )(using Context ): Classifiers =
752+ // Drop classes that subclass classes of the other set
753+ def filterSub (cs1 : List [ClassSymbol ], cs2 : List [ClassSymbol ]) =
754+ cs1.filter(cls1 => ! cs2.exists(cls2 => cls1.isSubClass(cls2)))
755+ (cs1, cs2) match
756+ case (Unclassified , _) | (_, Unclassified ) => Unclassified
757+ case (UnknownClassifier , _) | (_, UnknownClassifier ) => UnknownClassifier
758+ case (ClassifiedAs (cs1), ClassifiedAs (cs2)) =>
759+ ClassifiedAs (filterSub(cs1, cs2) ++ filterSub(cs2, cs1))
760+
677761 /** The place of - and cause for - creating a fresh capability. Used for
678762 * error diagnostics
679763 */
0 commit comments