(*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *)

theory DomainSepInv
imports
  "ArchIpc_AC" (* for transfer_caps_loop_pres_dest lec_valid_cap' set_simple_ko_get_tcb thread_set_tcb_fault_update_valid_mdb *)
  "Monads.WPBang"
begin

text \<open>
  We define and prove an invariant that is necessary to achieve domain
  separation on seL4. In its strongest form, we require that all IRQs, other than
  those for the timer, are inactive, and that no IRQControl or
  IRQHandler caps are present (to prevent any inactive IRQs from becoming
  active in the future).

  It always requires that there are no domain caps.
\<close>

text \<open>
  When @{term irqs} is @{term False} we require that non-timer IRQs are off permanently.
\<close>
definition domain_sep_inv :: "bool \<Rightarrow> 'a :: state_ext state \<Rightarrow> 'b :: state_ext state \<Rightarrow> bool" where
  "domain_sep_inv irqs st s \<equiv>
    (\<forall>slot. \<not> cte_wp_at ((=) DomainCap) slot s) \<and>
    (irqs \<or> (\<forall>irq slot. \<not> cte_wp_at ((=) IRQControlCap) slot s
                      \<and> \<not> cte_wp_at ((=) (IRQHandlerCap irq)) slot s
                      \<and> interrupt_states s irq \<noteq> IRQSignal
                      \<and> interrupt_states s irq \<noteq> IRQReserved
                      \<and> interrupt_states s = interrupt_states st))"

definition domain_sep_inv_cap where
  "domain_sep_inv_cap irqs cap \<equiv> case cap of
     IRQControlCap \<Rightarrow> irqs
   | IRQHandlerCap irq \<Rightarrow> irqs
   | DomainCap \<Rightarrow> False
   | _ \<Rightarrow> True"

lemma cte_wp_at_not_domain_sep_inv_cap:
  "cte_wp_at (not domain_sep_inv_cap irqs) slot s \<longleftrightarrow>
   ((irqs \<longrightarrow> False) \<and>
    (\<not> irqs \<longrightarrow> (cte_wp_at ((=) IRQControlCap) slot s \<or>
                 (\<exists> irq. cte_wp_at ((=) (IRQHandlerCap irq)) slot s)))) \<or>
   cte_wp_at ((=) DomainCap) slot s"
  apply (rule iffI)
   apply (drule cte_wp_at_norm)
   apply clarsimp
   apply (case_tac c, simp_all add: domain_sep_inv_cap_def pred_neg_def)
   apply (auto elim: cte_wp_at_weakenE split: if_splits)
  done

lemma domain_sep_inv_def2:
  "domain_sep_inv irqs st s =
    ((\<forall>slot. \<not> cte_wp_at ((=) DomainCap) slot s) \<and>
     (irqs \<or> (\<forall>irq slot. \<not> cte_wp_at ((=) IRQControlCap) slot s
                       \<and> \<not> cte_wp_at ((=) (IRQHandlerCap irq)) slot s)) \<and>
     (irqs \<or> (\<forall>irq. interrupt_states s irq \<noteq> IRQSignal
                  \<and> interrupt_states s irq \<noteq> IRQReserved
                  \<and> interrupt_states s = interrupt_states st)))"
  by (fastforce simp: domain_sep_inv_def)

lemma domain_sep_inv_wp:
  assumes nctrl:
    "\<And>slot. \<lbrace>(\<lambda>s. \<not> cte_wp_at (not domain_sep_inv_cap irqs) slot s) and P\<rbrace>
             f
             \<lbrace>\<lambda>_ s. \<not> cte_wp_at (not domain_sep_inv_cap irqs) slot s\<rbrace>"
  assumes irq_pres:
    "\<And>P. \<not> irqs \<Longrightarrow> \<lbrace>(\<lambda>s. P (interrupt_states s)) and R\<rbrace>
                     f
                     \<lbrace>\<lambda>_ s. P (interrupt_states s)\<rbrace>"
  shows
    "\<lbrace>domain_sep_inv irqs st and P and (\<lambda>s. irqs \<or> R s)\<rbrace>
     f
     \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  apply (clarsimp simp: domain_sep_inv_def2 valid_def)
  apply (subst conj_assoc[symmetric])
  apply (rule conjI)
   apply (rule conjI)
    apply (intro allI)
    apply (erule use_valid[OF _ hoare_strengthen_post[OF nctrl]])
     apply (fastforce simp: cte_wp_at_not_domain_sep_inv_cap)
    apply (fastforce simp: cte_wp_at_not_domain_sep_inv_cap)
   apply (fastforce elim!: use_valid[OF _ hoare_strengthen_post[OF nctrl]]
                     simp: cte_wp_at_not_domain_sep_inv_cap)
  apply (case_tac "irqs")
   apply blast
  apply (rule disjI2)
  apply simp
  apply (intro allI conjI)
    apply (erule_tac P1="\<lambda>x. x irq \<noteq> IRQSignal" in use_valid[OF _ irq_pres], assumption)
    apply blast
   apply (erule use_valid[OF _ irq_pres], assumption)
   apply blast
  apply (erule use_valid[OF _ irq_pres], assumption)
  apply blast
  done

lemma domain_sep_inv_triv:
  assumes cte_pres: "\<And>P slot. \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace> f \<lbrace>\<lambda>_ s. \<not> cte_wp_at P slot s\<rbrace>"
  assumes irq_pres: "\<And>P. \<lbrace>\<lambda>s. P (interrupt_states s)\<rbrace> f \<lbrace>\<lambda>_ s. P (interrupt_states s)\<rbrace>"
  shows "\<lbrace>domain_sep_inv irqs st\<rbrace> f \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  apply (rule domain_sep_inv_wp[where P="\<top>" and R="\<top>", simplified])
  apply (rule cte_pres, rule irq_pres)
  done

lemma domain_sep_inv_arch_state_update[simp]:
  "domain_sep_inv irqs st (s\<lparr>arch_state := blah\<rparr>) = domain_sep_inv irqs st s"
  by (simp add: domain_sep_inv_def)

lemma domain_sep_inv_machine_state_update[simp]:
  "domain_sep_inv irqs st (s\<lparr>machine_state := X\<rparr>) = domain_sep_inv irqs st s"
  by (simp add: domain_sep_inv_def)

lemma detype_interrupts_states[simp]:
  "interrupt_states (detype S s) = interrupt_states s"
  by (simp add: detype_def)

lemma detype_domain_sep_inv[simp]:
  "domain_sep_inv irqs st s \<longrightarrow> domain_sep_inv irqs st (detype S s)"
  by (fastforce simp: domain_sep_inv_def)

lemma domain_sep_inv_detype_lift:
  "\<lbrace>P\<rbrace> f \<lbrace>\<lambda>rv. domain_sep_inv irqs st\<rbrace> \<Longrightarrow>
   \<lbrace>P\<rbrace> f \<lbrace>\<lambda>rv s. domain_sep_inv irqs st (detype S s)\<rbrace>"
  by (strengthen detype_domain_sep_inv, assumption)

crunch domain_sep_inv[wp]: set_extra_badge "domain_sep_inv irqs st"

(* FIXME: following 3 lemmas clagged from FinalCaps *)
lemma set_cap_neg_cte_wp_at_other_helper':
  "\<lbrakk> oslot \<noteq> slot; ko_at (TCB x) (fst oslot) s;
     tcb_cap_cases (snd oslot) = Some (ogetF, osetF, orestr);
     kheap (s\<lparr>kheap := kheap s(fst oslot \<mapsto> TCB (osetF (\<lambda> x. cap) x))\<rparr>) (fst slot) = Some (TCB tcb);
     tcb_cap_cases (snd slot) = Some (getF, setF, restr); P (getF tcb) \<rbrakk>
     \<Longrightarrow> cte_wp_at P slot s"
  apply (case_tac "fst oslot = fst slot")
   apply (rule cte_wp_at_tcbI)
     apply (fastforce split: if_splits simp: obj_at_def)
    apply assumption
   apply (fastforce split: if_splits simp: tcb_cap_cases_def dest: prod_eqI)
  apply (rule cte_wp_at_tcbI)
    apply (fastforce split: if_splits simp: obj_at_def)
   apply assumption
  apply assumption
  done

lemma set_cap_neg_cte_wp_at_other_helper:
  "\<lbrakk> \<not> cte_wp_at P slot s; oslot \<noteq> slot; ko_at (TCB x) (fst oslot) s;
     tcb_cap_cases (snd oslot) = Some (getF, setF, restr) \<rbrakk>
     \<Longrightarrow> \<not> cte_wp_at P slot (s\<lparr>kheap := kheap s(fst oslot \<mapsto> TCB (setF (\<lambda> x. cap) x))\<rparr>)"
  apply (rule notI)
  apply (erule cte_wp_atE)
   apply (fastforce elim: notE intro: cte_wp_at_cteI split: if_splits)
  apply (fastforce elim: notE intro: set_cap_neg_cte_wp_at_other_helper')
  done

lemma set_cap_neg_cte_wp_at_other:
  "oslot \<noteq> slot \<Longrightarrow> set_cap cap oslot \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  unfolding set_cap_def
  apply (wp set_object_wp get_object_wp | wpc | simp add: split_def)+
  apply (intro allI impI conjI)
       apply (rule notI)
       apply (erule cte_wp_atE)
        apply (fastforce split: if_splits dest: prod_eqI elim: notE intro: cte_wp_at_cteI simp: obj_at_def)
       apply (fastforce split: if_splits elim: notE intro: cte_wp_at_tcbI)
      apply (auto dest: set_cap_neg_cte_wp_at_other_helper)
  done

lemma set_cap_neg_cte_wp_at:
  "\<lbrace>\<lambda>s. \<not> cte_wp_at P slot s \<and> \<not> P capa\<rbrace>
   set_cap capa slota
   \<lbrace>\<lambda>_ s. \<not> cte_wp_at P slot s\<rbrace>"
  apply (case_tac "slot = slota")
   apply simp
   apply (simp add: set_cap_def set_object_def)
   apply (rule hoare_pre)
    apply (wp get_object_wp | wpc)+
   apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  apply (rule hoare_pre)
   apply (rule set_cap_neg_cte_wp_at_other, simp+)
  done

lemma domain_sep_inv_cap_IRQControlCap:
  "\<lbrakk> domain_sep_inv_cap irqs cap; \<not> irqs \<rbrakk> \<Longrightarrow> cap \<noteq> IRQControlCap"
  by (auto simp: domain_sep_inv_cap_def)

lemma domain_sep_inv_cap_IRQHandlerCap:
  "\<lbrakk> domain_sep_inv_cap irqs cap; \<not> irqs \<rbrakk> \<Longrightarrow> cap \<noteq> IRQHandlerCap irq"
  by (auto simp: domain_sep_inv_cap_def)

lemma set_cap_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and K (domain_sep_inv_cap irqs cap)\<rbrace>
   set_cap cap slot
   \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  apply (rule hoare_gen_asm)
  apply (rule hoare_pre)
   apply (rule domain_sep_inv_wp)
   apply (wp set_cap_neg_cte_wp_at | simp add: pred_neg_def | blast)+
  done

lemma cte_wp_at_domain_sep_inv_cap:
  "\<lbrakk> domain_sep_inv irqs st s; cte_wp_at ((=) cap) slot s \<rbrakk>
     \<Longrightarrow> domain_sep_inv_cap irqs cap"
  apply (case_tac slot)
  apply (auto simp: domain_sep_inv_def domain_sep_inv_cap_def split: cap.splits)
  done

lemma weak_derived_irq_handler:
  "weak_derived (IRQHandlerCap irq) cap \<Longrightarrow> cap = (IRQHandlerCap irq)"
  by (auto simp: weak_derived_def copy_of_def same_object_as_def split: cap.splits if_splits)

(* FIXME: move to CSpace_AI *)
lemma weak_derived_DomainCap:
  "weak_derived c' c \<Longrightarrow> (c' = DomainCap) = (c = DomainCap)"
  apply (clarsimp simp: weak_derived_def)
  apply (erule disjE)
   apply (clarsimp simp: copy_of_def split: if_split_asm)
   apply (auto simp: is_cap_simps same_object_as_def
              split: cap.splits)[1]
  apply simp
  done

lemma cte_wp_at_weak_derived_domain_sep_inv_cap:
  "\<lbrakk> domain_sep_inv irqs st s; cte_wp_at (weak_derived cap) slot s \<rbrakk>
     \<Longrightarrow> domain_sep_inv_cap irqs cap"
  apply (cases slot)
  apply (force simp: domain_sep_inv_def domain_sep_inv_cap_def
              split: cap.splits
               dest: cte_wp_at_norm weak_derived_irq_handler weak_derived_DomainCap)
  done

lemma is_derived_IRQHandlerCap:
  "is_derived m src (IRQHandlerCap irq) cap \<Longrightarrow> (cap = IRQHandlerCap irq)"
  apply (clarsimp simp: is_derived_def)
  apply (case_tac cap, simp_all add: is_cap_simps cap_master_cap_def)
  done

(* FIXME: move to CSpace_AI *)
lemma DomainCap_is_derived:
  "is_derived m src DomainCap cap \<Longrightarrow> cap = DomainCap"
  by (auto simp: is_derived_def is_reply_cap_def is_master_reply_cap_def cap_master_cap_def
          split: cap.splits)

lemma cte_wp_at_is_derived_domain_sep_inv_cap:
  "\<lbrakk> domain_sep_inv irqs st s; cte_wp_at (is_derived (cdt s) slot cap) slot s \<rbrakk>
     \<Longrightarrow> domain_sep_inv_cap irqs cap"
  apply (cases slot)
  apply (fastforce simp: domain_sep_inv_def domain_sep_inv_cap_def
                  split: cap.splits
                   dest: cte_wp_at_norm DomainCap_is_derived is_derived_IRQHandlerCap)
  done

lemma domain_sep_inv_exst_update[simp]:
  "domain_sep_inv irqs st (trans_state f s) = domain_sep_inv irqs st s"
  by (simp add: domain_sep_inv_def)

lemma domain_sep_inv_is_original_cap_update[simp]:
  "domain_sep_inv irqs st (s\<lparr>is_original_cap := X\<rparr>) = domain_sep_inv irqs st s"
  by (simp add: domain_sep_inv_def)

lemma domain_sep_inv_cdt_update[simp]:
  "domain_sep_inv irqs st (s\<lparr>cdt := X\<rparr>) = domain_sep_inv irqs st s"
  by (simp add: domain_sep_inv_def)

crunch domain_sep_inv[wp]: update_cdt "domain_sep_inv irqs st"

lemma set_untyped_cap_as_full_domain_sep_inv[wp]:
  "set_untyped_cap_as_full a b c \<lbrace>domain_sep_inv irqs st\<rbrace>"
  apply (clarsimp simp: set_untyped_cap_as_full_def)
  apply (rule hoare_pre)
   apply (rule set_cap_domain_sep_inv)
  apply (case_tac a, simp_all add: domain_sep_inv_cap_def)
  done

lemma cap_insert_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and (\<lambda>s. cte_wp_at (is_derived (cdt s) slot cap) slot s)\<rbrace>
   cap_insert cap slot dest_slot
   \<lbrace>\<lambda>_. domain_sep_inv irqs st \<rbrace>"
  apply (simp add: cap_insert_def)
  apply (wpsimp wp: set_cap_domain_sep_inv get_cap_wp set_original_wp dxo_wp_weak)
  apply (blast dest: cte_wp_at_is_derived_domain_sep_inv_cap)
  done

lemma domain_sep_inv_cap_NullCap[simp]:
  "domain_sep_inv_cap irqs NullCap"
  by (simp add: domain_sep_inv_cap_def)

lemma cap_move_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and (\<lambda>s. cte_wp_at (weak_derived cap) slot s)\<rbrace>
   cap_move cap slot dest_slot
   \<lbrace>\<lambda>_. domain_sep_inv irqs st \<rbrace>"
  apply (simp add: cap_move_def)
  apply (wpsimp wp: set_cap_domain_sep_inv get_cap_wp set_original_wp dxo_wp_weak)
  apply (blast dest: cte_wp_at_weak_derived_domain_sep_inv_cap)
  done

lemma set_irq_state_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and (\<lambda>s. stt = interrupt_states s irq)\<rbrace>
   set_irq_state stt irq
   \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  apply (simp add: set_irq_state_def)
  apply (wp | simp add: do_machine_op_def | wpc)+
  done

locale DomainSepInv_1 =
  fixes state_ext_t :: "'state_ext :: state_ext itself"
  assumes arch_finalise_cap_domain_sep_inv[wp]:
    "arch_finalise_cap c x \<lbrace>\<lambda>s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  and arch_post_cap_deletion_domain_sep_inv[wp]:
    "arch_post_cap_deletion acap \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and init_arch_objects_domain_sep_inv[wp]:
    "init_arch_objects typ ptr n sz refs \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and prepare_thread_delete_domain_sep_inv[wp]:
    "prepare_thread_delete t \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and arch_finalise_cap_rv:
    "\<lbrace>\<lambda>_. P (NullCap,NullCap)\<rbrace> arch_finalise_cap c x \<lbrace>\<lambda>rv s :: det_ext state. P rv\<rbrace>"
begin

lemma deleted_irq_handler_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and K irqs\<rbrace>
   deleted_irq_handler irq
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (rule hoare_gen_asm)
  apply (simp add: deleted_irq_handler_def set_irq_state_def)
  apply wp
  apply (simp add: domain_sep_inv_def)
  done

lemma empty_slot_domain_sep_inv:
  "\<lbrace>\<lambda>s. domain_sep_inv irqs st s \<and> (\<not> irqs \<longrightarrow> b = NullCap)\<rbrace>
   empty_slot a b
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  unfolding empty_slot_def post_cap_deletion_def
  by (wpsimp wp: get_cap_wp set_cap_domain_sep_inv set_original_wp dxo_wp_weak
                 static_imp_wp deleted_irq_handler_domain_sep_inv)

end

lemma set_simple_ko_neg_cte_wp_at[wp]:
  "set_simple_ko f a b \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  apply (simp add: set_simple_ko_def)
  apply (wp set_object_wp_strong get_object_wp
         | simp add: partial_inv_def a_type_def split: kernel_object.splits)+
  apply (case_tac "a = fst slot")
   apply (clarsimp split: kernel_object.splits)
   apply (fastforce elim: cte_wp_atE simp: obj_at_def)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

crunch domain_sep_inv[wp]: set_simple_ko "domain_sep_inv irqs st"
  (wp: domain_sep_inv_triv)

lemma set_thread_state_neg_cte_wp_at[wp]:
  "set_thread_state a b \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  apply (simp add: set_thread_state_def)
  apply (wp set_object_wp get_object_wp dxo_wp_weak| simp)+
  apply (case_tac "a = fst slot")
   apply (clarsimp split: kernel_object.splits)
   apply (erule notE)
   apply (erule cte_wp_atE)
    apply (fastforce simp: obj_at_def)
   apply (drule get_tcb_SomeD)
   apply (rule cte_wp_at_tcbI)
     apply simp
    apply assumption
   apply (fastforce simp: tcb_cap_cases_def split: if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

lemma set_bound_notification_neg_cte_wp_at[wp]:
  "set_bound_notification a b \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  apply (simp add: set_bound_notification_def)
  apply (wp set_object_wp get_object_wp dxo_wp_weak| simp)+
  apply (case_tac "a = fst slot")
   apply (clarsimp split: kernel_object.splits)
   apply (erule notE)
   apply (erule cte_wp_atE)
    apply (fastforce simp: obj_at_def)
   apply (drule get_tcb_SomeD)
   apply (rule cte_wp_at_tcbI)
     apply simp
    apply assumption
   apply (fastforce simp: tcb_cap_cases_def split: if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

crunch domain_sep_inv[wp]: set_thread_state, set_bound_notification, get_bound_notification "domain_sep_inv irqs st"
  (wp: domain_sep_inv_triv)

lemma thread_set_tcb_fault_update_neg_cte_wp_at[wp]:
  "thread_set (tcb_fault_update blah) t \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  apply (simp add: thread_set_def)
  apply (wp set_object_wp get_object_wp | simp)+
  apply (case_tac "t = fst slot")
   apply (clarsimp split: kernel_object.splits)
   apply (erule notE)
   apply (erule cte_wp_atE)
    apply (fastforce simp: obj_at_def)
   apply (drule get_tcb_SomeD)
   apply (rule cte_wp_at_tcbI)
     apply simp
    apply assumption
   apply (fastforce simp: tcb_cap_cases_def split: if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

lemma thread_set_tcb_fault_update_domain_sep_inv[wp]:
  "thread_set (tcb_fault_update blah) t \<lbrace>domain_sep_inv irqs st\<rbrace>"
  by (wp domain_sep_inv_triv)

crunch domain_sep_inv[wp]: as_user "domain_sep_inv irqs st"
  (wp: domain_sep_inv_triv)

lemma get_cap_domain_sep_inv_cap:
  "\<lbrace>domain_sep_inv irqs st\<rbrace> get_cap cap \<lbrace>\<lambda>rv s. domain_sep_inv_cap irqs rv\<rbrace>"
  by (wpsimp wp: get_cap_wp simp: cte_wp_at_domain_sep_inv_cap)

crunch domain_sep_inv[wp]: cap_swap_for_delete "domain_sep_inv irqs st"
  (wp: get_cap_domain_sep_inv_cap dxo_wp_weak simp: crunch_simps ignore: cap_swap_ext)

lemma preemption_point_domain_sep_inv[wp]:
  "preemption_point \<lbrace>domain_sep_inv irqs st\<rbrace>"
  by (wp preemption_point_inv | simp)+

context DomainSepInv_1 begin

crunches cap_delete_one
  for domain_sep_inv[wp]: "\<lambda>s. domain_sep_inv irqs  (st :: 'state_ext state) (s :: det_ext state)"
  (wp: mapM_x_wp' unless_wp dxo_wp_weak simp: crunch_simps)

lemma reply_cancel_ipc_domain_sep_inv[wp]:
  "\<lbrace>domain_sep_inv irqs st\<rbrace>
   reply_cancel_ipc t
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs  (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (simp add: reply_cancel_ipc_def)
  apply (wp select_wp)
  apply (rule hoare_strengthen_post[OF thread_set_tcb_fault_update_domain_sep_inv])
  apply auto
  done

crunches finalise_cap
  for domain_sep_inv[wp]: "\<lambda>s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)"
  (wp: dxo_wp_weak)

lemma finalise_cap_domain_sep_inv_cap:
  "\<lbrace>\<lambda>s. domain_sep_inv_cap irqs cap\<rbrace>
   finalise_cap cap b
   \<lbrace>\<lambda>rv s :: det_ext state. domain_sep_inv_cap irqs (fst rv)\<rbrace>"
  including no_pre
  apply (case_tac cap)
             apply (wp | simp add: o_def split del: if_split  split: cap.splits
                       | fastforce split: if_splits simp: domain_sep_inv_cap_def)+
    apply (rule hoare_pre, wp, fastforce)
   apply (rule hoare_pre, simp, wp, fastforce simp: domain_sep_inv_cap_def)
  apply (wpc | wp arch_finalise_cap_rv | simp)+
  done

lemma finalise_cap_returns_NullCap:
  "\<lbrace>\<lambda>s. domain_sep_inv_cap irqs cap\<rbrace>
   finalise_cap cap b
   \<lbrace>\<lambda>rv s :: det_ext state. \<not> irqs \<longrightarrow> snd rv = NullCap\<rbrace>"
  apply (case_tac cap)
  by (wpsimp wp: arch_finalise_cap_rv simp: o_def domain_sep_inv_cap_def split_del: if_split)+

lemma rec_del_domain_sep_inv':
  notes drop_spec_valid[wp_split del] drop_spec_validE[wp_split del]
         rec_del.simps[simp del]
  shows
  "s \<turnstile> \<lbrace>domain_sep_inv irqs st\<rbrace>
       rec_del call
       \<lbrace>\<lambda>rv s. (case call of (FinaliseSlotCall x y) \<Rightarrow> (y \<or> fst rv) \<longrightarrow> \<not> irqs \<longrightarrow> snd rv = NullCap
                                                | _ \<Rightarrow> True) \<and>
               domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>,
       \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  proof (induct s arbitrary: rule: rec_del.induct, simp_all only: rec_del_fails hoare_fail_any)
  case (1 slot exposed s) show ?case
    apply (simp add: split_def rec_del.simps)
    apply (wpsimp wp: empty_slot_domain_sep_inv
                      drop_spec_validE[OF returnOk_wp]
                      drop_spec_validE[OF liftE_wp])
    apply (rule spec_strengthen_postE[OF "1.hyps"])
    apply fastforce
    done
  next
  case (2 slot exposed s) show ?case
    apply (simp add: rec_del.simps split del: if_split)
    apply (rule hoare_pre_spec_validE)
     apply (wp drop_spec_validE[OF returnOk_wp] drop_spec_validE[OF liftE_wp] set_cap_domain_sep_inv
            | simp add: split_def split del: if_split)+
           apply (rule spec_strengthen_postE)
            apply (rule "2.hyps", fastforce+)
          apply (rule drop_spec_validE, (wp preemption_point_inv| simp)+)[1]
         apply simp
         apply (rule spec_strengthen_postE)
          apply (rule "2.hyps", fastforce+)
         apply (wp  finalise_cap_domain_sep_inv_cap get_cap_wp
                   finalise_cap_returns_NullCap
                   drop_spec_validE[OF liftE_wp] set_cap_domain_sep_inv
               |simp split del: if_split
               |wp (once) hoare_drop_imps)+
    apply (blast dest: cte_wp_at_domain_sep_inv_cap)
    done
  next
  case (3 ptr bits n slot s) show ?case
    apply (simp add: rec_del.simps)
    apply (wp drop_spec_validE[OF returnOk_wp] drop_spec_validE[OF liftE_wp])
    apply (rule hoare_pre_spec_validE)
    apply (wp drop_spec_validE[OF assertE_wp])
    apply (fastforce)
    done
  next
  case (4 ptr bits n slot s) show ?case
    apply (simp add: rec_del.simps)
    apply (wp drop_spec_validE[OF returnOk_wp] drop_spec_validE[OF liftE_wp] set_cap_domain_sep_inv
              drop_spec_validE[OF assertE_wp] get_cap_wp | simp)+
    apply (rule spec_strengthen_postE[OF "4.hyps"])
     apply (simp add: returnOk_def return_def)
    apply (clarsimp simp: domain_sep_inv_cap_def)
    done
  qed

lemma rec_del_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st\<rbrace>
   rec_del call
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (rule validE_valid)
  apply (rule use_spec)
  apply (rule spec_strengthen_postE[OF rec_del_domain_sep_inv'])
  by fastforce

crunches cap_delete
  for domain_sep_inv[wp]: "\<lambda>s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)"

lemma cap_revoke_domain_sep_inv':
  notes drop_spec_valid[wp_split del] drop_spec_validE[wp_split del]
  shows
  "s \<turnstile> \<lbrace> domain_sep_inv irqs st\<rbrace>
   cap_revoke slot
   \<lbrace> \<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>, \<lbrace> \<lambda>_. domain_sep_inv irqs st\<rbrace>"
  proof(induct rule: cap_revoke.induct[where ?a1.0=s])
  case (1 slot s)
  show ?case
  apply (subst cap_revoke.simps)
  apply (rule hoare_pre_spec_validE)
   apply (wp "1.hyps")
           apply (wp spec_valid_conj_liftE2 | simp)+
           apply (wp drop_spec_validE[OF valid_validE[OF preemption_point_domain_sep_inv]]
                    drop_spec_validE[OF valid_validE[OF cap_delete_domain_sep_inv]]
                    drop_spec_validE[OF assertE_wp] drop_spec_validE[OF returnOk_wp]
                    drop_spec_validE[OF liftE_wp] select_wp
                | simp | wp (once) hoare_drop_imps)+
  done
qed

lemmas cap_revoke_domain_sep_inv[wp] = use_spec(2)[OF cap_revoke_domain_sep_inv']

end

lemma cap_move_cte_wp_at_other:
  "\<lbrace>cte_wp_at P slot and K (slot \<noteq> dest_slot \<and> slot \<noteq> src_slot)\<rbrace>
   cap_move cap src_slot dest_slot
   \<lbrace>\<lambda>_. cte_wp_at P slot\<rbrace>"
  unfolding cap_move_def
  by (wpsimp wp: set_cdt_cte_wp_at set_cap_cte_wp_at' dxo_wp_weak static_imp_wp set_original_wp)

lemma cte_wp_at_weak_derived_ReplyCap:
  "cte_wp_at ((=) (ReplyCap x False R)) slot s
   \<Longrightarrow> cte_wp_at (weak_derived (ReplyCap x False R)) slot s"
  apply (erule cte_wp_atE)
   apply (rule cte_wp_at_cteI)
      apply assumption
     apply assumption
    apply assumption
   apply simp
  apply (rule cte_wp_at_tcbI)
  apply auto
  done

lemma thread_set_tcb_registers_caps_merge_default_tcb_neg_cte_wp_at[wp]:
  "thread_set (tcb_registers_caps_merge default_tcb) word \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  unfolding thread_set_def
  apply (wp set_object_wp | simp)+
  apply (case_tac "word = fst slot")
   apply (clarsimp split: kernel_object.splits)
   apply (erule notE)
   apply (erule cte_wp_atE)
    apply (fastforce simp: obj_at_def)
   apply (drule get_tcb_SomeD)
   apply (rule cte_wp_at_tcbI)
     apply simp
    apply assumption
   apply (clarsimp simp: tcb_cap_cases_def tcb_registers_caps_merge_def split: if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

lemma thread_set_tcb_registers_caps_merge_default_tcb_domain_sep_inv[wp]:
  "thread_set (tcb_registers_caps_merge default_tcb) word \<lbrace>domain_sep_inv irqs st\<rbrace>"
  by (wp domain_sep_inv_triv)

lemma cancel_badged_sends_domain_sep_inv[wp]:
  "cancel_badged_sends epptr badge \<lbrace>domain_sep_inv irqs st\<rbrace>"
  apply (simp add: cancel_badged_sends_def)
  apply (wpsimp wp: dxo_wp_weak mapM_wp | simp add: filterM_mapM | wp (once) hoare_drop_imps)+
  done

lemma create_cap_domain_sep_inv[wp]:
  "create_cap tp sz p dev slot \<lbrace>domain_sep_inv irqs st\<rbrace>"
  apply (simp add: create_cap_def)
  apply (rule hoare_pre)
  apply (wp set_cap_domain_sep_inv dxo_wp_weak | wpc | simp)+
  apply clarsimp
  apply (case_tac tp, simp_all add: domain_sep_inv_cap_def)
  done

lemma retype_region_neg_cte_wp_at_not_domain_sep_inv_cap:
  "retype_region  base n sz ty dev \<lbrace>\<lambda>s. \<not> cte_wp_at (not domain_sep_inv_cap irqs) slot s\<rbrace>"
  apply (rule hoare_pre)
   apply (simp only: retype_region_def retype_addrs_def foldr_upd_app_if fun_app_def K_bind_def)
   apply (wp dxo_wp_weak | simp)+
  apply clarsimp
  apply (case_tac "fst slot \<in> (\<lambda>p. ptr_add base (p * 2 ^ obj_bits_api ty sz)) ` {0..<n}")
   apply clarsimp
   apply (erule cte_wp_atE)
    apply (clarsimp simp: default_object_def empty_cnode_def
                   split: apiobject_type.splits if_splits)
   apply (clarsimp simp: default_object_def default_tcb_def tcb_cap_cases_def
                  split: apiobject_type.splits if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_tcbI cte_wp_at_cteI)
  done

lemma retype_region_domain_sep_inv[wp]:
  "retype_region base n sz tp dev \<lbrace>domain_sep_inv irqs st\<rbrace>"
  apply (rule domain_sep_inv_wp[where P="\<top>" and R="\<top>", simplified])
   apply (rule retype_region_neg_cte_wp_at_not_domain_sep_inv_cap)
  apply wp
  done

lemma domain_sep_inv_cap_UntypedCap[simp]:
  "domain_sep_inv_cap irqs (UntypedCap dev base sz n)"
  by (simp add: domain_sep_inv_cap_def)

crunch domain_sep_inv[wp]: delete_objects "domain_sep_inv irqs st"
  (wp: domain_sep_inv_detype_lift)

context DomainSepInv_1 begin

crunches finalise_slot, invoke_untyped, send_signal
  for domain_sep_inv[wp]: "\<lambda>s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)"
  (wp: crunch_wps mapME_x_inv_wp simp: crunch_simps mapM_x_def_bak)

lemma invoke_cnode_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_cnode_inv ci\<rbrace>
   invoke_cnode ci
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  unfolding invoke_cnode_def
  apply (case_tac ci)
        apply (wp cap_insert_domain_sep_inv cap_move_domain_sep_inv | simp split del: if_split)+
     apply (rule hoare_pre)
      apply (wpsimp wp: cap_move_domain_sep_inv cap_move_cte_wp_at_other get_cap_wp,
             blast dest: cte_wp_at_weak_derived_domain_sep_inv_cap)+
   apply (wpsimp wp: cap_move_domain_sep_inv get_cap_wp)
   apply (fastforce dest:  cte_wp_at_weak_derived_ReplyCap)
  apply (wp | simp | wpc | rule hoare_pre)+
  done

end

lemma perform_page_invocation_domain_sep_inv_get_cap_helper:
  "\<lbrace>\<top>\<rbrace> get_cap blah \<lbrace>\<lambda>rv s. domain_sep_inv_cap irqs (ArchObjectCap (F rv))\<rbrace>"
  apply (simp add: domain_sep_inv_cap_def)
  apply (rule wp_post_taut)
  done

lemma set_object_tcb_context_update_neg_cte_wp_at:
  "\<lbrace>\<lambda>s. \<not> cte_wp_at P slot s \<and> obj_at ((=) (TCB tcb)) ptr s\<rbrace>
   set_object ptr (TCB (tcb\<lparr>tcb_arch := arch_tcb_context_set X (arch_tcb tcb)\<rparr>))
   \<lbrace>\<lambda>_ s. \<not> cte_wp_at P slot s\<rbrace>"
  apply (wp set_object_wp)
  apply clarsimp
  apply (case_tac "ptr = fst slot")
   apply (erule cte_wp_atE)
    apply (fastforce simp: obj_at_def)
   apply (erule notE)
   apply (clarsimp simp: obj_at_def)
   apply (rule cte_wp_at_tcbI)
      apply simp
     apply (fastforce)
    apply (fastforce simp: tcb_cap_cases_def split: if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

lemma set_object_tcb_arch_update_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and obj_at ((=) (TCB tcb)) ptr\<rbrace>
   set_object ptr (TCB (tcb\<lparr>tcb_arch := arch\<rparr>))
   \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  apply (wp set_object_wp)
  apply (simp add: domain_sep_inv_def fun_upd_def cte_wp_at_caps_of_state)
  apply (fastforce simp: caps_of_state_after_update obj_at_def tcb_cap_cases_def)
  done

lemma dxo_domain_sep_inv[wp]:
  "do_extended_op eop \<lbrace>domain_sep_inv irqs st\<rbrace>"
  by (simp | wp dxo_wp_weak)+

crunch domain_sep_inv[wp]: copy_mrs, set_message_info, set_mrs "domain_sep_inv irqs st"
  (wp: crunch_wps set_object_tcb_arch_update_domain_sep_inv simp: crunch_simps ignore: set_object)

lemma cap_insert_domain_sep_inv':
  "\<lbrace>domain_sep_inv irqs st and K (domain_sep_inv_cap irqs cap)\<rbrace>
   cap_insert cap slot dest_slot
   \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  apply (simp add: cap_insert_def)
  apply (wp set_cap_domain_sep_inv get_cap_wp dxo_wp_weak | simp split del: if_split)+
  done

lemma domain_sep_inv_cap_max_free_index_update[simp]:
  "domain_sep_inv_cap irqs (max_free_index_update cap) =
   domain_sep_inv_cap irqs cap"
  by (simp add: max_free_index_def free_index_update_def split: cap.splits)

lemma domain_sep_inv_cap_ArchObjectCap[simp]:
  "domain_sep_inv_cap irqs (ArchObjectCap arch_cap)"
  by (simp add: domain_sep_inv_cap_def)

locale DomainSepInv_2 = DomainSepInv_1 state_ext_t
  for state_ext_t :: "'state_ext :: state_ext itself" +
  assumes arch_switch_to_thread_domain_sep_inv[wp]:
    "arch_switch_to_thread t \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs (st :: 'state_ext state) s\<rbrace>"
  and arch_switch_to_idle_thread_domain_sep_inv[wp]:
    "arch_switch_to_idle_thread \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and arch_activate_idle_thread_domain_sep_inv[wp]:
    "arch_activate_idle_thread t \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and arch_mask_irq_signal_domain_sep_inv[wp]:
    "arch_mask_irq_signal irq \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and arch_derive_cap_domain_sep_inv[wp]:
    "\<lbrace>\<top>\<rbrace> arch_derive_cap acap \<lbrace>\<lambda>rv s :: det_ext state. domain_sep_inv_cap irqs rv\<rbrace>,-"
  and arch_post_modify_registers_domain_sep_inv[wp]:
    "arch_post_modify_registers cur x31 \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and arch_perform_invocation_domain_sep_inv[wp]:
    "\<lbrace>domain_sep_inv irqs st and valid_arch_inv ai\<rbrace>
     arch_perform_invocation ai
     \<lbrace>\<lambda>_ s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and arch_invoke_irq_handler_domain_sep_inv[wp]:
    "arch_invoke_irq_handler ihi \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and arch_invoke_irq_control_domain_sep_inv:
    "\<lbrace>domain_sep_inv irqs st and arch_irq_control_inv_valid ivk\<rbrace>
     arch_invoke_irq_control ivk
     \<lbrace>\<lambda>_ s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and handle_arch_fault_reply_domain_sep_inv[wp]:
    "handle_arch_fault_reply vmf thread x y \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and handle_hypervisor_fault_domain_sep_inv[wp]:
    "handle_hypervisor_fault t hf_t \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and handle_vm_fault_domain_sep_inv[wp]:
    "handle_vm_fault t vmf_t \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
  and handle_reserved_irq_domain_sep_inv[wp]:
    "handle_reserved_irq irq \<lbrace>\<lambda>s :: det_ext state. domain_sep_inv irqs st s\<rbrace>"
begin

(* when i is AckIRQ the preconditions here contradict each other, which
   is why this lemma is true *)
lemma invoke_irq_handler_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and irq_handler_inv_valid i\<rbrace>
   invoke_irq_handler i
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (case_tac i)
    apply (wp cap_insert_domain_sep_inv' | simp)+
   apply (rename_tac irq cap cslot_ptr s)
   apply (case_tac cap, simp_all add: domain_sep_inv_cap_def)[1]
  apply (wp | clarsimp)+
  done

(* similarly, the preconditions here tend to contradict one another *)
lemma invoke_control_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and irq_control_inv_valid i\<rbrace>
    invoke_irq_control i
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  including no_pre
  apply (case_tac i)
   apply (case_tac irqs)
    apply (wp cap_insert_domain_sep_inv' | simp )+
    apply (simp add: set_irq_state_def, wp, simp)
    apply (fastforce simp: domain_sep_inv_def domain_sep_inv_cap_def)
   apply (fastforce simp: valid_def domain_sep_inv_def)
  apply simp
  apply (wp arch_invoke_irq_control_domain_sep_inv)
  done

lemma derive_cap_domain_sep_inv_cap:
  "\<lbrace>\<lambda>s. domain_sep_inv_cap irqs cap\<rbrace>
   derive_cap slot cap
   \<lbrace>\<lambda>rv s :: det_ext state. domain_sep_inv_cap irqs rv\<rbrace>,-"
  apply (simp add: derive_cap_def)
  apply (rule hoare_pre)
  apply (wp | wpc | simp add: )+
  apply auto
  done

end

crunch domain_sep_inv[wp]: receive_signal, complete_signal "domain_sep_inv irqs st"

lemma domain_sep_inv_cap_ReplyCap[simp]:
  "domain_sep_inv_cap irqs (ReplyCap param_a param_b param_c)"
  by (simp add: domain_sep_inv_cap_def)

lemma setup_caller_cap_domain_sep_inv[wp]:
  "\<lbrace>domain_sep_inv irqs st\<rbrace>
   setup_caller_cap a b c
   \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  apply (simp add: setup_caller_cap_def split del:if_split)
  apply (wp cap_insert_domain_sep_inv' | simp)+
  done

lemma transfer_caps_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_objs and valid_mdb
        and (\<lambda> s. (\<forall>x\<in>set caps.
               s \<turnstile> fst x) \<and>
              (\<forall>x\<in>set caps.
               cte_wp_at
                (\<lambda>cp. fst x \<noteq> NullCap \<longrightarrow>
                      cp = fst x)
                (snd x) s \<and>
               real_cte_at (snd x) s))\<rbrace>
  transfer_caps mi caps endpoint receiver receive_buffer
  \<lbrace>\<lambda>_. domain_sep_inv irqs st\<rbrace>"
  unfolding transfer_caps_def
  apply (wpsimp wp: transfer_caps_loop_pres_dest cap_insert_domain_sep_inv
                    hoare_vcg_all_lift hoare_vcg_imp_lift)
  apply (fastforce elim: cte_wp_at_weakenE)
  done

context DomainSepInv_2 begin

lemma do_normal_transfer_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_objs and valid_mdb\<rbrace>
   do_normal_transfer sender send_buffer ep badge grant receiver recv_buffer
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  unfolding do_normal_transfer_def
  by (wp transfer_caps_domain_sep_inv hoare_vcg_ball_lift lec_valid_cap' | simp)+

crunches do_fault_transfer
  for domain_sep_inv[wp]: "\<lambda>s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)"

lemma do_ipc_transfer_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_objs and valid_mdb\<rbrace>
   do_ipc_transfer sender ep badge grant receiver
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  unfolding do_ipc_transfer_def
  apply (wp do_normal_transfer_domain_sep_inv hoare_vcg_all_lift | wpc | wp (once) hoare_drop_imps)+
  apply clarsimp
  done

lemma send_ipc_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_objs and valid_mdb and
    sym_refs \<circ> state_refs_of\<rbrace>
   send_ipc block call badge can_grant can_grant_reply thread epptr
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  unfolding send_ipc_def
  apply (wp setup_caller_cap_domain_sep_inv hoare_vcg_if_lift | wpc | simp split del:if_split)+
        apply (rule_tac Q="\<lambda> r s. domain_sep_inv irqs st s" in hoare_strengthen_post)
         apply (wp do_ipc_transfer_domain_sep_inv dxo_wp_weak | wpc | simp)+
     apply (wp (once) hoare_drop_imps)
     apply (wp get_simple_ko_wp)+
  apply clarsimp
  apply (fastforce simp: valid_objs_def valid_obj_def obj_at_def ep_q_refs_of_def
                         a_type_def ep_redux_simps neq_Nil_conv valid_ep_def
                   elim: ep_queued_st_tcb_at split: list.splits)
  done

lemma receive_ipc_base_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_objs and valid_mdb and
    sym_refs \<circ> state_refs_of and ko_at (Endpoint ep) epptr \<rbrace>
   receive_ipc_base aag receiver ep epptr rights is_blocking
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (clarsimp cong: endpoint.case_cong thread_get_def get_thread_state_def)
  apply (wp setup_caller_cap_domain_sep_inv dxo_wp_weak | wpc | simp split del: if_split)+
        apply (rule_tac Q="\<lambda> r s. domain_sep_inv irqs st s" in hoare_strengthen_post)
         apply (wp do_ipc_transfer_domain_sep_inv hoare_vcg_all_lift | wpc | simp)+
     apply (wpsimp wp: hoare_vcg_imp_lift[OF set_simple_ko_get_tcb, unfolded disj_not1]
                       hoare_vcg_all_lift get_simple_ko_wp
                 simp: a_type_def do_nbrecv_failed_transfer_def)+
  apply (fastforce simp: valid_objs_def valid_obj_def obj_at_def
                         ep_redux_simps neq_Nil_conv valid_ep_def
                  split: list.splits)
  done

lemma receive_ipc_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_objs and valid_mdb and sym_refs \<circ> state_refs_of\<rbrace>
   receive_ipc receiver cap is_blocking
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  unfolding receive_ipc_def
  apply (simp add: receive_ipc_def split: cap.splits, clarsimp)
  apply (rule hoare_seq_ext[OF _ get_simple_ko_sp])
  apply (rule hoare_seq_ext[OF _ gbn_sp])
  apply (case_tac ntfnptr, simp)
   apply (wp receive_ipc_base_domain_sep_inv get_simple_ko_wp | simp split: if_split option.splits)+
  done

lemma send_fault_ipc_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_objs and sym_refs \<circ> state_refs_of
                           and valid_mdb and K (valid_fault fault)\<rbrace>
   send_fault_ipc thread fault
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  unfolding send_fault_ipc_def
  apply (rule hoare_gen_asm)+
  apply (wp send_ipc_domain_sep_inv thread_set_valid_objs thread_set_tcb_fault_update_valid_mdb
            thread_set_refs_trivial thread_set_obj_at_impossible hoare_vcg_ex_lift
         | wpc | simp add: Let_def split_def lookup_cap_def valid_tcb_fault_update)+
     apply (wpe get_cap_inv[where P="domain_sep_inv irqs st and valid_objs and valid_mdb
                                                            and sym_refs o state_refs_of"])
     apply (wp | simp)+
  done

crunches do_reply_transfer, handle_fault, reply_from_kernel, restart
  for domain_sep_inv[wp]: "\<lambda>s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)"
  (wp: handle_arch_fault_reply_domain_sep_inv dxo_wp_weak crunch_wps ignore: thread_set)

end

crunch domain_sep_inv[wp]: setup_reply_master "domain_sep_inv irqs st"
  (wp: crunch_wps simp: crunch_simps)

lemma thread_set_tcb_ipc_buffer_update_neg_cte_wp_at[wp]:
  "thread_set (tcb_ipc_buffer_update f) t \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  unfolding thread_set_def
  apply (wp set_object_wp | simp)+
  apply (case_tac "t = fst slot")
   apply (clarsimp split: kernel_object.splits)
   apply (erule notE)
   apply (erule cte_wp_atE)
    apply (fastforce simp: obj_at_def)
   apply (drule get_tcb_SomeD)
   apply (rule cte_wp_at_tcbI)
     apply simp
    apply assumption
   apply (fastforce simp: tcb_cap_cases_def split: if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

lemma thread_set_tcb_ipc_buffer_update_domain_sep_inv[wp]:
  "thread_set (tcb_ipc_buffer_update f) t \<lbrace>domain_sep_inv irqs st\<rbrace>"
  by (rule domain_sep_inv_triv; wp)

lemma thread_set_tcb_fault_handler_update_neg_cte_wp_at[wp]:
  "thread_set (tcb_fault_handler_update f) t \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  unfolding thread_set_def
  apply (wp set_object_wp | simp)+
  apply (case_tac "t = fst slot")
   apply (clarsimp split: kernel_object.splits)
   apply (erule notE)
   apply (erule cte_wp_atE)
    apply (fastforce simp: obj_at_def)
   apply (drule get_tcb_SomeD)
   apply (rule cte_wp_at_tcbI)
     apply simp
    apply assumption
   apply (fastforce simp: tcb_cap_cases_def split: if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

lemma thread_set_tcb_fault_handler_update_domain_sep_inv[wp]:
  "thread_set (tcb_fault_handler_update f) t \<lbrace>domain_sep_inv irqs st\<rbrace>"
  by (rule domain_sep_inv_triv; wp)

lemma thread_set_tcb_tcb_mcpriority_update_neg_cte_wp_at[wp]:
  "thread_set (tcb_mcpriority_update f) t \<lbrace>\<lambda>s. \<not> cte_wp_at P slot s\<rbrace>"
  unfolding thread_set_def
  apply (wp set_object_wp | simp)+
  apply (case_tac "t = fst slot")
   apply (clarsimp split: kernel_object.splits)
   apply (erule notE)
   apply (erule cte_wp_atE)
    apply (fastforce simp: obj_at_def)
   apply (drule get_tcb_SomeD)
   apply (rule cte_wp_at_tcbI)
     apply simp
    apply assumption
   apply (fastforce simp: tcb_cap_cases_def split: if_splits)
  apply (fastforce elim: cte_wp_atE intro: cte_wp_at_cteI cte_wp_at_tcbI)
  done

lemma thread_set_tcb_tcp_mcpriority_update_domain_sep_inv[wp]:
  "thread_set (tcb_mcpriority_update f) t \<lbrace>domain_sep_inv irqs st\<rbrace>"
  by (rule domain_sep_inv_triv; wp)

lemma same_object_as_domain_sep_inv_cap:
  "\<lbrakk> same_object_as a cap; domain_sep_inv_cap irqs cap \<rbrakk>
     \<Longrightarrow> domain_sep_inv_cap irqs a"
  apply (case_tac a, simp_all add: same_object_as_def domain_sep_inv_cap_def)
  apply (case_tac cap, simp_all)
  done

lemma checked_cap_insert_domain_sep_inv:
  "check_cap_at a b (check_cap_at c d (cap_insert a b e)) \<lbrace>domain_sep_inv irqs st\<rbrace>"
  apply (wp get_cap_wp cap_insert_domain_sep_inv' | simp add: check_cap_at_def)+
  apply clarsimp
  apply (drule_tac cap=cap in cte_wp_at_domain_sep_inv_cap)
   apply assumption
  apply (erule (1) same_object_as_domain_sep_inv_cap)
  done

crunch domain_sep_inv[wp]: bind_notification,reschedule_required "domain_sep_inv irqs st"

lemma set_mcpriority_domain_sep_inv[wp]:
  "set_mcpriority tcb_ref mcp \<lbrace>domain_sep_inv irqs st\<rbrace>"
  unfolding set_mcpriority_def by wp

lemma invoke_domain_domain_set_inv:
  "invoke_domain word1 word2 \<lbrace>domain_sep_inv irqs st\<rbrace>"
  apply (simp add: invoke_domain_def)
  apply (wp dxo_wp_weak | simp)+
  done

context DomainSepInv_2 begin

lemma invoke_tcb_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and tcb_inv_wf tinv\<rbrace>
   invoke_tcb tinv
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (case_tac tinv)
         apply ((wp restart_domain_sep_inv hoare_vcg_if_lift mapM_x_wp[OF _ subset_refl]
                 | wpc | simp split del: if_split add: check_cap_at_def | clarsimp)+)[3]
      defer
      apply ((wp | simp)+)[2]
    (* just NotificationControl and ThreadControl left *)
    apply (rename_tac option)
    apply (case_tac option)
     apply  ((wp | simp)+)[1]
    apply (simp add: split_def cong: option.case_cong)
    apply (wp checked_cap_insert_domain_sep_inv hoare_vcg_all_lift_R hoare_vcg_all_lift
              hoare_vcg_const_imp_lift_R cap_delete_domain_sep_inv cap_delete_deletes
              dxo_wp_weak cap_delete_valid_cap cap_delete_cte_at static_imp_wp
           | wpc | strengthen
           | simp add: option_update_thread_def emptyable_def tcb_cap_cases_def
                       tcb_cap_valid_def tcb_at_st_tcb_at
                  del: set_priority_extended.dxo_eq)+
  done

lemma perform_invocation_domain_sep_inv':
  "\<lbrace>domain_sep_inv irqs st and valid_invocation iv and valid_objs
                           and valid_mdb and sym_refs \<circ> state_refs_of\<rbrace>
   perform_invocation block call iv
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (case_tac iv)
           apply (wp send_ipc_domain_sep_inv invoke_tcb_domain_sep_inv
                     invoke_cnode_domain_sep_inv invoke_control_domain_sep_inv
                     invoke_irq_handler_domain_sep_inv arch_perform_invocation_domain_sep_inv
                     invoke_domain_domain_set_inv
                  | simp add: split_def | blast)+
  done

lemma perform_invocation_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and valid_invocation iv and invs\<rbrace>
   perform_invocation block call iv
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (rule hoare_weaken_pre[OF perform_invocation_domain_sep_inv'])
  apply auto
  done

lemma handle_invocation_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and invs and ct_active\<rbrace>
   handle_invocation calling blocking
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (simp add: handle_invocation_def ts_Restart_case_helper
                   split_def liftE_liftM_liftME liftME_def bindE_assoc)
  apply (wp syscall_valid perform_invocation_domain_sep_inv set_thread_state_runnable_valid_sched
         | simp split del: if_split)+
        apply (rule_tac E="\<lambda>ft. domain_sep_inv irqs st and valid_objs and sym_refs \<circ> state_refs_of
                                                       and valid_mdb and (\<lambda>y. valid_fault ft)"
                    and R="Q" and Q=Q for Q in hoare_post_impErr)
         apply (wp | simp | clarsimp)+
      apply (rule_tac E="\<lambda>ft. domain_sep_inv irqs st and valid_objs and sym_refs \<circ> state_refs_of and
                              valid_mdb and (\<lambda>y. valid_fault (CapFault x False ft))"
                  and R="Q" and Q=Q for Q in hoare_post_impErr)
        apply (wp lcs_ex_cap_to2 | clarsimp)+
  apply (auto intro: st_tcb_ex_cap simp: ct_in_state_def)
  done

lemma handle_send_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and invs and ct_active\<rbrace>
   handle_send a
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (simp add: handle_send_def)
  apply (wp handle_invocation_domain_sep_inv)
  done

lemma handle_call_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and invs and ct_active\<rbrace>
   handle_call
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (simp add: handle_call_def)
  apply (wp handle_invocation_domain_sep_inv)
  done

lemma handle_reply_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and invs\<rbrace>
   handle_reply
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (simp add: handle_reply_def)
  apply (wp get_cap_wp | wpc)+
  apply auto
  done

crunches delete_caller_cap
  for domain_sep_inv[wp]: "\<lambda>s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)"

end

(* FIXME: clagged from Syscall_AC *)
lemma lookup_slot_for_thread_cap_fault:
  "\<lbrace>invs\<rbrace> lookup_slot_for_thread t s -, \<lbrace>\<lambda>f s. valid_fault (CapFault x y f)\<rbrace>"
  apply (simp add: lookup_slot_for_thread_def)
  apply (wp resolve_address_bits_valid_fault2)
  apply clarsimp
  apply (erule (1) invs_valid_tcb_ctable)
  done

lemma domain_sep_inv_cur_thread_update[simp]:
  "domain_sep_inv irqs st (s\<lparr>cur_thread := X\<rparr>) = domain_sep_inv irqs st s"
  apply (simp add: domain_sep_inv_def)
  done

lemma (in is_extended') domain_sep_inv[wp]: "I (domain_sep_inv irqs st)" by (rule lift_inv, simp)

context DomainSepInv_2 begin

lemma handle_recv_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and invs\<rbrace>
   handle_recv is_blocking
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (simp add: handle_recv_def Let_def lookup_cap_def split_def)
  apply (wp hoare_vcg_all_lift lookup_slot_for_thread_cap_fault receive_ipc_domain_sep_inv
            delete_caller_cap_domain_sep_inv get_cap_wp get_simple_ko_wp
         | wpc | simp
         | (rule_tac Q="\<lambda>rv. invs and (\<lambda>s. cur_thread s = thread)" in hoare_strengthen_post, wp,
            clarsimp simp: invs_valid_objs invs_sym_refs))+
     apply (rule_tac Q'="\<lambda>r s. domain_sep_inv irqs st s \<and> invs s \<and>
                               tcb_at thread s \<and> thread = cur_thread s" in hoare_post_imp_R)
      apply wp
     apply ((clarsimp simp add: invs_valid_objs invs_sym_refs
             | intro impI allI conjI
             | rule cte_wp_valid_cap caps_of_state_cteD
             | fastforce simp:  valid_fault_def)+)[1]
    apply (wp delete_caller_cap_domain_sep_inv | simp add: split_def cong: conj_cong)+
    apply (wp | simp add: invs_valid_objs invs_mdb invs_sym_refs tcb_at_invs)+
  done

crunches handle_interrupt, activate_thread, choose_thread
  for domain_sep_inv[wp]: "\<lambda>s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)"
  (wp: dxo_wp_weak crunch_wps)

lemma handle_event_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and invs and (\<lambda>s. ev \<noteq> Interrupt \<longrightarrow> ct_active s)\<rbrace>
   handle_event ev
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state)\<rbrace>"
  apply (case_tac ev, simp_all)
      apply (rule hoare_pre)
       apply (wpsimp wp: handle_send_domain_sep_inv handle_call_domain_sep_inv
                         handle_recv_domain_sep_inv handle_reply_domain_sep_inv hy_inv
              | simp add: invs_valid_objs invs_mdb invs_sym_refs valid_fault_def)+
     apply (rule_tac E="\<lambda>rv s. domain_sep_inv irqs (st :: 'state_ext state) (s :: det_ext state) \<and>
                               invs s \<and> valid_fault rv" and R="Q" and Q=Q for Q in hoare_post_impErr)
     apply (wp | simp add: invs_valid_objs invs_mdb invs_sym_refs valid_fault_def | auto)+
  done

lemma schedule_domain_sep_inv:
  "(schedule :: (unit,det_ext) s_monad) \<lbrace>domain_sep_inv irqs (st :: 'state_ext state)\<rbrace>"
  apply (simp add: schedule_def allActiveTCBs_def)
  apply (wp add: alternative_wp select_wp  guarded_switch_to_lift hoare_drop_imps
            del: ethread_get_wp
         | wpc | clarsimp simp: get_thread_state_def thread_get_def trans_state_update'[symmetric]
                                schedule_choose_new_thread_def)+
  done

lemma call_kernel_domain_sep_inv:
  "\<lbrace>domain_sep_inv irqs st and invs and (\<lambda>s. ev \<noteq> Interrupt \<longrightarrow> ct_active s)\<rbrace>
   call_kernel ev :: (unit,det_ext) s_monad
   \<lbrace>\<lambda>_ s. domain_sep_inv irqs (st :: 'state_ext state) s\<rbrace>"
  apply (simp add: call_kernel_def)
  apply (wp handle_interrupt_domain_sep_inv handle_event_domain_sep_inv schedule_domain_sep_inv
         | simp)+
  done

end

end


section \<open>Specification Commands\<close>
theory IMP2_Specification
imports IMP2_Basic_Simpset IMP2_Program_Analysis IMP2_Var_Postprocessor IMP2_Var_Abs
keywords "ensures"
     and "returns" "variant" "relation"
     and "program_spec" :: thy_goal
     and "procedure_spec" :: thy_goal
     and "recursive_spec" :: thy_goal
begin

subsection \<open>Abstraction over Program Variables\<close>

lemmas [named_ss vcg_bb cong] = refl[of "ANNOTATION _"]

ML \<open> structure IMP_Annotations
  = struct
    fun strip_annotations ctxt =
      Local_Defs.unfold ctxt (Named_Theorems.get ctxt @{named_theorems vcg_annotation_defs})

    fun strip_annotations_term ctxt =
      Thm.cterm_of ctxt #> Drule.mk_term #>
      strip_annotations ctxt #>
      Drule.dest_term #> Thm.term_of

    fun mk_ANNOTATION t = let val T=fastype_of t in
      Const (@{const_name ANNOTATION}, T --> T)$t end

    fun dest_ANNOTATION (Const (@{const_name ANNOTATION}, _)$t) = t
      | dest_ANNOTATION t = raise TERM("dest_ANNOTATION", [t]);

    (* Provides a context for reading the term, and a postprocessing function *)
    type term_annot_reader = Proof.context * (term -> term)
    fun gen_read_ta rd ((ctxt,post):term_annot_reader) src = rd ctxt src |> mk_BB_PROTECT |> post
    val read_ta_from_cartouche = gen_read_ta Term_Annot.read_term

    (* Annotations *)

    type cmd_annotation_readers = {
      rel_rd : term_annot_reader,
      variant_rd : term_annot_reader,
      invar_rd : term_annot_reader,
      com_post: term -> term
    }

    fun gen_interpret_annotations (annot_readers : cmd_annotation_readers) ctxt prog_t = let

      (* TODO/FIXME: Terms are read independently, which will cause too generic types to be inferred *)

      val read_invar = read_ta_from_cartouche (#invar_rd annot_readers)
      val read_variant = read_ta_from_cartouche (#variant_rd annot_readers)
      val read_rel = read_ta_from_cartouche (#rel_rd annot_readers)

      val mpt = map_types (map_type_tfree (K dummyT))

      fun interp_while_annot (t,@{syntax_const "_invariant_annotation"}) (R,V,I) = (R,V,read_invar t :: I)
        | interp_while_annot (t,@{syntax_const "_variant_annotation"})   (R,V,I) = (R,read_variant t :: V,I)
        | interp_while_annot (t,@{syntax_const "_relation_annotation"})  (R,V,I) = (read_rel t :: R,V,I)
        | interp_while_annot (_,ty) _ = error ("Unknown annotation type for while loop: " ^ ty)

      fun interp_while_annots annots = let
        val annots = HOLogic.strip_tuple annots
          |> map (apsnd (dest_Const #> fst) o Term_Annot.dest_annotated_term)

        val (Rs,Vs,Is) = fold interp_while_annot annots ([],[],[])

        val _ = length Rs > 1 andalso error "Multiple relation annotations to loop"
        val _ = length Vs > 1 andalso error "Multiple variant annotations to loop"
        val _ = length Is > 1 andalso error "Multiple invariants not yet supported. Combine them into one"

        local val m = map mk_ANNOTATION in
          val (Rs, Vs, Is) = (m Rs, m Vs, m Is)
        end

      in
        case (Rs,Vs,Is) of
          ([],[],[]) => mpt @{const While}
        | ([],[],[I]) => mpt @{const WHILE_annotI} $ I
        | ([],[V],[I]) => mpt @{const WHILE_annotVI} $ V $ I
        | ([R],[V],[I]) => mpt @{const WHILE_annotRVI ('a)} $ R $ V $ I
        | _ => error "Illegal combination of annotations to while loop. The legal ones are: None, I, VI, RVI"

      end

      fun interp (Const (@{const_abbrev While_Annot},_)$annots) = interp_while_annots annots
        | interp (a$b) = interp a $ interp b
        | interp (Abs (x,T,t)) = Abs (x,T,interp t)
        | interp t = t

      val prog_t = interp prog_t

      (* All terms have been abstracted over vinfo, and over the variables of v0info.
        Now, the whole term is valid wrt vinfo0/ctxt0, which contains declarations
        of variables0 and state0
        *** TODO: Ideally, one would like to check teh term in a context that does not include
              variables, but only state decl!
      *)
      val prog_t = Syntax.check_term ctxt prog_t

      (* We now also abstract over state0, to make the program a function from state0 to an (annotated) command *)
      val res = (#com_post annot_readers) prog_t

      (*
      (* Alternatively, we strip the annotations from the program. This should also remove state0! *)
      val prog_t = strip_annotations_term ctxt prog_t
      *)

    in
      res
    end

    (*
      Make a reader according to a list of program variable abstractions.
      Each list item has the form (vars,sfx,full), where vars is a list of IMP variables,
      sfx is a suffix to be appended to the Isabelle variables, and full indicates whether the
      abstraction over the state should be done immediately, or delayed.

      The result is a reader, and a second term transformation to do the delayed abstractions.
    *)
    fun gen_mk_reader rds ctxt = let
      fun mk [] ctxt = ((ctxt, I), I)
        | mk ((vars,sfx,full)::rds) ctxt = let
            val vars = IMP_Parser.merge_variables vars
            val (vinfo,ctxt) = Program_Variables.declare_variables vars sfx ctxt
            val ((ctxt,pps1),pps2) = mk rds ctxt

            val absv = Program_Variables.abstract_vars vinfo
            val abss = Program_Variables.abstract_state vinfo

            val (pps1,pps2) = if full then (absv #> pps1 #> abss,pps2) else (absv #> pps1, pps2 #> abss)
          in
            ((ctxt,pps1),pps2)
          end

    in
      mk rds ctxt
    end

    type rd_config = {
      add_vars  : IMP_Syntax.impvar list,           (* Additional variable declarations *)
      in_vars   : string list option,               (* Input variables: Default all vars *)
      out_vars  : string list option,               (* Output variables: Default all vars *)
      prog_vars : IMP_Syntax.impvar list            (* Variables in program *)
    }

    local
      fun vars_dflt (cfg:rd_config) NONE = (#prog_vars cfg) @ (#add_vars cfg)
        | vars_dflt (cfg:rd_config) (SOME vs) = let
            val (gvs,lvs) = List.partition (IMP_Syntax.is_global) vs
            val _ = if gvs <> [] then
              Pretty.block [Pretty.str "Ignoring global parameter/return variables: ",
                            Pretty.list "" "" (map (Pretty.str) gvs)]
              |> Pretty.string_of |> warning
            else ()

          in
            (filter (fst #> member op= lvs) (#prog_vars cfg @ #add_vars cfg ))
          @ (filter (fst #> IMP_Syntax.is_global) (#add_vars cfg))
          end

      fun cfg_in_vars cfg = vars_dflt cfg (#in_vars cfg)
      fun cfg_out_vars cfg = vars_dflt cfg (#out_vars cfg)

    in
      (* Standard annotation readers *)

      fun mk_annot_readers (cfg:rd_config) ctxt = let
        val in_vars = cfg_in_vars cfg
        val prog_vars = (#prog_vars cfg) @ (#add_vars cfg)

        val (rel_rd,_) = gen_mk_reader [] ctxt
        val (annot_rd,post) = gen_mk_reader [(in_vars,"\<^sub>0",false),(prog_vars,"",true)] ctxt
      in
        {
          rel_rd = rel_rd,
          variant_rd = annot_rd,
          invar_rd = annot_rd,
          com_post = post
        }
      end

      fun mk_pre_reader cfg ctxt = gen_mk_reader [(cfg_in_vars cfg,"",true)] ctxt |> #1

      fun mk_post_reader cfg ctxt = let
        val in_vars = cfg_in_vars cfg
        val out_vars = cfg_out_vars cfg
      in
        gen_mk_reader [(in_vars,"\<^sub>0",true),(out_vars,"",true)] ctxt |> #1
      end

      fun mk_variant_reader cfg ctxt = let
        val in_vars = cfg_in_vars cfg
      in
        gen_mk_reader [(in_vars,"",true)] ctxt |> #1
      end

      fun read_program cfg ctxt prog_t =
        gen_interpret_annotations (mk_annot_readers cfg ctxt) ctxt prog_t

    end

  end
\<close>

subsection \<open>Hoare Triple Syntax\<close>

(* In-Term notation for Hoare Triples*)
syntax "_Htriple" :: "cartouche_position \<Rightarrow> cartouche_position \<Rightarrow> cartouche_position \<Rightarrow> logic" ("\<^htriple>_ _ _")
syntax "_Htriple_Partial" :: "cartouche_position \<Rightarrow> cartouche_position \<Rightarrow> cartouche_position \<Rightarrow> logic" ("\<^htriple_partial>_ _ _")

ML \<open> structure VCG_Htriple_Syntax
  = struct

    fun mk_htriple' total env (pre,prog_t,post) = let
      (* Assemble Hoare-triple *)
      val HT_const = if total then @{const HT'} else @{const HT'_partial}
      val res = HT_const$ env $pre$prog_t$post
    in
      res
    end

    fun decon_cartouche_ast ((c as Const (@{syntax_const "_constrain"}, _)) $ Free (s, _) $ p) = (
      case Term_Position.decode_position p of
        SOME (pos, _) => ((s,pos) (*, fn t => c$t$p*))
      | NONE => raise TERM ("cartouche with invalid pos",[c,p])
    )
    | decon_cartouche_ast t = raise TERM ("decon_cartouche_ast",[t])

    fun htriple_tr total ctxt [tpre, prog_t, tpost] = let
      open IMP_Annotations

      val (vars,prog_t) = decon_cartouche_ast prog_t |> IMP_Parser.parse_command_at ctxt

      val cfg = { add_vars = [], in_vars=NONE, out_vars=NONE, prog_vars=vars }

      val prog_t = read_program cfg ctxt prog_t

      val tpre = decon_cartouche_ast tpre
        |> read_ta_from_cartouche (mk_pre_reader cfg ctxt)

      val tpost = decon_cartouche_ast tpost
        |> read_ta_from_cartouche (mk_post_reader cfg ctxt)

      val res = mk_htriple' total @{term "Map.empty :: program"} (tpre,prog_t,tpost)

    in
      IMP_Parser.mark_term res |> @{print}
    end
    | htriple_tr _ _ args = raise TERM ("htriple_tr", args)

  end
\<close>

parse_translation \<open>[
  (@{syntax_const "_Htriple"}, VCG_Htriple_Syntax.htriple_tr true),
  (@{syntax_const "_Htriple_Partial"}, VCG_Htriple_Syntax.htriple_tr false)
  ]
\<close>

term \<open>\<^htriple_partial> \<open>undefined x :: bool\<close> \<open>while (n\<noteq>0) @invariant \<open>x=1+undefined+n+foo\<close> n = n - 1; x=x+1\<close> \<open>True\<close>\<close>

term \<open>\<^htriple_partial> \<open>n\<ge>0+x+notex\<close> \<open>while (n\<noteq>0) n = n - 1; x=x+1\<close> \<open>n=0 \<and> x=1+x\<^sub>0\<close>\<close>
term \<open>\<^htriple> \<open>n\<ge>0+x+notex\<close> \<open>while (n\<noteq>0) n = n - 1; x=x+1\<close> \<open>n=0 \<and> x=1+x\<^sub>0\<close>\<close>

subsection \<open>Postprocessing of Proved Specifications\<close>

subsubsection \<open>Modified Sets\<close>

lemma HT_to_mod: "HT \<pi> P c Q = HT_mods \<pi> (ANALYZE (lhsv \<pi> c)) P c Q"
  by (auto simp: HT_mods_def BB_PROTECT_def HT_def intro: wp_strengthen_modset wp_conseq)

lemma HT_partial_to_mod: "HT_partial \<pi> P c Q = HT_partial_mods \<pi> (ANALYZE (lhsv \<pi> c)) P c Q"
  by (auto simp: HT_partial_mods_def BB_PROTECT_def HT_partial_def intro: wlp_strengthen_modset wlp_conseq)

lemma mk_lhsv_thm:
  assumes "c \<equiv> cmd"
  shows "lhsv \<pi> c = ANALYZE (lhsv \<pi> cmd)" "lhsv' c = ANALYZE (lhsv' cmd)"
  using assms by simp_all

  ML \<open> structure IMP2_Modified_Analysis
    = struct
      fun dest_HT_mods (Const (@{const_name HT_mods},_)$pi$mods$P$c$Q) = (true,pi,mods,P,c,Q)
        | dest_HT_mods (Const (@{const_name HT_partial_mods},_)$pi$mods$P$c$Q) = (false,pi,mods,P,c,Q)
        | dest_HT_mods t = raise TERM("dest_HT_mods",[t])

      (* Valid modset must be set literal of string literals *)
      val is_valid_modset = can (HOLogic.dest_set #> map HOLogic.dest_string)

      (* Weaker criterion for valid modset
      val is_valid_modset = not o exists_subterm (fn @{const lhsv} => true | _ => false)
      *)

      (* HT \<rightarrow> HT_mods *)
      fun mk_HT_mods ctxt thm = let
        (* Local_Defs.unfold will destroy names of bound variables here!?, so using simplify! *)
        fun simplified ctxt thms = simplify (Simplifier.clear_simpset ctxt addsimps thms)

        val ctxt = Named_Simpsets.put @{named_simpset vcg_bb} ctxt
      in
           thm
        |> simplified ctxt @{thms HT_to_mod HT_partial_to_mod}
        |> Simplifier.simplify ctxt
      end

      (* c\<equiv>com \<rightarrow> [ lhsv c = \<dots>, lshv' c = \<dots> ] *)
      fun mk_lhsv_thms ctxt def_thm = let
        val ctxt = Named_Simpsets.put @{named_simpset vcg_bb} ctxt
      in
        map (fn thm => (def_thm RS thm) |> Simplifier.simplify ctxt) @{thms mk_lhsv_thm}
      end

    end
  \<close>

subsubsection \<open>Parameter Passing\<close>
(* Forward assignment rule + renaming of assigned variable *)
lemma adjust_assign_after:
  assumes "HT \<pi> P c Q"
  shows "HT \<pi> P (c;;x[]::=y) (\<lambda>s\<^sub>0 s. \<exists>vx. Q s\<^sub>0 (s(x:=vx,y:=s x)))"
  using assms unfolding HT_def
  apply (auto simp: wp_eq[abs_def])
  by (metis (mono_tags, lifting) fun_upd_triv wp_conseq)

(* Standard backwards assignment rule *)
lemma adjust_assign_before:
  assumes HT: "HT \<pi> P c Q"
  shows "HT \<pi> (\<lambda>s. P (s(x:=s y)) ) (x[]::=y;; c) (\<lambda>s\<^sub>0 s. Q (s\<^sub>0(x:=s\<^sub>0 y)) s)"
  unfolding HT_def
  apply (clarsimp simp: wp_eq)
  using HT_def assms by auto

(* Forward + backward assignment rules, simultaneously for all local variables! *)
lemma adjust_scope:
  assumes HT: "HT \<pi> P c Q"
  shows "HT \<pi> (\<lambda>s. P (<<>|s>)) (SCOPE c) (\<lambda>s\<^sub>0 s. \<exists>l. Q (<<>|s\<^sub>0>) (<l|s>))"
  unfolding HT_def
  apply (clarsimp simp: wp_eq)
  by (smt HT_def assms combine_collapse combine_nest(1) wp_conseq)

(* Forward assignment rule + renaming of assigned variable *)
lemma adjust_assign_after_partial:
  assumes "HT_partial \<pi> P c Q"
  shows "HT_partial \<pi> P (c;;x[]::=y) (\<lambda>s\<^sub>0 s. \<exists>vx. Q s\<^sub>0 (s(x:=vx,y:=s x)))"
  using assms unfolding HT_partial_def
  apply (auto simp: wlp_eq[abs_def])
  by (metis (mono_tags, lifting) fun_upd_triv wlp_conseq)

(* Standard backwards assignment rule *)
lemma adjust_assign_before_partial:
  assumes HT: "HT_partial \<pi> P c Q"
  shows "HT_partial \<pi> (\<lambda>s. P (s(x:=s y)) ) (x[]::=y;; c) (\<lambda>s\<^sub>0 s. Q (s\<^sub>0(x:=s\<^sub>0 y)) s)"
  unfolding HT_partial_def
  apply (clarsimp simp: wlp_eq)
  using HT_partial_def assms by auto

(* Forward + backward assignment rules, simultaneously for all local variables! *)
lemma adjust_scope_partial:
  assumes HT: "HT_partial \<pi> P c Q"
  shows "HT_partial \<pi> (\<lambda>s. P (<<>|s>)) (SCOPE c) (\<lambda>s\<^sub>0 s. \<exists>l. Q (<<>|s\<^sub>0>) (<l|s>))"
  unfolding HT_partial_def
  apply (clarsimp simp: wlp_eq)
  by (smt HT_partial_def assms combine_collapse combine_nest(1) wlp_conseq)

definition "ADJUST_PRE_SCOPE P \<equiv> (\<lambda>s. P <<>|s>)"
definition "ADJUST_PRE_PARAM l G P \<equiv> (\<lambda>s. P (s(l:=s G)))"
definition "ADJUST_POST_SCOPE Q \<equiv> (\<lambda>s\<^sub>0 s. \<exists>l. Q (<<>|s\<^sub>0>) (<l|s>))"
definition "ADJUST_POST_PARAM l G Q \<equiv> (\<lambda>s\<^sub>0 s. Q (s\<^sub>0(l:=s\<^sub>0 G)) s)"
definition "ADJUST_POST_RETV G l Q \<equiv> (\<lambda>s\<^sub>0 s. \<exists>vx. Q s\<^sub>0 (s(G:=vx,l:=s G)))"

lemma HT_strengthen_modset:
  assumes "HT \<pi> P c Q"
  shows "HT \<pi> P c (\<lambda>s\<^sub>0 s. Q s\<^sub>0 s \<and> modifies (lhsv \<pi> c) s s\<^sub>0)"
  using assms unfolding HT_def by (auto intro: wp_strengthen_modset)

lemma HT_partial_strengthen_modset:
  assumes "HT_partial \<pi> P c Q"
  shows "HT_partial \<pi> P c (\<lambda>s\<^sub>0 s. Q s\<^sub>0 s \<and> modifies (lhsv \<pi> c) s s\<^sub>0)"
  using assms unfolding HT_partial_def by (auto intro: wlp_strengthen_modset)

context
  notes [abs_def, simp] = VAR_def
    ADJUST_PRE_SCOPE_def ADJUST_PRE_PARAM_def ADJUST_POST_SCOPE_def ADJUST_POST_PARAM_def ADJUST_POST_RETV_def
  notes [simp] = combine_query
begin

  text \<open>A lot of straightforward lemmas, to transfer specification of function body to function specification\<close>

  lemma ADJUST_PRE_SCOPE_unfolds:
    "\<And>P. ADJUST_PRE_SCOPE (\<lambda>_. P) = (\<lambda>_. P)"
    "\<And>P. ADJUST_PRE_SCOPE (\<lambda>s. VAR v (\<lambda>x. P x s)) = VAR v (\<lambda>x. ADJUST_PRE_SCOPE (\<lambda>s. P x s))"

    "\<And>P. is_global x \<Longrightarrow> ADJUST_PRE_SCOPE (\<lambda>s. VAR (s x i) (\<lambda>x. P x s)) = (\<lambda>s. VAR (s x i) (\<lambda>x. ADJUST_PRE_SCOPE (\<lambda>s. P x s) s))"
    "\<And>P. is_global x \<Longrightarrow> ADJUST_PRE_SCOPE (\<lambda>s. VAR (s x) (\<lambda>x. P x s)) = (\<lambda>s. VAR (s x) (\<lambda>x. ADJUST_PRE_SCOPE (\<lambda>s. P x s) s))"
    by auto

  lemma ADJUST_PRE_PARAM_unfolds:
    "\<And>P. ADJUST_PRE_PARAM l G (\<lambda>_. P) = (\<lambda>_. P)"
    "\<And>P. ADJUST_PRE_PARAM l G (\<lambda>s. VAR v (\<lambda>x. P x s)) = VAR v (\<lambda>x. ADJUST_PRE_PARAM l G (\<lambda>s. P x s))"

    "\<And>P. ADJUST_PRE_PARAM l G (\<lambda>s. VAR (s l i) (\<lambda>x. P x s)) = (\<lambda>s. VAR (s G i) (\<lambda>x. ADJUST_PRE_PARAM l G (\<lambda>s. P x s) s))"
    "\<And>P. ADJUST_PRE_PARAM l G (\<lambda>s. VAR (s l) (\<lambda>x. P x s)) = (\<lambda>s. VAR (s G) (\<lambda>x. ADJUST_PRE_PARAM l G (\<lambda>s. P x s) s))"

    "\<And>P. x\<noteq>l \<Longrightarrow> ADJUST_PRE_PARAM l G (\<lambda>s. VAR (s x i) (\<lambda>x. P x s)) = (\<lambda>s. VAR (s x i) (\<lambda>x. ADJUST_PRE_PARAM l G (\<lambda>s. P x s) s))"
    "\<And>P. x\<noteq>l \<Longrightarrow> ADJUST_PRE_PARAM l G (\<lambda>s. VAR (s x) (\<lambda>x. P x s)) = (\<lambda>s. VAR (s x) (\<lambda>x. ADJUST_PRE_PARAM l G (\<lambda>s. P x s) s))"
    by auto

  lemma ADJUST_POST_SCOPE_unfolds:
    "\<And>P. ADJUST_POST_SCOPE (\<lambda>_ _. P) = (\<lambda>_ _. P)"

    "\<And>P. is_global x \<Longrightarrow> ADJUST_POST_SCOPE (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x i) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x i) (\<lambda>x. ADJUST_POST_SCOPE (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    "\<And>P. is_global x \<Longrightarrow> ADJUST_POST_SCOPE (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x) (\<lambda>x. ADJUST_POST_SCOPE (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"

    "\<And>P. is_global x \<Longrightarrow> ADJUST_POST_SCOPE (\<lambda>s\<^sub>0 s. VAR (s x i) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s x i) (\<lambda>x. ADJUST_POST_SCOPE (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    "\<And>P. is_global x \<Longrightarrow> ADJUST_POST_SCOPE (\<lambda>s\<^sub>0 s. VAR (s x) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s x) (\<lambda>x. ADJUST_POST_SCOPE (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    by auto

  lemma ADJUST_POST_PARAM_unfolds:
    "\<And>P. ADJUST_POST_PARAM l G (\<lambda>_ _. P) = (\<lambda>_ _. P)"

    "\<And>P. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 l i) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 G i) (\<lambda>x. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    "\<And>P. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 l) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 G) (\<lambda>x. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"

    "\<And>P. x\<noteq>l \<Longrightarrow> ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x i) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x i) (\<lambda>x. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    "\<And>P. x\<noteq>l \<Longrightarrow> ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x) (\<lambda>x. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"

    "\<And>P. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. VAR (s x i) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s x i) (\<lambda>x. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    "\<And>P. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. VAR (s x) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s x) (\<lambda>x. ADJUST_POST_PARAM l G (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    by auto

  lemma ADJUST_POST_RETV_unfolds:
    "\<And>P. ADJUST_POST_RETV G l (\<lambda>_ _. P) = (\<lambda>_ _. P)"

    "\<And>P. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x i) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x i) (\<lambda>x. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    "\<And>P. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s\<^sub>0 x) (\<lambda>x. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"

    "\<And>P. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. VAR (s l i) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s G i) (\<lambda>x. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    "\<And>P. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. VAR (s l) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s G) (\<lambda>x. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"

    "\<And>P. \<lbrakk>x\<noteq>G; x\<noteq>l\<rbrakk> \<Longrightarrow> ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. VAR (s x i) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s x i) (\<lambda>x. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    "\<And>P. \<lbrakk>x\<noteq>G; x\<noteq>l\<rbrakk> \<Longrightarrow> ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. VAR (s x) (\<lambda>x. P x s\<^sub>0 s)) = (\<lambda>s\<^sub>0 s. VAR (s x) (\<lambda>x. ADJUST_POST_RETV G l (\<lambda>s\<^sub>0 s. P x s\<^sub>0 s) s\<^sub>0 s))"
    by auto

  lemmas ADJUST_unfolds = ADJUST_PRE_SCOPE_unfolds ADJUST_PRE_PARAM_unfolds
    ADJUST_POST_SCOPE_unfolds ADJUST_POST_PARAM_unfolds ADJUST_POST_RETV_unfolds

  (* TODO: Clean up these proofs! *)
  lemma HT_mods_adjust_scope:
    assumes "HT_mods \<pi> vs P c Q"
    shows "HT_mods \<pi> (Set.filter is_global vs) (ADJUST_PRE_SCOPE P) (SCOPE c) (ADJUST_POST_SCOPE Q)"
    using assms unfolding HT_mods_def
    apply (drule_tac adjust_scope)
    apply simp
    apply (drule_tac HT_strengthen_modset)
    apply (erule HT_conseq)
     apply simp
    apply (clarsimp simp: modifies_split)
    apply (drule (1) modifies_join)
    apply (auto elim: modifies_mono[rotated])
    done

  lemma HT_mods_adjust_param:
    assumes "HT_mods \<pi> vs P c Q"
    shows "HT_mods \<pi> (insert l vs) (ADJUST_PRE_PARAM l G P) (l[]::=G;; c) (ADJUST_POST_PARAM l G Q)"
    using assms unfolding HT_mods_def
    apply (drule_tac adjust_assign_before[of \<pi> P c _ l G])
    apply (erule HT_conseq)
     apply simp
    apply (auto simp: modifies_def)
    done

  lemma HT_mods_adjust_retv:
    assumes "HT_mods \<pi> vs P c Q"
    shows "HT_mods \<pi> (insert G vs) P (c;; G[]::=l) (ADJUST_POST_RETV G l Q)"
    using assms unfolding HT_mods_def
    apply simp
    apply (unfold HT_def; clarsimp simp: wp_eq)
    apply (drule spec, erule (1) impE)
    apply (erule wp_conseq)
    apply (auto simp add: wp_eq modifies_def)
    by (metis fun_upd_triv)

  lemma HT_partial_mods_adjust_scope:
    assumes "HT_partial_mods \<pi> vs P c Q"
    shows "HT_partial_mods \<pi> (Set.filter is_global vs) (ADJUST_PRE_SCOPE P) (SCOPE c) (ADJUST_POST_SCOPE Q)"
    using assms unfolding HT_partial_mods_def
    apply (drule_tac adjust_scope_partial)
    apply (drule_tac HT_partial_strengthen_modset)
    apply (erule HT_partial_conseq)
     apply simp
    apply (clarsimp simp: modifies_split)
    apply (drule (1) modifies_join)
    apply (auto elim: modifies_mono[rotated])
    done

  lemma HT_partial_mods_adjust_param:
    assumes "HT_partial_mods \<pi> vs P c Q"
    shows "HT_partial_mods \<pi> (insert l vs) (ADJUST_PRE_PARAM l G P) (l[]::=G;; c) (ADJUST_POST_PARAM l G Q)"
    using assms unfolding HT_partial_mods_def
    apply (drule_tac adjust_assign_before_partial[of \<pi> P c _ l G])
    apply (erule HT_partial_conseq)
     apply simp
    apply (auto simp: modifies_def)
    done

  lemma HT_partial_mods_adjust_retv:
    assumes "HT_partial_mods \<pi> vs P c Q"
    shows "HT_partial_mods \<pi> (insert G vs) P (c;; G[]::=l) (ADJUST_POST_RETV G l Q)"
    using assms unfolding HT_partial_mods_def
    apply simp
    apply (unfold HT_partial_def; clarsimp simp: wlp_eq)
    apply (drule spec, erule (1) impE)
    apply (erule wlp_conseq)
    apply (auto simp add: wlp_eq modifies_def)
    by (metis fun_upd_triv)

end

lemma HT_generalize_penv:
  assumes "HT_mods Map.empty mods P c Q"
  shows "HT_mods \<pi> mods P c Q"
  using assms unfolding HT_mods_def HT_def wp_def
  apply auto
  using big_step_mono_prog map_le_empty by blast

ML \<open>structure IMP2_Parameters
  = struct
    fun adjust_retv_rl ctxt G l thm = let
      val G = HOLogic.mk_string G |> Thm.cterm_of ctxt
      val l = HOLogic.mk_string l |> Thm.cterm_of ctxt

      val rs_thms = @{thms HT_mods_adjust_retv HT_partial_mods_adjust_retv}
        |> map (Drule.infer_instantiate' ctxt [NONE, NONE, NONE, NONE, NONE, SOME G, SOME l])
    in thm RS_fst rs_thms end

    fun adjust_param_rl ctxt G l thm = let
      val G = HOLogic.mk_string G |> Thm.cterm_of ctxt
      val l = HOLogic.mk_string l |> Thm.cterm_of ctxt

      val rs_thms = @{thms HT_mods_adjust_param HT_partial_mods_adjust_param}
        |> map (Drule.infer_instantiate' ctxt [NONE, NONE, NONE, NONE, NONE, SOME l, SOME G])
    in thm RS_fst rs_thms end

    fun adjust_scope_rl (_:Proof.context) thm = thm RS_fst @{thms HT_mods_adjust_scope HT_partial_mods_adjust_scope}

    fun adjust_proc_rl imp_params imp_retvs ctxt thm = let
      val params = IMP_Syntax.zip_with_param_names imp_params
      val retvs = IMP_Syntax.zip_with_ret_names imp_retvs

      val ctxt = Named_Simpsets.put @{named_simpset vcg_bb} ctxt
      val ctxt = ctxt addsimps @{thms ADJUST_unfolds}

      val thm = thm
        |> fold_rev (uncurry (adjust_retv_rl ctxt)) retvs
        |> fold_rev (uncurry (adjust_param_rl ctxt)) params
        |> adjust_scope_rl ctxt
        |> Simplifier.simplify ctxt

    in
      thm
    end

  end
\<close>

subsubsection \<open>Recursion\<close>
lemma HT_mods_fold_call:
  assumes "\<pi> p = Some c"
  assumes "HT_mods \<pi> mods P c Q"
  shows "HT_mods \<pi> mods P (PCall p) Q"
  using assms
  unfolding HT_mods_def HT_def
  by (auto simp: wp_eq wp_pcall_eq)

lemma localize_HT_mods:
  assumes "HT_mods \<pi> mods P (PCall p) Q"
  shows "HT_mods \<pi>' mods P (PScope \<pi> (PCall p)) Q"
  using assms unfolding HT_mods_def HT_def wp_def
  by (simp add: localize_recursion)

lemmas localize_HT_mods' = localize_HT_mods[where \<pi>'="Map.empty"]

definition "PROVE_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 \<Theta> \<equiv> \<forall>P c Q. (f\<^sub>0,(P,c,Q))\<in>\<Theta> \<and> P s\<^sub>0 \<longrightarrow> wp \<pi> (c s\<^sub>0) (Q s\<^sub>0) s\<^sub>0"

lemma PROVE_\<Theta>I[vcg_preprocess_rules]:
  "PROVE_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 {}"
  "\<lbrakk>\<lbrakk>RENAMING f\<^sub>0 f; BB_PROTECT (P s\<^sub>0)\<rbrakk> \<Longrightarrow> wp \<pi> (c s\<^sub>0) (Q s\<^sub>0) s\<^sub>0; PROVE_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 \<Theta>\<rbrakk> \<Longrightarrow> PROVE_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 (insert (f,(P,c,Q)) \<Theta>)"
  unfolding PROVE_\<Theta>_def BB_PROTECT_def RENAMING_def
  by auto

definition "JOIN_VARS f g P \<equiv> P f g"
lemma JOIN_VARS:
  "\<And>v f g P. JOIN_VARS (VAR v (\<lambda>x. f x)) g P = VAR v (\<lambda>x. JOIN_VARS (f x) g P)"
  "\<And>v f g P. JOIN_VARS f (VAR v (\<lambda>x. g x)) P = VAR v (\<lambda>x. JOIN_VARS f (g x) P)"
  "\<And>f g P. JOIN_VARS (BB_PROTECT f) (BB_PROTECT g) P = P f g"
  by (auto simp: JOIN_VARS_def BB_PROTECT_def VAR_def)

ML \<open>
  fun join_vars_rl ctxt thm0 = let
    val thm = Local_Defs.unfold ctxt @{thms JOIN_VARS} thm0

    val t = Thm.prop_of thm
    val cns = Term.add_const_names t []
    val _ = member op= cns @{const_name JOIN_VARS} andalso raise THM("join_vars_rl: not joined",~1,[thm0,thm])
  in
    thm
  end
\<close>

definition "ASSUME_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 R \<Theta> \<equiv> HT'set_r (\<lambda>f' s'. ((f' s'),(f\<^sub>0 s\<^sub>0))\<in>R ) \<pi> \<Theta>"

lemmas ASSUME_\<Theta>E1 = thin_rl[of "ASSUME_\<Theta> _ _ _ _ {}"]

lemma ASSUME_\<Theta>E2:
  assumes "ASSUME_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 R (insert (f,(P,c,Q)) \<Theta>)"
  obtains "HT' \<pi> (\<lambda>s. JOIN_VARS (f s) (P s) (\<lambda>v P. BB_PROTECT ((v,(f\<^sub>0 s\<^sub>0))\<in>R \<and> P))) c Q" "ASSUME_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 R \<Theta>"
  using assms unfolding ASSUME_\<Theta>_def HT'set_r_def JOIN_VARS_def BB_PROTECT_def by auto

lemmas ASSUME_\<Theta>E = ASSUME_\<Theta>E1 ASSUME_\<Theta>E2

lemma vcg_HT'setI:
  assumes "wf R"
  assumes RL: "\<And>f\<^sub>0 s\<^sub>0. \<lbrakk> ASSUME_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 R \<Theta> \<rbrakk> \<Longrightarrow> PROVE_\<Theta> \<pi> f\<^sub>0 s\<^sub>0 \<Theta>"
  shows "HT'set \<pi> \<Theta>"
  using assms HT'setI[of R \<pi> \<Theta>]
  unfolding ASSUME_\<Theta>_def PROVE_\<Theta>_def HT'set_def
  by auto

subsubsection \<open>Consistency Check\<close>

ML \<open>structure Spec_Consistency_Check
  = struct
      fun check_ht_mods thm = let
        val (_,_,mods,P,_,Q) = Thm.concl_of thm
          |> HOLogic.dest_Trueprop |> IMP2_Modified_Analysis.dest_HT_mods

        fun fail msg = raise THM ("Consistency check: "^msg,~1,[thm])

        (* Check that modset is OK *)
        val _ = IMP2_Modified_Analysis.is_valid_modset mods orelse fail "invalid modset"

        (* Check for relicts in pre/postcondition *)
        val bad_consts = [
          @{const_name ADJUST_PRE_PARAM},
          @{const_name ADJUST_PRE_SCOPE},
          @{const_name ADJUST_POST_PARAM},
          @{const_name ADJUST_POST_RETV},
          @{const_name ADJUST_POST_SCOPE}
          ]

        fun is_bad_const (n,_) = member op= bad_consts n

        val _ = exists_Const is_bad_const P andalso fail "VCG relict in precondition"
        val _ = exists_Const is_bad_const Q andalso fail "VCG relict in precondition"
      in
        ()
      end

  end
\<close>

(* Summarizes postprocessors for specifications *)
ML \<open>structure Spec_Postprocessing
  = struct
    (*
      Strip annotations
    *)
    fun cnv_HT'_to_HT ctxt =
         IMP_Annotations.strip_annotations ctxt
      #> Local_Defs.unfold ctxt @{thms HT'_unfolds}

    (*
      Modifies Analysis
    *)
    val cnv_HT_to_HTmod = IMP2_Modified_Analysis.mk_HT_mods

    (*
      Make procedure frame
    *)
    val cnv_body_to_proc = IMP2_Parameters.adjust_proc_rl

    (*
      Generalize over procedure environment
    *)
    fun cnv_HTmod_generalize_penv thm = thm RS_fst @{thms HT_generalize_penv asm_rl}

    (*
      Define command as constant
    *)
    fun define_HTmod_const binding thm lthy = let
      (* Extract Command *)
      val (_,_,_,_,cmd,_) = Thm.concl_of thm |> HOLogic.dest_Trueprop |> IMP2_Modified_Analysis.dest_HT_mods

      (* Define *)
      val lhs = Free (Binding.name_of binding, fastype_of cmd)
      val eqn = Logic.mk_equals (lhs,cmd)
      val ((lhs,(_,def_thm)),lthy) = Specification.definition (SOME (binding,NONE,Mixfix.NoSyn)) [] [] ((Binding.empty,[]),eqn) lthy

      (* Fold definition in theorem *)
      val thm = Local_Defs.fold lthy [def_thm] thm

      (* Sanity check: Has definition actually been folded? *)
      val _ = exists_subterm (curry op = lhs) (Thm.prop_of thm)
        orelse raise THM("spec_program_cmd: Failed to fold command definition",~1,[thm])

    in
      ((binding,def_thm,thm), lthy)
    end

    (*
      Declare defined command and specification to VCG
    *)
    fun declare_defined_spec (binding,def_thm,thm) lthy = let
      (* Sanity check: Various consistency checks *)
      val _ = Spec_Consistency_Check.check_ht_mods thm

      val note = snd oo Local_Theory.note

      (* Note specification theorem, register as [vcg_rule] *)
      val spec_thm_name = Binding.suffix_name "_spec" binding
      val lthy = note ((spec_thm_name,@{attributes [vcg_specs]}),[thm]) lthy

      (* Create lhsv-theorems, register as vcg_bb simp-rule *)
      val lhsv_thms = IMP2_Modified_Analysis.mk_lhsv_thms lthy def_thm
      val lhsv_thm_name = Binding.suffix_name "_lhsv" binding
      val lthy = note ((lhsv_thm_name,@{attributes [named_ss vcg_bb]}),lhsv_thms) lthy
    in
      lthy
    end

    fun define_declare binding thm =
        define_HTmod_const binding thm
      #> uncurry declare_defined_spec

  end
\<close>

subsection \<open>Program Specification Commands\<close>

ML \<open>structure Simple_Program_Specification
  = struct
    fun simple_spec_program_cmd binding partial add_vars params pre_src post_src cmd_src lthy = let
      val total = not partial

      open IMP_Annotations

      val (prog_vars,prog_t) = IMP_Parser.parse_command_at lthy cmd_src

      val cfg = case params of
        NONE => { add_vars = add_vars, in_vars=NONE, out_vars=NONE, prog_vars=prog_vars }
      | SOME (pvs,rvs) => { add_vars = add_vars, in_vars=SOME pvs, out_vars=SOME rvs, prog_vars=prog_vars }

      val prog_t = read_program cfg lthy prog_t

      val pre = gen_read_ta Syntax.read_term (mk_pre_reader cfg lthy) pre_src
      val post = gen_read_ta Syntax.read_term (mk_post_reader cfg lthy) post_src

      val goal = VCG_Htriple_Syntax.mk_htriple' total @{term "Map.empty::program"} (pre,prog_t,post)

      val goal = HOLogic.mk_Trueprop goal

      fun after_qed thmss lthy = let

        val param_post = case params of
          NONE => K I
        | SOME (pvs,rvs) => Spec_Postprocessing.cnv_body_to_proc pvs rvs

        val thm = flat thmss
          |> the_single
          |> Spec_Postprocessing.cnv_HT'_to_HT lthy
          |> Spec_Postprocessing.cnv_HT_to_HTmod lthy
          |> param_post lthy
          |> Spec_Postprocessing.cnv_HTmod_generalize_penv

        val lthy = Spec_Postprocessing.define_declare binding thm lthy
      in
        lthy
      end

    in Proof.theorem NONE after_qed [[(goal,[])]] lthy end

  end
\<close>

ML \<open>structure Recursive_Program_Specification
  = struct
      type proc_spec_src = {
        binding:  binding,                   (* name *)
        params: string list,                 (* parameters *)
        retvs: string list,                  (* return variables *)
        addvars: IMP_Syntax.impvar list,     (* additional variables *)
        pre_src: string,                     (* precondition src *)
        post_src: string,                    (* postcondition src *)
        variant_src: string,                 (* variant src *)
        cmd_src: string * Position.T         (* body src *)
      }

      type proc_spec = {
        binding:  binding,                   (* name *)
        params: string list,                 (* parameters *)
        retvs: string list,                  (* return variables *)
        addvars: IMP_Syntax.impvar list,     (* additional variables *)
        pre: term,                           (* precondition src *)
        post: term,                          (* postcondition src *)
        variant: term,                       (* variant src *)
        cmd: term                            (* body src *)
      }

      fun check_spec ctxt (spec_src : proc_spec_src) = let
        open IMP_Annotations

        val (prog_vars,prog_t) = IMP_Parser.parse_command_at ctxt (#cmd_src spec_src)
        val cfg = {
          add_vars = #addvars spec_src,
          in_vars=SOME (#params spec_src),
          out_vars=SOME (#retvs spec_src),
          prog_vars=prog_vars }

        val prog_t = read_program cfg ctxt prog_t

        val pre = gen_read_ta Syntax.read_term (mk_pre_reader cfg ctxt) (#pre_src spec_src)
        val post = gen_read_ta Syntax.read_term (mk_post_reader cfg ctxt) (#post_src spec_src)
        val variant = gen_read_ta Syntax.read_term (mk_variant_reader cfg ctxt) (#variant_src spec_src)

      in
        ({
          binding = (#binding spec_src),
          params=(#params spec_src),
          retvs=(#retvs spec_src),
          addvars=(#addvars spec_src),
          pre=pre,
          post=post,
          variant=variant,
          cmd=prog_t
        })
      end

      fun adjust_thm params retvs ctxt = let
        open Spec_Postprocessing
      in
        I
        #> cnv_HT'_to_HT ctxt
        #> cnv_HT_to_HTmod ctxt
        #> cnv_body_to_proc params retvs ctxt
      end

      fun gen_spec_program_cmd rel_src spec_srcs lthy = let

        val ctxt = lthy

        fun trace msg = tracing msg

        val _ = trace "(* Check Specification *)"

        val rel = the_default @{term \<open>measure nat\<close>} (Option.map (Syntax.read_term ctxt) rel_src)
        val relT = fastype_of rel |> HOLogic.dest_setT |> HOLogic.dest_prodT |> fst

        val specs = map (check_spec ctxt) spec_srcs

        val _ = trace "(* Create dummy procedure environment *)"
        (*val ctxt0 = ctxt*)
        val (pe_var,ctxt) = yield_singleton Proof_Context.add_fixes (@{binding \<pi>},SOME @{typ program},Mixfix.NoSyn) ctxt
        val pe_var = Free (pe_var,@{typ program})

        val _ = trace "(* Define initial goal *)"
        fun mk_theta_entry {variant, pre, cmd, post, ...} = HOLogic.mk_tuple [variant, HOLogic.mk_tuple [pre,cmd,post]]

        val thetaT = @{typ "'a \<Theta>elem_t"} |> typ_subst_atomic [(@{typ 'a},relT)]
        val theta = map mk_theta_entry specs |> HOLogic.mk_set thetaT

        val HT'setC = @{const HT'set ('a)} |> subst_atomic_types [(@{typ 'a},relT)]
        val goal = HT'setC $ pe_var $ theta
          |> HOLogic.mk_Trueprop

        val _ = trace "(* Start proof *)"
        val st = Thm.cterm_of ctxt goal |> Goal.init

        val _ = trace "(* Apply recursion rule, and solve wf-precondition *)"
        val crel = Thm.cterm_of ctxt rel
        val sr_rl = Drule.infer_instantiate' ctxt [SOME crel] @{thm vcg_HT'setI}

        val st = Det_Goal_Refine.apply1 "" (resolve_tac ctxt [sr_rl]) st
        val st = Det_Goal_Refine.apply1 "Failed to solve wf-goal"
          (SOLVED' (force_tac ctxt ORELSE' SELECT_GOAL (print_tac ctxt ""))) st

        val _ = trace "(* Explode theta-assumptions *)"
        val st = Det_Goal_Refine.apply1 "" (REPEAT_ALL_NEW (ematch_tac ctxt @{thms ASSUME_\<Theta>E})) st

        (* At this point, we have: \<And>f\<^sub>0 s\<^sub>0. \<lbrakk> HT' \<pi> \<dots>; HT' \<pi> \<dots>; \<dots> \<rbrakk> \<Longrightarrow> PROVE_\<Theta> \<pi> \<dots> *)

        val _ = trace "(* Join VARs in preconditions *)"
        val st = Det_Goal_Refine.apply1 "" (Thm_Mapping.map_all_prems_tac join_vars_rl ctxt) st

        val _ = trace "(* Wrap premises *)"
        fun wrap_thm_rl {params,retvs,...} ctxt thm = adjust_thm params retvs ctxt thm
        val wrap_thm_rls = map wrap_thm_rl specs
        val st = Det_Goal_Refine.apply1 "" (Thm_Mapping.map_prems_tac wrap_thm_rls ctxt) st

        val _ = trace "(* Read wrapped commands to define procedure environment *)"
        val cmds = Logic.prems_of_goal (Thm.prop_of st) 1
          |> map (
               HOLogic.dest_Trueprop
            #> IMP2_Modified_Analysis.dest_HT_mods
            #> #5
          )

        val _ = trace "(* Define procedure environment *)"
        fun proc_name_t_of {binding, ...} = HOLogic.mk_string (Binding.name_of binding)

        val penv_pairs = specs ~~ cmds
          |> map (apfst proc_name_t_of)

        val penv = HOLOption.mk_map_list @{typ pname} @{typ com} penv_pairs

        val eqn = Logic.mk_equals (pe_var,penv) |> Thm.cterm_of ctxt
        val (pe_def,ctxt) = yield_singleton (Assumption.add_assms Local_Defs.def_export) eqn ctxt

        val _ = trace "(* Add theorem for lhsv\<pi> \<pi> *)"
        val lhsv_pi_thm = (@{lemma \<open>\<And>\<pi> \<pi>r. \<pi>\<equiv>\<pi>r \<Longrightarrow> lhsv\<pi> \<pi> = ANALYZE (lhsv\<pi> \<pi>r)\<close> by auto} OF [pe_def])
          |> vcg_bb_simplify [] ctxt

        val ctxt = Context.proof_map (Named_Simpsets.add_simp @{named_simpset vcg_bb} lhsv_pi_thm) ctxt

        val _ = trace "(* Finish modifies-analysis in HT' assumptions *)"
        fun finish_mdf ctxt thm =
          (@{lemma \<open>\<And>\<pi> vs P c Q. HT_mods \<pi> vs P c Q \<Longrightarrow> HT_mods \<pi> (ANALYZE vs) P c Q\<close> by auto} OF [thm])
          |> vcg_bb_simplify [] ctxt

        val st = Det_Goal_Refine.apply1 "" (Thm_Mapping.map_all_prems_tac finish_mdf ctxt) st

        val _ = trace "(* Wrap assumptions into calls *)"
        (** First, show unfold theorems *)
        fun mk_unfold_thm (pname,com) = Goal.prove ctxt [] []
          (HOLogic.mk_eq (pe_var$pname,HOLOption.mk_Some com) |> HOLogic.mk_Trueprop)
          (fn {context=ctxt, ...} => ALLGOALS (vcg_bb_simp_tac [pe_def] ctxt))

        val unfold_thms = map mk_unfold_thm penv_pairs

        val wrap_call_rls = map (fn ufthm => fn _ => fn htthm => @{thm HT_mods_fold_call} OF [ufthm,htthm]) unfold_thms
        val st = Det_Goal_Refine.apply1 "" (Thm_Mapping.map_prems_tac wrap_call_rls ctxt) st

        val _ = trace "(* Focus on goal *)"
        val st_before_focus = st val ctxt_before_focus = ctxt
        val (focus as {context = ctxt, prems, ...},st) = Subgoal.focus ctxt 1 NONE st

        val _ = trace "(* Note ht-premises *)"
        val (_,ctxt) = Proof_Context.note_thms ""
            ((Binding.name "rec_rules",[Named_Theorems.add @{named_theorems vcg_specs}]),
              [(prems,[])]) ctxt

        val _ = trace "(* Prepare user proof *)"
        val termss = Thm.prems_of st |> map (fn t => (t,[]))

        (* --- user proof is logically here --- *)

        fun after_qed thmss goal_ctxt' = let
          val thms = flat thmss |> Proof_Context.export goal_ctxt' ctxt
          val _ = trace "(* Solve subgoals of st *)"
          val st = fold (fn thm => fn st => Thm.implies_elim st thm) thms st (* TODO: Use resolve_tac here? *)

          val st = singleton (Proof_Context.export ctxt (#context focus)) st

          val _ = trace "(* retrofit over focus, and finish goal *)"
          val st = Subgoal.retrofit (#context focus) ctxt_before_focus (#params focus) (#asms focus) 1 st st_before_focus
            |> Det_Goal_Refine.seq_first ""
          val ctxt = ctxt_before_focus

          val thm = Goal.finish ctxt st |> Goal.norm_result ctxt

          val _ = trace "(* Explode HT'set goal into single HTs *)"
          fun explode_HT'set thm = (case try (fn thm => thm RS @{thm HT'setD(1)}) thm of
            NONE => []
          | SOME thm' => thm' :: explode_HT'set (thm RS @{thm HT'setD(2)}))

          val thms = explode_HT'set thm

          val _ = trace "(* Adjust theorems *)"

          val thms = thms
            |> Thm_Mapping.map_thms ctxt wrap_thm_rls
            |> Thm_Mapping.map_thms ctxt wrap_call_rls

          val thms = map (fn thm => thm RS @{thm localize_HT_mods}) thms

          val _ = trace "(* Define constants *)"

          val ctxt = fold
            (fn ({binding,...},thm) => Spec_Postprocessing.define_declare binding thm)
            (specs ~~ thms) ctxt

        in
          ctxt
        end

      in Proof.theorem NONE after_qed [termss] ctxt end
  end
\<close>

ML \<open> structure Program_Specification_Parser
  = struct
    val parse_param_decls = Args.parens (Parse.enum "," Parse.name)

    val parse_var_decl = Parse.name -- Scan.optional (\<^keyword>\<open>[\<close>--\<^keyword>\<open>]\<close> >> K IMP_Syntax.ARRAY) IMP_Syntax.VAL
    val parse_var_decls = Scan.optional (\<^keyword>\<open>for\<close> |-- Scan.repeat parse_var_decl) []

    val parse_returns_clause = Scan.optional (
      \<^keyword>\<open>returns\<close> |-- (Args.parens (Parse.enum "," Parse.name) || Parse.name >> single)
    ) []

    val parse_proc_spec = (
          Parse.binding
       -- parse_param_decls
       -- parse_returns_clause
      --| \<^keyword>\<open>assumes\<close> -- Parse.term
      --| \<^keyword>\<open>ensures\<close> -- Parse.term
      --| \<^keyword>\<open>variant\<close> -- Parse.term
       -- parse_var_decls
      --| \<^keyword>\<open>defines\<close> -- (Parse.position (Parse.cartouche>>cartouche))
      ) >> (fn (((((((binding,params),retvs),pre_src),post_src),variant_src),addvars),cmd_src) =>
        {
          binding = binding,
          params=params,
          retvs=retvs,
          pre_src=pre_src,
          post_src=post_src,
          variant_src=variant_src,
          addvars=addvars,
          cmd_src=cmd_src} : Recursive_Program_Specification.proc_spec_src
      )

  end
\<close>

  ML \<open>
    let
      open Simple_Program_Specification Recursive_Program_Specification Program_Specification_Parser
    in

      Outer_Syntax.local_theory_to_proof \<^command_keyword>\<open>program_spec\<close> "Define IMP program and prove specification"
        ((Args.mode "partial" -- Parse.binding
          --| \<^keyword>\<open>assumes\<close> -- Parse.term
          --| \<^keyword>\<open>ensures\<close> -- Parse.term
           -- parse_var_decls
          --| \<^keyword>\<open>defines\<close> -- (Parse.position (Parse.cartouche>>cartouche))
          ) >> (fn (((((partial,bnd),pre_src),post_src),addvars),cmd_src) =>
                  simple_spec_program_cmd bnd partial addvars NONE pre_src post_src cmd_src));

    Outer_Syntax.local_theory_to_proof \<^command_keyword>\<open>procedure_spec\<close> "Define IMP procedure and prove specification"
      ((Args.mode "partial" -- Parse.binding
         -- parse_param_decls
         -- parse_returns_clause
        --| \<^keyword>\<open>assumes\<close> -- Parse.term
        --| \<^keyword>\<open>ensures\<close> -- Parse.term
         -- parse_var_decls
        --| \<^keyword>\<open>defines\<close> -- (Parse.position (Parse.cartouche>>cartouche))
        ) >>

          (fn (((((((partial,bnd),params),retvs),pre_src),post_src),addvars),cmd_src) =>
                simple_spec_program_cmd bnd partial addvars (SOME (params,retvs)) pre_src post_src cmd_src
             )
         );

      Outer_Syntax.local_theory_to_proof \<^command_keyword>\<open>recursive_spec\<close> "Define IMP procedure and prove specification"
        (    Scan.option (\<^keyword>\<open>relation\<close> |-- Parse.term)
          -- Parse.and_list1 parse_proc_spec
          >> (fn (rel,specs) => gen_spec_program_cmd rel specs))

    end
  \<close>

end