Skip to content

Commit ea3d827

Browse files
committed
Add a resugaring extension for Pulse
Using FStarLang/FStar#4068, add a resugaring pass to detect pulse computation types and print them in a nicer way. Example: ```fstar fn test (x : ref int) requires x |-> 'v returns old : int ensures x |-> ('v + 1) ** pure (old == old) { let old = !x; x := !x + 1; old } ``` Now outputs: ``` * Info at Print.fst(15,0-15,11): - Term Print.test has type x: Pulse.Lib.Reference.ref Prims.int -> fn requires x |-> 'v returns x : Prims.int ensures x |-> ('v + 1) ** Pulse.Lib.Core.pure (x == x) ``` Note that there is a bad shadowing of x in the postcondition. This seems difficult to fix (`x` is ppname_default).
1 parent 82f712b commit ea3d827

11 files changed

+215
-27
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
(*
2+
Copyright 2023 Microsoft Research
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*)
16+
17+
module PulseSyntaxExtension.Printing
18+
19+
open FStarC
20+
open FStarC.Effect
21+
22+
module SW = PulseSyntaxExtension.SyntaxWrapper
23+
module A = FStarC.Parser.AST
24+
module S = FStarC.Syntax.Syntax
25+
module U = FStarC.Syntax.Util
26+
module SS = FStarC.Syntax.Subst
27+
module DsEnv = FStarC.Syntax.DsEnv
28+
29+
open FStarC.Syntax.Syntax
30+
open FStarC.Syntax.Resugar
31+
open FStarC.Class.Show
32+
open FStarC.Class.PP
33+
open PulseSyntaxExtension.Env
34+
open FStarC.Pprint
35+
36+
let hua (t:term) : option (S.fv & list S.universe & S.args) =
37+
let t = U.unmeta t in
38+
let hd, args = U.head_and_args_full t in
39+
let hd = U.unmeta hd in
40+
match (SS.compress hd).n with
41+
| Tm_fvar fv -> Some (fv, [], args)
42+
| Tm_uinst ({ n = Tm_fvar fv }, us) -> Some (fv, us, args)
43+
| _ -> None
44+
45+
let p = FStarC.Parser.ToDocument.term_to_document
46+
47+
let vconcat (ds:list document) : document =
48+
match ds with
49+
| h::t ->
50+
List.fold_left (fun l r -> if r = empty then l else l ^^ hardline ^^ r) h t
51+
| [] ->
52+
empty
53+
54+
let print_pulse_computation_type
55+
(rng : Range.t)
56+
(e : DsEnv.env)
57+
(tag : string)
58+
(a opens pre post : term)
59+
: A.term
60+
=
61+
let retname_opt, post =
62+
match U.abs_formals post with
63+
| [b], t, _ ->
64+
let bv = b.binder_bv in
65+
if FStarC.Class.Setlike.mem bv (Syntax.Free.names t) then
66+
Some b.binder_bv, t
67+
else
68+
None, t
69+
| _ ->
70+
// If it returns unit, just apply the post to `()`
71+
if U.term_eq a S.t_unit then
72+
None, U.mk_app post [S.as_arg U.exp_unit]
73+
else
74+
let x = S.gen_bv "_ret" (Some rng) a in
75+
Some x, U.mk_app post [S.as_arg (S.bv_to_name x)]
76+
in
77+
let d =
78+
align <| hang 2 <| vconcat <| List.map (fun d -> group (hang 2 d)) [
79+
(if tag <> ""
80+
then doc_of_string tag ^/^ doc_of_string "fn"
81+
else doc_of_string "fn");
82+
doc_of_string "requires" ^/^ p (resugar_term' e pre);
83+
(if U.term_eq opens SW.tm_emp_inames then empty
84+
else
85+
(doc_of_string "opens" ^/^ p (resugar_term' e opens)));
86+
(match retname_opt with
87+
| None when U.term_eq a S.t_unit -> empty
88+
| None ->
89+
doc_of_string "returns" ^/^ p (resugar_term' e a)
90+
| Some bv ->
91+
doc_of_string "returns" ^/^ pp bv.ppname ^/^ colon ^/^ p (resugar_term' e a));
92+
doc_of_string "ensures" ^/^ p (resugar_term' e post);
93+
]
94+
in
95+
A.mk_term (A.LitDoc d) rng A.Expr
96+
97+
let resugar_pulse_type (e:DsEnv.env) (t:S.term) : A.term =
98+
let r = hua t in
99+
if None? r then raise SkipResugar;
100+
let Some (fv, us, args) = r in
101+
let tag, a, opens, pre, post =
102+
match args with
103+
| [(a, None); (pre, None); (post, None)]
104+
when S.fv_eq_lid fv stt_lid ->
105+
("", a, SW.tm_emp_inames, pre, post)
106+
107+
| [(a, None); _obs; (opens, None); (pre, None); (post, None)]
108+
when S.fv_eq_lid fv stt_atomic_lid ->
109+
("atomic", a, opens, pre, post)
110+
111+
| [(a, None); (opens, None); (pre, None); (post, None)]
112+
when S.fv_eq_lid fv stt_ghost_lid ->
113+
("ghost", a, opens, pre, post)
114+
115+
| _ -> raise SkipResugar
116+
in
117+
print_pulse_computation_type (pos t) e tag a opens pre post
118+
119+
let _ = register_pass resugar_pulse_type

test/LoopInvariants.fst.output.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Warning 274:
2+
- Implicitly opening namespace 'fstar.tactics.' shadows module 'print' in file
3+
"Print.fst".
4+
- Rename "Print.fst" to avoid conflicts.
5+
16
* Info at LoopInvariants.fst(64,17-64,20):
27
- Expected failure:
38
- When using multiple invariants, they must all be in the "new" style without a binder.

test/MatchBasic.fst.output.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Warning 274:
2+
- Implicitly opening namespace 'fstar.tactics.' shadows module 'print' in file
3+
"Print.fst".
4+
- Rename "Print.fst" to avoid conflicts.
5+
16
* Info at MatchBasic.fst(154,2-156,3):
27
- Expected failure:
38
- Could not verify that this match is exhaustive.

test/Print.fst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module Print
2+
3+
#lang-pulse
4+
open Pulse
5+
6+
fn test (x : ref int)
7+
requires x |-> 'v
8+
returns old : int
9+
ensures x |-> ('v + 1) ** pure (old == old)
10+
{
11+
let old = !x;
12+
x := !x + 1;
13+
old
14+
}
15+
#check test
16+
17+
assume val q : int -> slprop
18+
assume val test2 : stt_ghost int emp_inames emp q
19+
20+
#check test2
21+
22+
assume val r : unit -> slprop
23+
assume val test3 : stt_ghost unit emp_inames emp r
24+
25+
#check test3

test/Print.fst.output.expected

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
* Warning 274:
2+
- Implicitly opening namespace 'fstar.tactics.' shadows module 'print' in file
3+
"Print.fst".
4+
- Rename "Print.fst" to avoid conflicts.
5+
6+
* Info at Print.fst(15,0-15,11):
7+
- Term
8+
Print.test
9+
has type
10+
x: Pulse.Lib.Reference.ref Prims.int
11+
-> fn
12+
requires x |-> 'v
13+
returns x : Prims.int
14+
ensures x |-> ('v + 1) ** Pulse.Lib.Core.pure (x == x)
15+
16+
* Info at Print.fst(20,0-20,12):
17+
- Term
18+
Print.test2
19+
has type
20+
ghost fn
21+
requires Pulse.Lib.Core.emp
22+
returns _ret : Prims.int
23+
ensures Print.q _ret
24+
25+
* Info at Print.fst(25,0-25,12):
26+
- Term
27+
Print.test3
28+
has type
29+
ghost fn
30+
requires Pulse.Lib.Core.emp
31+
ensures Print.r ()
32+

test/Test.Recursion.fst.output.expected

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
* Warning 274:
2+
- Implicitly opening namespace 'fstar.tactics.' shadows module 'print' in file
3+
"Print.fst".
4+
- Rename "Print.fst" to avoid conflicts.
5+
16
* Info at Test.Recursion.fst(94,2-94,20):
27
- Expected failure:
38
- Ill-typed term: test_ghost_loop ()
49
- Expected a term of type
5-
Pulse.Lib.Core.stt_ghost Prims.unit
6-
Pulse.Lib.Core.emp_inames
7-
Pulse.Lib.Core.emp
8-
(fun _ -> Pulse.Lib.Core.emp)
10+
ghost fn
11+
requires Pulse.Lib.Core.emp
12+
ensures Pulse.Lib.Core.emp
913
- Could not prove termination
1014
- The SMT solver could not prove the query. Use --query_stats for more
1115
details.
@@ -14,10 +18,9 @@
1418
- Expected failure:
1519
- Ill-typed term: test5' (z - 1) (y - 1)
1620
- Expected a term of type
17-
Pulse.Lib.Core.stt_ghost Prims.unit
18-
Pulse.Lib.Core.emp_inames
19-
Pulse.Lib.Core.emp
20-
(fun _ -> Pulse.Lib.Core.emp)
21+
ghost fn
22+
requires Pulse.Lib.Core.emp
23+
ensures Pulse.Lib.Core.emp
2124
- Could not prove termination
2225
- The SMT solver could not prove the query. Use --query_stats for more
2326
details.

test/bug-reports/Bug174.fst.output.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
- Expected failure:
33
- Ill-typed term: !r
44
- Expected a term of type
5-
Pulse.Lib.Core.stt FStar.SizeT.t
6-
(r |-> Pulse.Class.PtsTo.Frac 1.0R v)
7-
(fun x ->
8-
r |-> Pulse.Class.PtsTo.Frac 1.0R v **
9-
Pulse.Lib.Core.rewrites_to x v)
5+
fn
6+
requires r |-> Pulse.Class.PtsTo.Frac 1.0R v
7+
returns x : FStar.SizeT.t
8+
ensures
9+
r |-> Pulse.Class.PtsTo.Frac 1.0R v ** Pulse.Lib.Core.rewrites_to x v
1010
- Assertion failed
1111
- The SMT solver could not prove the query. Use --query_stats for more
1212
details.

test/bug-reports/Bug206.fst.output.expected

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
- Expected failure:
33
- Ill-typed term: Bug206.test x
44
- Expected a term of type
5-
Pulse.Lib.Core.stt Prims.unit
6-
Pulse.Lib.Core.emp
7-
(fun _ -> Pulse.Lib.Core.emp)
5+
fn
6+
requires Pulse.Lib.Core.emp
7+
ensures Pulse.Lib.Core.emp
88
- Assertion failed
99
- The SMT solver could not prove the query. Use --query_stats for more
1010
details.

test/bug-reports/Bug266.fst.output.expected

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
- In typing environment:
1414
__#65 : Prims.squash (__ == Bug266.my_intro Prims.l_False),
1515
__#64 :
16-
Pulse.Lib.Core.stt_ghost Prims.unit
17-
Pulse.Lib.Core.emp_inames
18-
(Pulse.Lib.Core.pure Prims.l_False)
19-
Bug266.post,
16+
ghost fn
17+
requires Pulse.Lib.Core.pure Prims.l_False
18+
ensures Bug266.post (),
2019
uu___0#50 : Prims.unit
2120

test/bug-reports/PartialApp.fst.output.expected

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
- Tactic failed
1010
- This function is partially applied. Remaining type:
1111
y: t
12-
-> Pulse.Lib.Core.stt Prims.unit
13-
Pulse.Lib.Core.emp
14-
(fun _ -> Pulse.Lib.Core.emp)
12+
-> fn
13+
requires Pulse.Lib.Core.emp
14+
ensures Pulse.Lib.Core.emp
1515
- Did you forget to apply some arguments?
1616

0 commit comments

Comments
 (0)