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

[CN] Meta-issue: syntax overhaul #304

Open
dc-mak opened this issue Jun 5, 2024 · 5 comments
Open

[CN] Meta-issue: syntax overhaul #304

dc-mak opened this issue Jun 5, 2024 · 5 comments
Labels

Comments

@dc-mak
Copy link
Contributor

dc-mak commented Jun 5, 2024

A few existing issues have touched upon the current syntax. The solutions for those bring function and predicate a lot closer together.

  1. Remove if-restriction ([CN] Mitigate / remove restrictions on conditional branching in predicates #266)
  2. Remove predicate pointer first restriction ([CN] Remove predicate pointer first restriction #303)
  3. Unify namespaces for functions and predicates ([CN] Unify scopes of predicates and functions (confusing err msg for "return (Predicate(args))") #288)
  4. Type annotations for expressions ([CN] Type annotations for expressions #305)
  5. Smarter unfolidng for functions [CN] Use a auto-unfolding scheme for functions #483

Though not strictly dependent on the above, it may be sensible to wait until they are finished for the next tasks (which are closer to nice-to-haves).

  1. Add base type heap and disallow that to be used in arguments or in data types
    5a. Add new unified syntax and map to existing backend
    5b. Optional but good engineering: refactor backend to match new sytnax

As mentioned in #288, at the end of this we could write things like

i32 add_one(i32 x) {
  let y = x + 1;
  y;
}

heap<i32> intQueueAux(i32 x) {
  return add_one(x);
 }

heap<i32> intQueue0(i32 x) {
  intQueueAux(0);
}

heap<i32> intQueue(i32 x) {
  let y = add_one(x);
  take q = intQueueAux(y);
  return q;
}

Pros:

  • Concise.
  • Generics are familiar to many (and monads familiar to theorists).
  • Make polymorphism easier to add and explain later.
  • Makes the type of locks easier to add and explain later.

Cons:

  • Needs a few restrictions to keep things first-order for now (no heap<_> values in datatypes or in arguments).
  • Not requiring a return (and using return as the monad heap<_> ) could be confusing for C developers.
  • Bit unsure about take. Maybe let* or let <id> = *<heap_expr>;? Like in Idris' !-notation could be nice.
i32 add_one(i32 x) {
  return  x + 1;
}

heap<i32> intQueueAux(i32 x) {
  return heap(add_one(x));
}

heap<i32> intQueue(i32 x) {
  return intQueueAux(0);
}

heap<i32> intQueue(i32 x) {
  let y = add_one(x);
  let q = *intQueueAux(y); // This is redundant and for illustration only; desugars to --
  return heap(q);          // take _genvar = intQueueAux(y); let q = _genvar; return heap(q)
}
@bcpierce00
Copy link
Collaborator

bcpierce00 commented Jun 5, 2024 via email

@dc-mak
Copy link
Contributor Author

dc-mak commented Jun 5, 2024

Added, thanks

@dc-mak dc-mak added the cn label Jun 5, 2024
@dc-mak dc-mak changed the title [CN] Syntax overhaul [CN] Meta-issue: syntax overhaul Jun 7, 2024
@peterohanley
Copy link

Are the type annotations in declaration specs ever not fully determined by the declaration? If not, why have a separate form for the declaration spec at all? Concretely:

//before
int foo(int x, const char *y, struct bar **z);
/*@ spec foo(i32 x, pointer y, pointer z);
    requires ...; // hardcoded in grammar
    ensures ...; // hardcoded in grammar
@*/

//after
int foo(int x, const char *y, struct bar **z);
/*@ spec foo(x, y, z); // added to function_spec_item grammar node
    requires ...; // just another function_spec_item
    ensures ...; // just another function_spec_item
    trusted; // parses for free now
    accesses qux; // also parses for free
@*/

The arguments could also be removed. I like concretely bringing them into CN scope in the same block.

@cp526
Copy link
Collaborator

cp526 commented Aug 7, 2024

Are the type annotations in declaration specs ever not fully determined by the declaration? If not, why have a separate form for the declaration spec at all? Concretely:

//before
int foo(int x, const char *y, struct bar **z);
/*@ spec foo(i32 x, pointer y, pointer z);
    requires ...; // hardcoded in grammar
    ensures ...; // hardcoded in grammar
@*/

//after
int foo(int x, const char *y, struct bar **z);
/*@ spec foo(x, y, z); // added to function_spec_item grammar node
    requires ...; // just another function_spec_item
    ensures ...; // just another function_spec_item
    trusted; // parses for free now
    accesses qux; // also parses for free
@*/

The arguments could also be removed. I like concretely bringing them into CN scope in the same block.

The reason for this aspect of the spec syntax is that C does not require a function prototype to name all the arguments; and if it does, the Cerberus frontend (IIRC) forgets them. So spec has syntax to introduce names for the arguments. AIUI, originally the idea of spec was also to be able to give specifications to functions in a different source location from their declaration.

@thatplguy
Copy link

There are enough spec-related tickets (you can see a list of them here) that I've lost track of the broader language design issues we're considering. Here's a rough grouping of the current tickets.

Predicates

Taken together, these issues address two broad points:

  1. Conceptually, the term "resource predicate" is confusing, both to those familiar with program verification and newcomers. We should find a name for this concept that's more intuitive and use it in the docs/syntax/etc.
  2. The syntax around defining and using resource predicates currently imposes certain restrictions that we think can be alleviated. If we make all the changes above, we'll end up with less distinction between resource predicates and logical functions – which is good, because it means there are fewer special cases to learn and remember.

Syntactic sugar

Local vs global reasoning (and library models)

These issues are related to how specifications are associated with functions, and how/when specs are trusted vs. verified by CN. There are two use cases to keep in mind:

  1. Defining library models by creating trusted function specifications for functions without definitions.
  2. Defining specifications for exported functions (i.e. declared in a .h file) without leaking internal state into the external spec (e.g. about non-exported static variables).

Solver hints

Types

Datatypes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants