Skip to content

Commit d0bbb80

Browse files
committed
Do not unnecessarily invoke prover in impure specs.
1 parent a8bdbb9 commit d0bbb80

6 files changed

Lines changed: 22 additions & 1 deletion

File tree

lib/common/Pulse.Lib.Core.fsti

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ val pulse_eager_elim : unit
5050
val pulse_eager_intro : unit
5151
val pulse_intro : unit
5252

53+
(**
54+
Tags stateful functions where we don't need to run the prover to resolve the
55+
output in impure specs.
56+
57+
This happens when all the variables in the rhs of the rewrites_to are only in
58+
mkeys, e.g. in unobservable functions like Array.sub that are only stateful to
59+
ensure the requirement that we only extract `x + n` for live pointers, but where
60+
ther result of the function does not depend on the slprop context.
61+
*)
62+
val pulse_impure_spec_no_proof_required : unit
63+
5364
(** This attribute allows to do ambiguous proving when calling a function. *)
5465
val allow_ambiguous : unit
5566

lib/core/Pulse.Lib.Core.fst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ let pulse_eager_elim = ()
3030
let pulse_eager_intro = ()
3131
let pulse_intro = ()
3232

33+
let pulse_impure_spec_no_proof_required = ()
34+
3335
let allow_ambiguous = ()
3436

3537
let slprop = slprop

lib/pulse/lib/Pulse.Lib.Array.Core.fsti

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ ghost fn gsub_elim u#a (#t: Type u#a) (arr: array t) #f (#mask: nat->prop) (i j:
231231
pure (Seq.length v' == length arr /\
232232
(forall (k:nat). k < j - i ==> Seq.index v k == Seq.index v' (k + i)))
233233

234+
[@@pulse_impure_spec_no_proof_required]
234235
unobservable
235236
fn sub u#a (#t: Type u#a) (arr: array t) #f #mask (i: SZ.t) (j: erased nat)
236237
(#v: erased (Seq.seq (option t)) { SZ.v i <= j /\ j <= Seq.length (reveal v) })

lib/pulse/lib/Pulse.Lib.Reference.Array.fsti

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ open Pulse.Class.PtsTo
2222
open Pulse.Lib.Core
2323
open Pulse.Lib.Reference
2424

25+
[@@pulse_impure_spec_no_proof_required]
2526
inline_for_extraction
2627
unobservable
2728
fn to_array u#a (#a: Type u#a) (r: ref a) #p (#v: erased a)

lib/pulse/lib/Pulse.Lib.Reference.fsti

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ fn pts_to_uninit_not_null u#a (#a: Type u#a) (r:ref a)
157157

158158
val to_array_ghost #a (r: ref a) : GTot (array a)
159159

160+
[@@pulse_impure_spec_no_proof_required]
160161
unobservable
161162
fn to_array_mask u#a (#a: Type u#a) (r: ref a) #p (#v: erased a)
162163
requires r |-> Frac p v
@@ -175,6 +176,7 @@ fn return_to_array_mask u#a (#a: Type u#a) (r: ref a) #p #v #m
175176

176177
val array_at_ghost (#a: Type u#a) (arr: array a) (i: nat { i < length arr }) : GTot (r:ref a { to_array_ghost r == gsub arr i (i+1) })
177178

179+
[@@pulse_impure_spec_no_proof_required]
178180
unobservable
179181
fn array_at u#a (#a: Type u#a) (arr: array a) (i: SizeT.t) #p
180182
(#v: erased (Seq.seq (option a)) { SizeT.v i < length arr /\ length arr == Seq.length v /\ Some? (Seq.index v (SizeT.v i)) }) #mask
@@ -185,6 +187,7 @@ fn array_at u#a (#a: Type u#a) (arr: array a) (i: SizeT.t) #p
185187
ensures r |-> Frac p (Some?.v (Seq.index v (SizeT.v i)))
186188
ensures pts_to_mask arr #p v (fun k -> mask k /\ k <> SizeT.v i)
187189

190+
[@@pulse_impure_spec_no_proof_required]
188191
unobservable
189192
fn array_at_uninit u#a (#a: Type u#a) (arr: array a) (i: SizeT.t)
190193
(#v: erased (Seq.seq (option a)) { SizeT.v i < length arr /\ length arr == Seq.length v }) #mask

src/checker/Pulse.Checker.ImpureSpec.fst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ let prove (g: env) (goal: slprop) (ctxt: slprop) (r: range) : T.Tac unit =
6565
let (| g', ctxt', _ |) = Pulse.Checker.Prover.prove r g ctxt goal allow_amb in
6666
()
6767

68+
let is_no_proof_app (g: env) (t: term) : T.Tac bool =
69+
Pulse.Reflection.Util.head_has_attr_string "Pulse.Lib.Core.pulse_impure_spec_no_proof_required" t
70+
6871
let symb_eval_stateful_app (g: env) (ctxt: slprop) (t: term) : T.Tac R.term =
6972
let t, ty, _ = tc_term_phase1 g t in
7073
debug g (fun _ -> [ text "impure spec inferred type"; pp t; pp ty ]);
@@ -87,7 +90,7 @@ let symb_eval_stateful_app (g: env) (ctxt: slprop) (t: term) : T.Tac R.term =
8790
] (Some (RU.range_of_term t))
8891
| Some rwr ->
8992
let allow_amb = true in
90-
prove g pre ctxt (RU.range_of_term t);
93+
(if not (is_no_proof_app g t) then prove g pre ctxt (RU.range_of_term t));
9194
debug g (fun _ -> [text "evaluated" ^/^ pp t ^/^ text "to" ^/^ pp rwr]);
9295
let rwr = RU.deep_compress rwr in // TODO: maybe this fails on uvars...
9396
rwr

0 commit comments

Comments
 (0)