Ltac solve_Forall2_fn_data_at :=
solve
[ apply Forall2_nil
| apply Forall2_cons; [ apply fn_data_at_intro; [reflexivity | solve_msubst_eval_lvar] | solve_Forall2_fn_data_at]].
Ltac solve_canon_derives_stackframe :=
solve
[ simple apply canonicalize_stackframe_emp
| try unfold stackframe_of;
simple eapply canonicalize_stackframe;
[ prove_local2ptree
| solve_Forall2_fn_data_at
]
].
Ltac fold_frame_function_body :=
match goal with P := @abbreviate ret_assert _ |- _ => unfold abbreviate in P; subst P end;
match goal with |- semax _ _ _ ?R =>
match R with {| RA_return := (fun vl rho => bind_ret _ ?t ?P _ * stackframe_of ?f _) |} =>
apply semax_post with (frame_ret_assert (function_body_ret_assert t P) (stackframe_of f));
[ simpl_ret_assert; rewrite FF_sepcon; apply andp_left2; apply FF_left
| simpl_ret_assert; rewrite FF_sepcon; apply andp_left2; apply FF_left
| simpl_ret_assert; rewrite FF_sepcon; apply andp_left2; apply FF_left
| simpl_ret_assert; solve [auto]
|]
end
end.
Ltac forward_return :=
try fold_frame_function_body;
match goal with
| |- @semax ?CS _ ?Delta (PROPx ?P (LOCALx ?Q (SEPx ?R))) (Sreturn ?oe) _ =>
match oe with
| None =>
eapply semax_return_None;
[ (reflexivity || fail 1000 "Error: return type is not Tvoid")
| (solve_return_outer_gen || fail 1000 "unexpected failure in forward_return. Do not remove the stackframe")
| (solve_canon_derives_stackframe || fail 1000 "stackframe is unfolded or modified.")
| try match goal with Post := _ : ret_assert |- _ => subst Post; unfold abbreviate end;
try change_compspecs CS;
solve_return_inner_gen
| entailer_for_return]
| Some ?ret =>
let v := fresh "v" in evar (v: val);
let H := fresh "HRE" in
do_compute_expr Delta P Q R constr:(Ecast ret (ret_type Delta)) v H;
subst v;
eapply semax_return_Some;
[ exact H
| entailer_for_return
| (solve_return_outer_gen || fail 1000 "unexpected failure in forward_return. Do not remove the stackframe")
| (solve_canon_derives_stackframe || fail 1000 "stackframe is unfolded or modified.")
| try match goal with Post := _ : ret_assert |- _ => subst Post; unfold abbreviate end;
try change_compspecs CS;
solve_return_inner_gen
| entailer_for_return];
clear H
end
end.
Ltac test_simple_bound test incr :=
match incr with
|   Sset ?i (Ebinop Oadd (Etempvar ?j _) (Econst_int (Int.repr 1) _) _) =>
constr_eq i j;
match test with
| Ebinop Olt (Etempvar ?k _) _ _ =>
constr_eq i k
end
end.
(* Ltac forward_if_complain := (*semax_logic_and_or ||*)  fail 2 "Use this tactic:  forward_if POST, where POST is the post condition".  Ltac forward_while_complain := fail 2 "Use this tactic:  forward_while INV, where INV is the loop invariant".  Ltac forward_for_complain := fail 2 "Use this tactic:  forward_for INV PRE_INCR POST, where INV is the loop invariant, PRE_INCR is the invariant at the increment, and POST is the postcondition". *)
Ltac forward_skip := apply semax_skip.
Ltac is_array_type t :=
let t' := eval hnf in t in
match t' with Tarray _ _ _ => idtac end.
Ltac no_loads_expr e as_lvalue :=
match e with
| Econst_int _ _ => idtac
| Econst_float _ _ => idtac
| Econst_single _ _ => idtac
| Econst_long _ _ => idtac
| Evar _ ?t => match as_lvalue with true => idtac | false => is_array_type t end
| Etempvar _ _ => idtac
| Ederef ?e1 ?t => constr_eq as_lvalue true; no_loads_expr e1 true
| Eaddrof ?e1 _ => no_loads_expr e1 true
| Eunop _ ?e1 _ => no_loads_expr e1 as_lvalue
| Ebinop _ ?e1 ?e2 _ => no_loads_expr e1 as_lvalue ; no_loads_expr e2 as_lvalue
| Ecast ?e1 _ => no_loads_expr e1 as_lvalue
| Efield ?e1 _ ?t => match as_lvalue with true => idtac | false => is_array_type t end;
no_loads_expr e1 true
| Esizeof _ _ => idtac
| Ealignof _ _ => idtac
end.
Definition Undo__Then_do__forward_call_W__where_W_is_a_witness_whose_type_is_given_above_the_line_now := False.
Ltac advise_forward_call :=
prove_call_setup1 subsume_funspec_refl;
[ .. |
match goal with |- call_setup1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ ?A _ _ _ _ _ _ _ -> _ =>
lazymatch A with
| rmaps.ConstType ?T =>
fail "To prove this function call, use forward_call(W), where
W:"T"
is a WITH-clause witness"
| _ => fail "This function has a complex calling convention not recognized by forward_call"
end
end].
Ltac advise_prepare_postcondition :=
match goal with
| Post' := _ : ret_assert |- semax _ _ _ ?Post =>
tryif (constr_eq Post' Post) then (unfold abbreviate in Post'; subst Post') else idtac
end;
lazymatch goal with
| Delta' := @abbreviate tycontext _ |- semax ?Delta _ _ _ =>
tryif (constr_eq Delta' Delta)
then idtac
else fail "Please use abbreviate_semax to put your proof goal into standard form"
(*  | Delta' := @abbreviate tycontext _ |- semax ?Delta _ _ _ => idtac *)
| |- semax _ _ _ _ => fail "Please use abbreviate_semax to put your proof goal into standard form."
| |- _ => fail "Proof goal is not (semax _ _ _ _)."
end;
repeat match goal with
| MC := @abbreviate statement _ |- _ => subst MC; unfold abbreviate
| |- _ => simple apply seq_assoc1
end;
try simple eapply semax_seq.
Ltac forward_advise_loop c :=
try lazymatch c with
| Sfor _ _ Sskip ?body =>
unify (nobreaksx body) true;
fail "Use [forward; forward_while Inv] to prove this loop, where Inv is a loop invariant of type (environ->mpred)"
| Swhile _ ?body =>
unify (nobreaksx body) true;
fail "Use [forward_while Inv] to prove this loop, where Inv is a loop invariant of type (environ->mpred)"
| Sloop (Ssequence (Sifthenelse _ Sbreak Sskip) ?body) Sskip =>
unify (nobreaksx body) true;
fail "Use [forward_while Inv] to prove this loop, where Inv is a loop invariant of type (environ->mpred)"
end;
lazymatch c with
| Sfor _ ?test ?body ?incr  =>
tryif (unify (nobreaksx body) true; test_simple_bound test incr)
then fail "You can probably use [forward_for_simple_bound n Inv], provided that the upper bound of your loop can be expressed as a constant value (n:Z), and the loop invariant Inv can be expressed as (EX i:Z, ...).  Note that the Inv should not mention the LOCAL binding of the loop-count variable to the value i, and need not assert the PROP that i<=n; these will be inserted automatically.
Otherwise, you can use the general case: Use [forward_loop Inv] to prove this loop, where Inv is a loop invariant of type (environ -> mpred).  The [forward_loop] tactic will advise you if you need continue: or break: assertions in addition"
else fail "Use [forward_loop Inv] to prove this loop, where Inv is a loop invariant of type (environ -> mpred).  The [forward_loop] tactic will advise you if you need continue: or break: assertions in addition"
| Sloop _ _ =>
fail "Use [forward_loop Inv] to prove this loop, where Inv is a loop invariant of type (environ -> mpred).  The [forward_loop] tactic will advise you if you need continue: or break: assertions in addition"
end.
Ltac forward_advise_for :=
lazymatch goal with
| |- semax _ _ (Sfor _ _ ?body Sskip) ?R =>
tryif unify (no_breaks body) true
then fail "Use [forward_while Inv] to prove this loop, where Inv is a loop invariant of type (environ->mpred)"
else tryif has_evar R
then fail "Use [forward_for Inv Inv Post] to prove this loop, where Inv is a loop invariant of type (A -> environ -> mpred), and Post is a loop-postcondition. A is the type of whatever loop-varying quantity you have, such as the value of your loop iteration variable.  You can use the same Inv twice, before and after the for-loop-increment statement, because your for-loop-increment statement is trivial"
else fail "Use [forward_for Inv Inv] to prove this loop, where Inv is a loop invariant of type (A -> environ -> mpred).  A is the type of whatever loop-varying quantity you have, such as your loop iteration variable.  You can use the same Inv twice, before and after the for-loop-increment statement, because your for-loop-increment statement is trivial"
| |- semax _ _ (Sfor _ ?test ?body ?incr) ?R =>
tryif has_evar R
then tryif unify (no_breaks body) true
then tryif test_simple_bound test incr
then fail "You can probably use [forward_for_simple_bound n Inv], provided that the upper bound of your loop can be expressed as a constant value (n:Z), and the loop invariant Inv can be expressed as (EX i:Z, ...).  Note that the Inv need not mention the LOCAL binding of the loop-count variable to the value i, and need not assert the PROP that i<=n; these will be inserted automatically.
Otherwise, you can use the general case:
Use [forward_for Inv PreInc] to prove this loop, where Inv is a loop invariant of type (A -> environ -> mpred), and PreInc is the invariant (of the same type) just before the for-loop-increment statement"
else fail "Use [forward_for Inv PreInc] to prove this loop, where Inv is a loop invariant of type (A -> environ -> mpred), and PreInc is the invariant (of the same type) just before the for-loop-increment statement"
else fail "Use [forward_for Inv PreInc Post] to prove this loop, where Inv is a loop invariant of type (A -> environ -> mpred), PreInc is the invariant (of the same type) just before the for-loop-increment statement, and  Post is a loop-postcondition"
else tryif test_simple_bound test incr
then fail "You can probably use [forward_for_simple_bound n Inv], provided that the upper bound of your loop can be expressed as a constant value (n:Z), and the loop invariant Inv can be expressed as (EX i:Z, ...).  Note that the Inv need not mention the LOCAL binding of the loop-count variable to the value i, and need not assert the PROP that i<=n; these will be inserted automatically.
Otherwise, you can use the general case:
Use [forward_for Inv PreInc] to prove this loop, where Inv is a loop invariant of type (A -> environ -> mpred), and PreInc is the invariant (of the same type) for just before the for-loop-increment statement"
else fail "Use [forward_for Inv PreInc] to prove this loop, where Inv is a loop invariant of type (A -> environ -> mpred), and PreInc is the invariant (of the same type) for just before the for-loop-increment statement"
end.
Ltac forward_advise_if :=
advise_prepare_postcondition;
lazymatch goal with
| |- semax _ _ (Sifthenelse _ _ _) ?R =>
tryif has_evar R
then fail "Use [forward_if Post] to prove this if-statement, where Post is the postcondition of both branches, or try simply 'forward_if' without a postcondition to see if that is permitted in this case"
else fail "Use [forward_if] to prove this if-statement; you don't need to supply a postcondition"
end.
Ltac forward_advise_while :=
advise_prepare_postcondition;
lazymatch goal with
| |- semax _ _ (Swhile _ _) _ =>
fail "Use [forward_while Inv] to prove this loop, where Inv is the loop invariant"
end.
Ltac forward1 s :=  (* Note: this should match only those commands that
can take a normal_ret_assert *)
lazymatch s with
| Sassign _ _ => clear_Delta_specs; store_tac
| Sset _ ?e => clear_Delta_specs;
first [no_loads_expr e false; forward_setx | load_tac]
| Sifthenelse ?e _ _ => forward_advise_if
| Swhile _ _ => forward_advise_while
| Sfor _ _ _ _ => forward_advise_loop s
| Sloop _ _ => forward_advise_loop s
| Scall _ (Evar _ _) _ =>  advise_forward_call
| Sskip => forward_skip
end.
Ltac derives_after_forward :=
first [ simple apply derives_refl
| simple apply ENTAIL_refl
(*                     | simple apply normal_ret_assert_derives'' | simple apply normal_ret_assert_derives' *)
| idtac].
Ltac forward_break :=
eapply semax_pre; [ | apply semax_break ];
unfold_abbrev_ret;
simpl_ret_assert (*autorewrite with ret_assert*).
Ltac forward_continue :=
eapply semax_pre; [ | apply semax_continue ];
unfold_abbrev_ret;
simpl_ret_assert (*autorewrite with ret_assert*).
Ltac simpl_first_temp :=
try match goal with
| |- semax _ (PROPx _ (LOCALx (temp _ ?v :: _) _)) _ _ =>
let x := fresh "x" in set (x:=v);
simpl in x; unfold x; clear x
| |- (PROPx _ (LOCALx (temp _ ?v :: _) _)) |-- _ =>
let x := fresh "x" in set (x:=v);
simpl in x; unfold x; clear x
end.
Ltac fwd_result :=
repeat
(let P := fresh "P" in
match goal with
| |- context[remove_localdef_temp ?A ?B] =>
set (P := remove_localdef_temp A B);
hnf in P;
subst P
end);
unfold replace_nth, repinject; cbv beta iota zeta;
repeat simpl_proj_reptype.
Ltac check_precondition :=
lazymatch goal with
| |- semax _ (PROPx _ (LOCALx _ (SEPx ?R))) _ _ =>
lazymatch R with context [sepcon _ _ :: _] =>
fail "The SEP clause of the precondition contains * (separating conjunction).
You must flatten the SEP clause, e.g. by doing [Intros],
or else hide the * by making a Definition or using a freezer"
| _ => idtac
end
| |- semax _ (exp _) _ _ =>
fail "Before going 'forward', you need to move the existentially quantified variable at the head of your precondition 'above the line'.  Do this by the tactic 'Intros x', where 'x' is the name you want to give to this Coq variable"
| |- _ => fail "Your precondition is not in canonical form (PROP (..) LOCAL (..) SEP (..))"
end.
Ltac forward :=
try apply semax_ff;
check_Delta; check_POSTCONDITION;
repeat rewrite <- seq_assoc;
lazymatch goal with
| |- semax _ _ (Ssequence (Sreturn _) _) _ =>
apply semax_seq with FF; [ | apply semax_ff];
clear_Delta_specs; forward_return
| |- semax _ _ (Sreturn _) _ =>  clear_Delta_specs; forward_return
| |- semax _ _ (Ssequence Sbreak _) _ =>
apply semax_seq with FF; [ | apply semax_ff];  forward_break
| |- semax _ _ (Ssequence Scontinue _) _ =>
apply semax_seq with FF; [ | apply semax_ff];  forward_continue
| |- semax _ _ Sbreak _ => forward_break
| |- semax _ _ Scontinue _ => forward_continue
| |- semax _ _ Sskip _ => fwd_skip
| |- semax _ _ ?c0 _ =>
match c0 with
| Ssequence _ _ => idtac
| _ => rewrite -> semax_seq_skip
end;
match goal with
| |- semax _ _ (Ssequence ?c _) _ =>
check_precondition;
eapply semax_seq';
[ forward1 c
| fwd_result;
Intros;
abbreviate_semax;
try fwd_skip ]
end
end.

Lemma semax_stackframe_emp:
forall Espec {cs: compspecs} Delta P c R,
@semax cs Espec Delta P c R ->
@semax cs Espec Delta (P * emp) c (frame_ret_assert R emp) .