mit-plv / bedrock2

A work-in-progress language and compiler for verified low-level programming

Home Page:http://adam.chlipala.net/papers/LightbulbPLDI21/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Missing operators

samuelgruetter opened this issue · comments

Compared to C, bedrock2 is missing quite a few operators:

Unary operators:

  • unary minus -
  • logical not !
  • bitwise not ~

Binary operators:

  • unsigned division / and modulo %: unsigned variants are already in AST bopname, but no notations
  • unsigned <=, >, >=
  • not equal !=
  • lazy boolean &&/||

More missing operators that I'd postpone for later:

  • ternary conditional _ ? _ : _
  • signed <=, >, >=
  • signed division and modulo
  • pre and post ++ and -- (won't add because we don't want expressions with side-effects, maybe as a statement, though)
  • record access . and ->
  • array access []
  • compound assignment

This absence of operators can lead to more bugs, defeating the point of using bedrock2: When transcribing C to bedrock2, one has to work around missing operators, trying to emulate them with existing ones, and (based on experience), bugs can be introduced in this process. (Hopefully, they will be found during verification, but if one only verifies memory safety, they could be missed).

Most of these operators could be implemented as notations in terms of other operators:

Unary operators:

  • "- x" := (0 - x)
  • "! x" := (1 - (x != 0)), or "! x" := (x ? 0 : 1), or a primitive?
  • "~ x" := x ^ (-1)

Binary operators:

  • "/" := (expr.op divu)
  • "%" := (expr.op remu)
  • "x <= y" := (!(y < x))
  • "x > y" := (y < x)
  • "x >= y" := (!(x < y))
  • "x != y" := (! (x == y))
  • && and ||: as primitives, added to bopname, or maybe using a ternary if?

However, the above definitions are circular: ! is defined in terms of !=, and != is defined in terms of !.

A disadvantage of using notations is also that the C pretty printer won't print them (unless we add extra logic that detects the patterns), and that if the compiler wants to treat them specially, it also has to detect the pattern rather than just writing a case of an inductive constructor.

Overall, it seems that in order to support these operators, the at least some additions to the expr AST need to be made, while others can be implemented as notations, and that there is some flexibility as to what's a notation and what goes into the AST.
I'm a bit undecided regarding what should be notation vs in the expr AST. Opinions by @andres-erbsen or @jadephilipoom or others welcome 😉

I don't see any real reasons to prefer notations to new bopnames as long as the operation is generally useful. If giving semantics directly would result in nicer proof obligations, I'd say let's prefer adding bopnames.

I think you could do "! x" := (x == 0) if you wanted, though?

&& and ||: as primitives, added to bopname, or maybe using a ternary if?

I think we should only add them if we are going to match the semantics of C and its predecessors (and at that point we could indeed add conditional expressions as well). In particular 0 && *0 should be a safe expression. We could totally do this (heck, with the confidence we now have about postcondition-style semantics I'd feel fine even going to everything-is-an-expression land), but my hunch is that it's not the most impactful thing to work on right now.