Library FreeSpec.Core.Tactics



From ExtLib Require Import Monad.
From FreeSpec.Core Require Import Contract ImpureFacts HoareFacts.

Ltac destruct_if_when :=
  let equ_cond := fresh "equ_cond" in
  match goal with
  | |- context[when (negb ?B) _] ⇒ case_eq B; intros equ_cond; cbn
  | |- context[when ?B _] ⇒ case_eq B; intros equ_cond; cbn
  | |- context[if (negb ?B) then _ else _] ⇒ case_eq B; intros equ_cond; cbn
  | |- context[if ?B then _ else _] ⇒ case_eq B; intros equ_cond; cbn
  | _idtac
  end.

Ltac destruct_if_when_in hyp :=
  let equ_cond := fresh "equ" in
  match type of hyp with
  | context[when (negb ?B) _] ⇒ case_eq B;
                                 intro equ_cond;
                                 rewrite equ_cond in hyp
  | context[when ?B _] ⇒ case_eq B;
                          intro equ_cond;
                          rewrite equ_cond in hyp
  | context[if (negb ?B) then _ else _] ⇒ case_eq B;
                                           intro equ_cond;
                                           rewrite equ_cond in hyp
  | context[if ?B then _ else _] ⇒ case_eq B;
                                    intro equ_cond;
                                    rewrite equ_cond in hyp
  | _idtac
  end.

Ltac simplify_gens :=
  repeat match goal with
         | H : True |- _
           clear H

         | H: _ _ |- _
           destruct H

         | |- context[@proj_p ?ix ?ix (refl_MayProvide ?ix) _ ?e] ⇒
           change (@proj_p ix ix (refl_MayProvide ix) _ e) with (Some e);
           cbn match;
           cbn beta

         | H: context[@proj_p ?ix ?ix (refl_MayProvide ?ix) _ ?e] |- _
           change (@proj_p ix ix (refl_MayProvide ix) _ e) with (Some e) in H;
           cbn match in H;
           cbn beta in H

         | |- context[gen_witness_update _ _ _ _] ⇒
           unfold gen_witness_update;
           repeat (rewrite proj_inj_p_equ || rewrite distinguish)

         | H : context[gen_witness_update _ _ _ _] |- _
           unfold gen_witness_update in H;
           repeat (rewrite proj_inj_p_equ in H || rewrite distinguish in H)

         | |- context[gen_caller_obligation ?c ?ω ?e] ⇒
           unfold gen_caller_obligation;
           repeat (rewrite proj_inj_p_equ || rewrite distinguish)

         | H : context[gen_caller_obligation ?c ?ω ?e] |- _
           unfold gen_caller_obligation in H;
           repeat (rewrite proj_inj_p_equ in H || rewrite distinguish in H)

         | |- context[gen_callee_obligation ?c ?ω ?e ?x] ⇒
           unfold gen_callee_obligation;
           repeat (rewrite proj_inj_p_equ || rewrite distinguish)

         | H : context[gen_callee_obligation ?c ?ω ?e ?x] |- _
           unfold gen_callee_obligation in H;
           repeat (rewrite proj_inj_p_equ in H || rewrite distinguish in H)
         end.

#[local]
Ltac prove_impure :=
  repeat (cbn -[
              to_hoare
              gen_caller_obligation
              gen_callee_obligation
              gen_witness_update
          ] in *;
          simplify_gens;
          destruct_if_when);
  lazymatch goal with

  | |- _ _
    split;
    prove_impure

  | |- pre (to_hoare ?c (impure_map ?f ?p)) ?ω ⇒
    unfold impure_map;
    prove_impure

  | |- pre (to_hoare ?c (impure_apply ?p ?q)) ?ω ⇒
    unfold impure_map;
    prove_impure

  | |- _ _, _ _ = _ _
    let x := fresh "x" in
    let ω' := fresh "ω" in
    let o_caller := fresh "o_caller" in
    let equ := fresh "equ" in
    intros x ω' [o_caller equ];
    repeat rewrite equ in *; clear equ; clear ω';
    prove_impure

  | |- pre (to_hoare ?c ?p) ?ω ⇒
    let p := (eval hnf in p) in
    lazymatch p with
    | request_then ?e ?f
      let o_caller := fresh "o_caller" in
      assert (o_caller : gen_caller_obligation c ω e); [ prove_impure
                                                       | constructor; prove_impure
                                                       ]
    | local _constructor
    | impure_bind (impure_bind ?p ?f) ?g
      rewrite (impure_bind_assoc p f g);
      prove_impure
    | impure_bind ?p ?f
      apply to_hoare_pre_bind_assoc; [ eauto with freespec
                                     | let x := fresh "x" in
                                       let ω' := fresh "ω" in
                                       let hpost := fresh "hpost" in
                                       intros x ω' hpost;
                                       prove_impure
                                     ]

    | _eauto with freespec
    end

  | |- ?a
    eauto with freespec

  end.

Tactic Notation "prove" "impure" := prove_impure.
Tactic Notation "prove" "impure" "with" ident(db) := prove_impure; eauto with db.

Ltac unroll_post run :=
  repeat (cbn -[
              to_hoare
              gen_caller_obligation
              gen_callee_obligation
              gen_witness_update
          ] in *;
          simplify_gens;
          destruct_if_when_in run);
  lazymatch type of run with

  | post (to_hoare ?c ?p) ?ω ?x ?ω' ⇒
    let p := (eval hnf in p) in
    lazymatch p with
    | request_then ?e ?f
      inversion run; ssubst;
      clear run;
      lazymatch goal with
      | next : _, post (interface_to_hoare c _ e) _ _ _ _ |- _
        let ω'' := fresh "ω" in
        let o_callee := fresh "o_callee" in
        let run := fresh "run" in
        destruct next as [ω'' [o_callee run]];
        unroll_post run
      | _idtac
      end

    | local ?x
      inversion run; ssubst;
      clear run

    | impure_bind ?p ?f
      apply (to_hoare_post_bind_assoc c p f) in run;
      let run1 := fresh "run" in
      let run2 := fresh "run" in
      let x := fresh "x" in
      let ω := fresh "ω" in
      destruct run as [x [ω [run1 run2]]];
      unroll_post run1; unroll_post run2

    | ?aidtac
    end

  | ?aidtac
  end.