@@ -100,6 +100,8 @@ class CCState:
100100
101101 private var openExistentialScopes : List [MethodType ] = Nil
102102
103+ private var capIsRoot : Boolean = false
104+
103105object CCState :
104106
105107 opaque type Level = Int
@@ -144,6 +146,21 @@ object CCState:
144146 else
145147 op
146148
149+ /** Run `op` under the assumption that `cap` can subsume all other capabilties
150+ * except Result capabilities. Every use of this method should be scrutinized
151+ * for whether it introduces an unsoundness hole.
152+ */
153+ inline def withCapAsRoot [T ](op : => T )(using Context ): T =
154+ if isCaptureCheckingOrSetup then
155+ val ccs = ccState
156+ val saved = ccs.capIsRoot
157+ ccs.capIsRoot = true
158+ try op finally ccs.capIsRoot = saved
159+ else op
160+
161+ /** Is `caps.cap` a root capability that is allowed to subsume other capabilities? */
162+ def capIsRoot (using Context ): Boolean = ccState.capIsRoot
163+
147164 /** The currently opened existential scopes */
148165 def openExistentialScopes (using Context ): List [MethodType ] = ccState.openExistentialScopes
149166
@@ -441,14 +458,30 @@ extension (tp: Type)
441458 case AppliedType (tycon, _) => ! defn.isFunctionSymbol(tycon.typeSymbol)
442459 case _ => false
443460
444- /** Tests whether the type derives from `caps.Capability`, which means
445- * references of this type are maximal capabilities.
446- */
447- def derivesFromCapTrait (cls : ClassSymbol )(using Context ): Boolean = tp.dealias match
461+ /** Tests whether all CapturingType parts of the type that are traversed for
462+ * dcs computation satisfy at least one of two conditions:
463+ * 1. They decorate classes that extend the given capability class `cls`, or
464+ * 2. Their capture set is constant and consists only of capabilities
465+ * the derive from `cls` in the sense of `derivesFromCapTrait`.
466+ */
467+ def derivesFromCapTraitDeeply (cls : ClassSymbol )(using Context ): Boolean =
468+ val accumulate = new DeepTypeAccumulator [Boolean ]:
469+ def capturingCase (acc : Boolean , parent : Type , refs : CaptureSet ) =
470+ this (acc, parent)
471+ && (parent.derivesFromCapTrait(cls)
472+ || refs.isConst && refs.elems.forall(_.derivesFromCapTrait(cls)))
473+ def abstractTypeCase (acc : Boolean , t : TypeRef , upperBound : Type ) =
474+ this (acc, upperBound)
475+ accumulate(true , tp)
476+
477+ /** Tests whether the type derives from capability class `cls`. */
478+ def derivesFromCapTrait (cls : ClassSymbol )(using Context ): Boolean = tp.dealiasKeepAnnots match
448479 case tp : (TypeRef | AppliedType ) =>
449480 val sym = tp.typeSymbol
450481 if sym.isClass then sym.derivesFrom(cls)
451482 else tp.superType.derivesFromCapTrait(cls)
483+ case ReachCapability (tp1) =>
484+ tp1.widen.derivesFromCapTraitDeeply(cls)
452485 case tp : (TypeProxy & ValueType ) =>
453486 tp.superType.derivesFromCapTrait(cls)
454487 case tp : AndType =>
@@ -545,7 +578,7 @@ extension (tp: Type)
545578 var change = false
546579 def apply (t : Type ) =
547580 if variance <= 0 then t
548- else t.dealiasKeepAnnots match
581+ else t.dealias match
549582 case t @ CapturingType (p, cs) if cs.containsRootCapability =>
550583 change = true
551584 val reachRef = if cs.isReadOnly then ref.reach.readOnly else ref.reach
@@ -804,33 +837,6 @@ object ReadOnlyCapability extends AnnotatedCapability(defn.ReadOnlyCapabilityAnn
804837object ReachCapability extends AnnotatedCapability (defn.ReachCapabilityAnnot ):
805838 protected def unwrappable (using Context ) = Set (defn.MaybeCapabilityAnnot , defn.ReadOnlyCapabilityAnnot )
806839
807- /** Offers utility method to be used for type maps that follow aliases */
808- trait ConservativeFollowAliasMap (using Context ) extends TypeMap :
809-
810- /** If `mapped` is a type alias, apply the map to the alias, while keeping
811- * annotations. If the result is different, return it, otherwise return `mapped`.
812- * Furthermore, if `original` is a LazyRef or TypeVar and the mapped result is
813- * the same as the underlying type, keep `original`. This avoids spurious differences
814- * which would lead to spurious dealiasing in the result
815- */
816- protected def applyToAlias (original : Type , mapped : Type ) =
817- val mapped1 = mapped match
818- case t : (TypeRef | AppliedType ) =>
819- val t1 = t.dealiasKeepAnnots
820- if t1 eq t then t
821- else
822- // If we see a type alias, map the alias type and keep it if it's different
823- val t2 = apply(t1)
824- if t2 ne t1 then t2 else t
825- case _ =>
826- mapped
827- original match
828- case original : (LazyRef | TypeVar ) if mapped1 eq original.underlying =>
829- original
830- case _ =>
831- mapped1
832- end ConservativeFollowAliasMap
833-
834840/** An extractor for all kinds of function types as well as method and poly types.
835841 * It includes aliases of function types such as `=>`. TODO: Can we do without?
836842 * @return 1st half: The argument types or empty if this is a type function
@@ -884,3 +890,36 @@ object ContainsParam:
884890 if tycon.typeSymbol == defn.Caps_ContainsTrait
885891 && cs.typeSymbol.isAbstractOrParamType => Some ((cs, ref))
886892 case _ => None
893+
894+ /** A class encapsulating the assumulator logic needed for `CaptureSet.ofTypeDeeply`
895+ * and `derivesFromCapTraitDeeply`.
896+ * NOTE: The traversal logic needs to be in sync with narrowCaps in CaptureOps, which
897+ * replaces caps with reach capabilties. There are two exceptions, however.
898+ * - First, invariant arguments. These have to be included to be conservative
899+ * in dcs but must be excluded in narrowCaps.
900+ * - Second, unconstrained type variables are handled specially in `ofTypeDeeply`.
901+ */
902+ abstract class DeepTypeAccumulator [T ](using Context ) extends TypeAccumulator [T ]:
903+ val seen = util.HashSet [Symbol ]()
904+
905+ protected def capturingCase (acc : T , parent : Type , refs : CaptureSet ): T
906+
907+ protected def abstractTypeCase (acc : T , t : TypeRef , upperBound : Type ): T
908+
909+ def apply (acc : T , t : Type ) =
910+ if variance < 0 then acc
911+ else t.dealias match
912+ case t @ CapturingType (p, cs1) =>
913+ capturingCase(acc, p, cs1)
914+ case t : TypeRef if t.symbol.isAbstractOrParamType && ! seen.contains(t.symbol) =>
915+ seen += t.symbol
916+ abstractTypeCase(acc, t, t.info.bounds.hi)
917+ case AnnotatedType (parent, _) =>
918+ this (acc, parent)
919+ case t @ FunctionOrMethod (args, res) =>
920+ if args.forall(_.isAlwaysPure) then this (acc, root.resultToFresh(res))
921+ else acc
922+ case _ =>
923+ foldOver(acc, t)
924+ end DeepTypeAccumulator
925+
0 commit comments