Skip to content
Open
46 changes: 29 additions & 17 deletions compiler/src/dotty/tools/dotc/cc/Capability.scala
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,13 @@ object Capabilities:
/** Is this LocalCap at the right level to be able to subsume `ref`?
*/
def acceptsLevelOf(ref: Capability)(using Context): Boolean =
if ccConfig.useLocalCapLevels && !CCState.collapseLocalCaps then
ccOwner.isContainedIn(ref.levelOwner.widenOwner(skipModules = true))
|| classifier.derivesFrom(defn.Caps_Unscoped)
else ref.core match
ref.core match
case ResultCap(_) | _: ParamRef => false
case _ => true
case _ =>
!ccConfig.useLocalCapLevels
|| CCState.collapseLocalCaps
|| ccOwner.isContainedIn(ref.levelOwner.widenOwner(skipModules = true))
|| classifier.derivesFrom(defn.Caps_Unscoped)

/** Classify this LocalCap as `cls`, provided `isClassified` is still false.
* @param freeze Determines future `isClassified` state.
Expand Down Expand Up @@ -408,6 +409,7 @@ object Capabilities:
case ReadOnly(ref1) => ref1.classifier
case Maybe(ref1) => ref1.classifier
case self: LocalCap => self.hiddenSet.classifier
case self: ResultCap => self.origin.classifier
case _ => NoSymbol

/** Is this a reach reference of the form `x*` or a readOnly or maybe variant
Expand Down Expand Up @@ -1024,7 +1026,7 @@ object Capabilities:
case TypeArg(tp: Type)
case UnsafeAssumePure
case Formal(pref: ParamRef, app: tpd.Apply)
case ResultInstance(methType: Type, tree: Tree)
case ResultInstance(result: ResultCap, methType: Type, tree: Tree = EmptyTree)
case UnapplyInstance(info: MethodType)
case LocalInstance(restpe: Type)
case NewInstance(tp: Type, fields: List[Symbol])
Expand Down Expand Up @@ -1060,7 +1062,7 @@ object Capabilities:
if meth.exists
then i" when checking argument to parameter ${pref.paramName} of $meth"
else ""
case ResultInstance(mt, tree) =>
case ResultInstance(rc, mt, tree) =>
def methDescr(tree: Tree): String = tree match
case app: GenericApply =>
methDescr(app.fun)
Expand Down Expand Up @@ -1221,7 +1223,7 @@ object Capabilities:
end Internalize

/** Map top-level free ResultCaps one-to-one to LocalCap instances */
def resultToAny(tp: Type, origin: Origin)(using Context): Type =
def resultToAny(tp: Type, mkOrigin: ResultCap => Origin)(using Context): Type =
val subst = new TypeMap:
val seen = EqHashMap[ResultCap, LocalCap | GlobalCap]()
var localBinders: SimpleIdentitySet[MethodType] = SimpleIdentitySet.empty
Expand All @@ -1245,9 +1247,9 @@ object Capabilities:
else
// Create a LocalCap skolem that does not subsume anything
def localCapSkolem =
val c = LocalCap(origin)
c.hiddenSet.markSolved(provisional = false)
c
val lc = LocalCap(mkOrigin(c))
lc.hiddenSet.markSolved(provisional = false)
lc
seen.getOrElseUpdate(c, localCapSkolem) // map free references to LocalCap
case _ => super.mapCapability(c, deep)
end subst
Expand All @@ -1264,16 +1266,14 @@ object Capabilities:
case _ =>
super.mapOver(t)

class ToResult(localResType: Type, mt: MethodicType, sym: Symbol, fail: Message => Unit)(using Context) extends CapMap:
class ToResult(mt: MethodicType, sym: Symbol)(using Context) extends CapMap:

def apply(t: Type) = mapOver(t)

override def mapCapability(c: Capability, deep: Boolean) = c match
case c: LocalCap =>
if variance >= 0 then
if sym.isAnonymousFunction && c.classifier.derivesFrom(defn.Caps_Unscoped) then
c
else if sym.exists && !c.ccOwner.isContainedIn(sym) then
if sym.exists && !c.ccOwner.isContainedIn(sym) then
//println(i"not mapping $c with ${c.ccOwner} in $sym")
c
else
Expand Down Expand Up @@ -1310,9 +1310,21 @@ object Capabilities:
end inverse
end ToResult

/** Map all ResultCaps that have the same primaryResultCap as one of the elements
* of `rcs` to their LocalCap origins.
*/
class RetractResult(rcs: SimpleIdentitySet[ResultCap])(using Context) extends TypeMap:
def apply(t: Type) = mapOver(t)
override def mapCapability(c: Capability, deep: Boolean) = c match
case c: ResultCap if rcs.exists(_.primaryResultCap == c.primaryResultCap) =>
c.primaryResultCap.origin match
case origin: LocalCap => origin
case _ => c
case _ => super.mapCapability(c, deep)

/** Replace all occurrences of `caps.any` or LocalCap in parts of this type by an existentially bound
* variable bound by `mt`. Stop at function or method types since these have been mapped before.
*/
def toResult(tp: Type, mt: MethodicType, sym: Symbol, fail: Message => Unit)(using Context): Type =
ToResult(tp, mt, sym, fail)(tp)
def toResult(tp: Type, mt: MethodicType, sym: Symbol)(using Context): Type =
ToResult(mt, sym)(tp)
end Capabilities
86 changes: 58 additions & 28 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,53 @@ extension (tp: Type)
if tp.isArrayUnderStrictMut then defn.Caps_Unscoped
else tp.classSymbols.map(_.classifier).foldLeft(defn.AnyClass)(leastClassifier)

/** The classifiers of all terminal capabilities in the span capture set of `tp` */
def embeddedLocalCaps(using Context): List[Capability] =
tp.spanCaptureSet.elems.filter(_.core.isInstanceOf[LocalCap]).toList

/** If classifier `clsfier` exists: A localCap instance with this classifier,
* possibly wrapped in a readOnly.
*/
def impliedCaptures(clsfier: Symbol, contributing: List[Symbol], readOnly: => Boolean)(using Context): CaptureSet =
clsfier match
case clsfier: ClassSymbol =>
val lcap = LocalCap(Origin.NewInstance(tp, contributing))
if clsfier != defn.AnyClass then
lcap.hiddenSet.adoptClassifier(clsfier)
(if readOnly then lcap.readOnly else lcap).singletonCaptureSet
case _ =>
CaptureSet.empty

/** Does this (methodic) type have `any` in the span capture set of its
* result type?
*/
def hasCapInResult(using Context): Boolean =
val (mt: MethodicType) = tp.stripPoly.runtimeChecked
mt.resType.spanCaptureSet.containsGlobalOrLocalCap

/** The implied captures of a lambda that come from its result type.
* This is the set that needs to be added to a lambda type to ensure
* monotonicity of function types.
*/
def impliedLambdaCaptures(using Context): CaptureSet = tp match
case RefinedType(_, rname, rinfo) if rname == nme.apply =>
rinfo.impliedLambdaCaptures
case tp: PolyType =>
tp.resType.impliedLambdaCaptures
case tp: MethodicType =>
val localCaps = tp.resType.embeddedLocalCaps
val impliedClr = localCaps
.map(_.classifier)
.collect:
case cl: ClassSymbol => cl
.commonAncestor
tp.impliedCaptures(impliedClr, Nil, localCaps.forall(_.isReadOnly))
.showing(i"implied lambda captures of $tp = $result", capt)
case CapturingType(parent, _) =>
parent.impliedLambdaCaptures
case _ =>
CaptureSet.empty

extension (tp: MethodOrPoly)
/** A method marks an existential scope unless it is the prefix of a curried method */
def marksExistentialScope(using Context): Boolean =
Expand Down Expand Up @@ -574,10 +621,6 @@ extension (cls: ClassSymbol) {
ccState.fieldsWithExplicitTypes // pick fields with explicit types for classes in this compilation unit
.getOrElse(cls, cls.info.decls.toList) // pick all symbols in class scope for other classes

def commonAncestor(clss: List[ClassSymbol]): Symbol =
if clss.isEmpty then NoSymbol
else clss.reduce(greatestClassifier)

/** The implied classifier of the LocalCap of the class instance, derived from
* - the clasifiers of the LocalCaps in the span capture sets of all fields
* - the implied classifiers of the parent classes
Expand All @@ -588,15 +631,15 @@ extension (cls: ClassSymbol) {
def impliedClassifier(cls: Symbol): Symbol = cls match
case cls: ClassSymbol =>
val fieldClassifiers =
knownFields(cls).flatMap(classifiersOfLocalCapsInType)
knownFields(cls).flatMap(classifiersOfLocalCapsInInfo)
val parentClassifiers =
cls.parentSyms.map(impliedClassifier).collect:
case cl: ClassSymbol => cl
val stateClassifiers =
if cls.typeRef.isStatefulType(varsOnly = true)
then cls.classifier :: Nil
else Nil
commonAncestor(fieldClassifiers ++ parentClassifiers ++ stateClassifiers)
(fieldClassifiers ++ parentClassifiers ++ stateClassifiers).commonAncestor
case _ => NoSymbol

def contributingFields(cls: Symbol): List[Symbol] = cls match
Expand All @@ -606,24 +649,11 @@ extension (cls: ClassSymbol) {
ownFields ++ parentFields
case _ => Nil

def maybeRO(ref: Capability, fields: List[Symbol]) =
if !cls.typeRef.isStatefulType() && fields.forall(allLocalCapsInTypeAreRO)
then ref.readOnly
else ref

def localCap(fields: List[Symbol]) =
LocalCap(Origin.NewInstance(core, fields))

val impliedClr = impliedClassifier(cls)
val contributing = contributingFields(cls)
val impliedSet = impliedClr match
case impliedClr: ClassSymbol =>
val result = localCap(contributing)
if impliedClr != defn.AnyClass then
result.hiddenSet.adoptClassifier(impliedClr)
maybeRO(result, contributing).singletonCaptureSet
case _ =>
CaptureSet.empty
def readOnly =
!cls.typeRef.isStatefulType() && contributing.forall(allLocalCapsInTypeAreRO)
val impliedSet = core.impliedCaptures(impliedClr, contributing, readOnly)
(impliedSet, contributing)
}

Expand Down Expand Up @@ -859,16 +889,12 @@ extension (sym: Symbol) {
* enclosing class.
*/
def memberCaps(using Context): List[Capability] =
if sym.contributesLocalCapsToClass then
sym.info.spanCaptureSet.elems
.filter(_.isTerminalCapability)
.toList
else Nil
if sym.contributesLocalCapsToClass then sym.info.embeddedLocalCaps else Nil

/** The classifiers of all terminal capabilities comntributed by this symbol
* to the capture set of the enclosing class.
*/
def classifiersOfLocalCapsInType(using Context): List[ClassSymbol] =
def classifiersOfLocalCapsInInfo(using Context): List[ClassSymbol] =
memberCaps.map(_.classifier).collect:
case cl: ClassSymbol => cl

Expand Down Expand Up @@ -900,6 +926,10 @@ extension (tp: AnnotatedType) {
case _ => false
}

extension (clss: List[ClassSymbol])
def commonAncestor(using Context): Symbol =
if clss.isEmpty then NoSymbol else clss.reduce(greatestClassifier)

/** A prototype that indicates selection */
class PathSelectionProto(val selector: Symbol, val pt: Type, val tree: Tree) extends typer.ProtoTypes.WildcardSelectionProto

Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/cc/CaptureSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,9 @@ sealed abstract class CaptureSet extends Showable:
* does not contain a ResultCap?
*/
final def containsGlobalOrLocalCap(using Context) =
!containsResultCapability
&& elems.exists: elem =>
elems.exists: elem =>
elem.core match
case _: GlobalCap => true
case GlobalAny => true
case _: LocalCap => true
case _ => false

Expand Down
Loading
Loading