-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Existential capabilities design draft #20470
Closed
Closed
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The code is very provisional and likely to change. The important part of this first commit is the design, which is a doc comment in |
odersky
force-pushed
the
cc-ex
branch
3 times, most recently
from
May 26, 2024 09:06
b6669df
to
48f2be2
Compare
Rebased on top of #20396. |
A maximal capability is one that derives from `caps.Cap`. Also: drop the given caps.Cap. It's not clear why there needs to be a given for it.
The current handling of class type refinements is unsound. We cannot simply use a variable for the capture set of a class argument. What we need to do instead is treat class arguments as tracked. In this commit we at least allow explicitly declared tracked arguments. This needed two modifications: - Don't additionally add a capture set for tracked arguments - Handle the case where a capture reference is of a singleton type which is another capture reference. As a next step we should treat all class arguments as implicitly tracked.
Replace with references that inherit trait `Capability`.
Drop convention that classes inheriting from universal capturing types are capability classes. Capture sets of parents are instead ignored. The convention led to algebraic anomalies. For instance if class C extends A => B, Serializable then C <: (A => B) & Serializable, which has an empty capture set. Yet we treat every occurrence of C as implicitly carrying `cap`.
Only enrich classes with capture refinements for a parameter if the deep capture set of the parameter's type is nonempty.
… adaptation This change lets more ref trees with underlying function types keep their singleton types.
- Avoid creating capture sets of untrackable references - Refine disallowRootCapability to consider only explicit captures
When an expression has a type that derives from caps.Capability, add an explicit capture set. Also: Address other review comments
odersky
force-pushed
the
cc-ex
branch
2 times, most recently
from
May 31, 2024 20:16
a339e04
to
2a4c496
Compare
Superseded by #20531 |
bracevac
added a commit
that referenced
this pull request
Jul 19, 2024
The design has changed quite a bit relative to #20470. It is written up as a doc comment on object cc.Existentials.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
In adapt:
x
as follows:method as owner, and use its termRef
x
x*
.In avoidance of a type T:
with a fresh EX-bound variable, and EX-quantify T over all freshly created EX-bound variables.
In cv-computation (markFree):
the owning method. They have to be widened to dcs(x), or, where this is not
possible, it's an error.
In well-formedness checking of explicitly written type T:
under a box.
Subtype rules
a
.Representation:
EX a.T[a] is represented as
where
excap
is a global synthetic symbol.Subtype checking algorithm, general scheme:
Maintain two structures in TypeComparer:
openExistentials
corresponds to the list of existential variables stored in the environment.assocExistentials
maps existential variables bound by existentials appearing on the rightof a subtype judgement to a list of possible associations. Initally this is openExistentials
at the time when the existential on the right was dropped. It can become a single existential
when the existentially bound key variable is unified with one of the variables in the
environment.
Subtype checking algorithm, steps to add for tp1 <:< tp2:
If tp1 is an existential EX a.tp1a:
If tp2 is an existential EX a.tp2a:
If tp1 and tp2 are existentially bound variables
TermRef(pre1/pre2: RecThis, excap)
:Existential source syntax:
Existential types are ususally not written in source, since we still allow the
^
syntax that can express most of them more concesely (see below for translation rules).
But we should also allow to write existential types explicity, even if it ends up mainly
for debugging. To express them, we add the following trait definition in the caps object:
A typical expression of an existential is then
Existential types are expanded at Typer to the RecType syntax presented above. It is checked
that the type argument is a dependent function type with one argument of type
Capability
andthat this argument is used only in capture sets of the result type.
Existential types can only appear at the top-level of legal existential scopes. These are:
a self type of a class.
Expansion of ^:
We drop
cap
as a capture reference, but keep the unqualified^
syntax.This now expands to existentials. The translation treats each legal existential scope
separately. If existential scopes nest, the inner ones are translated first.
The expansion algorithm is then defined as follows:
In an existential scope, replace every occurrence of ^ with a fresh existentially
bound variable and quantify over all variables such introduced.
After this step, type aliases are expanded. If aliases have aliases in arguments,
the outer alias is expanded before the aliases in the arguments. Each time an alias
is expanded that reveals a
^
, apply step (1).The algorithm ends when no more alieases remain to be expanded.
^ captures outside an existential scope or the right hand side of a type alias (e.g. in
a class parent) are not allowed.
Examples:
A => B
is an alias type that expands to(A -> B)^
, which expands toEX c. A ->{c} B
.Iterator[A => B]
expands toEX c. Iterator[A ->{c} B]
A -> B^
expands toA -> EX c.B^{c}
.If we define
type Fun[T] = A -> T
, thenFun[B^]
expands toEX c.Fun[B^{c}]
, whichdealiases to
EX c.A -> B^{c}
.If we define
then the type alias expands to
since the result type of the RHS is a legal existential scope.