Library CFML.CFTactics

Set Implicit Arguments.
Require Import LibCore Shared CFHeaps.
Require Export CFPrint.



cfml_find_hprop_at X returns the hprop X ~> T that appears in the pre-condition.

Ltac cfml_find_hprop_at X :=
  match goal with |- ?R ?H ?Q =>
  match H with context [ X ~> ?T ] =>
     constr:(X ~> T) end end.


Auxiliary tactic for obtaining a boolean answer to the question "is E an evar?". TODO: move to TLC

Ltac is_evar_as_bool E :=
  constr:(ltac:(first
    [ is_evar E; exact true
    | exact false ])).


Ltac cfml_get_precondition tt :=
  match goal with |- ?R ?H ?Q => constr:(H) end.


Ltac cfml_get_postcondition tt :=
  match goal with |- ?E =>
  match get_fun_arg E with (_,?Q) => constr:(Q)
  end end.

cfml_postcondition_is_evar tt returns a boolean indicating whether the post-condition of the current goal is an evar.

Ltac cfml_postcondition_is_evar tt :=
  let Q := cfml_get_postcondition tt in
  is_evar_as_bool Q.



Ltac cfml_get_goal_fun tt :=
  match cfml_get_goal tt with
  | spec ?f ?n ?P => constr:(f)
  | app ?f ?xs ?H ?Q => constr:(f)
  | tag tag_apply (app ?f ?xs) ?H ?Q => constr:(f)
  end.


Ltac cfml_get_goal_arity tt :=
  let aux xs :=
    let n := eval compute in (List.length xs) in constr:(n) in
  match cfml_get_goal tt with
  | spec ?f ?n ?P => constr:(n)
  | app ?f ?xs ?H ?Q => aux xs
  | tag tag_apply (app ?f ?xs) ?H ?Q => aux xs
  end.


Ltac cfml_show_types_dyn_list L :=
  match L with
  | nil => idtac
  | (@dyn ?T ?x) :: ?R => idtac T "->"; cfml_show_types_dyn_list R
  end.


Ltac cfml_get_app_args E :=
  match E with
  | app ?f ?xs ?H ?Q => constr:(xs)
  | tag tag_apply (app ?f ?xs) ?H ?Q => constr:(xs)
  end.


Ltac cfml_get_goal_app_args tt :=
  match goal with |- ?G => cfml_get_app_args G end.


Ltac cfml_get_app_ret E :=
  match E with
  | @app_def ?f ?xs ?B ?H ?Q => constr:(B)
  | tag tag_apply (@app_def ?f ?xs ?B) ?H ?Q => constr:(B)
  end.


Ltac cfml_get_goal_app_ret tt :=
  match goal with |- ?G => cfml_get_app_ret G end.


Ltac cfml_show_app_type E :=
  let L := cfml_get_app_args E in
  cfml_show_types_dyn_list L;
  let B := cfml_get_app_ret E in
  idtac B.


Ltac cfml_show_app_type_goal tt :=
  match goal with |- ?G => cfml_show_app_type G end.

Ltac cfml_show_app_type_goal tt ::=
  let G := cfml_get_goal tt in
  cfml_show_app_type G.


Ltac cfml_show_app_type_concl S :=
  forwards_nounfold_then S ltac:(fun K =>
    cfml_show_app_type K).



xcur is a convenient way to obtain the current precondition. Syntax is: let H := xcur in ....
Example: let H := xcur in xif (\[= 3] \*+ H) .

Ltac xcur :=
  cfml_get_precondition tt.


xclean is a tactic that performs some clean up throughout the goal by taking care of:
  • rewriting facts such as true = isTrue P into P
  • simplifying partially applied equality, e.g. (= 3) n to n = 3.
See reflect_clean tt from the Shared.v file.
Remark: this tactic is automatically called by xpull.

Ltac xclean_core :=
  reflect_clean tt.

Tactic Notation "xclean" :=
  reflect_clean tt.



xauto is a specialized version of auto that works well in program verification.
  • it will not attempt any work if the head of the goal has a tag (i.e. if it is a characteristif formula),
  • it is able to conclude a goal using xok
  • it calls substs to substitute all equalities before trying to call automation.
Tactics xauto, xauto~ and xauto* can be configured independently.
xsimpl~ is equivalent to xsimpl; xauto~. xsimpl* is equivalent to xsimpl; xauto*.

Ltac xok_core cont :=
  solve [ hnf; apply refl_rel_incl'
        | apply pred_incl_refl
        | apply hsimpl_to_qunit; reflexivity
        | hsimpl; cont tt ].

Ltac math_0 ::= xclean.
Ltac xauto_common cont :=
  first [
    cfml_check_not_tagged tt;
    try solve [ cont tt
              | solve [ apply refl_equal ]
              | xok_core ltac:(fun _ => solve [ cont tt | substs; cont tt ] )
              | substs; if_eq; solve [ cont tt | apply refl_equal ] ]
  | idtac ].

Ltac xauto_tilde_default cont := xauto_common cont.
Ltac xauto_star_default cont := xauto_common cont.

Ltac xauto_tilde := xauto_tilde_default ltac:(fun _ => auto_tilde).
Ltac xauto_star := xauto_star_default ltac:(fun _ => auto_star).

Tactic Notation "xauto" "~" := xauto_tilde.
Tactic Notation "xauto" "*" := xauto_star.
Tactic Notation "xauto" := xauto~.

DEPRECATED Extensions for hsimpl to use xauto
Tactic Notation "hsimpl" "~" constr(L) := hsimpl L; xauto~. Tactic Notation "hsimpl" "~" constr(X1) constr(X2) := hsimpl X1 X2; xauto~. Tactic Notation "hsimpl" "~" constr(X1) constr(X2) constr(X3) := hsimpl X1 X2 X3; xauto~.


xok proves a goal of the form H ==> ?H' or Q ===> ?Q' by unifying the right-hand-side with the left-hand-side.
It also tries to call hsimpl to see if it solves the goal. TODO: is this last feature of xok useful?

Tactic Notation "xok" :=
  xok_core ltac:(idcont).
Tactic Notation "xok" "~" :=
  xok_core ltac:(fun _ => xauto~).
Tactic Notation "xok" "*" :=
  xok_core ltac:(fun _ => xauto*).


xsimpl performs a heap entailement simplification using hsimpl, then calls the tactic xclean for clean up.

Ltac xsimpl_core := hsimpl; xclean.

Tactic Notation "xsimpl" := xsimpl_core.
Tactic Notation "xsimpl" "~" := xsimpl; xauto~.
Tactic Notation "xsimpl" "*" := xsimpl; xauto*.


Ltac xsimpl_core_with E := hsimpl E; xclean.
Tactic Notation "xsimpl" constr(E) := xsimpl_core_with E.
Tactic Notation "xsimpl" "~" constr(E) := xsimpl E; xauto~.
Tactic Notation "xsimpl" "*" constr(E) := xsimpl E; xauto*.

Tactic Notation "xsimpl" constr(L) :=
  hsimpl L.
Tactic Notation "xsimpl" constr(X1) constr(X2) :=
  xsimpl (>> X1 X2).
Tactic Notation "xsimpl" constr(X1) constr(X2) constr(X3) :=
  xsimpl (>> X1 X2 X3).
Tactic Notation "xsimpl" constr(X1) constr(X2) constr(X3) constr(X4) :=
  xsimpl (>> X1 X2 X3 X4).
Tactic Notation "xsimpl" constr(X1) constr(X2) constr(X3) constr(X4) constr(X5) :=
  xsimpl (>> X1 X2 X3 X4 X5).

Tactic Notation "xsimpl" "~" constr(L) :=
  hsimpl L; xauto~.
Tactic Notation "xsimpl" "~" constr(X1) constr(X2) :=
  xsimpl (>> X1 X2); xauto~.
Tactic Notation "xsimpl" "~" constr(X1) constr(X2) constr(X3) :=
  xsimpl (>> X1 X2 X3); xauto~.
Tactic Notation "xsimpl" "~" constr(X1) constr(X2) constr(X3) constr(X4) :=
  xsimpl (>> X1 X2 X3 X4); xauto~.
Tactic Notation "xsimpl" "~" constr(X1) constr(X2) constr(X3) constr(X4) constr(X5) :=
  xsimpl (>> X1 X2 X3 X4 X5); xauto~.

Tactic Notation "xsimpl" "*" constr(L) :=
  hsimpl L; xauto*.
Tactic Notation "xsimpl" "*" constr(X1) constr(X2) :=
  xsimpl (>> X1 X2); xauto*.
Tactic Notation "xsimpl" "*" constr(X1) constr(X2) constr(X3) :=
  xsimpl (>> X1 X2 X3); xauto*.
Tactic Notation "xsimpl" "*" constr(X1) constr(X2) constr(X3) constr(X4) :=
  xsimpl (>> X1 X2 X3 X4); xauto*.
Tactic Notation "xsimpl" "*" constr(X1) constr(X2) constr(X3) constr(X4) constr(X5) :=
  xsimpl (>> X1 X2 X3 X4 X5); xauto*.


xlocal proves a goal of the form is_local F or is_local_pred F, by exploiting the fact that
  • F may be of the form local F'
  • is_local F may be an assumption
  • is_local_pred S may be an assumption, with F = S x

Ltac xlocal_core tt ::=
  first [ assumption
        | apply app_local;
          let E := fresh in intro E; discriminate E
        | apply local_is_local
        
        | match goal with H: is_local_pred ?S |- is_local (?S _) => apply H end ].



xpre applies to a goal of the form F H Q and allows to strengthen the pre-condition H. It produces F ?H' Q and H ==> ?H' \* \GC, where Hexists HG, HG can be instantiated with garbage to collect.

Tactic Notation "xpre" constr(H) :=
  eapply (@local_gc_pre H); [ try xlocal | xtag_pre_post | ].


xpost applies to a goal of the form F H Q and allows to weaken the pre-condition Q. It produces F H ?Q' and ?Q' ==> ?Q \* \GC.
xpost Q' applies to a goal of the form F H Q and leaves the goals F H Q' and Q' ===> Q.
xpost H' is a shorthand for xpost (#H').


Lemma xpost_lemma : forall B Q' Q (F:~~B) H,
  is_local F ->
  F H Q' ->
  Q' ===> Q ->
  F H Q.
Proof using. intros. applys* local_weaken. Qed.

Tactic Notation "xpost" :=
  eapply xpost_lemma; [ try xlocal | xtag_pre_post | ].

Lemma fix_post : forall (B:Type) (Q':B->hprop) (F:~~B) (H:hprop) Q,
  is_local F ->
  F H Q' ->
  Q' ===> Q ->
  F H Q.
Proof using. intros. apply* local_weaken. Qed.

Ltac xpost_core Q :=
  let Q' := match type of Q with
    | hprop => constr:(fun (_:unit) => Q)
    | _ => constr:(Q)
    end in
  match cfml_postcondition_is_evar tt with
  | true => apply (@fix_post _ Q'); [ try xlocal | xtag_pre_post | apply refl_rel_incl' ]
  | false => apply (@fix_post _ Q'); [ try xlocal | xtag_pre_post | ]
  end.

Tactic Notation "xpost" constr(Q) :=
  xpost_core Q.


xpull_check_not_needed tt applies to a goal of the form F H Q and raises an error if H contains existentials or pure propositions that could have been extracted using xpull

Ltac xpull_check_not_needed tt :=
  let H := cfml_get_precondition tt in
  hpullable_rec H.

Auxiliary definitions for xpull.

Ltac xpull_core tt :=
  match goal with
  | |- _ ==> _ => hpull; xclean
  | |- _ ===> _ => let r := fresh "r" in intros r; hpull; xclean
  | |- _ => simpl; hclean; instantiate
  end.

xpull extracts existentials and pure propositions from the precondition H of a goal of the form F H Q, or from the left-hand-side H of a goal of the form H ==> H' or H ===> H'.
It calls xclean which is useful for cleaning up

Tactic Notation "xpull" :=
  xpull_core tt; xtag_pre_post.

xpulls calls xpull, assumes that this call produces an equality at the head of the goal, and it then substitutes this equality away.

Tactic Notation "xpulls" :=
  let E := fresh "TEMP" in xpull; intros E; subst_hyp E.

DEPRECATED xpull as I1 .. IN should now be written xpull. => I1 .. IN
Tactic Notation "xpull" "as" simple_intropattern(I1) := xpull; intros I1; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) := xpull; intros I1 I2; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) simple_intropattern(I3) := xpull; intros I1 I2 I3; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) := xpull; intros I1 I2 I3 I4; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5) := xpull; intros I1 I2 I3 I4 I5; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5) simple_intropattern(I6) := xpull; intros I1 I2 I3 I4 I5 I6; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5) simple_intropattern(I6) simple_intropattern(I7) := xpull; intros I1 I2 I3 I4 I5 I6 I7; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5) simple_intropattern(I6) simple_intropattern(I7) simple_intropattern(I8) := xpull; intros I1 I2 I3 I4 I5 I6 I7 I8; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5) simple_intropattern(I6) simple_intropattern(I7) simple_intropattern(I8) simple_intropattern(I9) := xpull; intros I1 I2 I3 I4 I5 I6 I7 I8 I9; xclean. Tactic Notation "xpull" "as" simple_intropattern(I1) simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5) simple_intropattern(I6) simple_intropattern(I7) simple_intropattern(I8) simple_intropattern(I9) simple_intropattern(I10) := xpull; intros I1 I2 I3 I4 I5 I6 I7 I8 I9 I10; xclean.


xgc H' applies to a goal of the form F H Q and substracts H' from H.
xgc_but H' substracts everything but H' from H.
xgc_all substracts everything from H, leaving F \[] Q. It is equivalent to xgc_but \[].
xgc_post applied to the goal F H Q replaces the goal with F H ?Q' and Q' ===> Q \*+ \GC, which allows to perform garbage collection.
xgc x is same as xgc (x ~> T) where T is read in the pre-condition.
TODO: xgc with several arguments, or a list of arguments.

Ltac xgc_remove_hprop H :=
  eapply (@local_gc_pre_on H);
    [ try xlocal
    | try affine
    | hsimpl
    | xtag_pre_post ].

Ltac xgc_remove_core X :=
  xpull_check_not_needed tt;
  match type of X with
  | hprop => xgc_remove_hprop X
  | _ => let E := cfml_find_hprop_at X in
         xgc_remove_hprop E
  end.

Ltac xgc_keep_hprop H :=
  eapply (@local_gc_pre H);
    [ try xlocal
    | hsimpl
    | xtag_pre_post ].

Ltac xgc_keep_core X :=
  xpull_check_not_needed tt;
  match type of X with
  | hprop => xgc_keep_hprop X
  | _ => let E := cfml_find_hprop_at X in
         xgc_keep_hprop E
  end.

Ltac xgc_post_core :=
  xpull_check_not_needed tt;
  eapply local_gc_post;
  [ try xlocal | | xtag_pre_post ].

Tactic Notation "xgc" constr(H) :=
  xgc_remove_core H.

Tactic Notation "xgc_but" constr(H) :=
  xgc_keep_core H.

Tactic Notation "xgc_post" :=
  xgc_post_core.



Lemma local_gc_pre_all : forall B Q (F:~~B) H,
  is_local F ->
  affine H ->
  F \[] Q ->
  F H Q.
Proof using. intros. apply* (@local_gc_pre_on H). hsimpl. Qed.

Tactic Notation "xgc_all" :=
  eapply local_gc_pre_all; [ try xlocal | xtag_pre_post ].


xframe H' applies to a goal of the form F H Q and substracts H' from both H and Q.
xframe_but H' substracts everything but H'.


Lemma xframe_lemma : forall H1 H2 B Q1 (F:~~B) H Q,
  is_local F ->
  H ==> H1 \* H2 ->
  F H1 Q1 ->
  Q1 \*+ H2 ===> Q ->
  F H Q.
Proof using. intros. apply* local_frame. Qed.

Ltac xframe_remove_core H :=
  xpull_check_not_needed tt;
  eapply xframe_lemma with (H2 := H);
    [ try xlocal
    | hsimpl
    | xtag_pre_post
    | ].

Tactic Notation "xframe" constr(H) :=
  xframe_remove_core H.

Ltac xframe_keep_core H :=
  xpull_check_not_needed tt;
  eapply xframe_lemma with (H1 := H);
    [ try xlocal
    | hsimpl
    | xtag_pre_post
    | ].

Tactic Notation "xframe_but" constr(H) :=
  xframe_keep_core H.

xframe_on p applies to a goal of the form F H Q and calls xframe (p ~> M), where M is guessed from the occurence of p ~> M that can be found in the pre-condition H.
Variants: xframe_on p1 p2 and xframe_on p1 p2 p3.

Ltac xframes_core_1 s :=
  match goal with |- ?R ?H ?Q =>
    match H with context [ s ~> ?M ] =>
      xframe (s ~> M) end end.

Tactic Notation "xframes" constr(s1) :=
  xframes_core_1 s1.

Ltac xframes_core_2 s1 s2 :=
  match goal with |- ?R ?H ?Q =>
    match H with context [ s1 ~> ?M1 ] =>
      match H with context [ s2 ~> ?M2 ] =>
        xframe (s1 ~> M1 \* s2 ~> M2) end end end.

Tactic Notation "xframes" constr(s1) constr(s2) :=
  xframes_core_2 s1 s2.

Ltac xframes_core_3 s1 s2 s3 :=
  match goal with |- ?R ?H ?Q =>
    match H with context [ s1 ~> ?M1 ] =>
      match H with context [ s2 ~> ?M2 ] =>
        match H with context [ s3 ~> ?M3 ] =>
          xframe (s1 ~> M1 \* s2 ~> M2 \* s3 ~> M3)
  end end end end.

Tactic Notation "xframes" constr(s1) constr(s2) constr(s3) :=
  xframes_core_3 s1 s2 s3.


xapply E applies a lemma E modulo frame/weakening. It applies to a goal of the form F H Q, and replaces it with F ?H' ?Q', applies E to the goal, then produces the side condition H ==> ?H' and,
  • if Q is instantiated, then leaves ?Q' ===> Q \* \GC
  • otherwise instantiates Q with Q'.
xapplys E is like xapply E but also attemps to simplify the subgoals.

Ltac xapply_core H cont1 cont2 :=
  forwards_nounfold_then H ltac:(fun K =>
    match cfml_postcondition_is_evar tt with
    | true => eapply local_frame; [ xlocal | sapply K | cont1 tt | try xok ]
    | false => eapply local_frame_gc; [ xlocal | sapply K | cont1 tt | cont2 tt ]
    end).

Ltac xapply_base H :=
  xpull_check_not_needed tt;
  xapply_core H ltac:(fun tt => idtac) ltac:(fun tt => idtac).

Ltac xapplys_base H :=
  xpull_check_not_needed tt;
  xapply_core H ltac:(fun tt => hcancel) ltac:(fun tt => hsimpl).

Tactic Notation "xapply" constr(H) :=
  xapply_base H.
Tactic Notation "xapply" "~" constr(H) :=
  xapply H; xauto~.
Tactic Notation "xapply" "*" constr(H) :=
  xapply H; xauto*.

Tactic Notation "xapplys" constr(H) :=
  xapplys_base H.
Tactic Notation "xapplys" "~" constr(H) :=
  xapplys H; xauto~.
Tactic Notation "xapplys" "*" constr(H) :=
  xapplys H; xauto*.



xchange E applies to a goal of the form F H Q and to a lemma E of type H1 ==> H2 or H1 = H2. It replaces the goal with F H' Q, where H' is computed by replacing H1 with H2 in H.
The substraction is computed by solving H ==> H1 \* ?H' with hsimpl. If you need to solve this implication by hand, use xchange_no_simpl E instead.
xchange <- E is useful when E has type H2 = H1 instead of H1 = H2.
xchange_show E is useful to visualize the instantiation of the lemma used to implement xchange.


Lemma xchange_lemma : forall H1 H1' H2 B H Q (F:~~B),
  is_local F ->
  (H1 ==> H1') ->
  (H ==> H1 \* H2) ->
  F (H1' \* H2) Q ->
  F H Q.
Proof using.
  introv W1 L W2 M. applys local_frame __ \[]; eauto.
  hsimpl. hchange~ W2. hsimpl~. rew_heap~.
Qed.

Ltac xchange_apply L cont :=
   eapply xchange_lemma;
     [ try xlocal | applys L | cont tt | xtag_pre_post ].

Ltac xchange_forwards L modif cont :=
  forwards_nounfold_then L ltac:(fun K =>
  match modif with
  | __ =>
     match type of K with
     | _ = _ => xchange_apply (@pred_incl_proj1 _ _ _ K) cont
     | _ => xchange_apply K cont
     end
  | _ => xchange_apply (@modif _ _ _ K) cont
  end).

Ltac xchange_with_core cont H H' :=
  eapply xchange_lemma with (H1:=H) (H1':=H');
    [ try xlocal | | cont tt | xtag_pre_post ].

Ltac xchange_core cont E modif :=
  match E with
  | ?H ==> ?H' => xchange_with_core cont H H'
  | _ => xchange_forwards E modif ltac:(fun _ => instantiate; cont tt)
  end.

Ltac xchange_base cont E modif :=
  xpull_check_not_needed tt;
  match goal with
  | |- _ ==> _ => hchange_base E modif
  | |- _ ===> _ => hchange_base E modif
  | _ => xchange_core cont E modif
  end.

Tactic Notation "xchange" constr(E) :=
  xchange_base ltac:(fun tt => hsimpl) E __.
Tactic Notation "xchange" "~" constr(E) :=
  xchange E; xauto~.
Tactic Notation "xchange" "*" constr(E) :=
  xchange E; xauto*.

Tactic Notation "xchange" "<-" constr(E) :=
  xchange_base ltac:(fun tt => hsimpl) E pred_incl_proj2.
Tactic Notation "xchange" "~" "<-" constr(E) :=
  xchange <- E; xauto~.
Tactic Notation "xchange" "*" "<-" constr(E) :=
  xchange <- E; xauto*.

Tactic Notation "xchange_no_simpl" constr(E) :=
  xchange_base ltac:(fun tt => idtac) E __.
Tactic Notation "xchange_no_simpl" "<-" constr(E) :=
  xchange_base ltac:(fun tt => idtac) E pred_incl_proj2.

Tactic Notation "xchange_show" constr(E) :=
  xchange_forwards E __ ltac:(idcont).
Tactic Notation "xchange_show" "<-" constr(E) :=
  xchange_forwards pred_incl_proj2 ltac:(idcont).




xopen is a tactic for applying xchange without having to explicitly specify the name of a focusing lemma.
xopen p applies to a goal of the form F H Q or H ==> H' or Q ===> Q'. It first searches for the pattern p ~> T in the pre-condition, then looks up in a database for the focus lemma E associated with the form T, and then calls xchange E.
xopen_show p shows the lemma found, it is useful for debugging.
Example for registering a focusing lemma: Hint Extern 1 (RegisterOpen (Tree _)) => Provide tree_open. See Demo_proof.v for examples.
Then, use: xopen p.
Variants:
  • xopenx t is short for xopen t; xpull
  • xopenxs t is short for xopen t; xpulls // EXPERIMENTAL
  • xopen2 p to perform xopen p twice. Only applies when there is no existentials to be extracted after the first xopen.

Ltac get_refocus_args tt :=
  match goal with
  | |- ?Q1 ===> ?Q2 => constr:((Q1,Q2))
  | |- ?H1 ==> ?H2 => constr:((H1,H2))
  | |- ?R ?H1 ?Q2 => constr:((H1,Q2))
  
  end.

Ltac get_refocus_constr_in H t :=
  match H with context [ t ~> ?T ] => constr:(T) end.

Ltac xopen_constr t :=
  match get_refocus_args tt with (?H1,?H2) =>
  get_refocus_constr_in H1 t end.

Ltac xopen_core t :=
  let C1 := xopen_constr t in
  ltac_database_get database_xopen C1;
  let K := fresh "TEMP" in
  intros K; xchange (K t); clear K.

Ltac xopen_show_core t :=
  let C1 := xopen_constr t in
  pose C1;
  try ltac_database_get database_xopen C1;
  intros.

Tactic Notation "xopen_show" constr(t) :=
  xopen_show_core t.

Tactic Notation "xopen" constr(t) :=
  xopen_core t.
Tactic Notation "xopen" "~" constr(t) :=
  xopen t; xauto~.
Tactic Notation "xopen" "*" constr(t) :=
  xopen t; xauto*.

Tactic Notation "xopen2" constr(x) :=
  xopen x; xopen x.
Tactic Notation "xopen2" "~" constr(x) :=
  xopen2 x; xauto_tilde.
Tactic Notation "xopen2" "*" constr(x) :=
  xopen2 x; xauto_star.

Tactic Notation "xopenx" constr(t) :=
  xopen t; xpull.

Tactic Notation "xopenxs" constr(t) :=
  xopen t; xpulls.


xclose is the symmetrical of xopen. It works in the same way, except that it looks for an unfocusing lemma.
xclose p applies to a goal of the form F H Q or H ==> H' or Q ===> Q'. It first searches for the pattern p ~> T in the pre-condition, then looks up in a database for the unfocus lemma E associated with the form T, and then calls xchange E.
xclose_show p shows the lemma found, it is useful for debugging.
Example for registering a focusing lemma:
Hint Extern 1 (RegisterClose (Ref Id (MNode _ ))) => Provide tree_node_close.
Then, use: xclose p.
Variants:
  • xclose p1 .. pn is short for xclose p1; ..; xclose pn
  • xclose2 p to perform xclose p twice.
  • xclose (>> p X Y) where the extra arguments are used to provide explicit arguments to instantiate the "closing" lemma.

Ltac xclose_get_ptr args :=
  match args with (boxer ?t)::_ => t end.

Ltac xclose_constr args :=
  let t := xclose_get_ptr args in
  match get_refocus_args tt with (?H1,?H2) =>
  get_refocus_constr_in H1 t end.

Ltac xclose_core args :=
  let args := list_boxer_of args in
  let C1 := xclose_constr args in
  ltac_database_get database_xclose C1;
  let K := fresh "TEMP" in
  intros K;
  let E := constr:((boxer K)::args) in
  xchange E;
  clear K.

Ltac xclose_show_core args :=
  let args := list_boxer_of args in
  let C1 := xclose_constr args in
  pose C1;
  try ltac_database_get database_xclose C1;
  intros.

Tactic Notation "xclose_show" constr(t) :=
  xclose_show_core t.

Tactic Notation "xclose" constr(t) :=
  xclose_core t.
Tactic Notation "xclose" "~" constr(t) :=
  xclose t; xauto~.
Tactic Notation "xclose" "*" constr(t) :=
  xclose t; xauto*.

Tactic Notation "xclose" constr(t1) constr(t2) :=
  xclose t1; xclose t2.
Tactic Notation "xclose" constr(t1) constr(t2) constr(t3) :=
  xclose t1; xclose t2 t3.
Tactic Notation "xclose" constr(t1) constr(t2) constr(t3) constr(t4) :=
  xclose t1; xclose t2 t3 t4.

Tactic Notation "xclose2" constr(x) :=
  xclose x; xclose x.
Tactic Notation "xclose2" "~" constr(x) :=
  xclose2 x; xauto_tilde.
Tactic Notation "xclose2" "*" constr(x) :=
  xclose2 x; xauto_star.



xgen E applies to a goal of the form H ==> H', where H contains occurences of E in the sense H = J E, and it abstract E from H, turning the goal into (Hexists X, J X) ==> H'.

Lemma xgen_lemma : forall A (J:A->hprop) (E:A),
  J E ==> heap_is_pack J.
Proof using. intros. hsimpl*. Qed.

Ltac xgen_abstract H E :=
  let Jx := eval pattern E in H in
  match Jx with ?J _ => constr:(J) end.

Ltac xgen_no_simpl E :=
  match goal with |- ?H ==> _ =>
    let J := xgen_abstract H E in
    eapply pred_incl_trans; [ apply (@xgen_lemma _ J E) | ] end.

Ltac xgen_base E :=
  xgen_no_simpl E.

Tactic Notation "xgen" constr(E1) :=
  xgen_base E1.





xlet is used to reason on a let node, i.e. on a goal of the form (Let x := F1 in F2) H Q.
Use xlet Q to provide a postcondition for F1. Use xlet as y to rename x into y. Use xlet Q as y to do both.

Ltac xlet_core cont0 cont1 cont2 :=
  apply local_erase;
  match goal with |- cf_let ?F1 (fun x => _) ?H ?Q =>
    cont0 tt;
    split; [ | cont1 x; cont2 tt ];
    xtag_pre_post
  end.

Ltac xlet_pre tt :=
  xpull_check_not_needed tt;
  xuntag tag_let.

Ltac xlet_def cont0 cont1 cont2 :=
  xlet_pre tt; xlet_core cont0 cont1 cont2.

Ltac xlet_intros_cont x :=
  let a := fresh x in intros a.

Ltac xlet_spec_as Q x :=
  xlet_def
    ltac:(fun _ => exists Q)
    ltac:(fun _ => intros x)
    ltac:(fun _ => try xpull).

Ltac xlet_spec Q :=
  xlet_def
    ltac:(fun _ => exists Q)
    xlet_intros_cont
    ltac:(fun _ => try xpull).

Ltac xlet_as x :=
  xlet_def
    ltac:(fun _ => esplit)
    ltac:(fun _ => intros x)
    ltac:(fun _ => idtac).

Ltac xlet_basic tt :=
  xlet_def
    ltac:(fun _ => esplit)
    xlet_intros_cont
    ltac:(fun _ => idtac).

Tactic Notation "xlet" constr(Q) "as" simple_intropattern(x) :=
  xlet_spec_as Q x.

Tactic Notation "xlet" constr(Q) :=
  xlet_spec Q.

Tactic Notation "xlet" "as" simple_intropattern(x) :=
  xlet_as x.

Tactic Notation "xlet" :=
  xlet_basic tt.

Tactic Notation "xlet" "~" := xlet; xauto~.
Tactic Notation "xlet" "~" "as" simple_intropattern(x) := xlet as x; xauto~.
Tactic Notation "xlet" "~" constr(Q) := xlet Q; xauto~.
Tactic Notation "xlet" "~" constr(Q) "as" simple_intropattern(x) := xlet Q as x; xauto~.
Tactic Notation "xlet" "*" := xlet; xauto*.
Tactic Notation "xlet" "*" "as" simple_intropattern(x) := xlet as x; xauto*.
Tactic Notation "xlet" "*" constr(Q) := xlet Q; xauto*.
Tactic Notation "xlet" "*" constr(Q) "as" simple_intropattern(x) := xlet Q as x; xauto*.

DEPRECATED xlets
Tactic Notation "xlets" constr(Q) := xlet Q; | intro_subst .


xlet_poly is used to reason on a let node, i.e. on a goal of the form (LetPoly {A1} x := F1 in F2) H Q.
Variants:
  • xlet_poly P H to describe the pure postcondition and the final state.
  • xlet_poly_keep P to provide a pure postcondition and use the current pre-condition as final state.
  • xlet_poly P to provide a pure postcondition and introduce a unification variable for the imperative state.
  • xlet_poly as y to rename x into y.
  • xlet_poly P H as y.

Ltac xlet_poly_core cont0 cont1 cont2 :=
  apply local_erase;
  cont0 tt;
  split; [ intros | cont1 tt; cont2 tt ];
  xtag_pre_post.

Ltac xlet_poly_def cont0 cont1 cont2 :=
  xpull_check_not_needed tt;
  xuntag tag_let_poly;
  xlet_poly_core cont0 cont1 cont2.

Tactic Notation "xlet_poly" constr(P) constr(H) "as" simple_intropattern(x) simple_intropattern(Hx) :=
  xlet_poly_def
    ltac:(fun _ => exists P; exists H)
    ltac:(fun _ => intros x Hx)
    ltac:(fun _ => try xpull).

Tactic Notation "xlet_poly" constr(P) constr(H) "as" simple_intropattern(x) :=
  let Hx := fresh "H" x in
  xlet_poly P H as x Hx.

Tactic Notation "xlet_poly" constr(P) constr(H) :=
  xlet_poly_def
    ltac:(fun _ => exists P; exists H)
    ltac:(fun _ => intro)
    ltac:(fun _ => try xpull).

Tactic Notation "xlet_poly" constr(P) "as" simple_intropattern(x) simple_intropattern(Hx) :=
  xlet_poly_def
    ltac:(fun _ => exists P; esplit)
    ltac:(fun _ => intros x Hx)
    ltac:(fun _ => idtac).

Tactic Notation "xlet_poly" constr(P) "as" simple_intropattern(x) :=
  let Hx := fresh "H" x in
  xlet_poly P as x Hx.

Tactic Notation "xlet_poly" constr(P) :=
  xlet_poly_def
    ltac:(fun _ => exists P; esplit)
    ltac:(fun _ => intro)
    ltac:(fun _ => idtac).

Tactic Notation "xlet_poly_keep" constr(P) "as" simple_intropattern(x) simple_intropattern(Hx) :=
  let H := cfml_get_precondition tt in
  xlet_poly P H as x.

Tactic Notation "xlet_poly_keep" constr(P) "as" simple_intropattern(x) :=
  let Hx := fresh "H" x in
  xlet_poly_keep P as x Hx.

Tactic Notation "xlet_poly_keep" constr(P) :=
  let H := cfml_get_precondition tt in
  xlet_poly P H.



xseq is used to reason on a seq node, i.e. on a goal of the form (F1 ;; F2) H Q.
Use xseq H' to provide a post-condition for F1, where H' may be of type hprop or of type unit->hprop.
Use xseq_no_xpull H' to prevent xpull from being called automatically after xseq H'.

Ltac xseq_core cont0 cont1 :=
  apply local_erase;
  cont0 tt;
  split; [ | cont1 tt ];
  xtag_pre_post.

Ltac xseq_pre tt :=
  xpull_check_not_needed tt;
  xuntag tag_seq.

Ltac xseq_noarg_core tt :=
  xseq_pre tt;
  xseq_core
    ltac:(fun _ => esplit)
    ltac:(fun _ => idtac).

Ltac xseq_arg_core H :=
  xseq_pre tt;
  xseq_core
    ltac:(fun _ => first [ exists (#H) | exists H ])
    ltac:(fun _ => try xpull).

Ltac xseq_no_xpull_core H :=
  xseq_pre tt;
  xseq_core
     ltac:(fun _ => first [ exists (#H) | exists H ])
     ltac:(fun _ => idtac).

Tactic Notation "xseq" :=
  xseq_noarg_core tt.

Tactic Notation "xseq" constr(H) :=
  xseq_arg_core H.

Tactic Notation "xseq_no_xpull" constr(H) :=
  xseq_no_xpull_core H.

Tactic Notation "xseq" "~" := xseq; xauto~.
Tactic Notation "xseq" "~" constr(H) := xseq H; xauto~.
Tactic Notation "xseq" "*" := xseq; xauto*.
Tactic Notation "xseq" "*" constr(H) := xseq H; xauto*.


xval is used to reason on a let-value node, i.e. on a goal of the form (Val x := v in F1) H Q. It produces an equality x = v.
Use xval as y to rename x into y. Use xval as y E to rename x into y and name E the equality y=v.

Ltac xval_pre tt :=
  xpull_check_not_needed tt;
  xuntag tag_val;
  apply local_erase.

Ltac xval_core x Hx :=
  xval_pre tt;
  intros x Hx;
  xtag_pre_post.

Tactic Notation "xval" "as" simple_intropattern(x) simple_intropattern(Hx) :=
  xval_core x Hx.

Tactic Notation "xval" "as" simple_intropattern(x) :=
  let Hx := fresh "P" x in xval_core x Hx.

Ltac xval_anonymous_impl tt :=
  xval_pre tt;
  intro;
  let x := get_last_hyp tt in
  revert x;
  let Hx := fresh "P" x in
  intros x Hx;
  xtag_pre_post.

Tactic Notation "xval" :=
  xval_anonymous_impl tt.

xvals is like xval but it immediately substitutes the equality x = v produced by xval.

Ltac xvals_core tt :=
  xval_pre tt; intro; intro_subst; xtag_pre_post.

Tactic Notation "xvals" :=
  xvals_core tt.

xval_st P is used to assign an abstract specification to the value. Instead of producing x = v as hypothesis, it produces P x as hypothesis, and issues P v as subgoal.
Use xval_st P as y to rename x into y. Use xval_st P as y Hy to rename x into y and specify the name of the hypothesis P y.

Ltac xval_st_core P x Hx :=
  let E := fresh in
  intros x E;
  asserts Hx: (P x); [ subst x | clear E; xtag_pre_post ].

Ltac xval_st_impl P x Hx :=
  xval_pre tt; xval_st_core P x Hx.

Tactic Notation "xval_st" constr(P) "as" simple_intropattern(x) simple_intropattern(Hx) :=
  xval_st_impl P x Hx.

Tactic Notation "xval_st" constr(P) "as" simple_intropattern(x) :=
  let Hx := fresh "P" x in xval_st_impl P x Hx.

Ltac xval_st_anonymous_impl P :=
  xval_pre tt; intro; let x := get_last_hyp tt in revert x;
  let Hx := fresh "P" x in xval_st_core P x Hx.

Tactic Notation "xval_st" constr(P) :=
  xval_st_anonymous_impl P.


xind_skip allows to assume the current goal to be already true. It is useful to test a proof before justifying termination. It applies to a goal G and turns it into G -> G. Typical usage: xind_skip ;=> IH.
Use it for development purpose only.
Variant: xind_skip as IH, equivalent to xind_skip ;=> IH.

Tactic Notation "xind_skip" :=
  let H := fresh "IH" in skip_goal H; gen H.

Tactic Notation "xind_skip" "as" ident(IH) :=
  skip_goal IH.


xfun applies to a goal of the form (LetFunc f := H in F) H Q. The tactic introduces an hypothesis describing the most-general specification of f, and leaves F H Q as goal.
  • xfun as f can be used to name the function.
  • xfun as f Hf can be used to name the function and its specification.
  • xfun P can be used to give a specification for f, proved with respect to the most-general specification. Here, P should take the form fun f => spec_of_f. When this tactic fails, try xfun_no_simpl P as f Sf. intros. xapp_types. apply Sf.
  • xfun_ind R P is a shorthand for proving a recursive function by well-founded induction on the first argument quantified in P. It is similar to xfun_no_simpl P, followed by intros n and induction_wf IH: R n, and then exploiting the characteristic formula. The binary relation R needs to be shown well-founded. Typical relation includes downto 0 and upto n for induction on the type int. TODO: show a demo when R is a measure.
  • xfun_no_simpl P is like xfun P but does not attempt to automatically exploit the most general specification for proving the special specification. Use xapp or apply to continue the proof.
  • xfun_ind_no_simpl R P is like xfun_ind R P but does not attempt to automatically exploit the most general specification for proving the special specification. Use xapp or apply to continue the proof.
  • xfun_ind_skip P is a development tactic used to skip the need to justify termination.
  • Also available: xfun P as f xfun P as f Hf xfun_no_simpl P as f xfun_no_simpl P as f Hf xfun_ind R P as IH xfun_ind_no_simpl R P as IH xfun_ind_skip_no_simpl P

Ltac xfun_pre tt :=
  xuntag tag_fun; apply local_erase.

Ltac xfun_arg0 tt :=
  xfun_pre tt;
  intro;
  let f := get_last_hyp tt in
  let Hf := fresh "S" f in
  intros Hf;
  xtag_pre_post.

Tactic Notation "xfun" :=
  xfun_arg0 tt.

Ltac xfun_arg1 f :=
  xfun_pre tt;
  intros f;
  let Hf := fresh "S" f in
  intros Hf;
  xtag_pre_post.

Tactic Notation "xfun" "as" ident(f) :=
  xfun_arg1 f.

Ltac xfun_arg2 f Hf :=
  xfun_pre tt;
  intros f Hf;
  xtag_pre_post.

Tactic Notation "xfun" "as" ident(f) ident(Hf) :=
  xfun_arg2 f Hf.


Ltac xfun_spec_as f P Hf cont1 :=
  let H := fresh "B" f in
  assert (H: P f);
  [ cont1 Hf
  | clear Hf; rename H into Hf; hnf in Hf ];
  xtag_pre_post.

Ltac xfun_spec_as_0 P cont :=
  xfun_pre tt;
  intro;
  let f := get_last_hyp tt in
  let Hf := fresh "S" f in
  intros Hf;
  xfun_spec_as f P Hf cont.

Ltac xfun_spec_as_1 P f cont :=
  xfun_pre tt;
  intros f;
  let Hf := fresh "S" f in
  intros Hf;
  xfun_spec_as f P Hf cont.

Ltac xfun_spec_as_2 P f Hf cont :=
  xfun_pre tt;
  intros f Hf;
  xfun_spec_as f P Hf cont.


Ltac xfun_simpl Hf :=
  intros; eapply Hf; clear Hf; xtag_pre_post.
Tactic Notation "xfun" constr(P) :=
  xfun_spec_as_0 P ltac:(fun Hf => xfun_simpl Hf).

Tactic Notation "xfun" constr(P) "as" ident(f) :=
  xfun_spec_as_1 P f ltac:(fun Hf => xfun_simpl Hf).

Tactic Notation "xfun" constr(P) "as" ident(f) ident(Hf) :=
  xfun_spec_as_2 P f Hf ltac:(fun Hf => xfun_simpl Hf).

Tactic Notation "xfun_no_simpl" constr(P) :=
  xfun_spec_as_0 P ltac:(fun _ => idtac).

Tactic Notation "xfun_no_simpl" constr(P) "as" ident(f) :=
  xfun_spec_as_1 P f ltac:(fun _ => idtac).

Tactic Notation "xfun_no_simpl" constr(P) "as" ident(f) ident(Hf) :=
  xfun_spec_as_2 P f Hf ltac:(fun _ => idtac).


Ltac xfun_ind_core_no_simpl_then R P IH cont :=
  xfun_spec_as_0 P ltac:(fun Hf =>
    intro;
    let X := get_last_hyp tt in
    induction_wf_core_then IH R X ltac:(fun _ => cont Hf)).

Ltac xfun_ind_core_no_simpl R P IH :=
  xfun_ind_core_no_simpl_then R P IH ltac:(fun Hf => idtac).

Ltac xfun_ind_core R P IH :=
  xfun_ind_core_no_simpl_then R P IH ltac:(fun Hf =>
    intros; apply (proj2 Hf); clear Hf).

Tactic Notation "xfun_ind" constr(R) constr(P) "as" ident(IH) :=
  xfun_ind_core R P IH.

Tactic Notation "xfun_ind" constr(R) constr(P) :=
  let IH := fresh "IH" in xfun_ind R P as IH.

Tactic Notation "xfun_ind_no_simpl" constr(R) constr(P) "as" ident(IH) :=
  xfun_ind_core_no_simpl R P IH.

Tactic Notation "xfun_ind_no_simpl" constr(R) constr(P) :=
  let IH := fresh "IH" in xfun_ind_core_no_simpl R P IH.

Ltac xfun_ind_skip_then P IH cont :=
  xfun_spec_as_0 P ltac:(fun Hf =>
    intro;
    let X := get_last_hyp tt in
    skip_goal IH;
    cont Hf).

Ltac xfun_ind_skip_core P IH :=
  xfun_ind_skip_then P IH ltac:(fun Hf =>
    intros; apply (proj2 Hf); clear Hf).

Tactic Notation "xfun_ind_skip" constr(P) :=
  let IH := fresh "IH" in xfun_ind_skip_core P IH.

Tactic Notation "xfun_ind_skip_no_simpl" constr(P) :=
  let IH := fresh "IH" in
  xfun_ind_skip_then P IH ltac:(fun Hf => idtac).


xret applies to a goal of the form (Ret v) H Q, (or, more generally, on goals of the from (Let x := (Ret V) in F) H Q in which case xlet is called first).
It changes the goal to H ==> Q v \* \GC, where \GC can be instantiated with garbage. Use xret_no_gc to only produce the goal H ==> Q v.
Note that xret automatically calls xclean then xpull.
Variants:
  • xret_no_clean may be used to disable xclean and xpull.
  • xret_no_pull may be used to disable xpull.
  • xret_no_gc can be used to not introduce an existentially- quantified heap for garbage collection.
  • xrets has a different behavior depending on the goal:
    • If the goal is Ret v, then it is short for xret; xsimpl.
    • If the goal is Let .., then it is short for xret; xpull; try intro_subst.
  • xret Q is like xret but assigns the post-condition of the formula Ret v to Q.
  • xrets Q is like xrets but assigns the post-condition of the formula Ret v to Q.


Lemma xret_lemma : forall B (v:B) H (Q:B->hprop),
  H ==> Q v \* \GC ->
  local (fun H' Q' => H' ==> Q' v) H Q.
Proof using.
  introv W. eapply (@local_gc_pre_on (\GC)).
  auto. affine. hchanges W. apply~ local_erase. hsimpl.
Qed.


Lemma xret_lemma_unify : forall B (v:B) H,
  local (fun H' Q' => H' ==> Q' v) H (fun x => \[x = v] \* H).
Proof using.
  intros. apply~ local_erase. hsimpl. auto.
Qed.


Lemma xret_no_gc_lemma : forall B (v:B) H (Q:B->hprop),
  H ==> Q v ->
  local (fun H' Q' => H' ==> Q' v) H Q.
Proof using.
  introv W. apply~ local_erase.
Qed.

Ltac xret_apply_lemma tt :=
  first [ apply xret_lemma_unify
        | apply xret_lemma ].

Ltac xret_no_gc_core tt :=
  first [ apply xret_lemma_unify
        | eapply xret_no_gc_lemma ].

Ltac xret_pre cont1 cont2 :=
  xpull_check_not_needed tt;
  match cfml_get_tag tt with
  | tag_ret => cont1 tt
  | tag_let => xlet; [ cont1 tt | instantiate; cont2 tt ]
  end.

Ltac xret_no_clean_core tt :=
  xret_pre
    ltac:(fun _ => xret_apply_lemma tt)
    ltac:(fun _ => idtac).

Ltac xret_no_pull_core tt :=
  xret_pre
    ltac:(fun _ => xret_apply_lemma tt; xclean)
    ltac:(fun _ => idtac).

Ltac xret_core tt :=
  xret_pre
    ltac:(fun _ => xret_apply_lemma tt; xclean)
    ltac:(fun _ => try xpull).

Tactic Notation "xret_no_clean" :=
  xret_no_clean_core tt.

Tactic Notation "xret_no_pull" :=
  xret_no_pull_core tt.

Tactic Notation "xret" :=
  xret_core tt.
Tactic Notation "xret" "~" :=
  xret; xauto~.
Tactic Notation "xret" "*" :=
  xret; xauto*.

Tactic Notation "xret_no_gc" :=
  xret_no_gc_core tt.
Tactic Notation "xret_no_gc" "~" :=
  xret_no_gc; xauto~.
Tactic Notation "xret_no_gc" "*" :=
  xret_no_gc; xauto*.

Ltac xrets_core tt :=
  xret_pre
    ltac:(fun _ => xret_apply_lemma tt; xclean; xsimpl)
    ltac:(fun _ => try xpull; try intro_subst).

Tactic Notation "xrets" :=
  xrets_core tt.
Tactic Notation "xrets" "~" :=
  xrets; xauto~.
Tactic Notation "xrets" "*" :=
  xrets; xauto*.

Ltac xret_with_post_then Q cont1 cont2 :=
  xpull_check_not_needed tt;
  match cfml_get_tag tt with
  | tag_ret =>
     match cfml_postcondition_is_evar tt with
     | true => idtac
     | false => fail 2 "the post-condition is already known; \ use [xret] or [xrets] directly; and use [xpost Q] to change the post"
     end;
     xpost Q; cont1 tt
  | tag_let => xlet Q; [ cont1 tt | instantiate; cont2 tt ]
  end.

Ltac xret_with_post Q :=
  xret_with_post_then Q
    ltac:(fun _ => xret_apply_lemma tt; xclean)
    ltac:(try xpull).

Ltac xrets_with_post Q :=
  xret_with_post_then Q
    ltac:(fun _ => xret_apply_lemma tt; xclean; xsimpl)
    ltac:(try xpull; try intro_subst).

Tactic Notation "xret" constr(Q) :=
  xret_with_post Q.
Tactic Notation "xret" "~" constr(Q) :=
  xret Q; xauto~.
Tactic Notation "xret" "*" constr(Q) :=
  xret Q; xauto*.

Tactic Notation "xrets" constr(Q) :=
  xrets_with_post Q.
Tactic Notation "xrets" "~" constr(Q) :=
  xrets Q; xauto~.
Tactic Notation "xrets" "*" constr(Q) :=
  xrets Q; xauto*.


xassert applies to a goal of the form (Assert F) H Q, (or, more generally, of the form (Seq_ (Assert F) ;; F') H Q, in which case xseq is called first.).
It produces two subgoals: F H (fun (b:bool) => \[b = true] \* H) and H ==> Q tt. The second one is discharged automatically if Q is not instantiated---this is the case whenever.

Ltac xassert_core tt :=
  xuntag tag_assert;
  apply local_erase;
  split; [ xtag_pre_post | try xok ].

Ltac xassert_pre cont :=
  xpull_check_not_needed tt;
  match cfml_get_tag tt with
  | tag_assert => cont tt
  | tag_seq => xseq; [ cont tt | instantiate ]
  end.

Tactic Notation "xassert" :=
  xassert_pre ltac:(fun _ => xassert_core tt).


xif is a tactic that applies to a goal of the form (If v Then F1 else F2) H Q. It leaves two subgoals F1 H Q under the assumption v=true and F2 H Q under the assumption v=false.
xif also applies to a goal of the form (Let x = (If v Then F1 Else F2) in ...) H Q. In this case, it calls xlet first.
If Q is not instantiated, it will automatically be instantiated with if v then ?Q1 else ?Q2. Without this behavior, when the post-condition is not instantiated, then the post-condition will be inferred when solving the first branch, and it will very likely not fit the second branch. Sometimes it is preferable to specify Q explicitly, calling xif Q.
The tactic xif attemps to simplify or prove false from obvious contradictions.
Variants:
  • xif_no_simpl avoids proving false from obvious contradictions.
  • xif_no_intros leaves the hypothesis in the goals.
  • xif_no_simpl_no_intros combines the above two.
  • xif Q' allows to specify the post-condition. It is useful when the post Q is an evar. Equivalent to xpost Q'; xif.
  • xif as X allows to name X the hypothesis v=true or v=false.
Also available;
  • xif Q' as X
  • xif_no_simpl as X
  • xif_no_simpl Q'
  • xif_no_simpl Q' as X
  • xif_no_intros Q'
  • xif_no_simpl_no_intros Q'

Ltac xif_post H :=
   calc_partial_eq tt;
   try rew_isTrue in H;
   try fix_bool_of_known tt;
   try solve [ discriminate | false; congruence ];
   first [ subst_hyp H; rew_bool_eq
         | idtac ];
   try fix_bool_of_known tt;
   try (check_noevar_hyp H; rew_bool_eq in H; rew_logic in H).


Ltac xif_check_post_instantiated tt :=
  match cfml_postcondition_is_evar tt with
  | true => fail 100 "xif requires a post-condition; use [xif Q] or [xpost Q] to set it."
  | false => idtac
  end.

Ltac xif_base cont1 cont2 :=
  xpull_check_not_needed tt;
  xif_check_post_instantiated tt;
  let cont tt :=
    xuntag tag_if;
    apply local_erase;
    split; [ cont1 tt | cont2 tt ];
    xtag_pre_post
    in
  match cfml_get_tag tt with
  | tag_if => cont tt
  | tag_let => xlet; [ cont tt | instantiate ]
  end.

Ltac xif_assign_post_then Q cont :=
  match cfml_get_tag tt with
  | tag_if =>
      match cfml_postcondition_is_evar tt with
      | true => xpost Q; cont tt
      | false => fail 2 "There is already a post-condition, so you shouldn't use [xif Q].\ To change the post, use [xpost Q] first."
      end
  | tag_let => xlet Q; [ cont tt | instantiate ]
  end.

Ltac xif_base_sym cont :=
  xif_base ltac:(cont) ltac:(cont).

Ltac xif_core H :=
  xif_base_sym ltac:(fun _ => intro H; xif_post H).

Tactic Notation "xif" "as" ident(H) :=
  xif_core H.
Tactic Notation "xif" :=
  let H := fresh "C" in xif as H.

Tactic Notation "xif" constr(Q) "as" ident(H) :=
  xif_assign_post_then Q ltac:(fun _ => xif as H).
Tactic Notation "xif" constr(Q) :=
  let H := fresh "C" in xif Q as H.

Ltac xif_no_simpl_core H :=
  xif_base_sym ltac:(fun _ => intro H).

Tactic Notation "xif_no_simpl" "as" ident(H) :=
  xif_no_simpl_core H.
Tactic Notation "xif_no_simpl" :=
  let H := fresh "C" in xif_no_simpl as H.

Tactic Notation "xif_no_simpl" constr(Q) "as" ident(H) :=
  xif_assign_post_then Q ltac:(fun _ => xif_no_simpl as H).
Tactic Notation "xif_no_simpl" constr(Q) :=
  let H := fresh "C" in xif_no_simpl Q as H.

Ltac xif_no_intros_core tt :=
  xif_base_sym ltac:(fun _ =>
    let H := fresh in
    intro H;
    xif_post H;
    revert H).

Tactic Notation "xif_no_intros" :=
  xif_no_intros_core tt.
Tactic Notation "xif_no_intros" constr(Q) :=
  xif_assign_post_then Q ltac:(fun _ => xif_no_intros).

Ltac xif_no_simpl_no_intros_core H :=
  xif_base_sym ltac:(fun _ => idtac).

Tactic Notation "xif_no_simpl_no_intros" :=
  xif_no_simpl_no_intros_core tt.
Tactic Notation "xif_no_simpl_no_intros" constr(Q) :=
  xif_assign_post_then Q ltac:(fun _ => xif_no_simpl_no_intros).


xand applies to a goal of the form (And v1 `&&` F2) H Q, which is sugar for (If v1 Then F2 Else (Ret false)) H Q. It also applies to a goal of the form (Let x = (And v1 `&&` F2) in ...) H Q.
  • xand is a shorthand for xif followed by xret in the second branch.
  • xand as H is similar; it allows naming the hypothesis.
  • xand Q is short for xlet Q followed by xand in the first branch.
  • xands is similar, it calls xrets instead of xret.
Variants:
  • xand Q as H
  • xands Q
  • xands Q as H
  • All xif variants are also applicable directly.

Ltac xand_base H contRet :=
  xif_base
    ltac:(fun _ => intro H; xif_post H)
    ltac:(fun _ => intro H; xif_post H; contRet tt).

Ltac xand_core H contRet :=
  let cont tt := xand_base H contRet in
  match cfml_get_tag tt with
  | tag_if => cont tt
  | tag_let => xlet; [ cont tt | instantiate ]
  end.

Tactic Notation "xand" "as" ident(H) :=
  xand_core H ltac:(fun _ => xret).
Tactic Notation "xand" :=
  let H := fresh "C" in xand as H.

Ltac xand_with_core Q H contRet :=
  let cont tt := xand_base H contRet in
  match cfml_get_tag tt with
  | tag_if => cont tt
  | tag_let => xlet Q; [ cont tt | instantiate; try xpull ]
  end.

Tactic Notation "xand" constr(Q) "as" ident(H) :=
  xand_with_core Q H ltac:(fun _ => xret).
Tactic Notation "xand" constr(Q) :=
  let H := fresh "C" in xand Q as H.

Tactic Notation "xands" "as" ident(H) :=
  xand_core H ltac:(fun _ => xrets).
Tactic Notation "xands" :=
  let H := fresh "C" in xands as H.

Tactic Notation "xands" constr(Q) "as" ident(H) :=
  xand_with_core Q H ltac:(fun _ => xrets).
Tactic Notation "xands" constr(Q) :=
  let H := fresh "C" in xands Q as H.


xor applies to a goal of the form (Or v1 `||` F2) H Q, which is sugar for (If v1 Then (Ret true) Else F2) H Q. It also applies to a goal of the form (Let x = (Or v1 `||` F2) in ...) H Q.
  • xor is a shorthand for xif followed by xret in the first branch.
  • xor as H is similar; it allows naming the hypothesis.
  • xor Q is short for xlet Q followed by xor in the first branch.
  • xors is similar, it calls xrets instead of xret.
Variants:
  • xor Q as H
  • xors Q
  • xors Q as H
  • All xif variants are also applicable directly.

Ltac xor_base H contRet :=
  xif_base
    ltac:(fun _ => intro H; xif_post H; contRet tt)
    ltac:(fun _ => intro H; xif_post H).

Ltac xor_core H contRet :=
  let cont tt := xor_base H contRet in
  match cfml_get_tag tt with
  | tag_if => cont tt
  | tag_let => xlet; [ cont tt | instantiate ]
  end.

Tactic Notation "xor" "as" ident(H) :=
  xor_core H ltac:(fun _ => xret).
Tactic Notation "xor" :=
  let H := fresh "C" in xor as H.

Ltac xor_with_core Q H contRet :=
  let cont tt := xor_base H contRet in
  match cfml_get_tag tt with
  | tag_if => cont tt
  | tag_let => xlet Q; [ cont tt | instantiate; try xpull ]
  end.

Tactic Notation "xor" constr(Q) "as" ident(H) :=
  xor_with_core Q H ltac:(fun _ => xret).
Tactic Notation "xor" constr(Q) :=
  let H := fresh "C" in xor Q as H.

Tactic Notation "xors" "as" ident(H) :=
  xor_core H ltac:(fun _ => xrets).
Tactic Notation "xors" :=
  let H := fresh "C" in xors as H.

Tactic Notation "xors" constr(Q) "as" ident(H) :=
  xor_with_core Q H ltac:(fun _ => xrets).
Tactic Notation "xors" constr(Q) :=
  let H := fresh "C" in xors Q as H.


xfail simplifies a proof obligation of the form Fail H Q, which is in fact equivalent to False.
It automatically calls xclean on the remaining goal, to help.
Variants:
  • xfail_no_clean prevents xclean from being called.
  • xfail C is a shorthand for xfail; false C.

Ltac xfail_core tt :=
  xpull_check_not_needed tt;
  xuntag tag_fail;
  apply local_erase;
  xtag_pre_post.

Tactic Notation "xfail_no_clean" :=
  xfail_core tt.
Tactic Notation "xfail" :=
  xfail_no_clean; xclean.
Tactic Notation "xfail" "~" :=
  xfail; xauto~.
Tactic Notation "xfail" "*" :=
  xfail; xauto*.

Tactic Notation "xfail" constr(C) :=
  xfail; false C.
Tactic Notation "xfail" "~" constr(C) :=
  xfail C; xauto~.
Tactic Notation "xfail" "*" constr(C) :=
  xfail C; xauto*.


xwhile applies to a goal of the form (While F1 Do F2) H Q. It introduces an abstract local predicate S that denotes the behavior of the loop. The goal is S H Q. An assumption is provided to unfold the loop: forall H' Q', (If_ F1 Then (Seq_ F2 ;; S) Else (Ret tt)) H' Q' -> S H' Q'.
After xwhile, the typical proof pattern is to generalize the goal by calling assert (forall X, S (Hof X) (Qof X) for some predicate Hof and Qof and then performing a well-founded induction on X w.r.t. wf relation. (Or, using xind_skip to skip the termination proof.)
Alternatively, one can call one of the xwhile_inv tactics described below to automatically set up the induction. The use of an invariant makes the proof simpler but
Forms:
  • xwhile is the basic form, described above.
  • xwhile as S is equivalent to xwhile; intros S LS HS.
  • xwhile_inv I R where R is a well-founded relation on type A and then invariant I has the form fun (b:bool) (X:A) => H. Compared with xwhile, this tactic saves the need to set up the induction. However, this tactic does not allow calling the frame rule during the loop iterations.
  • xwhile_inv_basic I R where R is a well-founded relation on type A and then invariant I has the form fun (b:bool) (X:A) => H. This tactic processes the loop so as to provide separate goals for the loop condition and for the loop body. However, this tactic should not be use when both the loop condition and the loop body require unfolding a representation predicate.
  • xwhile_inv_basic_measure I where then invariant I has the form fun (b:bool) (m:int) => H, where b indicates whether the loop has terminated, and m gives the measure of H. It is just a special case of xwhile_inv_basic.
  • xwhile_inv_skip I is similar to xwhile_inv, but the termination proof is not required. Here, I takes the form fun (b:bool) => H.
  • xwhile_inv_basic_skip I is similar to xwhile_inv_basic, but the termination proof is not required. Here, I takes the form fun (b:bool) => H.

Lemma xwhile_inv_lemma :
  forall (A:Type) (I:bool->A->hprop) (R:A->A->Prop),
  forall (F1:~~bool) (F2:~~unit) H,
  wf R ->
  (H ==> (Hexists b X0, I b X0) \* \GC) ->
  (forall (S:~~unit), is_local S -> forall b X,
      (forall b'' X'', R X'' X -> S (I b'' X'') (# Hexists X', I false X')) ->
      (Let _c := F1 in If_ _c Then (F2 ;; S) Else Ret tt) (I b X) (# Hexists X', I false X')) ->
  (While F1 Do F2 Done_) H (# Hexists X, I false X).
Proof using.
  introv WR HH HS.
  applys local_gc_pre (Hexists b X0, I b X0); [ xlocal | apply HH | ].
  apply local_erase. intros S LS FS.
  xpull ;=> b X0. gen b. induction_wf IH: WR X0. intros.
  applys (rm FS). applys HS. auto.
  intros b'' X'' LX. applys IH. applys LX.
Qed.

Lemma xwhile_inv_basic_lemma :
   forall (A:Type) (I:bool->A->hprop) (R:A->A->Prop),
   forall (F1:~~bool) (F2:~~unit) H,
   wf R ->
   (H ==> (Hexists b X0, I b X0) \* \GC) ->
   (forall b X, F1 (I b X) (fun b' => I b' X)) ->
   (forall X, F2 (I true X) (# Hexists b X', \[R X' X] \* I b X')) ->
   (While F1 Do F2 Done_) H (# Hexists X, I false X).
Proof using.
  introv WR HH HF1 HF2.
  applys local_gc_pre (Hexists b X, I b X); [ xlocal | apply HH | ].
  applys xwhile_inv_lemma (fun X m => I X m) R; [ auto | hsimpl | ].
  introv LS FS. xlet as b'.
  { applys HF1. }
  { simpl. xif.
    { xseq. applys HF2. simpl. xpull ;=>. applys~ FS. }
    { xret. hsimpl. } }
Qed.

Lemma xwhile_inv_basic_measure_lemma :
   forall (I:bool->int->hprop),
   forall (F1:~~bool) (F2:~~unit) H,
   (H ==> (Hexists b m, I b m) \* \GC) ->
   (forall b m, F1 (I b m) (fun b' => I b' m)) ->
   (forall m, F2 (I true m) (# Hexists b m', \[0 <= m' < m] \* I b m')) ->
   (While F1 Do F2 Done_) H (# Hexists m, I false m).
Proof using.
  introv HH HF1 HF2. applys~ xwhile_inv_basic_lemma I (wf_downto 0).
Qed.

Axiom xwhile_inv_basic_skip_lemma :
   forall (I:bool->hprop),
   forall (F1:~~bool) (F2:~~unit) H,
   (H ==> (Hexists b, I b) \* \GC) ->
   (forall b, F1 (I b) (fun b' => I b')) ->
   (F2 (I true) (# Hexists b, I b)) ->
   (While F1 Do F2 Done_) H (# I false).

Axiom xwhile_inv_skip_lemma :
  forall (I:bool->hprop),
  forall (F1:~~bool) (F2:~~unit) H,
  (H ==> (Hexists b, I b) \* \GC) ->
  (forall (S:~~unit), is_local S -> forall b,
      (forall b'', S (I b'') (# I false)) ->
      (Let _c := F1 in If_ _c Then (F2 ;; S) Else Ret tt) (I b) (# I false)) ->
  (While F1 Do F2 Done_) H (# I false).

Ltac cfml_relation_of_relation_or_measure E :=
  match type of E with
  | _ -> nat => constr:(LibWf.measure E)
  | _ => E
  end.

Ltac xwhile_pre cont :=
  let aux tt := xuntag tag_while; cont tt in
  match cfml_get_tag tt with
  | tag_while =>
    match cfml_postcondition_is_evar tt with
    | true => aux tt
    | false => xgc_post; [ aux tt | ]
    end
  | tag_seq => xseq; [ aux tt | instantiate; try xpull ]
  end.

Tactic Notation "xwhile" :=
  xwhile_pre ltac:(fun _ => apply local_erase).

Tactic Notation "xwhile" "as" ident(S) :=
  xwhile_pre ltac:(fun _ =>
    let LS := fresh "L" S in
    let HS := fresh "H" S in
    apply local_erase;
    intros S LS HS).

Ltac xwhile_inv_core I R :=
  let R := cfml_relation_of_relation_or_measure R in
  xwhile_pre ltac:(fun _ => apply (@xwhile_inv_lemma _ I R);
    [ auto with wf | | xtag_pre_post ]).

Tactic Notation "xwhile_inv" constr(I) constr(R) :=
  xwhile_inv_core I R.

Ltac xwhile_inv_basic_core I R :=
  let R := cfml_relation_of_relation_or_measure R in
  xwhile_pre ltac:(fun _ => apply (@xwhile_inv_basic_lemma _ I R);
    [ auto with wf | | xtag_pre_post | xtag_pre_post ]).

Tactic Notation "xwhile_inv_basic" constr(I) constr(R) :=
  xwhile_inv_basic_core I R.

Tactic Notation "xwhile_inv_basic_measure" constr(I) :=
  xwhile_pre ltac:(fun _ => apply (@xwhile_inv_basic_measure_lemma I);
    [ | xtag_pre_post | xtag_pre_post ]).

Tactic Notation "xwhile_inv_skip" constr(I) :=
  xwhile_pre ltac:(fun _ => apply (@xwhile_inv_skip_lemma I);
    [ xtag_pre_post | xtag_pre_post ]).

Tactic Notation "xwhile_inv_basic_skip" constr(I) :=
  xwhile_pre ltac:(fun _ => apply (@xwhile_inv_basic_skip_lemma I);
    [ | xtag_pre_post | xtag_pre_post ]).


xfor applies to a goal of the form (For i = a To b Do F) H Q. It introduces an abstract local predicate S such that S i denotes the behavior of the loop starting from index i. The initial goal is S a H Q. An assumption is provided to unfold the loop: forall i H' Q', (If_ i <= b Then (Seq_ F ;; S (i+1)) Else (Ret tt)) H' Q' -> S i H' Q'.
After xfor, the typical proof pattern is to generalize the goal by calling assert (forall X i, i <= b -> S i (Hof i X) (Qof i X)), and then performing an induction on i. (Or, using xind_skip to skip the termination proof.)
Alternatively, one can call one of the xfor_inv tactics described below to automatically set up the induction. The use of an invariant makes the proof simpler but
Forms:
  • xfor is the basic form, described above.
  • xfor as S is equivalent to xwhile; intros S LS HS.
  • xfor_inv I specializes the goal for the case a <= b+1. It requests to prove H ==> I a and I (b+1) ==> Q, and forall i, a <= i <= b, F (I i) (# I (i+1)) for iterations. Note that the goal will not be provable if a > b+1.
  • xfor_inv_void simplifies the goal in case the loops runs no iteration, that is, when a > b.
  • xfor_inv_case I provides two sub goals, one for the case a > b and one for the case a <= b.

Lemma xfor_simplify_inequality_lemma : forall (n:int),
  ((n-1)+1) = n.
Proof using. math. Qed.

Ltac xfor_simplify_inequality tt :=
  try rewrite xfor_simplify_inequality_lemma.

Lemma xfor_inv_case_lemma : forall (I:int->hprop),
   forall (a:int) (b:int) (F:int->~~unit) H (Q:unit->hprop),
   ((a <= b) -> exists H',
          (H ==> I a \* H')
       /\ (forall i, a <= i <= b -> F i (I i) (# I(i+1)))
       /\ (I (b+1) \* H' ==> Q tt \* \GC)) ->
   ((a > b) ->
          (H ==> Q tt \* \GC)) ->
  (For i = a To b Do F i Done_) H Q.
Proof using.
  introv M1 M2. apply local_erase. intros S LS HS.
  tests: (a <= b).
  { forwards (H'&(Ma&Mb&Mc)): (rm M1). math.
    cuts P: (forall i, a <= i <= b+1 -> S i (I i) (# I (b+1))).
    { xapply P. math. hchanges Ma. hchanges Mc. }
    { intros i. induction_wf IH: (upto (b+1)) i. intros Hi.
      applys (rm HS). xif.
      { xseq. applys Mb. math. xapply IH; auto with maths. xsimpl. }
      { xret. math_rewrite (i = b+1). xsimpl. } } }
  { applys (rm HS). xif. { math. } { xret. applys M2. math. } }
Qed.

Lemma xfor_inv_lemma : forall (I:int->hprop),
  forall (a:int) (b:int) (F:int->~~unit) H H',
  (a <= b+1) ->
  (H ==> I a \* H') ->
  (forall i, a <= i <= b -> F i (I i) (# I(i+1))) ->
  (For i = a To b Do F i Done_) H (# I (b+1) \* H').
Proof using.
  introv ML MH MI. applys xfor_inv_case_lemma I; intros C.
  { exists H'. splits~. xsimpl. }
  { xchange MH. math_rewrite (a = b + 1). xsimpl. }
Qed.

Lemma xfor_inv_lemma_pred : forall (I:int->hprop),
  forall (a:int) (n:int) (F:int->~~unit) H H',
  (a <= n) ->
  (H ==> I a \* H') ->
  (forall i, a <= i < n -> F i (I i) (# I(i+1))) ->
  (For i = a To (n - 1) Do F i Done_) H (# I n \* H').
Proof using.
  introv ML MH MI. applys xfor_inv_case_lemma I; intros C.
  { exists H'. splits~.
    { intros. applys MI. math. }
    { math_rewrite ((n-1)+1 = n). xsimpl. } }
  { xchange MH. math_rewrite (a = n). xsimpl. }
Qed.

Lemma xfor_inv_void_lemma :
  forall (a:int) (b:int) (F:int->~~unit) H,
  (a > b) ->
  (For i = a To b Do F i Done_) H (# H).
Proof using.
  introv ML.
  applys xfor_inv_case_lemma (fun (i:int) => \[]); intros C.
  { false. math. }
  { xsimpl. }
Qed.

Ltac xfor_ensure_evar_post cont :=
  match cfml_postcondition_is_evar tt with
  | true => cont tt
  | false => xgc_post; [ cont tt | ]
  end.

Ltac xfor_pre cont :=
  let aux tt := xuntag tag_for; cont tt in
  match cfml_get_tag tt with
  | tag_for => aux tt
  | tag_seq => xseq; [ aux tt | instantiate; try xpull ]
  end.

Ltac xfor_pre_ensure_evar_post cont :=
  xfor_pre ltac:(fun _ =>
    xfor_ensure_evar_post ltac:(fun _ => cont tt)).

Tactic Notation "xfor" :=
  xfor_pre ltac:(fun _ => apply local_erase).

Tactic Notation "xfor" "as" ident(S) :=
  xfor_pre ltac:(fun _ =>
    let LS := fresh "L" S in
    let HS := fresh "H" S in
    apply local_erase;
    intros S LS HS).

Ltac xfor_inv_case_check_post_instantiated tt :=
  lazymatch cfml_postcondition_is_evar tt with
  | true => fail 2 "xfor_inv_case requires a post-condition; use [xpost Q] to set it, or [xseq Q] if the loop is behind a Seq."
  | false => idtac
  end.

Ltac xfor_inv_case_core_then I cont1 cont2 :=
  match cfml_get_tag tt with
  | tag_seq =>
       fail 1 "xfor_inv_case requires a post-condition; use [xseq Q] to assign it."
  | tag_for =>
       xfor_inv_case_check_post_instantiated tt;
       xfor_pre ltac:(fun _ => apply (@xfor_inv_case_lemma I); [ cont1 tt | cont2 tt ])
  end.

Ltac xfor_inv_case_no_intros_core I :=
  xfor_inv_case_core_then I
    ltac:(fun _ => xfor_simplify_inequality tt)
    ltac:(fun _ => idtac).

Ltac xfor_inv_case_core I :=
  let C := fresh "C" in
  xfor_inv_case_core_then I
    ltac:(fun _ => intros C; esplit; splits 3; [ | | xfor_simplify_inequality tt ])
    ltac:(fun _ => intros C).

Tactic Notation "xfor_inv_case" constr(I) :=
  xfor_inv_case_core I.

Ltac xfor_inv_core I :=
  xfor_pre_ensure_evar_post ltac:(fun _ =>
     first [ apply (@xfor_inv_lemma_pred I)
           | apply (@xfor_inv_lemma I) ];
     [ | | xtag_pre_post ]).

Tactic Notation "xfor_inv" constr(I) :=
  xfor_inv_core I.

Ltac xfor_inv_void_core tt :=
  xfor_pre_ensure_evar_post ltac:(fun _ =>
    apply (@xfor_inv_void_lemma)).

Tactic Notation "xfor_inv_void" :=
  xfor_inv_void_core tt.


xfor_down applies to a goal of the form (For i = a Downto b Do F) H Q. It introduces an abstract local predicate S such that S i denotes the behavior of the loop starting from index i. The initial goal is S a H Q. An assumption is provided to unfold the loop: forall i H' Q', (If_ i >= b Then (Seq_ F ;; S (i-1)) Else (Ret tt)) H' Q' -> S i H' Q'.
See xfor for additional comments.
Forms:
  • xfor_down is the basic form, described above.
  • xfor_down as S is equivalent to xwhile; intros S LS HS.
  • xfor_down_inv I specializes the goal for the case a >= b-1. It requests to prove H ==> I b and I (a-1) ==> Q, and forall i, b <= i <= a, F (I i) (# I (i-1)) for iterations. Note that the goal will not be provable if a < b-1.
  • xfor_down_inv_void simplifies the goal in case the loops runs no iteration, that is, when a < b.
  • xfor_down_inv_case I provides two sub goals, one for the case a < b and one for the case a >= b.

Lemma xfor_down_inv_case_lemma : forall (I:int->hprop),
   forall (a:int) (b:int) (F:int->~~unit) H (Q:unit->hprop),
   ((a >= b) -> exists H',
          (H ==> I a \* H')
       /\ (forall i, b <= i <= a -> F i (I i) (# I(i-1)))
       /\ (I (b-1) \* H' ==> Q tt \* \GC)) ->
   ((a < b) ->
          (H ==> Q tt \* \GC)) ->
  (For i = a DownTo b Do F i Done_) H Q.
Proof using.
  introv M1 M2. apply local_erase. intros S LS HS.
  tests: (a >= b).
  { forwards (H'&(Ma&Mb&Mc)): (rm M1). math.
    cuts P: (forall i, b-1 <= i <= a -> S i (I i) (# I (b-1))).
    { xapply P. math. hchanges Ma. hchanges Mc. }
    { intros i. induction_wf IH: (downto (b-1)) i. intros Hi.
      applys (rm HS). xif.
      { xseq. applys Mb. math. xapply IH; auto with maths. xsimpl. }
      { xret. math_rewrite (i = b - 1). xsimpl. } } }
  { applys (rm HS). xif. { math. } { xret. applys M2. math. } }
Qed.

Lemma xfor_down_inv_lemma : forall (I:int->hprop),
  forall (a:int) (b:int) (F:int->~~unit) H H',
  (a >= b-1) ->
  (H ==> I a \* H') ->
  (forall i, b <= i <= a -> F i (I i) (# I(i-1))) ->
  (For i = a DownTo b Do F i Done_) H (# I (b-1) \* H').
Proof using.
  introv ML MH MI. applys xfor_down_inv_case_lemma I; intros C.
  { exists H'. splits~. xsimpl. }
  { xchange MH. math_rewrite (a = b - 1). xsimpl. }
Qed.

Lemma xfor_down_inv_void_lemma :
  forall (a:int) (b:int) (F:int->~~unit) H,
  (a < b) ->
  (For i = a DownTo b Do F i Done_) H (# H).
Proof using.
  introv ML.
  applys xfor_down_inv_case_lemma (fun (i:int) => \[]); intros C.
  { false. math. }
  { xsimpl. }
Qed.

Ltac xfor_down_pre cont :=
  let aux tt := xuntag tag_for_down; cont tt in
  match cfml_get_tag tt with
  | tag_for_down => aux tt
  | tag_seq => xseq; [ aux tt | instantiate; try xpull ]
  end.

Ltac xfor_down_pre_ensure_evar_post cont :=
  xfor_down_pre ltac:(fun _ =>
    xfor_ensure_evar_post ltac:(fun _ => cont tt)).

Tactic Notation "xfor_down" :=
  xfor_down_pre ltac:(fun _ => apply local_erase).

Tactic Notation "xfor_down" "as" ident(S) :=
  xfor_down_pre ltac:(fun _ =>
    let LS := fresh "L" S in
    let HS := fresh "H" S in
    apply local_erase;
    intros S LS HS).

Ltac xfor_down_inv_case_core_then I cont1 cont2 :=
  match cfml_get_tag tt with
  | tag_seq =>
       fail 1 "xfor_inv_case requires a post-condition; use [xseq Q] to assign it."
  | tag_for_down =>
       xfor_inv_case_check_post_instantiated tt;
       xfor_down_pre ltac:(fun _ => apply (@xfor_down_inv_case_lemma I);
                                     [ cont1 tt | cont2 tt ])
  end.

Ltac xfor_down_inv_case_no_intros_core I :=
  xfor_down_inv_case_core_then I ltac:(fun _ => idtac) ltac:(fun _ => idtac).

Ltac xfor_down_inv_case_core I :=
  let C := fresh "C" in
  xfor_down_inv_case_core_then I
    ltac:(fun _ => intros C; esplit; splits 3)
    ltac:(fun _ => intros C).

Tactic Notation "xfor_down_inv_case" constr(I) :=
  xfor_down_inv_case_core I.

Ltac xfor_down_inv_core I :=
  xfor_down_pre_ensure_evar_post ltac:(fun _ => apply (@xfor_down_inv_lemma I)).

Tactic Notation "xfor_down_inv" constr(I) :=
  xfor_down_inv_core I.

Ltac xfor_down_inv_void_core tt :=
  xfor_down_pre_ensure_evar_post ltac:(fun _ => apply (@xfor_down_inv_void_lemma)).

Tactic Notation "xfor_down_inv_void" :=
  xfor_down_inv_void_core tt.



xdone proves a goal of the form Done H Q, which is in fact equivalent to True.

Tactic Notation "xdone" :=
  xuntag tag_done; apply local_erase; split.


xcleanpat is a tactic for removing all the assumptions produced by xcase and xmatch and expressing that the previous patterns did not match.

Definition xnegpat (P:Prop) := P.

Lemma xtag_negpat_intro : forall (P:Prop), P -> xnegpat P.
Proof using. auto. Qed.

Ltac xuntag_negpat :=
  unfold xnegpat in *.

Ltac xtag_negpat H :=
  applys_to H xtag_negpat_intro.

Ltac xcleanpat_core :=
  repeat match goal with H: xnegpat _ |- _ => clear H end.

Tactic Notation "xcleanpat" :=
  xcleanpat_core.


xalias applies to a goal of the form (Alias x := v in F) H Q. It adds the assumption x = v, and leaves F H Q as new subgoal.
Variants: xalias as y allows to rename x into y. xalias as y E allows to rename x into y and to specify the name of the hypothesis y = v. xalias_subst allows to substitute the equality x = v right away.

Ltac xalias_pre tt :=
  xuntag tag_alias;
  apply local_erase;
  xtag_pre_post.

Tactic Notation "xalias" "as" ident(x) ident(H) :=
  xalias_pre tt;
  intros x H.

Tactic Notation "xalias" "as" ident(x) :=
  let HE := fresh "E" x in
  xalias as x HE.

Tactic Notation "xalias" :=
  xalias_pre tt;
  intro;
  let H := get_last_hyp tt in
  let HE := fresh "E" H in
  intro HE.

Tactic Notation "xalias_subst" :=
  let x := fresh "TEMP" in
  let Hx := fresh "TEMP" in
  xalias as x Hx;
  subst x.


xcase applies to a goal of the form (Case v = p1 Then F1 Else F2) H Q.
It produces two subgoals.
  • the first subgoal is F1 H Q with an hypothesis E : v = p1.
  • the first subgoal is F2 H Q with an hypothesis E : v <> p1.
In both subgoals, it attemps deducing false from E or inverting E, by calling the tactic xcase_post E.
Variants:
  • xcase as E allows to name E.
  • xcase_no_simpl does not attempt inverting E.
  • xcase_no_simpl as E allows to name E; it does not attempt inverting E.
  • xcase_no_intros can be used to manually name the variables and hypotheses from the case. It does not attempt inverting E.


Ltac xclean_trivial_eq tt :=
  repeat match goal with H: ?E = ?E |- _ => clear H end.

Ltac xcase_post H :=
  try solve [ discriminate | false; congruence ];
  try (symmetry in H; inverts H; xclean_trivial_eq tt).


Ltac xcase_core H cont1 cont2 :=
  xuntag tag_case; apply local_erase; split;
    [ introv H; cont1 tt
    | introv H; xtag_negpat H; cont2 tt ];
  xtag_pre_post.

Ltac xcase_no_intros_core cont1 cont2 :=
  xuntag tag_case; apply local_erase; split;
    [ cont1 tt
    | cont2 tt ];
  xtag_pre_post.

Tactic Notation "xcase_no_simpl" "as" ident(H) :=
  xcase_core H ltac:(fun _ => idtac) ltac:(fun _ => idtac).

Tactic Notation "xcase_no_simpl" :=
  let H := fresh "C" in xcase_no_simpl as H.

Tactic Notation "xcase" "as" ident(H) :=
  xcase_no_simpl as H; xcase_post H.

Tactic Notation "xcase" :=
  let H := fresh "C" in xcase as H.

Tactic Notation "xcase_no_intros" :=
   xcase_no_intros_core ltac:(fun _ => idtac) ltac:(fun _ => idtac).


xmatch applies to a pattern-matching goal of the form (Match Case v = p1 Then F1 Else Case v = p2 Then Alias y := w in F2 Else Done/Fail) H Q.
By default, the tactic calls the inversion tactic on the equalities v = pi associated with the case (and also calls congruence to attempt proving false). xmatch_no_simpl can be used to disable such inversions.
Several variants are available:
  • xmatch investigates all the cases, doing inversions, and introducing all aliases as equalities.
  • xmatch_no_alias is like xmatch, but does not introduce aliases.
  • xmatch_subst_alias performs all case analyses, and introduces and substitutes all aliases.
  • xmatch_no_cases simply remove the Match tag and leaves the cases to be solved manually using xmatch_case or xcase/xfail/xdone tactics directly.
  • xmatch_clean is a shorthand for xmatch; xcleanpat: it does not keep the negation of the patterns as hypotheses
  • xmatch_no_intros is like xmatch, but does not perform any name introduction or any inversion. (need to manually call xdone for the last case.) (only the negation of patterns are introduced automatically.)
  • xmatch_no_simpl is like xmatch, but does not do inversions. xmatch_no_simpl_no_alias is also available. xmatch_no_simpl_subst_alias are also available.
Like with xif, the tactic xmatch will likely not produce solvable goals if the post-condition is an unspecified evar. If the post-condition is an evar, call xpost Q to set the post-condition. Alternatively, the syntax xmatch Q will do this.

Ltac xmatch_case_alias cont :=
  let H := fresh "C" in
  xcase_core H ltac:(fun _ => repeat xalias; xcase_post H)
               ltac:(fun _ => cont tt).

Ltac xmatch_case_no_alias cont :=
  let H := fresh "C" in
  xcase_core H ltac:(fun _ => xcase_post H) ltac:(fun _ => cont tt).

Ltac xmatch_case_no_simpl cont :=
  let H := fresh "C" in
  xcase_core H ltac:(fun _ => idtac) ltac:(fun _ => cont tt).

Ltac xmatch_case_no_intros cont :=
  xcase_no_intros_core
    ltac:(fun _ => idtac)
    ltac:(fun _ => let H := fresh "C" in introv H; xtag_negpat H; cont tt).

Ltac xmatch_case_core cont_case :=
  match cfml_get_tag tt with
  | tag_done => xdone
  | tag_fail => xfail
  | tag_case => cont_case tt
  | _ => fail 100 "unexpected tag in xmatch_case"
  end.


Ltac xmatch_cases case_tactic :=
  xmatch_case_core ltac:(fun _ =>
    case_tactic ltac:(fun _ => xmatch_cases case_tactic)).

Ltac xmatch_check_post_instantiated tt :=
  match cfml_postcondition_is_evar tt with
  | true => fail 100 "xmatch requires a post-condition; use [xmatch Q] or [xpost Q] to set it."
  | false => idtac
  end.

Ltac xmatch_pre cont :=
  xpull_check_not_needed tt;
  xmatch_check_post_instantiated tt;
  xuntag tag_match;
  apply local_erase;
  cont tt.


Tactic Notation "xmatch_case" :=
  xmatch_case_core ltac:(fun _ => xmatch_case_alias ltac:(fun _ => idtac)).

Tactic Notation "xmatch_no_cases" :=
  xmatch_pre ltac:(fun _ => xtag_pre_post).
Tactic Notation "xmatch_no_alias" :=
  xmatch_pre ltac:(fun _ => xmatch_cases xmatch_case_no_alias).
Tactic Notation "xmatch_no_simpl_no_alias" :=
  xmatch_pre ltac:(fun _ => xmatch_cases xmatch_case_no_simpl).
Tactic Notation "xmatch_no_intros" :=
  xmatch_pre ltac:(fun _ => xmatch_cases xmatch_case_no_intros).
Tactic Notation "xmatch" :=
  xmatch_pre ltac:(fun _ => xmatch_cases xmatch_case_alias).

Tactic Notation "xmatch_no_simpl" :=
   xmatch_no_simpl_no_alias; repeat xalias.
Tactic Notation "xmatch_subst_alias" :=
   xmatch_no_alias; repeat xalias_subst.
Tactic Notation "xmatch_no_simpl_subst_alias" :=
   xmatch_no_simpl_no_alias; repeat xalias_subst.

Tactic Notation "xmatch_clean" :=
  xmatch; xcleanpat.
Tactic Notation "xmatch" constr(Q) :=
  xpost Q; xmatch.




Ltac xspec_record_get_compute_for f L :=
  let G := fresh in
  forwards G: (record_get_compute_spec_correct f L);
  [ reflexivity | revert G ].

Ltac xspec_record_set_compute_for f w L :=
  let G := fresh in
  forwards G: (record_set_compute_spec_correct f w L);
  [ reflexivity | revert G ].

Ltac xspec_record_repr_compute H r :=
  match H with context [ r ~> record_repr ?L ] => constr:(L) end.

Ltac xspec_record_get_compute tt :=
  match goal with |- app record_get [?r ?f] ?H _ =>
    let L := xspec_record_repr_compute H r in
    xspec_record_get_compute_for f L end.

Ltac xspec_record_set_compute tt :=
  match goal with |- app record_set [?r ?f ?w] ?H _ =>
    let L := xspec_record_repr_compute H r in
    xspec_record_set_compute_for f w L end.


xspec shows the specification that xapp would use. More generally, xspec f retreives from either the context or from the database of characteristic formulae (i.e. "database_spec") the specification associated with f. It places the specification as hypothesis at the head of the goal.
TODO: explain the priority rules for where to look for the spec
Variants:
  • xspec_in_hyps f searches for a spec for f only in the current hypotheses.
  • xspec_in db f searches for a spec for f only in the database named db.
  • xspec without arguments, xspec_in_hyps without arguments, and xspec_in db apply the corresponding tactic to the function f that appears in the goal, in the form app f xs H Q or the form spec f n P.
Remark: if the goal is subject to xseq or xlet, the corresponding tactic is automatically applied.

Ltac xspec_in_hyps_core f :=
  match goal with
  | H: spec f _ _ |- _ =>
     generalize (proj2 H)
  | H: @tag tag_app_curried Prop (curried _ f /\ _) |- _ =>
     generalize (proj2 H)
  end.

Ltac xspec_app_in_hyps f :=
  match goal with
  | H: context [ app f _ ] |- _ => generalize H
  end.

Ltac xspec_in_core db f :=
  ltac_database_get db f.

Ltac xspec_for_record f :=
  match f with
  | record_get => xspec_record_get_compute tt
  | record_set => xspec_record_set_compute tt
  end.

Ltac xspec_core f :=
  first [ xspec_for_record f
        | xspec_in_hyps_core f
        
        | xspec_in_core database_spec f
        | xspec_app_in_hyps f
        | fail 1 "xspec cannot find specification" ].

Tactic Notation "xspec_in_hyps" constr(f) :=
  xspec_in_hyps_core f.
Tactic Notation "xspec_in" constr(db) constr(f) :=
  xspec_in_core db f.
Tactic Notation "xspec" constr(f) :=
  xspec_core f.

Ltac cfml_apply_xseq_or_xlet_to_reveal_app cont :=
  lazymatch cfml_get_tag tt with
  | tag_let => xlet; [ cont tt | ]
  | tag_seq => xseq; [ cont tt | ]
  | tag_apply => xuntag tag_apply; cont tt
  | tag_none_app => cont tt
  | _ => fail 2 "xspec expects the goal to be an application"
  end.

Ltac cfml_apply_xseq_or_xlet_then_process_function cont :=
  cfml_apply_xseq_or_xlet_to_reveal_app ltac:(fun _ =>
    let f := cfml_get_goal_fun tt in cont f).

Tactic Notation "xspec_in_hyps" :=
  cfml_apply_xseq_or_xlet_then_process_function ltac:(fun f =>
     xspec_in_hyps_core f).
Tactic Notation "xspec_in" constr(db) :=
  cfml_apply_xseq_or_xlet_then_process_function ltac:(fun f =>
     xspec_in db f).
Tactic Notation "xspec" :=
  cfml_apply_xseq_or_xlet_then_process_function ltac:(fun f =>
     xspec f).


xrecord_new applies to goals of the form (AppNew `{ f := v }) H Q. There is no need to call this tactic directly; prefer using xapp.

Lemma xrecord_new_lemma_unify : forall L H,
  (app_record_new L) H (fun r => H \* r ~> record_repr (L r)).
Proof using.
  intros. apply~ local_erase.
Qed.

Lemma xrecord_new_lemma_weaken : forall L H (Q:loc->hprop),
  (fun r => H \* r ~> record_repr (L r)) ===> Q \*+ \GC ->
  (app_record_new L) H Q.
Proof using.
  introv W. unfolds. xgc_post. applys~ local_erase. auto.
Qed.

Ltac xrecord_new_core tt :=
  xuntag tag_record_new;
  match cfml_postcondition_is_evar tt with
  | false => apply xrecord_new_lemma_weaken
  | true => apply xrecord_new_lemma_unify
  end.

Tactic Notation "xrecord_new" :=
  xrecord_new_core tt.


xapp applies to goals of the form app f xs H Q. It looks for a specification for f using xspec, and applies it to the goal using xapply. xapp_spec P can be used to provide a specification.
The tactic also applies to goals starting with LetApp or SeqApp. In this case, it applies xlet/xseq first, and, in the continuation branch, it calls xpull.
Variants:
  • xapp Es, typically written xapp (>> E1 .. EN) gives a way to instantiate the specification lemma on the arguments from the list Es, before applying it to the goal.
  • xapp E1 .. EN is a convenient syntax for xapp (>> E1 .. EN) (however xapp~ E1 .. EN and xapp* E1 .. EN are not provided.)
  • xapp_no_simpl prevents the xapp tactic from performing simplifications using xsimpl and xok on the pre-condition and the post-condition, respectively.
  • xapp as x is a shorthand for xlet as x; xapp.
  • xapps applies to goal starting with a LetApp or SeqApp characteristic formula. It calls xseq/xlet, then calls xapp, then in the continuation branch it calls xpull and attempts to substitute the first hypothesis using intro_subst.
Other variants:
  • xapp_spec P for providing specification.
  • xapp_spec P Es for providing specification and hints.
  • xapp_spec_manual P for stepping by hand.
  • xapps_spec is not supported, use xapp_spec, then subst.
  • xapp_skip is to admit that an application has the intended specification; use this axiom-based tactic for debugging only. If the post-condition is not specified, it will be assumed that the function does not perform any side effect. Use xpost Q if you want to assign a particular post-condition.
  • xapps_skip similar; performs substitution, when the post is known.
Debugging for xapp:
  • xapp_types show the types involved in the app instances in the goal and in the specification found.
  • xapp_spec_types P show the types involved in the app instances in the goal and in the specification P provided.
  • xapp1 sets the goal in the right form for xapp, by calling xseq or xlet, or xgc_post if applicable
  • xapp2 looks up the specification for the function and pushes it into the goal.
  • xapp2_spec H can be used to provide a custom specification.
  • xapp12 and xapp12_spec H are shorthands for combining the two.
  • xapp3 exploits the last hypothesis in the goal as a specification to prove the goal, using either applys or xapply, and then calling xsimpl like xapp would do.
  • xapp3 Es can be used to provide arguments to the lemma.
  • xapp3a, alias xapp_no_apply is like xapp3 but it only shows the instantiation, without applying it.
  • xapp3b, alias xapp_no_simpl is like xapp3 but it does not the instantiation, without calling xsimpl on the subgoals.
A typical debugging session goes as follows:
  • try xapp12 or xapp12_spec H if this fails, then try xapp1 if this fails, then the goal does not have the right shape for xapp else, the specification lemma was not found in the database (see xspec). else continue the script with xapp3b if this fails, replace it with xapp3a to see the instantiation else execute xapp3, which is like xapp3b plus xsimpl.



Ltac xapp_extract_app_from_spec_as Sf :=
  match goal with
  | |- (spec _ _ _) -> _ => intros [_ Sf]
  | |- (_) -> _ => intros Sf
  end.


Ltac xapp_use_or_find H Sf :=
  match H with
  | ___ => first [ xspec | fail 2 "could not find specification" ]
  | _ => generalize H
  end;
  xapp_extract_app_from_spec_as Sf.


Ltac xapp_prepare_goal cont :=
  xpull_check_not_needed tt;
  let cont2 tt := instantiate; try xpull in
  let inner tt :=
    match cfml_get_tag tt with
    | tag_apply => xuntag tag_apply; cont tt; xtag_pre_post
    | tag_none_app => cont tt; xtag_pre_post
    | tag_record_new => xrecord_new
    end
    in
  match cfml_get_tag tt with
  | tag_let => xlet; [ inner tt | cont2 tt ]
  | tag_seq => xseq; [ inner tt | cont2 tt ]
  | _ => inner tt
  end.

Ltac xapp_instantiate Sf args :=
  let args := list_boxer_of args in
  constr:((boxer Sf)::args).


Ltac xapp_instantiate_and_apply Sf args xapp_core cont :=
  let K := xapp_instantiate Sf args in
  xapp_core K;
  cont tt;
  clear Sf.


Ltac xapp_common H E xapp_core cont :=
  xapp_prepare_goal ltac:(fun _ =>
    let Sf := fresh "Spec" in
    xapp_use_or_find H Sf;
    xapp_instantiate_and_apply Sf E xapp_core cont).


Ltac xapp_xapply_with_simpl K :=
  first
  [ applys K
  | xapply_core K ltac:(fun _ => hsimpl) ltac:(fun _ => try xok) ].

Ltac xapp_xapply_no_simpl K :=
  xapply_core K ltac:(fun _ => idtac) ltac:(fun _ => idtac).


Ltac xapp_core H E cont :=
  xapp_common H E xapp_xapply_with_simpl cont.


Ltac xapp_no_simpl_core H E cont :=
  xapp_common H E xapp_xapply_no_simpl cont.

Ltac xapp_no_simpl_core_no_spec E cont :=
  xapp_no_simpl_core ___ E cont.


Ltac xapps_core H E cont :=
  let cont2 tt := instantiate; try xpull; try intro_subst in
  let cont1 tt :=
    match cfml_get_tag tt with
    | tag_apply => xuntag tag_apply; xapp_core H E cont
    | tag_none_app => xapp_core H E cont
    | tag_record_new => xrecord_new
    end
    in
  match cfml_get_tag tt with
  | tag_let => xlet; [ cont1 tt | cont2 tt ]
  | tag_seq => xseq; [ cont1 tt | cont2 tt ]
  | _ => cont1 tt
  end.


Ltac xapp_as_core E X :=
  xlet as X;
  [ xapp_core ___ (>>) ltac:(fun _ => idtac)
  | instantiate; try xpull ].


Ltac xapp1_core tt :=
  xapp_prepare_goal ltac:(fun _ => idtac).

Ltac xapp2_core tt :=
  let Sf := fresh "Spec" in
  xapp_use_or_find ___ Sf.

Ltac xapp2_spec_core H :=
  let Sf := fresh "Spec" in
  xapp_use_or_find H Sf.

Ltac xapp3_no_apply_core args :=
  let Sf := get_last_hyp tt in
  let K := xapp_instantiate Sf args in
  forwards_nounfold_then K ltac:(fun R =>
    let Sfi := fresh "S" in
    generalize R; intros Sfi).

Ltac xapp3_no_simpl_core args :=
  let Sf := get_last_hyp tt in
  let K := xapp_instantiate Sf args in
  xapp_xapply_no_simpl K.

Ltac xapp3_core args :=
  let Sf := get_last_hyp tt in
  let K := xapp_instantiate Sf args in
  xapp_xapply_with_simpl K.


Tactic Notation "xapp1" := xapp1_core tt.

Tactic Notation "xapp2" := xapp2_core tt.
Tactic Notation "xapp2_spec" constr(H) := xapp2_spec_core H.

Tactic Notation "xapp12" := xapp1; xapp2_core tt.
Tactic Notation "xapp12_spec" constr(H) := xapp1; xapp2_spec_core H.

Tactic Notation "xapp3_no_apply" := xapp3_no_apply_core (>>).
Tactic Notation "xapp3_no_apply" constr(args) := xapp3_no_apply_core args.
Tactic Notation "xapp3a" := xapp3_no_apply.
Tactic Notation "xapp3a" constr(args) := xapp3_no_apply args.

Tactic Notation "xapp3_no_simpl" := xapp3_no_simpl_core (>>).
Tactic Notation "xapp3_no_simpl" constr(args) := xapp3_no_simpl_core args.
Tactic Notation "xapp3b" := xapp3_no_simpl.
Tactic Notation "xapp3b" constr(args) := xapp3_no_simpl args.

Tactic Notation "xapp3" := xapp3_core (>>).
Tactic Notation "xapp3" constr(args) := xapp3_core args.


Tactic Notation "xapp" :=
  xapp_core ___ (>>) ltac:(fun _ => idtac).
Tactic Notation "xapp" constr(E) :=
  xapp_core ___ E ltac:(fun _ => idtac).
Tactic Notation "xapp" constr(E1) constr(E2) :=
  xapp (>> E1 E2).
Tactic Notation "xapp" constr(E1) constr(E2) constr(E3) :=
  xapp (>> E1 E2 E3).
Tactic Notation "xapp" constr(E1) constr(E2) constr(E3) constr(E4) :=
  xapp (>> E1 E2 E3 E4).
Tactic Notation "xapp" constr(E1) constr(E2) constr(E3) constr(E4) constr(E5) :=
  xapp (>> E1 E2 E3 E4 E5).

Tactic Notation "xapp" "~" :=
  xapp_core ___ (>>) ltac:(fun _ => xauto~).
Tactic Notation "xapp" "~" constr(E) :=
  xapp_core ___ E ltac:(fun _ => xauto~).
Tactic Notation "xapp" "~" constr(E1) constr(E2) :=
  xapp~ (>> E1 E2).
Tactic Notation "xapp" "~" constr(E1) constr(E2) constr(E3) :=
  xapp~ (>> E1 E2 E3).
Tactic Notation "xapp" "~" constr(E1) constr(E2) constr(E3) constr(E4) :=
  xapp~ (>> E1 E2 E3 E4).
Tactic Notation "xapp" "~" constr(E1) constr(E2) constr(E3) constr(E4) constr(E5) :=
   xapp~ (>> E1 E2 E3 E4 E5).

Tactic Notation "xapp" "*" :=
  xapp_core ___ (>>) ltac:(fun _ => xauto*).
Tactic Notation "xapp" "*" constr(E) :=
  xapp_core ___ E ltac:(fun _ => xauto*).
Tactic Notation "xapp" "*" constr(E1) constr(E2) :=
  xapp* (>> E1 E2).
Tactic Notation "xapp" "*" constr(E1) constr(E2) constr(E3) :=
  xapp* (>> E1 E2 E3).
Tactic Notation "xapp" "*" constr(E1) constr(E2) constr(E3) constr(E4) :=
  xapp* (>> E1 E2 E3 E4).
Tactic Notation "xapp" "*" constr(E1) constr(E2) constr(E3) constr(E4) constr(E5) :=
   xapp* (>> E1 E2 E3 E4 E5).


Tactic Notation "xapp_spec" constr(H) :=
  xapp_core H (>>) ltac:(fun _ => idtac).
Tactic Notation "xapp_spec" "~" constr(H) :=
  xapp_core H (>>) ltac:(fun _ => xauto~). Tactic Notation "xapp_spec" "*" constr(H) :=
  xapp_core H (>>) ltac:(fun _ => xauto*).
Tactic Notation "xapp_spec" constr(H) constr(E) :=
  xapp_core H E ltac:(fun _ => idtac).
Tactic Notation "xapp_spec" "~" constr(H) constr(E) :=
  xapp_core H E ltac:(fun _ => xauto~).
Tactic Notation "xapp_spec" "*" constr(H) constr(E) :=
  xapp_core H E ltac:(fun _ => xauto*).


Tactic Notation "xapps" :=
  xapps_core ___ (>>) ltac:(fun _ => idtac).
Tactic Notation "xapps" constr(E) :=
  xapps_core ___ E ltac:(fun _ => idtac).
Tactic Notation "xapps" constr(E1) constr(E2) :=
  xapps (>> E1 E2).
Tactic Notation "xapps" constr(E1) constr(E2) constr(E3) :=
  xapps (>> E1 E2 E3).
Tactic Notation "xapps" constr(E1) constr(E2) constr(E3) constr(E4) :=
  xapps (>> E1 E2 E3 E4).
Tactic Notation "xapps" constr(E1) constr(E2) constr(E3) constr(E4) constr(E5) :=
  xapps (>> E1 E2 E3 E4 E5).

Tactic Notation "xapps" "~" :=
  xapps; xauto~.
Tactic Notation "xapps" "*" :=
  xapps; xauto*.
Tactic Notation "xapps" "~" constr(E) :=
  xapps E; xauto~.
Tactic Notation "xapps" "*" constr(E) :=
  xapps E; xauto*.


Tactic Notation "xapps_spec" constr(H) :=
  xapps_core H (>>) ltac:(fun _ => idtac).
Tactic Notation "xapps_spec" constr(H) constr(E) :=
  xapps_core H E ltac:(fun _ => idtac).
Tactic Notation "xapps_spec" constr(H) constr(E1) constr(E2) :=
  xapps_spec H (>> E1 E2).
Tactic Notation "xapps_spec" constr(H) constr(E1) constr(E2) constr(E3) :=
  xapps_spec H (>> E1 E2 E3).
Tactic Notation "xapps_spec" constr(H) constr(E1) constr(E2) constr(E3) constr(E4) :=
  xapps_spec H (>> E1 E2 E3 E4).
Tactic Notation "xapps_spec" constr(H) constr(E1) constr(E2) constr(E3) constr(E4) constr(E5) :=
  xapps_spec H (>> E1 E2 E3 E4 E5).

Tactic Notation "xapps_spec" "~" constr(H) :=
  xapps_spec H; xauto~.
Tactic Notation "xapps_spec" "*" constr(H) :=
  xapps_spec H; xauto*.
Tactic Notation "xapps_spec" "~" constr(H) constr(E) :=
  xapps_spec H E; xauto~.
Tactic Notation "xapps_spec" "*" constr(H) constr(E) :=
  xapps_spec H E; xauto*.


Tactic Notation "xapp_no_simpl" :=
  xapp_no_simpl_core_no_spec (>>) ltac:(fun _ => idtac).
Tactic Notation "xapp_no_simpl" "~" :=
  xapp_no_simpl_core_no_spec (>>) ltac:(fun _ => xauto~).
Tactic Notation "xapp_no_simpl" "*" :=
  xapp_no_simpl_core_no_spec (>>) ltac:(fun _ => xauto* ).
Tactic Notation "xapp_no_simpl" constr(E) :=
  xapp_no_simpl_core_no_spec E ltac:(fun _ => idtac).
Tactic Notation "xapp_no_simpl" "~" constr(E) :=
  xapp_no_simpl_core_no_spec E ltac:(fun _ => xauto~).
Tactic Notation "xapp_no_simpl" "*" constr(E) :=
  xapp_no_simpl_core_no_spec E ltac:(fun _ => xauto* ).


Tactic Notation "xapp" "as" simple_intropattern(X) :=
  xapp_as_core (>>) X.
Tactic Notation "xapp" "~" "as" simple_intropattern(X) :=
  xapp as X; xauto~.
Tactic Notation "xapp" "*" "as" simple_intropattern(X) :=
  xapp as X; xauto*.

Tactic Notation "xapp" constr(E) "as" simple_intropattern(X) :=
  xapp_as_core E X.
Tactic Notation "xapp" "~" constr(E) "as" simple_intropattern(X) :=
  xapp E as X; xauto~.
Tactic Notation "xapp" "*" constr(E) "as" simple_intropattern(X) :=
  xapp E as X; xauto*.


Axiom app_skip : forall f xs B H (Q:B->hprop),
  app f xs H Q.

Lemma app_skip_same : forall f xs B H,
  app f xs H (fun r:B => H).
Proof using. intros. apply app_skip. Qed.

Ltac xapp_skip_core tt :=
  match cfml_postcondition_is_evar tt with
  | false => apply app_skip
  | true => apply app_skip_same
  end.

Tactic Notation "xapp_skip" :=
  xapp_prepare_goal ltac:(fun _ => xapp_skip_core tt).

Ltac xapps_skip_core tt :=
  let cont2 tt := instantiate; try xpull; try intro_subst in
  let cont1 tt :=
    match cfml_get_tag tt with
    | tag_apply => xuntag tag_apply; xapp_skip_core
    | tag_none_app => xapp_skip_core
    | tag_record_new => xapp_skip_core
    end
    in
  match cfml_get_tag tt with
  | tag_let => xlet; [ cont1 tt | cont2 tt ]
  | tag_seq => xseq; [ cont1 tt | cont2 tt ]
  | _ => cont1 tt
  end.

Tactic Notation "xapps_skip" :=
  xapps_skip_core tt.

xapp_types

Ltac xapp_types_core_noarg tt :=
  xapp_prepare_goal ltac:(fun _ =>
    idtac "=== type of app in goal ===";
    cfml_show_app_type_goal tt;
    idtac "=== type of app in spec === ";
    let S := fresh "Spec" in
    xapp_use_or_find ___ S;
    forwards_nounfold_skip_sides_then S ltac:(fun K =>
      let T := type of K in
      cfml_show_app_type T);
    clear S).

Tactic Notation "xapp_types" :=
  xapp_types_core_noarg tt.

xapp_spec_types

Ltac xapp_spec_types_core_noarg S :=
  xapp_prepare_goal ltac:(fun _ =>
    idtac "=== type of app in goal ===";
    cfml_show_app_type_goal tt;
    idtac "=== type of app in spec === ";
    forwards_nounfold_skip_sides_then S ltac:(fun K =>
      let T := type of K in
      cfml_show_app_type T)).

Tactic Notation "xapp_spec_types" constr(S) :=
  xapp_spec_types_core_noarg S.





xcf applies to a goal with a conclusion of the form spec f n K or app f xs H Q. It looks up the characteristic formula associated with f in the database "database_cf", and exploits it to start proving the goal. Note that xcf first step is to call intros.
When xcf fails to apply, it is (most usually) because the number of arguments, or the types of the arguments, or the return type, does not match.
Variants:
  • xcf_show will only display the CF lemma found in the database, putting it in a fresh hypothesis.
  • xcf_show f is similar, only f is provided explicitly.
  • xcf_show as H xcf_show f as H are similiar but allow providing name of the assumption.
  • xcf_types shows the type of the application in the goal, compared with the one from the specification.
  • xcf_types S is similar, with S the specification lemma.


Ltac solve_type :=
  match goal with |- Type => exact unit end.

Ltac remove_head_unit tt :=
  repeat match goal with
  | |- unit -> _ => intros _
  end.

Ltac xcf_post tt :=
  cbv beta;
  remove_head_unit tt.

Ltac xcf_find f :=
  ltac_database_get database_cf f.

Ltac xcf_core_app_exploit H :=
  sapply H;
  instantiate;
  try solve_type;
  clear H;
  xcf_post tt;
  xtag_pre_post.


Ltac xcf_core_spec f :=
  xcf_find f;
  let C := fresh "Curried" in
  let H := fresh "Spec" in
  intros [C H];
  split;
  [ clear H; try apply C
  | clear C; intros; xcf_core_app_exploit H ].

Ltac xcf_core_app f :=
  xcf_find f;
  let H := fresh "Spec" in
  intros [_ H];
  xcf_core_app_exploit H.
Ltac xcf_fallback f :=
  idtac "Warning: could not exploit the specification; maybe the types don't match; check them using [xcf_types]; if you intend to use the specification manually, use [xcf_show as S].";
  xcf_find f;
  let Sf := fresh "Spec" in
  intros Sf.

Ltac xcf_top_value f :=
  xcf_find f;
  let Sf := fresh "Spec" in
  intros Sf;
  rewrite Sf;
  clear Sf;
  try solve_type.
Ltac xcf_core tt :=
  intros;
  xuntag_pre_post;
  match goal with
  | |- spec ?f ?n ?P => first [ xcf_core_spec f | xcf_fallback f | fail 2 ]
  | |- curried ?n ?f /\ ?P => first [ xcf_core_spec f | xcf_fallback f | fail 2 ]
  | |- app ?f ?xs ?H ?Q => first [ xcf_core_app f | xcf_top_value f | xcf_fallback f | fail 2 ]
  | |- tag tag_apply (app ?f ?xs) ?H ?Q => first [ xuntag tag_apply; xcf_core_app f | xcf_fallback f | fail 2 ]
  | |- ?f = _ => first [ xcf_top_value f | xcf_fallback f | fail 2 ]
  | _ => fail 1 "need to call [xcf_show f as H], where [f] is the name of the definition"
  end.

Tactic Notation "xcf" :=
  xcf_core tt.
Tactic Notation "xcf" "~" :=
  xcf; xauto_tilde.
Tactic Notation "xcf" "*" :=
  xcf; xauto_star.


Ltac xcf_show_name f H :=
  first [ intros [_ H]
        | intros H ].


Ltac xcf_show_core H :=
  intros;
  xuntag_pre_post;
  let f :=
    match goal with
    | |- spec ?f ?n ?P => constr:(f)
    | |- app ?f ?xs ?H ?Q => constr:(f)
    | |- tag tag_apply (app ?f ?xs) ?H ?Q => constr:(f)
    | |- ?f = _ => constr:(f)
    | _ => fail 1 "need to call [xcf_show f], where [f] is the name of the definition"
    end in
  xcf_find f;
  xcf_show_name f H;
  xtag_pre_post.

Tactic Notation "xcf_show" "as" ident(H) :=
  xcf_show_core H.

Tactic Notation "xcf_show" constr(f) "as" ident(H) :=
  xcf_find f; xcf_show_name f H.

Tactic Notation "xcf_show" :=
  let H := fresh "Spec" in xcf_show as H.

Tactic Notation "xcf_show" constr(f) :=
  let H := fresh "Spec" in xcf_show f as H.

Ltac xcf_types_core tt :=
  let S := fresh "Spec" in
  first [intros [_ S] | intros S];
  idtac "=== type of app in goal ===";
  cfml_show_app_type_goal tt;
  idtac "=== type of app in code === ";
  forwards_nounfold_skip_sides_then S ltac:(fun K =>
    let T := type of K in
    cfml_show_app_type T);
  clear S.

Ltac xcf_types_core_noarg tt :=
  intros;
  let H := fresh in
  xcf_show_core H;
  revert H;
  xcf_types_core tt.

Ltac xcf_types_core_arg S :=
  intros;
  generalize S;
  xcf_types_core tt.

Tactic Notation "xcf_types" :=
  xcf_types_core_noarg tt.

Tactic Notation "xcf_types" constr(S) :=
  xcf_types_core_arg S.



Ltac xcf_find_in_goal :=
  
  match goal with |- @tag _ _ (app ?f _) _ _ => xcf_find f end.


Ltac xpartial :=
  
  xcf_find_in_goal; intros [ ? _ ];
  
  xapply app_partial;
  unfold length;
  
  try solve [ eauto ];
  
  simpl;
  
  try solve [ math | xsimpl~ ];
  
  match goal with h: curried _ _ |- _ => clear h end.



xname_pre X applies to a goal of the form F H Q and defines X as a shorname for the pre-condition H.

Tactic Notation "xname_pre" ident(X) :=
  match goal with |- ?R ?H ?Q => sets X: H end.

xname_post X applies to a goal of the form F H Q and defines X as a shorname for the post-condition Q.

Tactic Notation "xname_post" ident(X) :=
  match goal with |- ?R ?H ?Q => sets X: Q end.



xcredit m replaces $n is the goal with $m, and generates the equality n = m. It is typically used before a credit split operation, e.g. to replace $n with $a \* $b, when n = a + b.
LATER: add demos for this tactic.

Ltac xcredit goal :=
  match goal with
  | |- context[\$ goal] =>
      idtac
  | |- context[\$ ?n] =>
      math_rewrite (n = goal)
  end.


xpay applys to a goal of the form (Pay_ ;; F1) H Q. It is used to eliminate the call to Pay, by spending one credit.
xpay (n + m) where n and m are numbers such that: where n denotes the remainder, and m denotes the payment that need to be performed (e.g. 1). WARNING: the interface of the latter might change.
LATER: add demos for this tactic.

Ltac xpay_start tt :=
  xuntag tag_pay; apply local_erase; esplit; split.

Ltac xpay_core tt :=
  xpay_start tt; [ unfold pay_one; hsimpl | ].

Tactic Notation "xpay" := xpay_core tt.

Ltac xpay_on_impl goal :=
  xcredit goal;
  first [ rewrite credits_split_eq ];
  xpay.

Tactic Notation "xpay" constr(goal) :=
  xpay_on_impl goal.


xpay_skip is used to skip the paying of one credit; use it for development purposes only.
LATER: add demos for this tactic.

Ltac xpay_fake tt :=
  xpay_start tt;
  [ assert (pay_one_fake: forall H, pay_one H H);
     [ admit
     | apply pay_one_fake ]
  | ].

Tactic Notation "xpay_skip" := xpay_fake tt.



Ltac xgc_credit_core HP :=
  let H := fresh in
  let E := fresh in
  destruct (credits_le_rest HP) as (H&HA&E);
  xchange E; [ xgc H; clear H HA E; hclean ].

Tactic Notation "xgc_credit" constr(HP) :=
  xgc_credit_core HP.

Lemma demo_xgc_credit_core : forall m n B (F : ~~B) Q,
  m <= n ->
  is_local F ->
  F (\$m) Q ->
  F (\$n) Q.
Proof.
  introv H L HH.
  xgc_credit H.
  assumption.
Qed.


Tactic xskip_credits eliminates all occurrences of credits in the goal, by replacing \$ x with the empty heap predicate \.
Implementing using skip_credits then hsimpl. Should only be used for development purpose.
LATER: add demos for this tactic.

Ltac xskip_credits_base :=
  skip_credits_core; hsimpl.

Tactic Notation "xskip_credits" :=
  xskip_credits_base.


xintros is a more powerful variant of intros. It introduces the hypotheses that appear in the goal. It is able to decompose conjunctions and existential quantifiers, and attempts to substitute away equalities.

Lemma dissolve_pair: forall A B C : Prop, (A -> B -> C) -> A /\ B -> C.
Proof. tauto. Qed.

Lemma dissolve_exists: forall A (B : A -> Prop) (C : Prop),
                       (forall x, B x -> C) -> (exists x, B x) -> C.
Proof. introv ? (x&?). eauto. Qed.

Ltac xintros :=
  match goal with
  | |- ?x = _ -> _ =>
      intro; try subst x; xintros
  | |- (_ /\ _) -> _ =>
      eapply dissolve_pair; xintros
  | |- (exists x, _) -> _ =>
      eapply dissolve_exists; xintros
  | |- forall x, _ =>
      intro; xintros
  | _ =>
      idtac
  end.

xstraight is used when the goal is a Hoare triple. It attempts to make progress, by performing symbolic execution, as long as the code is a straight-line sequence of calls to known functions. It progresses only as long as there are zero or one subgoals, so it stops at the first difficulty.

Ltac xstraight_step :=
  first [ xapp* | xret* ].

Ltac xstraight :=
  
  repeat (xstraight_step; [ xintros ]);
  
  try solve [ xstraight_step ].

  • xstep automatically applies the appropriate xtactic. The use of the xstep tactic should be for development only; it makes proof script harder to fix upon changes.
  • xgo repeatedly call xstep. Important: xgo should only be used to conclude simple goals.
  • xcf_go is short for xcf; xgo.

Ltac xstep_once tt :=
  match cfml_get_tag tt with
  | tag_ret => xret
  | tag_apply => xapp
  | tag_none_app => xapp
  | tag_record_new => xapp
  | tag_val => xval
  | tag_fun => xfun
  | tag_let => xlet
  | tag_match => xmatch
  | tag_case => xcase
  | tag_fail => xfail
  | tag_done => xdone
  | tag_alias => xalias
  | tag_seq => xseq
  | tag_if => xif
  | tag_for => fail 1
  | tag_while => fail 1
  | tag_assert => xassert
  | tag_pay => xpay
  | _ =>
     match goal with
     | |- _ ==> _ => first [ xsimpl | fail 2 ]
     | |- _ ===> _ => first [ xsimpl | fail 2 ]
     end
  end.


Ltac xstep_core tt :=
  try (xpull; intros); xstep_once tt; instantiate.

Ltac xgo_core tt :=
  repeat (xstep_core tt).

Tactic Notation "xstep" :=
  xstep_core tt.
Tactic Notation "xstep" "~" :=
  xstep; xauto~.
Tactic Notation "xstep" "*" :=
  xstep; xauto*.

Tactic Notation "xgo" :=
  xgo_core tt.
Tactic Notation "xgo" "~" :=
  xgo; xauto~.
Tactic Notation "xgo" "*" :=
  xgo; xauto*.

Tactic Notation "xcf_go" :=
  xcf; xgo.
Tactic Notation "xcf_go" "~" :=
  xcf_go; xauto~.
Tactic Notation "xcf_go" "*" :=
  xcf_go; xauto*.