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

Support private functions and old expressions in Motoko-to-Viper translation #3675

Merged
merged 9 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions emacs/motoko-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"while"
"prim"
"invariant"
"old"
"implies"
))
;; Braces introduce blocks; it's nice to make them stand
Expand Down
7 changes: 7 additions & 0 deletions src/ir_def/construct.ml
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,13 @@ let orE : Ir.exp -> Ir.exp -> Ir.exp = fun e1 e2 ->
ifE e1 (trueE ()) e2
let impliesE : Ir.exp -> Ir.exp -> Ir.exp = fun e1 e2 ->
orE (notE e1) e2
let oldE : Ir.exp -> Ir.exp = fun e ->
{ it = (primE (CallPrim [typ e]) [e]).it;
at = no_region;
note = Note.{ def with
typ = typ e;
}
}

let rec conjE : Ir.exp list -> Ir.exp = function
| [] -> trueE ()
Expand Down
1 change: 1 addition & 0 deletions src/ir_def/construct.mli
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ val notE : exp -> exp
val andE : exp -> exp -> exp
val orE : exp -> exp -> exp
val impliesE : exp -> exp -> exp
val oldE : exp -> exp
val conjE : exp list -> exp

val declare_idE : id -> typ -> exp -> exp
Expand Down
1 change: 1 addition & 0 deletions src/lowering/desugar.ml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ and exp' at note = function
| S.AndE (e1, e2) -> (andE (exp e1) (exp e2)).it
| S.OrE (e1, e2) -> (orE (exp e1) (exp e2)).it
| S.ImpliesE (e1, e2) -> (impliesE (exp e1) (exp e2)).it
| S.OldE e -> (oldE (exp e)).it
| S.IfE (e1, e2, e3) -> I.IfE (exp e1, exp e2, exp e3)
| S.SwitchE (e1, cs) -> I.SwitchE (exp e1, cases cs)
| S.TryE (e1, cs) -> I.TryE (exp e1, cases cs)
Expand Down
1 change: 1 addition & 0 deletions src/mo_def/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ module Make (Cfg : Config) = struct
| AndE (e1, e2) -> "AndE" $$ [exp e1; exp e2]
| OrE (e1, e2) -> "OrE" $$ [exp e1; exp e2]
| ImpliesE (e1, e2) -> "ImpliesE"$$ [exp e1; exp e2]
| OldE e -> "OldE" $$ [exp e]
| IfE (e1, e2, e3) -> "IfE" $$ [exp e1; exp e2; exp e3]
| SwitchE (e, cs) -> "SwitchE" $$ [exp e] @ List.map case cs
| WhileE (e1, e2) -> "WhileE" $$ [exp e1; exp e2]
Expand Down
1 change: 1 addition & 0 deletions src/mo_def/syntax.ml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ and exp' =
| AndE of exp * exp (* conjunction *)
| OrE of exp * exp (* disjunction *)
| ImpliesE of exp * exp (* implication *)
| OldE of exp (* old-expression *)
| IfE of exp * exp * exp (* conditional *)
| SwitchE of exp * case list (* switch *)
| WhileE of exp * exp (* while-do loop *)
Expand Down
3 changes: 3 additions & 0 deletions src/mo_frontend/assertions.mly
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ let is_verification () =
(*
%nonassoc IMPLIES (* see parser.mly *)
*)
%token OLD

%%

Expand All @@ -45,5 +46,7 @@ let is_verification () =
{ is_verification () &&& AssertE(Postcondition, e) @? at $sloc }
| ASSERT COLON s=NAT COLON ASYNC e=exp_nest
{ is_verification () &&& AssertE(Concurrency s, e) @? at $sloc }
| OLD e=exp_nest
{ is_verification () &&& OldE e @? at $sloc }

%%
1 change: 1 addition & 0 deletions src/mo_frontend/definedness.ml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ let rec exp msgs e : f = match e.it with
| AndE (e1, e2) -> exps msgs [e1; e2]
| OrE (e1, e2) -> exps msgs [e1; e2]
| ImpliesE (e1, e2) -> exps msgs [e1; e2]
| OldE e -> exp msgs e
| IfE (e1, e2, e3) -> exps msgs [e1; e2; e3]
| SwitchE (e, cs) -> exp msgs e ++ cases msgs cs
| TryE (e, cs) -> exp msgs e ++ cases msgs cs
Expand Down
1 change: 1 addition & 0 deletions src/mo_frontend/effect.ml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ let rec infer_effect_exp (exp:Syntax.exp) : T.eff =
| TagE (_, exp1)
| DotE (exp1, _)
| NotE exp1
| OldE (exp1)
aterga marked this conversation as resolved.
Show resolved Hide resolved
| AssertE (_, exp1)
| LabelE (_, _, exp1)
| BreakE (_, exp1)
Expand Down
1 change: 1 addition & 0 deletions src/mo_frontend/error_reporting.ml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ let terminal2token (type a) (symbol : a terminal) : token =
| T_ANDASSIGN -> ANDASSIGN
| T_AND -> AND
| T_IMPLIES -> IMPLIES
| T_OLD -> OLD
| T_ADDOP -> ADDOP
| T_ACTOR -> ACTOR
| T_INVARIANT -> INVARIANT
Expand Down
1 change: 1 addition & 0 deletions src/mo_frontend/printers.ml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ let string_of_symbol = function
| X (T T_ANDASSIGN) -> binassign "&="
| X (T T_AND) -> "and"
| X (T T_IMPLIES) -> "implies"
| X (T T_OLD) -> "old"
| X (T T_ADDOP) -> unop "+"
| X (T T_ACTOR) -> "actor"
| X (T T_INVARIANT) -> "invariant"
Expand Down
1 change: 1 addition & 0 deletions src/mo_frontend/source_lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ rule token mode = parse
| "in" { IN }
| "invariant" as s { if mode.verification then INVARIANT else ID s }
| "implies" as s { if mode.verification then IMPLIES else ID s }
| "old" as s { if mode.verification then OLD else ID s }
| "import" { IMPORT }
| "module" { MODULE }
| "not" { NOT }
Expand Down
3 changes: 3 additions & 0 deletions src/mo_frontend/source_token.ml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type token =
| AND
| OR
| IMPLIES
| OLD
| NOT
| IMPORT
| MODULE
Expand Down Expand Up @@ -184,6 +185,7 @@ let to_parser_token :
| AND -> Ok Parser.AND
| OR -> Ok Parser.OR
| IMPLIES -> Ok Parser.IMPLIES
| OLD -> Ok Parser.OLD
| NOT -> Ok Parser.NOT
| IMPORT -> Ok Parser.IMPORT
| MODULE -> Ok Parser.MODULE
Expand Down Expand Up @@ -373,6 +375,7 @@ let string_of_parser_token = function
| Parser.UNDERSCORE -> "UNDERSCORE"
| Parser.INVARIANT -> "INVARIANT"
| Parser.IMPLIES -> "IMPLIES"
| Parser.OLD -> "OLD"

let is_lineless_trivia : token -> void trivia option = function
| SINGLESPACE -> Some (Space 1)
Expand Down
1 change: 1 addition & 0 deletions src/mo_frontend/static.ml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ let rec exp m e = match e.it with
| TryE _
| BangE _
| ImpliesE _
| OldE _
-> err m e.at

and dec_fields m dfs = List.iter (fun df -> dec m df.it.dec) dfs
Expand Down
1 change: 1 addition & 0 deletions src/mo_frontend/traversals.ml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ let rec over_exp (f : exp -> exp) (exp : exp) : exp = match exp.it with
f { exp with it = OrE (over_exp f exp1, over_exp f exp2) }
| ImpliesE (exp1, exp2) ->
f { exp with it = ImpliesE (over_exp f exp1, over_exp f exp2) }
| OldE exp1 -> f { exp with it = OldE (over_exp f exp1) }
| WhileE (exp1, exp2) ->
f { exp with it = WhileE (over_exp f exp1, over_exp f exp2) }
| LoopE (exp1, exp2_opt) ->
Expand Down
4 changes: 3 additions & 1 deletion src/mo_frontend/typing.ml
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ let rec is_explicit_exp e =
| BreakE _ | RetE _ | ThrowE _ ->
false
| VarE _
| RelE _ | NotE _ | AndE _ | OrE _ | ImpliesE _ | ShowE _ | ToCandidE _ | FromCandidE _
| RelE _ | NotE _ | AndE _ | OrE _ | ImpliesE _ | OldE _ | ShowE _ | ToCandidE _ | FromCandidE _
| AssignE _ | IgnoreE _ | AssertE _ | DebugE _
| WhileE _ | ForE _
| AnnotE _ | ImportE _ ->
Expand Down Expand Up @@ -1318,6 +1318,8 @@ and infer_exp'' env exp : T.typ =
check_exp_strong env T.bool exp2
end;
T.bool
| OldE exp1 ->
infer_exp_promote env exp1
| IfE (exp1, exp2, exp3) ->
if not env.pre then check_exp_strong env T.bool exp1;
let t2 = infer_exp env exp2 in
Expand Down
2 changes: 2 additions & 0 deletions src/mo_interpreter/interpret.ml
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,8 @@ and interpret_exp_mut env exp (k : V.value V.cont) =
k V.(Bool (as_bool v1 <= as_bool v2))
)
)
| OldE exp1 ->
interpret_exp env exp1 (fun v1 -> k v1)
aterga marked this conversation as resolved.
Show resolved Hide resolved
| IfE (exp1, exp2, exp3) ->
interpret_exp env exp1 (fun v1 ->
if V.as_bool v1
Expand Down
35 changes: 28 additions & 7 deletions src/viper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Contributing to _Motoko-san_
<a name="Running"></a>

```bash
[nix-shell:motoko] moc -viper input.mo > output.vpr
[nix-shell:motoko] moc --viper input.mo > output.vpr
```

You may then verify the `output.vpr` file using [Viper](https://viper.ethz.ch/). Soon, there will be an interactive IDE integration for VS Code, s.t. the outputs do not need to be verified by manually invoking Viper.
Expand Down Expand Up @@ -185,13 +185,13 @@ Below, we summarize the language features that _Motoko-san_ currently supports.
* Immutable: `let y = ...`
* Fields may _not_ be initialized via block expressions: `let z = { ... };`

* **Public functions** — Only functions of `async ()` type with no arguments are supported:
* **Functions** — Only functions of `()` and `async ()` type with no arguments are supported:

`public func claim() : async () = { ... };`
`public shared func claim() : async () = { ... };`

Supporting function arguments and return values is _simple_.
`private func reward() : () = { ... };`

* **Private functions** — Currently not supported (extension is _simple_).
Supporting function arguments and return values is _simple_.

* **Local declarations** — Only local variable declarations with trivial left-hand side are supported:

Expand Down Expand Up @@ -239,13 +239,34 @@ Below, we summarize the language features that _Motoko-san_ currently supports.

* `assert:func` — Function preconditions

* `assert:return` — Function postconditions
* `assert:return` — Function postconditions.

* These may refer to variables in the _initial_ state of the function call using the syntax `(old <exp>)`, for example:

```motoko
var x : Int;
private func dec() : () {
x -= 1;
assert:return x < old(x);
};
```

is equivalent to

```motoko
var x : Int;
private func dec() : () {
let old_x = x;
x -= 1;
assert:return x < old_x;
};
```

* `assert:system` — Compile-time assertions

**Loop invariants** — Extension is _simple_.

**Pure functions** — The tool could be easily extended with a keyword, e.g., `@pure`, to specify functions that are verifier to be side-effect free; such functions could be used inside other code specifications, e.g., `assert:invariant is_okay()` for some `@pure func is_okay() : Bool`. This feature requires private functions.
**Pure functions** — The tool could be easily extended with a keyword, e.g., `@pure`, to specify functions that are verifier to be side-effect free; such functions could be used inside other code specifications, e.g., `assert:invariant is_okay()` for some `@pure func is_okay() : Bool`.

Further information
-------------------
Expand Down
32 changes: 29 additions & 3 deletions src/viper/pretty.ml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ let comma ppf () = fprintf ppf ",@ "

let semi ppf () = fprintf ppf ";@ "

let pp_info ppf NoInfo = ()
let pp_info ppf info =
match info with
| NoInfo -> ()
| ActorInit -> fprintf ppf "@[// translation of actor initializers @]"
| PublicFunction x -> fprintf ppf "@[// translation of PUBLIC function %s@]" x
| PrivateFunction x -> fprintf ppf "@[// translation of _private_ function %s@]" x

let rec pp_prog ppf p =
match p.it with
Expand Down Expand Up @@ -118,6 +123,8 @@ and pp_exp ppf exp =
| Implies _ -> "==>" | OrE _ -> "||" | AndE _ -> "&&"
| _ -> failwith "not a binary operator" in
fprintf ppf "(%a %s %a)" pp_exp e1 op pp_exp e2
| Old e ->
fprintf ppf "@[old(%a)@]" pp_exp e
| PermE p -> pp_perm ppf p
| AccE (fldacc, perm) -> fprintf ppf "@[acc(%a,%a)@]" pp_fldacc fldacc pp_exp perm
| _ -> fprintf ppf "@[// pretty printer not implemented for node at %s@]" (string_of_region exp.at)
Expand Down Expand Up @@ -179,8 +186,27 @@ and pp_stmt' ppf = function
fprintf ppf "@[<v 2>/*concurrency max %s, cond: s %a*/@]"
max
pp_exp exp
| MethodCallS (_, _, _)
| LabelS (_, _) -> failwith "MethodCallS or LabelS?"
| MethodCallS (rs, m, args) ->
let () = match rs with
| [] -> ()
| r :: rs ->
let () = fprintf ppf "@[%s@]" r.it in
List.iter (fun r ->
fprintf ppf ", @[%s@]" r.it
) rs
in
let () = if rs != [] then
fprintf ppf " := "
in
let () = fprintf ppf "@[%s(@]" m.it in
let () = match args with
| [] -> ()
| arg :: args ->
let () = fprintf ppf "@[%a@]" pp_exp arg in
fprintf ppf "@[%a@]" (pp_print_list ~pp_sep:comma pp_exp) args
in
fprintf ppf ")"
| LabelS (_, _) -> failwith "LabelS?"

and pp_fldacc ppf fldacc =
match fldacc with
Expand Down
9 changes: 7 additions & 2 deletions src/viper/syntax.ml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
type info = NoInfo
type info =
| NoInfo
| ActorInit
| PublicFunction of string
| PrivateFunction of string

type id = (string, info) Source.annotated_phrase

Expand Down Expand Up @@ -41,6 +45,7 @@ and exp' =
| AndE of exp * exp
| OrE of exp * exp
| Implies of exp * exp
| Old of exp
aterga marked this conversation as resolved.
Show resolved Hide resolved
| FldAcc of fldacc
| PermE of perm (* perm_amount *)
| AccE of fldacc * exp (* acc((rcvr: exp).field, (exp: perm_amount)) *)
Expand All @@ -64,7 +69,7 @@ and tmpl' = (Mo_def.Syntax.exp -> exp) -> exp
and fldacc = exp * id

and stmt' =
| MethodCallS of id * exp list * id list
| MethodCallS of id list * id * exp list
| ExhaleS of exp
| InhaleS of exp
| AssertS of exp
Expand Down
Loading