Skip to content

Remove ^ in capture set syntax #22725

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

Closed
wants to merge 3 commits into from

Conversation

noti0na1
Copy link
Member

@noti0na1 noti0na1 commented Mar 5, 2025

This PR simplifies the capture set syntax by removing unnecessary ^ symbols in capture sets.
This change is the first step making the syntax cleaner and more consistent for capture polymorphism.

  1. Added a new InCaptureSet mode in Mode.scala to handle capture set annotation typing;
  2. Modified the parser to remove the ^ suffix from capture set syntax
  3. If InCaptureSet is set and a term ident or select is not found, retry typing as a type wrapped in CapsOf.

@noti0na1 noti0na1 changed the title Remove upper arrow Remove ^ in capture set syntax Mar 5, 2025
@noti0na1 noti0na1 requested a review from bracevac March 5, 2025 20:01
Copy link
Contributor

@bracevac bracevac left a comment

Choose a reason for hiding this comment

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

Coolio!

@noti0na1 noti0na1 marked this pull request as ready for review March 5, 2025 22:36
@noti0na1 noti0na1 requested a review from odersky March 6, 2025 12:10
@bracevac
Copy link
Contributor

bracevac commented Mar 6, 2025

Let's rebase this on #22539 and wait until after the release cutoff before merging.

@noti0na1
Copy link
Member Author

noti0na1 commented Mar 6, 2025

I don't think we need to rebase onto the separation checking PR. The change here is only inside the Typer and should be independent to the core cc phase.

@odersky
Copy link
Contributor

odersky commented Mar 6, 2025

How about all the test cases that need to be updated? I think it's better to rebase.

@odersky
Copy link
Contributor

odersky commented Mar 11, 2025

This scheme has the advantage that the code is very simple. But there is an issue: Assume we have

object C
...
def foo[C^](x: A^{C}) = ...

then A^{C} will refer to the outer C and probably fail type checking. What's worse, if the outer C actually reads

val C: Capability = ...

then we get scope inversion. The outer C will be selected over the inner C. I think we should try to avoid these problems by using a scheme similar to CBCompanion where when we run the Namer on [C^] we add C under a term name and as a type name in the same scope. Then we will always find the innermost C.

@noti0na1
Copy link
Member Author

I see, if we keep searching terms first, we need to create a "fake" symbol for every type bounded by CapSet to avoid scope inversion.

@noti0na1
Copy link
Member Author

I need to wait for the syntax change in #22758, so it is easier to tell which type def is a capture variable.

@odersky
Copy link
Contributor

odersky commented Mar 12, 2025

I agree. I first thought this was independent of the syntax changes, but maybe that's not the case.

@bracevac
Copy link
Contributor

@noti0na1 it's fairly complete now, modulo adapting the test cases.

odersky added a commit that referenced this pull request May 12, 2025
…#23063)

Closes #22490
Builds on #22725
Supersedes #22902

This syntax for capture variables and members caters to the view that
"they are another kind", and most conventions we know from higher-kinded
type parameters carry over.

A postfix `^` (analogous to `[_]`) indicates that a type
parameter/member ranges over capture sets
```scala
def sayHi[C^](l: Logger^{C}) = l.log("hello world")

trait LList[T, C^]:
  type D^ >: C // or equivalently >: {C}
  val node: Ref[T^{D}]^
```

These variables can be bounded by capture-set literals:
```scala
trait Foo:
  type C^ 
  type D^ >: {logger} <: {logger, async, this.C}
  type E^ : Monad // error, context bounds are forbidden
```

Just as with higher-kinded type parameters, the kind is "contagious",
and we can often avoid the hat if the bounds are concrete capture sets
or other capture variables:

```scala
trait Foo:
  type C >: {} <: {cap}     // no hat needed
  type D >: C               // dito
  type E = {x,y,z}          // dito
  type F >: Object <: {cap} // error, ill-kinded
```

Capture variables will be erased if capture checking is turned off. In
non-capture-checked code, they are essentially type variables within the
interval `>: caps.CapSet <: caps.CapSet`. With capture checking turned
on and without explicit bounds, the interval corresponds to `>:
caps.CapSet^{} <: caps.CapSet^{caps.cap}`.

## Tasks
- [x] Bullet-proof the "can avoid the hat" for binders scheme.
- [x] Grammar doc.
- [x] Update language reference.
- [x] Port new tests.
- [x] Clarify when and where to produce attachments.
- [x] Forbid nonsense intervals mixing capture sets and types.
@noti0na1 noti0na1 closed this May 12, 2025
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.

3 participants