Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 RefinedType(_, rname, rinfo) if rname == nme.apply =>
rinfo.impliedLambdaCaptures
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