Skip to content
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
wants to merge 14 commits into from
Closed

Conversation

odersky
Copy link
Contributor

@odersky odersky commented May 25, 2024

In adapt:

  • If an EX is toplevel in expected type, replace with a fresh capture set variable
  • If an EX is toplevel in actual type, find a trackable replacement x as follows:
    • If actual type is a trackable ref, pick that
    • Otherwise, create a fresh skolem val symbol with currently enclosing
      method as owner, and use its termRef
  • Then,
    • If the EX-bound variable appears only at toplevel, replace it with x
    • Otherwise, replace it with a fresh reach capability x*.

In avoidance of a type T:

  • Replace every local variable in T (including locally created EX-skolems)
    with a fresh EX-bound variable, and EX-quantify T over all freshly created EX-bound variables.
  • Check that no local variable appears under a box.

In cv-computation (markFree):

  • Reach capabilities x* of a parameter x cannot appear in the capture set of
    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:

  • If T is not the type of a parameter, check that no EX-bound variable appears
    under a box.

Subtype rules

  • new alphabet: existentially bound variables a.
  • they can be stored in environments Gamma.
  • they are alpha-renable, usual hygiene conditions apply
Gamma |- EX a.T <: U
  if Gamma, a |- T <: U

Gamma |- T <: EX a.U
  if a in Gamma, T <: U

Representation:

EX a.T[a] is represented as

    r @ RecType(T[TermRef[r.recThis, excap]]

where excap is a global synthetic symbol.

Subtype checking algorithm, general scheme:

Maintain two structures in TypeComparer:

  openExistentials: List[RecThis]
  assocExistentials: Map[RecThis, List[RecThis]]

openExistentials corresponds to the list of existential variables stored in the environment.
assocExistentials maps existential variables bound by existentials appearing on the right
of 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:

     val saved = openExistentials
     openExistentials = tp1.recThis :: openExistentials
     try tp1a <:< tp2
     finally openExistentials = saved
    
  • If tp2 is an existential EX a.tp2a:

     val saved = assocExistentials
     assocExistentials = assocExistentials + (tp2.recThis -> openExistentials
     try tp1 <:< tp2a
     finally assocExistentials = saved
    
  • If tp1 and tp2 are existentially bound variables TermRef(pre1/pre2: RecThis, excap):

     assocExistentials(pre2).contains(pre1) &&
     { assocExistentials(pre2) = List(pre1); true }
    

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:

    trait Exists[X]

A typical expression of an existential is then

    Exists[(x: Capability) => A ->{x} B]

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 and
that 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:

  • The type of a binding: i.e a type of a parameter or val, a result type of a def, or
    a self type of a class.
  • The type of a type ascription in an expression or pattern
  • The argument and result types of a function.

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:

  1. In an existential scope, replace every occurrence of ^ with a fresh existentially
    bound variable and quantify over all variables such introduced.

  2. 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).

  3. 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 to EX c. A ->{c} B.

  • Iterator[A => B] expands to EX c. Iterator[A ->{c} B]

  • A -> B^ expands to A -> EX c.B^{c}.

  • If we define type Fun[T] = A -> T, then Fun[B^] expands to EX c.Fun[B^{c}], which
    dealiases to EX c.A -> B^{c}.

  • If we define

        type F = A -> Fun[B^]
    

    then the type alias expands to

        type F = A -> EX c.A -> B^{c}
    

    since the result type of the RHS is a legal existential scope.

@odersky odersky requested review from Linyxus and noti0na1 May 25, 2024 11:03
@odersky
Copy link
Contributor Author

odersky commented May 25, 2024

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 Existential.scala and also copied to be the description of this PR.

@odersky odersky force-pushed the cc-ex branch 3 times, most recently from b6669df to 48f2be2 Compare May 26, 2024 09:06
@odersky odersky marked this pull request as ready for review May 27, 2024 11:45
@odersky
Copy link
Contributor Author

odersky commented May 27, 2024

Rebased on top of #20396.

odersky added 13 commits May 29, 2024 10:55
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 odersky force-pushed the cc-ex branch 2 times, most recently from a339e04 to 2a4c496 Compare May 31, 2024 20:16
@odersky
Copy link
Contributor Author

odersky commented Jun 7, 2024

Superseded by #20531

@odersky odersky closed this Jun 7, 2024
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
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant