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

Drop @capability annotations #20396

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

odersky
Copy link
Contributor

@odersky odersky commented May 13, 2024

Replace with references that inherit trait Capability.

Also: Handle tracked vals in classes.

As a next step, addCaptureRefinements should use the tracked logic for all capturing parameters. Right now, we create a fresh capture set variable instead, but that is too loose.

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`.
@@ -11,4 +11,6 @@ import annotation.experimental
* THere, the capture set of any instance of `CanThrow` is assumed to be
* `{*}`.
*/
@experimental final class capability extends StaticAnnotation
@experimental
@deprecated("To make a class a capability, let it derive from the `Capability` trait instead")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not phrase this as, ...let it extend the...? derives has an established meaning which isn't what's intended here.

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
@odersky
Copy link
Contributor Author

odersky commented May 19, 2024

Summary of changes:

  • Drop @capability
  • Instead allow a class to extend a new trait caps.Capablity. So instead of @capability class IO it would be class IO extends caps.Capability.
  • The treatment of the two is different, though. @capability added ^ to references during setup. The new capability classes are instead treated as primitive maximal capabilities without having cap in their capture set.
  • We also retract the convention that capture sets in the parents of a class are somehow inherited by references to those classes.
  • We also refine box adaptation not to create spurious captureset variables in function types, which leads to more references with function typed being kept as singletons instead of being widened.
  • Finally, we refine the scheme that adds parameter refinements to class types. We now refine only if the parameter has a capture set (for the others it does not matter) and is not tracked (since tracked parameter dependencies are already handled anyway)

Copy link
Member

@noti0na1 noti0na1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still some import of annotation.capability can be deleted; the document should be updated as well.

Comment on lines +206 to +208
/** Does type derive from caps.Capability?, which means it references of this
* type are maximal capabilities?
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks if the type derives from `caps.Capability`, which means 
references of this type are maximal capabilities.

@@ -158,6 +159,10 @@ sealed abstract class CaptureSet extends Showable:
case _ => false
|| x.match
case ReachCapability(x1) => x1.subsumes(y.stripReach)
case x: TermRef =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do similar check for TermRef y as well.

@@ -2273,6 +2273,9 @@ object Types extends TypeUtils {
/** Is this reference the generic root capability `cap` ? */
def isRootCapability(using Context): Boolean = false

/** Is this reference capability that does not derive from another capability ? */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little confused when we should use isMaxCapability or isRootCapability? since "max capability" should cover the cases of "root capability". For example, inside subsumes, I feel isMaxCapability is a better choice.

| of an enclosing function literal with expected type () ?->{cap1} I
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/byname.scala:4:2 -----------------------------------------
4 | def f() = if cap1 == cap1 then g else g // error
| ^
| Found: (x$0: Int) ->{cap2} Int
| Required: (x$0: Int) -> Int
| Found: ((x$0: Int) ->{cap2} Int)^{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there additional empty cs added to the function type?


def test =
val x1: C1 = new C1
val x2: C1 = new C2 // error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this test is deleted? A term with type C2 or C3 should be tracked, and not able to be assigned to a pure C1.

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.

None yet

3 participants