-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Exclusive capabilities #22218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exclusive capabilities #22218
Changes from 1 commit
22cb87b
216973e
234d0dc
a239bfa
d211bf9
fd327e3
b0be75b
c377f49
1cdb21b
937b8af
438ea60
c1f2542
9ca1776
564c847
94fcabc
e229807
84b5bff
c1ad0c1
65a1301
9f40649
b0628d1
813c585
a41c10f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -100,19 +100,25 @@ trait CaptureRef extends TypeProxy, ValueType: | |||||
/** Is this reference the generic root capability `cap` or a Fresh.Cap instance? */ | ||||||
final def isCapOrFresh(using Context): Boolean = isCap || isFresh | ||||||
|
||||||
/** Is this reference one the generic root capabilities `cap` or `cap.rd` ? */ | ||||||
/** Is this reference one of the generic root capabilities `cap` or `cap.rd` ? */ | ||||||
final def isRootCapability(using Context): Boolean = this match | ||||||
case ReadOnlyCapability(tp1) => tp1.isCapOrFresh | ||||||
case _ => isCapOrFresh | ||||||
|
||||||
/** Is this reference capability that does not derive from another capability ? */ | ||||||
/** Is this reference a capability that does not derive from another capability? | ||||||
* Includes read-only versions of maximal capabilities. | ||||||
*/ | ||||||
final def isMaxCapability(using Context): Boolean = this match | ||||||
case tp: TermRef => tp.isCap || tp.info.derivesFrom(defn.Caps_Exists) | ||||||
case tp: TermParamRef => tp.underlying.derivesFrom(defn.Caps_Exists) | ||||||
case Fresh.Cap(_) => true | ||||||
case ReadOnlyCapability(tp1) => tp1.isMaxCapability | ||||||
case _ => false | ||||||
|
||||||
/** An exclusive capability is a capability that derives | ||||||
* indirectly from a maximal capability without goinh through | ||||||
* a read-only capability first. | ||||||
*/ | ||||||
final def isExclusive(using Context): Boolean = | ||||||
!isReadOnly && (isMaxCapability || captureSetOfInfo.isExclusive) | ||||||
|
||||||
|
@@ -159,8 +165,6 @@ trait CaptureRef extends TypeProxy, ValueType: | |||||
* X: CapSet^c1...CapSet^c2, (CapSet^c1) subsumes y ==> X subsumes y | ||||||
* Y: CapSet^c1...CapSet^c2, x subsumes (CapSet^c2) ==> x subsumes Y | ||||||
* Contains[X, y] ==> X subsumes y | ||||||
* | ||||||
* TODO: Move to CaptureSet | ||||||
*/ | ||||||
final def subsumes(y: CaptureRef)(using ctx: Context, vs: VarState = VarState.Separate): Boolean = | ||||||
|
||||||
|
@@ -239,7 +243,7 @@ trait CaptureRef extends TypeProxy, ValueType: | |||||
end subsumes | ||||||
|
||||||
/** This is a maximal capabaility that subsumes `y` in given context and VarState. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* @param canAddHidden If true we allow maximal capabilties to subsume all other capabilities. | ||||||
* @param canAddHidden If true we allow maximal capabilities to subsume all other capabilities. | ||||||
* We add those capabilities to the hidden set if this is Fresh.Cap | ||||||
* If false we only accept `y` elements that are already in the | ||||||
* hidden set of this Fresh.Cap. The idea is that in a VarState that | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -636,7 +636,8 @@ object CaptureSet: | |
*/ | ||
def solve()(using Context): Unit = | ||
if !isConst then | ||
val approx = upperApprox(empty).map(Fresh.FromCap(NoSymbol).inverse) | ||
val approx = upperApprox(empty) | ||
.map(Fresh.FromCap(NoSymbol).inverse) // Fresh.Cap --> cap | ||
.showing(i"solve $this = $result", capt) | ||
//println(i"solving var $this $approx ${approx.isConst} deps = ${deps.toList}") | ||
val newElems = approx.elems -- elems | ||
|
@@ -1139,6 +1140,7 @@ object CaptureSet: | |
|
||
/** A template for maps on capabilities where f(c) <: c and f(f(c)) = c */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this not be an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's always one or the other. BiTypeMaps are better for type inference. |
||
private abstract class NarrowingCapabilityMap(using Context) extends BiTypeMap: | ||
|
||
def mapRef(ref: CaptureRef): CaptureRef | ||
|
||
def apply(t: Type) = t match | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -519,8 +519,7 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
def includeCallCaptures(sym: Symbol, resType: Type, tree: Tree)(using Context): Unit = resType match | ||||||
case _: MethodOrPoly => // wait until method is fully applied | ||||||
case _ => | ||||||
if sym.exists then | ||||||
if curEnv.isOpen then markFree(capturedVars(sym), tree) | ||||||
if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), tree) | ||||||
|
||||||
/** Under the sealed policy, disallow the root capability in type arguments. | ||||||
* Type arguments come either from a TypeApply node or from an AppliedType | ||||||
|
@@ -556,16 +555,21 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
if param.isUseParam then markFree(arg.nuType.deepCaptureSet, errTree) | ||||||
end disallowCapInTypeArgs | ||||||
|
||||||
/** Rechecking idents involves: | ||||||
* - adding call captures for idents referring to methods | ||||||
* - marking as free the identifier with any selections or .rd | ||||||
* modifiers implied by the expected type | ||||||
*/ | ||||||
override def recheckIdent(tree: Ident, pt: Type)(using Context): Type = | ||||||
val sym = tree.symbol | ||||||
if sym.is(Method) then | ||||||
// If ident refers to a parameterless method, charge its cv to the environment | ||||||
includeCallCaptures(sym, sym.info, tree) | ||||||
else if !sym.isStatic then | ||||||
// Otherwise charge its symbol, but add all selections implied by the e | ||||||
// expected type `pt`. | ||||||
// Example: If we have `x` and the expected type says we select that with `.a.b`, | ||||||
// we charge `x.a.b` instead of `x`. | ||||||
// Otherwise charge its symbol, but add all selections and also any `.rd` | ||||||
// modifier implied by the expected type `pt`. | ||||||
// Example: If we have `x` and the expected type says we select that with `.a.b` | ||||||
// where `b` is a read-only method, we charge `x.a.b.rd` instead of `x`. | ||||||
def addSelects(ref: TermRef, pt: Type): CaptureRef = pt match | ||||||
case pt: PathSelectionProto if ref.isTracked => | ||||||
if pt.sym.isReadOnlyMethod then | ||||||
|
@@ -582,7 +586,8 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
super.recheckIdent(tree, pt) | ||||||
|
||||||
/** The expected type for the qualifier of a selection. If the selection | ||||||
* could be part of a capabaility path, we return a PathSelectionProto. | ||||||
* could be part of a capability path or is a a read-only method, we return | ||||||
* a PathSelectionProto. | ||||||
*/ | ||||||
override def selectionProto(tree: Select, pt: Type)(using Context): Type = | ||||||
val sym = tree.symbol | ||||||
|
@@ -616,6 +621,9 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
} | ||||||
case _ => denot | ||||||
|
||||||
// Don't allow update methods to be called unless the qualifier captures | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// contain an exclusive referenece. TODO This should probabkly rolled into | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// qualifier logic once we have it. | ||||||
if tree.symbol.isUpdateMethod && !qualType.captureSet.isExclusive then | ||||||
report.error( | ||||||
em"""cannot call update ${tree.symbol} from $qualType, | ||||||
|
@@ -651,8 +659,8 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
selType | ||||||
}//.showing(i"recheck sel $tree, $qualType = $result") | ||||||
|
||||||
/** Hook for massaging a function before it is applied. Copies all @use annotations | ||||||
* on method parameter symbols to the corresponding paramInfo types. | ||||||
/** Hook for massaging a function before it is applied. Copies all @use and @consume | ||||||
* annotations on method parameter symbols to the corresponding paramInfo types. | ||||||
*/ | ||||||
override def prepareFunction(funtpe: MethodType, meth: Symbol)(using Context): MethodType = | ||||||
val paramInfosWithUses = | ||||||
|
@@ -682,7 +690,8 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
includeCallCaptures(meth, res, tree) | ||||||
res | ||||||
|
||||||
/** Recheck argument, and, if formal parameter carries a `@use`, | ||||||
/** Recheck argument against a "freshened" version of `formal` where toplevel `cap` | ||||||
* occurrences are replaced by `Fresh.Cap`. Also, if formal parameter carries a `@use`, | ||||||
* charge the deep capture set of the actual argument to the environment. | ||||||
*/ | ||||||
protected override def recheckArg(arg: Tree, formal: Type)(using Context): Type = | ||||||
|
@@ -773,16 +782,21 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
|
||||||
/** First half of result pair: | ||||||
* Refine the type of a constructor call `new C(t_1, ..., t_n)` | ||||||
* to C{val x_1: T_1, ..., x_m: T_m} where x_1, ..., x_m are the tracked | ||||||
* parameters of C and T_1, ..., T_m are the types of the corresponding arguments. | ||||||
* to C{val x_1: @refineOverride T_1, ..., x_m: @refineOverride T_m} | ||||||
* where x_1, ..., x_m are the tracked parameters of C and | ||||||
* T_1, ..., T_m are the types of the corresponding arguments. The @refineOveride | ||||||
* annotations avoid problematic intersections of capture sets when those | ||||||
* parameters are selected. | ||||||
* | ||||||
* Second half: union of initial capture set and all capture sets of arguments | ||||||
* to tracked parameters. | ||||||
* to tracked parameters. The initial capture set `initCs` is augmented with | ||||||
* - Fresh.Cap if `core` extends Mutable | ||||||
* - Fresh.Cap.rd if `core` extends Capability | ||||||
*/ | ||||||
def addParamArgRefinements(core: Type, initCs: CaptureSet): (Type, CaptureSet) = | ||||||
var refined: Type = core | ||||||
var allCaptures: CaptureSet = | ||||||
if core.derivesFromMutable then CaptureSet.fresh() | ||||||
if core.derivesFromMutable then initCs ++ CaptureSet.fresh() | ||||||
else if core.derivesFromCapability then initCs ++ Fresh.Cap().readOnly.singletonCaptureSet | ||||||
else initCs | ||||||
for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do | ||||||
|
@@ -1488,7 +1502,7 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
/** If actual is a capturing type T^C extending Mutable, and expected is an | ||||||
* unboxed non-singleton value type not extending mutable, narrow the capture | ||||||
* set `C` to `ro(C)`. | ||||||
* The unboxed condition ensures that the expected is not a type variable | ||||||
* The unboxed condition ensures that the expected type is not a type variable | ||||||
* that's upper bounded by a read-only type. In this case it would not be sound | ||||||
* to narrow to the read-only set, since that set can be propagated | ||||||
* by the type variable instantiation. | ||||||
|
@@ -1514,9 +1528,9 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
actual | ||||||
else | ||||||
val improvedVAR = improveCaptures(actual.widen.dealiasKeepAnnots, actual) | ||||||
val improvedRO = improveReadOnly(improvedVAR, expected) | ||||||
val improved = improveReadOnly(improvedVAR, expected) | ||||||
val adapted = adaptBoxed( | ||||||
improvedRO.withReachCaptures(actual), expected, tree, | ||||||
improved.withReachCaptures(actual), expected, tree, | ||||||
covariant = true, alwaysConst = false, boxErrors) | ||||||
if adapted eq improvedVAR // no .rd improvement, no box-adaptation | ||||||
then actual // might as well use actual instead of improved widened | ||||||
|
@@ -1563,17 +1577,19 @@ class CheckCaptures extends Recheck, SymTransformer: | |||||
|
||||||
/** Check that overrides don't change the @use or @consume status of their parameters */ | ||||||
override def additionalChecks(member: Symbol, other: Symbol)(using Context): Unit = | ||||||
def fail(msg: String) = | ||||||
report.error( | ||||||
OverrideError(msg, self, member, other, self.memberInfo(member), self.memberInfo(other)), | ||||||
if member.owner == clazz then member.srcPos else clazz.srcPos) | ||||||
for | ||||||
(params1, params2) <- member.rawParamss.lazyZip(other.rawParamss) | ||||||
(param1, param2) <- params1.lazyZip(params2) | ||||||
do | ||||||
def checkAnnot(cls: ClassSymbol) = | ||||||
if param1.hasAnnotation(cls) != param2.hasAnnotation(cls) then | ||||||
fail(i"has a parameter ${param1.name} with different @${cls.name} status than the corresponding parameter in the overridden definition") | ||||||
report.error( | ||||||
OverrideError( | ||||||
i"has a parameter ${param1.name} with different @${cls.name} status than the corresponding parameter in the overridden definition", | ||||||
self, member, other, self.memberInfo(member), self.memberInfo(other) | ||||||
), | ||||||
if member.owner == clazz then member.srcPos else clazz.srcPos) | ||||||
|
||||||
checkAnnot(defn.UseAnnot) | ||||||
checkAnnot(defn.ConsumeAnnot) | ||||||
end OverridingPairsCheckerCC | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.