Theory CLanguage

(*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 * Copyright (c) 2022 Apple Inc. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 *)

chapter ‹More Building Blocks for our C-Language Model›

theory CLanguage
  imports 
    CProof
    Lens
begin

section ‹addr bounds›

lemma addr_card_eq: "addr_card = 2^LENGTH(addr_bitsize)"
  by (simp add: addr_card_def card_word)

lemma size_of_bnd: "size_of TYPE('a::mem_type) < 2^LENGTH(addr_bitsize)"
  by (rule less_le_trans[OF max_size]) (simp add: addr_card_eq)

lemma size_of_mem_type[simp]: "size_of TYPE('c::mem_type)  0"
  by simp

lemma addr_card_len_of_conv: "addr_card =  2 ^ len_of TYPE(addr_bitsize)"
  by (simp add: addr_card)

lemma intvl_split:
  " n  a   { p :: ('a :: len) word ..+ n } = { p ..+ a }  { p + of_nat a ..+ (n - a)}"
(*    supply unsigned_of_nat [simp del] *)
  apply (rule set_eqI, rule iffI)
   apply (clarsimp simp: intvl_def not_less)
  subgoal for k 
    apply (rule exI[where x=k])
    apply clarsimp
    apply (rule classical)
    apply (drule_tac x="k - a" in spec)
    apply (clarsimp simp: not_less)
    apply (metis diff_less_mono not_less)
    done
  subgoal for x
    apply (clarsimp simp: intvl_def not_less)
    apply (rule exI[where x="unat (x - p)"])
    apply clarsimp
    apply (erule disjE)
     apply clarsimp
     apply (metis le_unat_uoi less_or_eq_imp_le not_less order_trans)
    apply clarsimp
    apply (metis le_def le_eq_less_or_eq le_unat_uoi less_diff_conv
        add.commute of_nat_add)
    done
  done

section "More Heap Typing"


primrec
  htd_upd :: "addr  typ_slice list  heap_typ_desc  heap_typ_desc"
where
  "htd_upd p [] d = d"
| "htd_upd p (x#xs) d = htd_upd (p+1) xs (d(p := (True, x)))"

definition (in c_type) ptr_force_type :: "'a ptr  heap_typ_desc  heap_typ_desc" where
  "ptr_force_type p  htd_upd (ptr_val p) (typ_slices TYPE('a))"

definition ptr_force_types :: "'a::c_type ptr list  heap_typ_desc  heap_typ_desc" where
  "ptr_force_types = fold ptr_force_type"


definition ptr_force_free :: "addr  nat  heap_typ_desc  heap_typ_desc" where
  "ptr_force_free p b = ptr_force_types (map (λn. PTR(8 word) p +p n) (map of_nat [0..<2^b]))"

definition ptr_u :: "'a::c_type ptr  (addr × typ_uinfo)" where
  "ptr_u p = (ptr_val p, typ_uinfo_t TYPE('a))"

abbreviation "ptr_span_u  (λ(a, t). {a ..+ size_td t})"

definition typ_slices_u :: "typ_uinfo  typ_slice list" where
  "typ_slices_u t = map (λn. list_map (typ_slice_t t n)) [0..<size_td t]"

definition ptr_force_type_u :: "typ_uinfo  addr  heap_typ_desc  heap_typ_desc" where
  "ptr_force_type_u t a  htd_upd a (typ_slices_u t)"

lemma heap_update_list_id: "heap_update_list x [] = (λx. x)"
  by auto

lemma to_bytes_word8:
  "to_bytes (v :: word8) xs = [v]"
  by (simp add: to_bytes_def typ_info_word word_rsplit_same)

lemma heap_update_heap_update_list:
   " ptr_val p = q + (of_nat (length l)); Suc (length l) < addr_card  
      heap_update (p :: word8 ptr) v (heap_update_list q l s) = (heap_update_list q (l @ [v]) s)"
  by (metis to_bytes_word8 heap_update_def heap_update_list_concat_fold)

lemma htd_upd_empty[simp]: "htd_upd p [] = id"
  by (simp add: fun_eq_iff)

lemma htd_upd_append:
  "htd_upd p (xs @ ys) = htd_upd (p + of_nat (length xs)) ys  htd_upd p xs"
  by (induction xs arbitrary: p) (simp_all add: fun_eq_iff ac_simps)

lemma htd_upd_singleton[simp]: "htd_upd p [x] = upd_fun p (λh. (True, x))"
  by (simp add: fun_eq_iff upd_fun_def)

lemma intvl_Suc_eq: "{p ..+ Suc n} = insert p {p + 1 ..+ n}"
  using intvl_split[of 1 "Suc n" p] by (auto simp add: intvl_def)

lemma htd_upd_disj: "p  {p' ..+ length v}  htd_upd p' v h p = h p"
  by (induction v arbitrary: p' h)
     (auto simp add: intvl_Suc_eq fun_upd_other simp del: fun_upd_apply)

lemma htd_upd_head:
  "xs  []  length xs  addr_card  htd_upd p xs s p = (True, hd xs)"
  using intvl_Suc_nmem''[of "length xs" p] htd_upd_disj[of p "p + 1" "tl xs" _]
  by (cases xs) (simp_all add: addr_card_eq del: intvl_Suc_nmem')

lemma htd_upd_at:
  "i < length xs  length xs  addr_card  htd_upd p xs s (p + of_nat i) = (True, xs ! i)"
proof (induction i arbitrary: p xs s)
  case 0 with htd_upd_head[of xs p s] show ?case by (simp add: hd_conv_nth)
next
  case (Suc n)
  from Suc.prems show ?case by (cases xs) (simp_all add: Suc.IH flip: add.assoc)
qed

lemma ptr_force_type_disj:
  "p  ptr_span (p' :: 'a::mem_type ptr)  ptr_force_type p' h p = h p"
  unfolding ptr_force_type_def
  by (intro htd_upd_disj) simp_all

lemma ptr_force_types_disj:
  fixes xs :: "'a::mem_type ptr list"
  assumes "x. x  set xs  i  ptr_span x"
  shows "ptr_force_types xs h i = h i"
  by (use assms in induction xs rule: rev_induct)
     (auto simp: ptr_force_types_def ptr_force_type_disj)

subsection ‹Heap type tag and valid simple footprint›
(*
 * Each address in the heap can contain one of three things:
 *
 *   - A type tag, which inidicates that this address is the first
 *     byte of an object;
 *
 *   - A footprint, which indicates that this address is a latter byte
 *     of an object;
 *
 *   - Nothing, which indicates that this address does not fall inside
 *     an object.
 *)
datatype heap_typ_contents =
    HeapType typ_uinfo
  | HeapFootprint
  | HeapEmpty

(*
 * Given a Tuch-style heap representation (where each memory location
 * contains a set of different types, representing nested field types)
 * calculate a single top-level type of the heap.
 *
 * We just want to commit to a single type for this heap location,
 * and nothing more.
 *)
definition
  heap_type_tag :: "heap_typ_desc  addr  heap_typ_contents"
where
  "heap_type_tag d a 
     (if fst (d a) = False  (x. (snd (d a)) x = None)  (x. (snd (d a)) x  None) then
       HeapEmpty
     else
       case (snd (d a)) (GREATEST x. snd (d a) x  None) of
         Some (_, False)  HeapFootprint
       | Some (x, True)  HeapType x
       | None  HeapEmpty)"



(*
 * Determine if the heap has a valid footprint for the given type at
 * the given address.
 *
 * A valid footprint means that the user has committed that the given
 * memory location will only be used for the given type.
 *
 * A "simple" footprint differs from the Tuch-style because we only
 * commit to a single type, and have no support for accessing nested
 * structures.
 *)
definition
  valid_simple_footprint :: "heap_typ_desc  addr  typ_uinfo  bool"
where
  "valid_simple_footprint d x t 
    heap_type_tag d x = HeapType t 
      (y. y  {x + 1..+  (size_td t)- Suc 0}  heap_type_tag d y = HeapFootprint)"

lemma valid_simple_footprint_size_td:
  assumes valid: "valid_simple_footprint d x t" 
  shows "size_td t  addr_card"
proof (cases "size_td t  addr_card")
  case True
  then show ?thesis by simp
next
  case False
  from valid have "heap_type_tag d x = HeapType t"
    by (simp add: valid_simple_footprint_def)
  moreover
  from False have "x  {x + 1..+  (size_td t)- Suc 0}"
    apply (clarsimp simp add: intvl_def)
    apply (rule exI [where x = "addr_card - Suc 0"])
    by (metis (mono_tags, opaque_lifting) One_nat_def Suc_pred' diff_less_mono diff_zero 
        neq0_conv not_less not_less_eq_eq of_nat_1 of_nat_Suc of_nat_addr_card unatSuc 
        unat_minus_abs zero_diff zero_neq_one)
  with valid have "heap_type_tag d x = HeapFootprint" by (auto simp add: valid_simple_footprint_def)
  ultimately have False by simp
  thus ?thesis ..
qed

lemma valid_simple_footprintI:
  " heap_type_tag d x = HeapType t; y. y  {x + 1..+(size_td t) - Suc 0}  heap_type_tag d y = HeapFootprint 
       valid_simple_footprint d x t"
  by (clarsimp simp: valid_simple_footprint_def)

lemma valid_simple_footprintD:
  "valid_simple_footprint d x t  heap_type_tag d x = HeapType t"
  by (simp add: valid_simple_footprint_def)

lemma valid_simple_footprintD2:
  " valid_simple_footprint d x t; y  {x + 1..+(size_td t) - Suc 0}   heap_type_tag d y = HeapFootprint"
  by (simp add: valid_simple_footprint_def)

lemma typ_slices_not_empty:
    "typ_slices (x::('a::{mem_type} itself))  []"
  apply (clarsimp simp: typ_slices_def)
  done

lemma last_typ_slice_t:
    "(last (typ_slice_t t 0)) = (t, True)"
  apply (cases t)
  apply clarsimp
  done

lemma last_typ_slice_t_non_zero:
    "k  0  (last (typ_slice_t t k)) = (t, False)"
  apply (cases t)
  apply clarsimp
  done

lemma if_eqI:
 " a  x = z; ¬ a  y = z   (if a then x else y) = z"
  by simp

lemma heap_type_tag_ptr_retyp:
    "snd (s (ptr_val t)) = Map.empty 
        heap_type_tag (ptr_retyp (t :: 'a::mem_type ptr) s) (ptr_val t) = HeapType (typ_uinfo_t TYPE('a))"
  apply (unfold ptr_retyp_def heap_type_tag_def)
  apply (subst htd_update_list_index, fastforce, fastforce)+
  apply (rule if_eqI)
   apply clarsimp
   apply (erule disjE)
    apply (erule_tac x=0 in allE)
    apply clarsimp
   apply (erule_tac x="length (typ_slice_t (typ_uinfo_t TYPE('a)) 0)" in allE)
   apply (clarsimp simp: list_map_eq)
  apply (clarsimp simp: list_map_eq last_conv_nth [simplified, symmetric] last_typ_slice_t
            split: option.splits if_split_asm prod.splits)
  done

lemma not_snd_last_typ_slice_t:
    "k  0  ¬ snd (last (typ_slice_t z k))"
  by (cases z, clarsimp)

lemma heap_type_tag_ptr_retyp_rest:
    " snd (s (ptr_val t + k)) = Map.empty; 0 < k; unat k < size_td (typ_uinfo_t TYPE('a))  
        heap_type_tag (ptr_retyp (t :: 'a::mem_type ptr) s) (ptr_val t + k) = HeapFootprint"
  apply (unfold ptr_retyp_def heap_type_tag_def)
  apply (subst htd_update_list_index, simp, clarsimp,
      metis intvlI size_of_def word_unat.Rep_inverse)+
  apply (rule if_eqI)
   apply clarsimp
   apply (erule disjE)
    apply (erule_tac x=0 in allE)
    apply (clarsimp simp: size_of_def)
   apply (erule_tac x="length (typ_slice_t (typ_uinfo_t TYPE('a)) (unat k))" in allE)
   apply (clarsimp simp: size_of_def list_map_eq)
  apply (clarsimp simp: list_map_eq last_conv_nth [simplified, symmetric] size_of_def
      split: option.splits if_split_asm prod.splits bool.splits)
   apply (metis surj_pair)
  apply (subst (asm) (2) surjective_pairing)
  apply (subst (asm) not_snd_last_typ_slice_t)
   apply clarsimp
   apply unat_arith
  apply simp
  done

lemma typ_slices_addr_card [simp]:
    "length (typ_slices (x::('a::{mem_type} itself))) < addr_card"
  apply (clarsimp simp: typ_slices_def)
  done

lemma unat_less_impl_less:
    "unat a < unat b  a < b"
  by unat_arith

lemma valid_simple_footprint_ptr_retyp:
    " k < size_td (typ_uinfo_t TYPE('a)). snd (s (ptr_val t + of_nat k)) = Map.empty;
        1  size_td (typ_uinfo_t TYPE('a));
        size_td (typ_uinfo_t TYPE('a)) < addr_card 
         valid_simple_footprint (ptr_retyp (t :: 'a::mem_type ptr) s) (ptr_val t) (typ_uinfo_t TYPE('a))"
  apply (clarsimp simp: valid_simple_footprint_def)
  apply (rule conjI)
   apply (subst heap_type_tag_ptr_retyp)
    apply (erule allE [where x="0"])
    apply clarsimp
   apply clarsimp
  apply (clarsimp simp: intvl_def)
  subgoal for k
    apply (erule_tac x="k + 1" in allE)
    apply (erule impE)
     apply (metis One_nat_def less_diff_conv)
    apply (subst add.assoc, subst heap_type_tag_ptr_retyp_rest)
       apply clarsimp
      apply (cases "1 + of_nat k = (0 :: addr)")
       apply (metis add.left_neutral intvlI intvl_Suc_nmem size_of_def)
      apply unat_arith
     apply clarsimp
     apply (metis lt_size_of_unat_simps size_of_def Suc_eq_plus1 One_nat_def less_diff_conv of_nat_Suc)
    apply simp
    done
  done

lemma heap_type_tag_cong: "s p = s' p  heap_type_tag s p = heap_type_tag s' p"
  by (simp add: heap_type_tag_def cong: if_cong)

lemma heap_type_tag:
  assumes eq: "h p = (f, list_map l)"
  shows "heap_type_tag h p =
    (if ¬ f  l = [] then HeapEmpty else
      case last l of (x, b)  if b then HeapType x else HeapFootprint)"
  unfolding heap_type_tag_def eq
  by (auto simp add: list_map_def)
     (auto simp: split_beta' last_conv_nth list_map_eq simp flip: list_map_def)

lemma valid_simple_footprint_cong_state:
  assumes t: "wf_size_desc t"
  assumes eq: "p'. p'  {p ..+size_td t}  s p' = s' p'"
  shows "valid_simple_footprint s p t  valid_simple_footprint s' p t"
  unfolding valid_simple_footprint_def
  using eq wf_size_desc_gt(1)[OF t]
  using intvl_split[of 1 "size_td t" p]
  by (intro arg_cong2[where f="(∧)"] all_cong arg_cong[where f="λx. x = _"] heap_type_tag_cong)
     (auto simp: intvl_def)

lemma heap_type_tag_ptr_force_type_HeapType:
  fixes x :: "'a::mem_type ptr"
  shows "heap_type_tag (ptr_force_type x s) (ptr_val x) = HeapType (typ_uinfo_t TYPE('a))"
  by (subst heap_type_tag)
     (auto simp: ptr_force_type_def htd_upd_head typ_slices_not_empty max_size[THEN less_imp_le]
                 hd_conv_nth last_typ_slice_t)

lemma heap_type_tag_ptr_force_type_HeapFootprint:
  fixes p :: "'a::mem_type ptr"
  shows "p'  {ptr_val p + 1 ..+ size_of TYPE('a) - Suc 0} 
    heap_type_tag (ptr_force_type p s) p' = HeapFootprint"
  unfolding intvl_def
  apply (clarsimp simp: less_diff_conv add.assoc ptr_force_type_def simp flip: of_nat_Suc)
  subgoal premises prems for k
    using prems(1)
    apply (subst heap_type_tag)
    apply (subst htd_upd_at)
    apply (simp_all add: max_size[THEN less_imp_le])
    apply (simp add: last_typ_slice_t split_beta' not_snd_last_typ_slice_t)
    done
  done

lemma valid_simple_footprint_ptr_force_type_iff:
  fixes p :: "'a::mem_type ptr"
  assumes t: "wf_size_desc t"
  shows "valid_simple_footprint (ptr_force_type p s) a t 
    (valid_simple_footprint s a t  disjnt {a ..+ size_td t} (ptr_span p)) 
    (t = typ_uinfo_t TYPE('a)  p = Ptr a)"
proof cases
  assume disjnt: "disjnt {a ..+ size_td t} (ptr_span p)"
  moreover have "p  Ptr a"
    using disjnt[unfolded disjnt_iff, THEN spec, of a] t[THEN wf_size_desc_gt(1)]
    by (cases p) (auto simp: intvl_self)
  moreover have "valid_simple_footprint (ptr_force_type p s) a t = valid_simple_footprint s a t"
    using t disjnt
    by (intro valid_simple_footprint_cong_state ptr_force_type_disj) (simp_all add: disjnt_iff)
  ultimately show ?thesis
    by simp
next
  assume ndisjnt: "¬ disjnt {a ..+ size_td t} (ptr_span p)"
  from intvl_inter[OF this[unfolded disjnt_def]]
  consider "a = ptr_val p"
    | "a  {ptr_val p + 1 ..+ size_of TYPE('a) - 1}"
    | "ptr_val p  {a + 1 ..+ size_td t - Suc 0}" "ptr_val p  a"
    by (auto dest: intvl_neq_start)
  then show ?thesis
  proof cases
    case 1
    have "valid_simple_footprint (ptr_force_type p s) (ptr_val p) (typ_uinfo_t TYPE('a))"
      by (auto simp: valid_simple_footprint_def heap_type_tag_ptr_force_type_HeapType
                     heap_type_tag_ptr_force_type_HeapFootprint size_of_tag)
    with 1 valid_simple_footprintD[of "ptr_force_type p s" a t] ndisjnt
    show ?thesis
      by (auto simp: heap_type_tag_ptr_force_type_HeapType)
  next
    case 2 with valid_simple_footprintD[of "ptr_force_type p s" a t] show ?thesis
      by (auto simp: heap_type_tag_ptr_force_type_HeapFootprint ndisjnt)
  next
    case 3 with valid_simple_footprintD2[of "ptr_force_type p s" a t "ptr_val p"] show ?thesis
      by (auto simp add: heap_type_tag_ptr_force_type_HeapType ndisjnt)
  qed
qed

lemma valid_simple_footprint_fold_ptr_force_type_iff:
  fixes ps :: "'a::mem_type ptr list"
  assumes [simp]: "wf_size_desc t"
  shows "distinct_prop (λp1 p2. disjnt (ptr_span p1) (ptr_span p2)) ps 
    valid_simple_footprint (fold ptr_force_type ps s) a t 
      (valid_simple_footprint s a t  disjnt {a ..+ size_td t} (pset ps. ptr_span p)) 
      (t = typ_uinfo_t TYPE('a)  Ptr a  set ps)"
  by (induction ps arbitrary: s)
     (auto simp: valid_simple_footprint_ptr_force_type_iff size_of_tag distinct_prop_append)

section "Pointers to local (stack) variables"

definition "stack_typ_info t = (stack_byte_name  td_names t)"

lemma stack_typ_info_export_uinfo[simp]: "stack_typ_info (export_uinfo t) = stack_typ_info t"
  by (simp add: stack_typ_info_def)

lemma stack_typ_info_td_set:
  assumes stack_typ: "stack_typ_info t" 
  assumes t': "(t', n)  td_set t 0" 
  shows "typ_name t'  stack_byte_name"
proof (cases "typ_name t' = pad_typ_name")
  case True
  then show ?thesis by (simp add: stack_byte_name_def pad_typ_name_def)
next
  case False
  from td_set_td_names [OF t' False]
  have "typ_name t'  td_names t" .
  with stack_typ show ?thesis
    by (auto simp add: stack_typ_info_def)
qed


lemma stack_typ_info_td_set_stack_byte:
  assumes stack_typ: "stack_typ_info t" 
  assumes t': "(t', n)  td_set t 0" 
  shows "t'  typ_uinfo_t TYPE(stack_byte)"
  using stack_typ_info_td_set [OF stack_typ t']
  apply (cases t')
  apply (simp add: typ_uinfo_t_def typ_info_stack_byte)
  done

class stack_type = c_type +
  assumes stack_typ_info: "stack_typ_info (typ_info_t TYPE('a))"
begin
lemma stack_typ_uinfo: "stack_typ_info (typ_uinfo_t TYPE('a))" 
  using stack_typ_info
  by (simp add: typ_uinfo_t_def)

lemma no_stack_byte [simp]: "typ_uinfo_t TYPE('a)  typ_uinfo_t TYPE(stack_byte)"
  by (metis order.refl stack_typ_info_td_set_stack_byte stack_typ_uinfo typ_tag_le_def)

end

lemma stack_typ_info_no_stack_byte: 
  "stack_typ_info t  t  typ_uinfo_t TYPE(stack_byte)"
  using stack_typ_info_def typ_tag_le_def stack_typ_info_td_set_stack_byte by fastforce


lemma stack_typ_info_empty_typ_info: 
  "nm  stack_byte_name  stack_typ_info (empty_typ_info algn nm)"
  by (simp add: empty_typ_info_def stack_typ_info_def  typ_info_stack_byte)


lemma td_set_list_to_set: 
  "(t, m)  td_set_list xs n  (x k. x  set xs  (t, k)  td_set (dt_fst x) 0)"
  apply (induct xs arbitrary: m n)
   apply simp
  by (metis Un_iff dt_tuple.collapse insertCI list.set(2) td_set_list.simps(2) 
      td_set_offset_0_conv' ts5)

lemma td_names_list_to_set: 
  "nm  td_names_list xs  (x. x  set xs  nm  td_names (dt_fst x))"
  apply (induct xs arbitrary: )
   apply simp
  by (metis Un_iff dt_tuple.collapse insert_iff list.set(2) td_names_list.simps(2) tnm5)

lemma set_to_td_names_list: 
  "x  set xs  nm  td_names (dt_fst x)  nm  td_names_list xs"
  apply (induct xs arbitrary: )
   apply simp
  by (metis Un_iff dt_tuple.collapse insert_iff list.set(2) td_names_list.simps(2) tnm5)


lemma stack_typ_info_TypAggregateI:
  assumes xs: "x. x  set xs  stack_typ_info (dt_fst x)"  
  assumes nm: "nm  stack_byte_name"
  shows "stack_typ_info (TypDesc algn (TypAggregate (xs)) nm)"
  using xs nm
  apply (auto simp add: stack_typ_info_def stack_byte_name_def dest: td_names_list_to_set)
  done

lemma TypAggregate_not_stack_byte: 
  "TypDesc algn (TypAggregate xs) nm  typ_uinfo_t TYPE(stack_byte)"
  by (auto simp add: typ_info_stack_byte typ_uinfo_t_def)

lemma stack_typ_info_TypAggregateD:
  assumes aggr:  "stack_typ_info (TypDesc algn (TypAggregate (xs)) nm)"
  assumes x: "x  set xs" 
  shows "stack_typ_info (dt_fst x)" 
  using aggr x
  by  (auto simp add: stack_typ_info_def stack_byte_name_def dest:set_to_td_names_list)

lemma stack_typ_info_extend_ti:
"stack_typ_info s  stack_typ_info t  
stack_typ_info (extend_ti s t algn fn d)"
  apply (cases s)
  by (simp add: stack_typ_info_def)


lemma stack_typ_info_ti_pad_combine: 
  "stack_typ_info t  stack_typ_info (ti_pad_combine n t) "
  apply (simp add: ti_pad_combine_def Let_def)
  apply (rule stack_typ_info_extend_ti)
   apply assumption
  apply (auto simp add: stack_typ_info_def typ_uinfo_t_def typ_info_stack_byte )
  done


lemma stack_typ_info_export_uinfo_adjust_ti':
  shows "stack_typ_info (adjust_ti (typ_info_t TYPE('b::stack_type)) acc upd)"
proof -
  from stack_typ_info
  have "stack_typ_info (typ_info_t TYPE('b))".
  then show ?thesis
    by (simp add: stack_typ_info_def)
qed

lemma stack_typ_info_export_uinfo_adjust_ti:
  shows "stack_typ_info (typ_info_t (TYPE('b)))  stack_typ_info (adjust_ti (typ_info_t TYPE('b::c_type)) acc upd)"
  by (simp add: stack_typ_info_def)

lemma stack_typ_info_ti_typ_combine': 
  "stack_typ_info t  
  stack_typ_info (ti_typ_combine TYPE('b::stack_type) acc upd algn nm t)"
  apply (simp add: ti_typ_combine_def)
  apply (rule stack_typ_info_extend_ti)
   apply assumption
  apply (rule stack_typ_info_export_uinfo_adjust_ti')
  done

lemma stack_typ_info_ti_typ_combine: 
  "stack_typ_info (typ_info_t (TYPE('b)))  stack_typ_info t  
  stack_typ_info (ti_typ_combine TYPE('b::c_type) acc upd algn nm t)"
  apply (simp add: ti_typ_combine_def)
  apply (rule stack_typ_info_extend_ti)
   apply assumption
  apply (simp add: stack_typ_info_export_uinfo_adjust_ti)
  done

lemma stack_typ_info_ti_typ_pad_combine': 
"stack_typ_info t 
  stack_typ_info (ti_typ_pad_combine TYPE('b::stack_type) acc upd algn nm t)"
  by (auto simp add: ti_typ_pad_combine_def Let_def stack_typ_info_ti_typ_combine' stack_typ_info_ti_pad_combine)

lemma stack_typ_info_ti_typ_pad_combine: 
"stack_typ_info (typ_info_t (TYPE('b)))  stack_typ_info t 
  stack_typ_info (ti_typ_pad_combine TYPE('b::c_type) acc upd algn nm t)"
  by (auto simp add: ti_typ_pad_combine_def Let_def stack_typ_info_ti_typ_combine stack_typ_info_ti_pad_combine)

lemma stack_typ_info_map_align: 
  "stack_typ_info t  stack_typ_info (map_align algn t)"
  by (cases t) (auto simp add: stack_typ_info_def)

lemma stack_typ_info_final_pad: "stack_typ_info t  
  stack_typ_info (final_pad algn t)"
  by (auto simp add: final_pad_def Let_def stack_typ_info_ti_pad_combine stack_typ_info_map_align)

lemmas stack_typ_info_intros =
 stack_typ_info_empty_typ_info
 stack_typ_info_ti_typ_pad_combine
 stack_typ_info_final_pad
 stack_typ_info

named_theorems stack_typ_info


definition valid_root_footprint :: "heap_typ_desc  addr  typ_uinfo  bool" where
  "valid_root_footprint d x t  
     let n = size_td t in
       0 < n  (y. y < n 
                    snd (d (x + of_nat y)) = list_map (typ_slice_t t y)  fst (d (x + of_nat y)))"

lemma valid_root_footprint_valid_footprint: "valid_root_footprint d x t  valid_footprint d x t"
  by (auto simp add: valid_root_footprint_def valid_footprint_def Let_def)

lemma valid_root_footprint_valid_footprint_dom_conv: 
     "valid_root_footprint d a t 
       
      (valid_footprint d a t  
      (n. n < size_td t  dom (snd (d (a + of_nat n))) = {0..<length (typ_slice_t t n)}))"
  apply (clarsimp simp add: valid_footprint_def valid_root_footprint_def Let_def)
  apply (intro iffI conjI)
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by (metis list_map_eq not_None_eq dom_list_map map_le_same_dom_eq)
  by (metis dom_list_map map_le_same_dom_eq)

lemma valid_root_footprint_dom_typing: 
  "valid_root_footprint d a t  n < size_td t  
     dom (snd (d (a + of_nat n))) = {0..<length (typ_slice_t t n)}"
  by (simp add: valid_root_footprint_valid_footprint_dom_conv)
 
lemma valid_root_footprint_typing_conv: 
  fixes p::"'a::c_type ptr"
  assumes valid: "valid_root_footprint d (ptr_val p) (typ_uinfo_t TYPE('a))"
  assumes n: "n < size_of (TYPE('a))" 
  shows "d (ptr_val p + of_nat n) = (True, list_map (typ_slice_t (typ_uinfo_t TYPE('a)) n))"
  using valid n
  by (simp add: valid_root_footprint_def Let_def size_of_def prod_eq_iff)

(* Determine if the given pointer is valid in the given heap. *)
definition
  root_ptr_valid :: "heap_typ_desc  'a::c_type ptr  bool"
where
  "root_ptr_valid d p 
     valid_root_footprint d (ptr_val (p::'a ptr)) (typ_uinfo_t TYPE('a))   
     c_guard p"

lemma root_ptr_valid_c_guard: "root_ptr_valid d p  c_guard p"
  by (simp add: root_ptr_valid_def)

lemma root_ptr_valid_typing_conv:
  fixes p::"'a::c_type ptr"
  assumes valid: "root_ptr_valid d p"
  assumes n: "n < size_of (TYPE('a))" 
  shows "d (ptr_val p + of_nat n) = (True, list_map (typ_slice_t (typ_uinfo_t TYPE('a)) n))"
  using valid
  by (simp add: root_ptr_valid_def valid_root_footprint_typing_conv [OF _ n])

lemma root_ptr_valid_h_t_valid: "root_ptr_valid d p  d, c_guard t p"
  by (simp add: h_t_valid_def root_ptr_valid_def valid_root_footprint_valid_footprint_dom_conv)

lemma valid_root_footprint_cong_state:
  assumes t: "wf_size_desc t"
  assumes eq: "p'. p'  {p ..+size_td t}  s p' = s' p'"
  shows "valid_root_footprint s p t  valid_root_footprint s' p t"
  unfolding valid_root_footprint_def Let_def
  using eq wf_size_desc_gt(1)[OF t]
  using intvl_split[of 1 "size_td t" p]
  by (metis intvlI)

lemma (in mem_type) valid_root_foot_print_ptr_force_type:
  "valid_root_footprint
    (ptr_force_type (p::'a ptr) s) (ptr_val p) (typ_uinfo_t (TYPE('a)))"
  by (simp add: htd_upd_at local.ptr_force_type_def 
      local.size_of_fold order_less_imp_le valid_root_footprint_def)

lemma list_map_greatest_last: "xs  []  last xs = v  list_map xs (GREATEST k. v. list_map xs k = Some v) = Some v"
proof (induct n == "length xs" arbitrary: xs)
  case 0
  then show ?case by simp
next
  case (Suc n xs)
  show ?case
  proof (cases xs)
    case Nil
    then show ?thesis using Suc by simp
  next
    case (Cons x xs')
    have xs: "xs = x#xs'" by fact
    show ?thesis
    proof (cases xs')
      case Nil

      have *: "(GREATEST k::nat. v'. [0  v] k = Some v') = 0"
        by (rule Greatest_equality) (auto simp add: fun_upd_def split: if_split_asm)

      from Nil show ?thesis using Suc.prems xs 
        by (simp add: list_map_def * fun_upd_def Greatest_equality)
    next
      case (Cons x' xs'')
      from Suc xs Cons obtain n: "n = length xs'" and non_empty: "xs'  []" and last: "last xs' = v"
        by simp
      
      from Suc.hyps (1)  [OF n non_empty last]
      have hyp: "list_map xs' (GREATEST x. v. list_map xs' x = Some v) = Some v" .
      from hyp show ?thesis by (simp add: xs Cons add: list_map_eq split: if_split_asm)
    qed
  qed
qed


lemma valid_root_footprint_valid_simple_footprint:
  assumes valid_td: "size_td t  addr_card"
  shows "valid_root_footprint d x t  valid_simple_footprint d x t"
  apply (clarsimp simp add: valid_root_footprint_def valid_simple_footprint_def Let_def)
proof -
  assume sz: "0 < size_td t"
  assume root[rule_format]: "y < size_td t. snd (d (x + word_of_nat y)) = list_map (typ_slice_t t y)  fst (d (x + word_of_nat y))"
  from root [of 0, simplified] sz obtain 
    d_x: "d x = (True, list_map (typ_slice_t t 0))"
    by (cases "d x") auto

  have x_HeapType: "heap_type_tag d x = HeapType t"
  proof -
    from last_typ_slice_t have "last (typ_slice_t t 0) = (t, True)" .
  
    from list_map_greatest_last [OF typ_slice_t_not_empty this]
    have *: "list_map (typ_slice_t t 0) (GREATEST k. a b. list_map (typ_slice_t t 0) k = Some (a,b)) = Some (t, True)"
      by auto
    from d_x  show ?thesis
      apply (clarsimp simp  add: heap_type_tag_def *, intro conjI)
      using * apply blast
      by (metis less_irrefl list_map_eq option.simps(3))
  qed

  show "heap_type_tag d x = HeapType t  (y. y  {x + 1..+size_td t - Suc 0}  heap_type_tag d y = HeapFootprint)"
  proof
    from x_HeapType 
    show "heap_type_tag d x = HeapType t" .
  next
    show "y. y  {x + 1..+size_td t - Suc 0}  heap_type_tag d y = HeapFootprint"
    proof (standard+)
      fix y
      assume y_bounds: "y  {x + 1..+size_td t - Suc 0}"
      with sz obtain off where y_off: "y = x + word_of_nat off" and off: "off < size_td t" 
        by (meson intvlD intvl_plus_sub_Suc)
      from root [OF off] y_off have d_y: "d y = (True, list_map (typ_slice_t t off))"
        by (cases "d (x + word_of_nat off)") auto
     

      have "x  {x + 1..+size_td t - Suc 0}"
        using intvl_Suc_nmem'' [OF valid_td [simplified addr_card_len_of_conv]] by blast
      with y_bounds y_off have off_non_zero: "off  0" by (cases off) auto

      from last_typ_slice_t_non_zero [OF off_non_zero] have "last (typ_slice_t t off) = (t, False)" .
      from list_map_greatest_last [OF typ_slice_t_not_empty this]
      have *: "list_map (typ_slice_t t off) (GREATEST k. a b. list_map (typ_slice_t t off) k = Some (a,b)) =
                 Some (t, False)"
        by auto
      from d_y show "heap_type_tag d y = HeapFootprint" 
        apply (clarsimp simp  add: heap_type_tag_def *, intro conjI)
        using * apply blast
        by (metis less_irrefl list_map_eq option.simps(3))
    qed
  qed
qed

lemma valid_root_footprint_valid_simple_footprint_typ_uinfo_t:
  assumes valid_root: "valid_root_footprint d x (typ_uinfo_t TYPE('a::mem_type))" 
  shows "valid_simple_footprint d x (typ_uinfo_t TYPE('a::mem_type))"
  apply (rule valid_root_footprint_valid_simple_footprint [OF _ valid_root])
  by (metis less_imp_le max_size size_of_def typ_uinfo_size)

lemma first_in_intvl:
  "b  0  a  {a ..+ b}"
  by (force simp: intvl_def)

lemma list_map_comono:
  assumes  s: "list_map m m list_map n"
  shows    "m  n"
  using s
proof (induct m arbitrary: n rule: rev_induct)
  case Nil thus ?case unfolding list_map_def by simp
next
  case (snoc x xs)

  from snoc.prems have
    sm: "[length xs  x] ++ list_map xs m list_map n"
    unfolding list_map_def by simp

  hence xsn: "xs  n"
    by (rule snoc.hyps [OF map_add_le_mapE])

  have "list_map n (length xs) = Some x" using sm
    by (simp add: map_le_def list_map_def merge_dom2 set_zip)

  hence "length xs < length n" and "x = n ! length xs"
    by (auto simp add: list_map_eq split: if_split_asm)

  thus "xs @ [x]  n" using xsn
    by (simp add: append_one_prefix less_eq_list_def)
qed

lemma valid_root_footprint_overlap_sub_typ: 
  assumes valid_root_x: "valid_root_footprint d x t"
  assumes valid_y: "valid_footprint d y s" 
  assumes overlap: "{x ..+ size_td t}  {y ..+ size_td s}  {}"
  shows "s  t"
  using valid_root_x overlap valid_y 
  by (auto simp add: valid_root_footprint_def valid_footprint_def Let_def typ_tag_le_def)
    (metis intvlD list_map_comono typ_slice_sub typ_tag_le_def)

lemma valid_root_footprint_type_neq:
  " valid_root_footprint d p s;
     valid_root_footprint d q t;
     s  t  
   p  {q..+ (size_td t)}"
  by (metis antisym_conv disjoint_iff first_in_intvl neq0_conv valid_root_footprint_def 
      valid_root_footprint_overlap_sub_typ valid_root_footprint_valid_footprint)

lemma valid_root_footprint_ptr_force_type_iff:
  fixes p :: "'a::mem_type ptr"
  assumes t: "wf_size_desc t"
  shows "valid_root_footprint (ptr_force_type p s) a t 
    (valid_root_footprint s a t  disjnt {a ..+ size_td t} (ptr_span p)) 
    (t = typ_uinfo_t TYPE('a)  p = Ptr a)"
proof cases
  assume disjnt: "disjnt {a ..+ size_td t} (ptr_span p)"
  moreover have "p  Ptr a"
    using disjnt[unfolded disjnt_iff, THEN spec, of a] t[THEN wf_size_desc_gt(1)]
    by (cases p) (auto simp: intvl_self)
  moreover have "valid_root_footprint (ptr_force_type p s) a t = valid_root_footprint s a t"
    using t disjnt
    by (intro valid_root_footprint_cong_state ptr_force_type_disj) (simp_all add: disjnt_iff)
  ultimately show ?thesis
    by simp
next
  assume ndisjnt: "¬ disjnt {a ..+ size_td t} (ptr_span p)"
  show ?thesis
    using ndisjnt
    by (metis (no_types) valid_root_footprint_valid_simple_footprint_typ_uinfo_t disjnt_def
        valid_root_foot_print_ptr_force_type intvl_inter ptr_val.ptr_val_def size_of_tag t
        valid_simple_footprint_ptr_force_type_iff valid_root_footprint_type_neq)
qed

lemma valid_root_footprint_fold_ptr_force_type_iff:
  fixes ps :: "'a::mem_type ptr list"
  assumes [simp]: "wf_size_desc t"
  shows "distinct_prop (λp1 p2. disjnt (ptr_span p1) (ptr_span p2)) ps 
    valid_root_footprint (fold ptr_force_type ps s) a t 
      (valid_root_footprint s a t  disjnt {a ..+ size_td t} (pset ps. ptr_span p)) 
      (t = typ_uinfo_t TYPE('a)  Ptr a  set ps)"
  by (induction ps arbitrary: s)
     (auto simp: valid_root_footprint_ptr_force_type_iff size_of_tag distinct_prop_append)

(* Two valid footprints will either overlap completely or not at all. *)
lemma valid_simple_footprint_neq:
  assumes valid_p: "valid_simple_footprint d p s"
      and valid_q: "valid_simple_footprint d q t"
      and neq: "p  q"
  shows "p  {q..+ (size_td t)}"
proof -
  have heap_type_p: "heap_type_tag d p = HeapType s"
    apply (metis valid_p valid_simple_footprint_def)
    done

  have heap_type_q: "heap_type_tag d q = HeapType t"
    apply (metis valid_q valid_simple_footprint_def)
    done

  have heap_type_q_footprint:
    "x. x  {(q + 1)..+(size_td t - Suc 0)}  heap_type_tag d x = HeapFootprint"
    apply (insert valid_q)
    apply (simp add: valid_simple_footprint_def)
    done

  show ?thesis
    using heap_type_q_footprint heap_type_p neq
         intvl_neq_start heap_type_q
    by (metis heap_typ_contents.simps(2))
qed

(* Two valid footprints with different types will never overlap. *)
lemma valid_simple_footprint_type_neq:
  " valid_simple_footprint d p s;
     valid_simple_footprint d q t;
     s  t  
   p  {q..+ (size_td t)}"
  apply (subgoal_tac "p  q")
   apply (rule valid_simple_footprint_neq, simp_all)[1]
  apply (clarsimp simp: valid_simple_footprint_def)
  done

lemma valid_simple_footprint_neq_disjoint:
  " valid_simple_footprint d p s; valid_simple_footprint d q t; p  q  
      {p..+(size_td s)}  {q..+ (size_td t)} = {}"
  apply (rule ccontr)
  apply (fastforce simp: valid_simple_footprint_neq dest!: intvl_inter)
  done

lemma valid_simple_footprint_type_neq_disjoint:
  " valid_simple_footprint d p s;
     valid_simple_footprint d q t;
     s  t  
   {p..+(size_td s)}  {q..+ (size_td t)} = {}"
  apply (subgoal_tac "p  q")
   apply (rule valid_simple_footprint_neq_disjoint, simp_all)[1]
  apply (clarsimp simp: valid_simple_footprint_def)
  done

lemma valid_simple_footprint_disjnt_or_eq:
  "valid_simple_footprint d a1 t1  valid_simple_footprint d a2 t2 
    disjnt {a1 ..+ size_td t1} {a2 ..+ size_td t2}  (a1 = a2  t1 = t2)"
  using valid_simple_footprint_type_neq_disjoint[of d a1 t1 a2 t2]
  using valid_simple_footprint_neq_disjoint[of d a1 t1 a2 t2]
  by (auto simp: disjnt_def)

lemma valid_root_footprint_type_neq_disjoint:
  " valid_root_footprint d p s;
     valid_root_footprint d q t;
     s  t  
   {p..+(size_td s)}  {q..+ (size_td t)} = {}"
  by (metis intvl_inter valid_root_footprint_type_neq)

lemma valid_root_footprint_neq:
  assumes valid_p: "valid_root_footprint d p s"
      and valid_q: "valid_root_footprint d q t"
      and neq: "p  q"
    shows "p  {q..+ (size_td t)}"
proof 
  assume p: "p  {q..+size_td t}"
  then have "¬ t  s"
    by (metis (no_types, opaque_lifting) less_irrefl less_le_trans neq valid_footprint_sub2 
        valid_p valid_q valid_root_footprint_valid_footprint)
  moreover
  from p have "{p..+size_td s}  {q..+size_td t}  {}"
    by (metis disjoint_iff first_in_intvl less_irrefl valid_p valid_root_footprint_def)

  from valid_root_footprint_overlap_sub_typ [OF valid_p  valid_root_footprint_valid_footprint [OF valid_q ] this] 
  have "t  s" .
  ultimately show False
    by blast
qed

lemma valid_root_footprint_neq_disjoint:
  " valid_root_footprint d p s; valid_root_footprint d q t; p  q  
      {p..+(size_td s)}  {q..+ (size_td t)} = {}"
  by (metis intvl_inter valid_root_footprint_neq)

lemma valid_root_footprint_disjnt_or_eq:
  "valid_root_footprint d a1 t1  valid_root_footprint d a2 t2 
    disjnt {a1 ..+ size_td t1} {a2 ..+ size_td t2}  (a1 = a2  t1 = t2)"
  using valid_root_footprint_type_neq_disjoint[of d a1 t1 a2 t2]
  using valid_root_footprint_neq_disjoint[of d a1 t1 a2 t2]
  by (auto simp: disjnt_def)

definition ptr_aligned_u :: "typ_uinfo  addr  bool" where
  "ptr_aligned_u t a   2^(align_td t) dvd unat a"

lemma ptr_aligned_ptr_aligned_u_conv:
  fixes p::"'a::c_type ptr"
  shows "ptr_aligned p = ptr_aligned_u (typ_uinfo_t TYPE('a)) (ptr_val p)"
  by (simp add: ptr_aligned_def ptr_aligned_u_def align_of_def typ_uinfo_t_def)

definition c_null_guard_u :: "typ_uinfo  addr  bool" where
  "c_null_guard_u t a  0  {a..+size_td t}"

lemma c_null_guard_c_null_guard_u_conv:
  fixes p::"'a::c_type ptr"
  shows "c_null_guard p = c_null_guard_u (typ_uinfo_t TYPE('a)) (ptr_val p)"
  by (simp add: c_null_guard_def c_null_guard_u_def size_of_def)

definition c_guard_u :: "typ_uinfo  addr  bool" where
  "c_guard_u t a   ptr_aligned_u t a  c_null_guard_u t a"

lemma c_guard_c_guard_u_conv:
  fixes p::"'a::c_type ptr"
  shows "c_guard p = c_guard_u (typ_uinfo_t TYPE('a)) (ptr_val p)"
  by (simp add: ptr_aligned_ptr_aligned_u_conv c_guard_def c_guard_u_def c_null_guard_c_null_guard_u_conv)

definition
  root_ptr_valid_u :: "typ_uinfo  heap_typ_desc  addr  bool" where
  "root_ptr_valid_u t d a  valid_root_footprint d a t  c_guard_u t a"

definition
  cvalid_u :: "typ_uinfo  heap_typ_desc  addr  bool" where
  "cvalid_u t d a  valid_footprint d a t  c_guard_u t a"

lemma root_ptr_valid_root_ptr_valid_u_conv:
  fixes p::"'a::c_type ptr"
  shows "root_ptr_valid d p = root_ptr_valid_u (typ_uinfo_t TYPE('a)) d (ptr_val p)"
  by (simp add: root_ptr_valid_def root_ptr_valid_u_def c_guard_c_guard_u_conv)

lemma root_ptr_valid_ptr_force_type:
  "c_guard p  root_ptr_valid (ptr_force_type p s) (p::'a::mem_type ptr)"
  by (simp add: root_ptr_valid_root_ptr_valid_u_conv root_ptr_valid_u_def c_guard_c_guard_u_conv
                valid_root_foot_print_ptr_force_type)
lemma cvalid_cvalid_u_conv:
  fixes p::"'a::c_type ptr"
  shows "d t p = cvalid_u (typ_uinfo_t TYPE('a)) d (ptr_val p)"
  by  (simp add: h_t_valid_def cvalid_u_def c_guard_c_guard_u_conv)

lemma root_ptr_valid_u_cvalid_u: "root_ptr_valid_u t d a  cvalid_u t d a"
  by (simp add: root_ptr_valid_u_def cvalid_u_def valid_root_footprint_valid_footprint)

lemma fold_root_ptr_valid_u:
 "root_ptr_valid_u (typ_uinfo_t TYPE('a::c_type)) d a = root_ptr_valid d (PTR ('a) a)"
  by (simp add: root_ptr_valid_root_ptr_valid_u_conv)

lemma ptr_force_type_eq_ptr_force_type_u:
  "ptr_force_type (p :: 'a::c_type ptr) = ptr_force_type_u (typ_uinfo_t TYPE('a)) (ptr_val p)"
  by (simp add: ptr_force_type_def ptr_force_type_u_def typ_slices_def typ_slices_u_def
      size_of_def)

lemma typ_slices_u_length [simp]: "length (typ_slices_u t) = size_td t"
  by (simp add: typ_slices_u_def)

lemma typ_slices_u_index [simp]:
  "n < size_td t  typ_slices_u t ! n = list_map (typ_slice_t t n)"
  by (simp add: typ_slices_u_def)

lemma valid_root_footprint_ptr_force_type_u:
  "wf_size_desc t  size_td t < addr_card 
    valid_root_footprint (ptr_force_type_u t a h) a t"
  by (simp add: valid_root_footprint_def ptr_force_type_u_def Let_def wf_size_desc_gt htd_upd_at)

lemma valid_root_footprint_ptr_force_type_u_size:
  "wf_size_desc t  size_td t < addr_card 
    valid_root_footprint (ptr_force_type_u t a h) a t"
  by (simp add: valid_root_footprint_def ptr_force_type_u_def Let_def wf_size_desc_gt htd_upd_at)


definition 
stack_alloc:: "addr set  'a:: mem_type itself  heap_typ_desc  ('a ptr × heap_typ_desc) set" where
stack_alloc 𝒮 T d = {
  (p::'a ptr, d').
     ptr_span p  𝒮  
     typ_uinfo_t (TYPE('a))  typ_uinfo_t (TYPE(stack_byte)) 
     (a  ptr_span p. root_ptr_valid d (PTR (stack_byte) a)) 
     ptr_aligned p  
     d' = ptr_force_type p d  
    }

(* FIXME support for local arrays*)
definition 
stack_allocs:: "nat  addr set  'a:: mem_type itself  heap_typ_desc  ('a ptr × heap_typ_desc) set" where
stack_allocs n 𝒮 T d = {
  (p::'a ptr, d').
     0 < n 
     (i < n. ptr_span (p +p int i)  𝒮)  
     typ_uinfo_t (TYPE('a))  typ_uinfo_t (TYPE(stack_byte)) 
     (a  {ptr_val p ..+ n * size_of TYPE('a)} . root_ptr_valid d (PTR (stack_byte) a)) 
     ptr_aligned p  
     d' = fold (λi. ptr_force_type (p +p int i)) [0..<n] d  
    }

lemma stack_alloc_stack_allocs_conv: "stack_alloc = stack_allocs 1"
  by (simp add: stack_alloc_def stack_allocs_def fun_eq_iff)

lemma htd_update_list_other:  
  assumes bound: "length xs < addr_card"
  assumes notin: "x  {p..+length xs}" 
  shows "htd_update_list p xs d x = d x"
  using bound notin
proof (induct xs arbitrary: p d x)
  case Nil
  then show ?case by simp
next
  case (Cons x1 xs)
  from Cons.prems obtain 
    "Suc (length xs) < addr_card" and
    bound': "length xs < addr_card" and
    "x  {p..+Suc (length xs)}" and
    notin': "x  {(p + 1)..+(length xs)}" and
    neq: "x  p" 
    apply clarsimp
    by (metis One_nat_def add_diff_cancel_left' intvl_plus_sub_Suc intvl_self 
        plus_1_eq_Suc zero_less_Suc)

  note hyp = Cons.hyps [OF bound' notin', where d = "(d(p := (True, snd (d p) ++ x1)))"]
  show ?case by (simp add: hyp fun_upd_other neq)
qed

lemma dom_typ_slice_t_stack_byte: 
  "dom (list_map (typ_slice_t (typ_uinfo_t TYPE(stack_byte)) n)) = {0}"
  by (simp add: typ_info_stack_byte typ_uinfo_t_def)

lemma htd_update_list_same':
  "0 < unat k; unat k  addr_card - length v  htd_update_list (p + k) v h p = h p"
  apply (insert htd_update_list_same [where v=v and p=p and h=h and k="unat k"])
  apply clarsimp
  done

lemma dom_htd_update_list:
 assumes xs_bound: "length xs < addr_card" 
 assumes n_bound: "n < length xs" 
 shows "dom (snd (htd_update_list a xs d (a + word_of_nat n))) =
         dom (snd (d (a + word_of_nat n)))  dom (xs ! n)"
  using xs_bound n_bound
proof (induct xs arbitrary: n a d)
  case Nil
  then show ?case by simp
next
  case (Cons x xs)
  from Cons.prems obtain 
    "Suc (length xs) < addr_card" and
    lxs: "length xs < addr_card" and
    n: "n < Suc (length xs)"
    by auto
  note hyp =  Cons.hyps [OF lxs]
  show ?case
  proof (cases n)
    case 0
    show ?thesis
      apply (simp add: 0)
      by (smt (verit, ccfv_threshold) One_nat_def add_cancel_left_right add_diff_cancel_left' 
          dom_map_add fun_upd_same htd_update_list_same' le_eq_less_or_eq le_iff_add 
          less_Suc_eq_0_disj lxs nat_neq_iff snd_conv sup_commute unsigned_1 zero_order(1))
  next
    case (Suc m)
    have conv: "a + 1 + word_of_nat m = a + (1 + word_of_nat m)"
      by simp
  
    from Suc n have m_xs: "m < length xs" by simp
    note hyp = hyp [where a = "a + 1" and d = "(d(a := (True, snd (d a) ++ x)))" and n = m, OF m_xs, simplified conv] 
    show ?thesis 
      apply (simp add: Suc)
      apply (simp add: hyp )
      by (smt (verit, best) add_cancel_left_right add_diff_cancel_left' add_leE fun_upd_other 
          less_natE linorder_not_less lxs m_xs one_plus_x_zero plus_1_eq_Suc)
  qed
qed

lemma dom_ptr_retyp:
  fixes p::"'a::mem_type ptr"
  assumes n: "n < size_of TYPE('a)"
  shows "dom (snd (ptr_retyp p d (ptr_val p + of_nat n))) = 
          dom (snd (d (ptr_val p + of_nat n)))  
          {0..<length (typ_slice_t (typ_uinfo_t TYPE('a)) n)}"
proof -
  have sz_bound: "size_of TYPE('a) < addr_card" by simp
  hence l_slices: "length (typ_slices TYPE('a)) < addr_card" by simp
  with sz_bound n have n': "n < length (typ_slices TYPE('a))" by simp 

  from n'
  have dom_conv: "dom (typ_slices TYPE('a) ! n) = {0..<length (typ_slice_t (typ_uinfo_t TYPE('a)) n)}"
    by (simp)
  show ?thesis
    apply (simp add: ptr_retyp_def size_of_def )
    apply (simp add:  dom_htd_update_list [OF l_slices n', where a = "ptr_val p" and d = d] dom_conv)
    done
qed

lemma length_typ_slice_t: "0 < length (typ_slice_t t n )" 
  by (cases t) auto 

lemma valid_root_footprint_retyp_stack':
  fixes p::"'a::mem_type ptr"
  assumes stack: "a  ptr_span p. valid_root_footprint d a (typ_uinfo_t TYPE(stack_byte))"
  shows "valid_root_footprint(ptr_retyp p d) (ptr_val p) (typ_uinfo_t TYPE('a))"
proof -
  {
    fix n 
    assume n_bound: "n < size_td (typ_info_t TYPE('a))"
    have "dom (snd (ptr_retyp p d (ptr_val p + word_of_nat n))) =
           {0..<length (typ_slice_t (typ_uinfo_t TYPE('a)) n)}" 
    proof -
      from n_bound have n_sz: "n < size_of (TYPE('a))" by (simp add: size_of_def)
      from n_sz
      have "ptr_val p + word_of_nat n  ptr_span p"
        by (simp add: intvlI)
      note conv = valid_root_footprint_dom_typing [OF stack [rule_format, OF this], where n=0, simplified ]
      have dom_eq: "dom (snd (d (ptr_val p + word_of_nat n))) = {0}"
        by (simp add: conv typ_uinfo_t_def typ_info_stack_byte)

      from length_typ_slice_t have l: "0 < length (typ_slice_t (typ_uinfo_t TYPE('a)) n)" by simp
      show ?thesis 
        apply (simp add: dom_ptr_retyp [OF n_sz] dom_eq )
        using l atLeastLessThan_iff by blast
    qed
  } note  dom_conv = this
  show ?thesis
    by (simp add: valid_root_footprint_valid_footprint_dom_conv ptr_retyp_valid_footprint dom_conv)
qed


lemma (in mem_type) ptr_force_type_valid_footprint:
  "valid_footprint (ptr_force_type p d) (ptr_val (p::'a ptr)) (typ_uinfo_t TYPE('a))"
  using valid_root_foot_print_ptr_force_type valid_root_footprint_valid_footprint by blast

lemma ptr_force_type_valid_footprint:
  "valid_footprint (ptr_force_type p d) (ptr_val (p::'a::mem_type ptr)) (typ_uinfo_t TYPE('a))"
  using valid_root_foot_print_ptr_force_type valid_root_footprint_valid_footprint by blast

lemma valid_root_footprint_retyp_stack:
  fixes p::"'a::mem_type ptr"
  assumes stack: "a  ptr_span p. valid_root_footprint d a (typ_uinfo_t TYPE(stack_byte))"
  shows "valid_root_footprint(ptr_force_type p d) (ptr_val p) (typ_uinfo_t TYPE('a))"
proof -
  {
    fix n 
    assume n_bound: "n < size_td (typ_info_t TYPE('a))"
    have "dom (snd (ptr_force_type p d (ptr_val p + word_of_nat n))) =
           {0..<length (typ_slice_t (typ_uinfo_t TYPE('a)) n)}" 
    proof -
      from n_bound have n_sz: "n < size_of (TYPE('a))" by (simp add: size_of_def)
      from n_sz
      have "ptr_val p + word_of_nat n  ptr_span p"
        by (simp add: intvlI)
      note conv = valid_root_footprint_dom_typing [OF stack [rule_format, OF this], where n=0, simplified ]
      have dom_eq: "dom (snd (d (ptr_val p + word_of_nat n))) = {0}"
        by (simp add: conv typ_uinfo_t_def typ_info_stack_byte)

      from length_typ_slice_t have l: "0 < length (typ_slice_t (typ_uinfo_t TYPE('a)) n)" by simp
      show ?thesis 
        by (simp add: n_sz size_of_tag valid_root_foot_print_ptr_force_type 
            valid_root_footprint_dom_typing)
    qed
  } note  dom_conv = this
  show ?thesis
    by (simp add: valid_root_footprint_valid_footprint_dom_conv ptr_force_type_valid_footprint dom_conv)
qed

lemma root_ptr_valid_retyp_stack':
  fixes p::"'a::mem_type ptr"
  assumes stack: "a  ptr_span p. root_ptr_valid d (PTR (stack_byte) a)"
  assumes aligned: "ptr_aligned p"
  shows "root_ptr_valid (ptr_retyp p d) p"
proof -
  from stack 
  have stack': "a  ptr_span p. valid_root_footprint d a (typ_uinfo_t TYPE(stack_byte))"
    by (simp add: root_ptr_valid_def)
  from stack aligned have c_guard: "c_guard p"
    apply (simp add: root_ptr_valid_def c_guard_def c_null_guard_def)
    using first_in_intvl by blast
  
  from valid_root_footprint_retyp_stack' [OF stack'] 
  have "valid_root_footprint (ptr_retyp p d) (ptr_val p) (typ_uinfo_t TYPE('a))".
  with c_guard show ?thesis
    by (simp add: root_ptr_valid_def)
qed

lemma root_ptr_valid_retyp_stack:
  fixes p::"'a::mem_type ptr"
  assumes stack: "a  ptr_span p. root_ptr_valid d (PTR (stack_byte) a)"
  assumes aligned: "ptr_aligned p"
  shows "root_ptr_valid (ptr_force_type p d) p"
proof -
  from stack 
  have stack': "a  ptr_span p. valid_root_footprint d a (typ_uinfo_t TYPE(stack_byte))"
    by (simp add: root_ptr_valid_def)
  from stack aligned have c_guard: "c_guard p"
    apply (simp add: root_ptr_valid_def c_guard_def c_null_guard_def)
    using first_in_intvl by blast
  
  from valid_root_footprint_retyp_stack [OF stack'] 
  have "valid_root_footprint (ptr_force_type p d) (ptr_val p) (typ_uinfo_t TYPE('a))".
  with c_guard show ?thesis
    by (simp add: root_ptr_valid_def)
qed

lemma fold_ptr_retyp_other:
  fixes p::"'a::mem_type ptr"
  assumes a_notin: "a  {ptr_val p ..+ n * size_of TYPE('a)}"
  shows "(fold (λi. ptr_retyp (p +p int i)) [0..<n] d) a = d a"
  using a_notin 
proof (induct n arbitrary: d)
  case 0
  then show ?case by simp
next
  case (Suc n)
  from Suc.prems have a_notin: "a  {ptr_val p..+ Suc n * size_of TYPE('a)}" .
  from a_notin 
  have a_notin_n: "a  {ptr_val p..+ n * size_of TYPE('a)}" 
    by (metis add_leD2 intvlD intvlI linorder_not_less mult.commute mult_Suc_right)
  from a_notin 
  have a_notin_Suc: "a  ptr_span (p +p int n)"
    apply (clarsimp simp add: intvl_def ptr_add_def)
    by (metis (no_types, opaque_lifting) add.commute add_less_cancel_right mult.commute of_nat_add of_nat_mult)

  have fold_split: "(fold (λi. ptr_retyp (p +p int i)) ([0..<Suc n]) d) = 
        (fold (λi. ptr_retyp (p +p int i)) ([0..<n] @ [n]) d)"
    apply (simp only: Many_More.split_upt_on_n [where n=n])
    apply simp
    done
  show ?case 
    apply (simp only: fold_split)
    apply (simp only: fold_append)
    apply (simp)
    apply (subst (2) Suc.hyps [OF a_notin_n, symmetric])
    apply (rule ptr_retyp_d [OF a_notin_Suc])
    done
qed

lemma (in mem_type) ptr_force_type_d:
  "x  {ptr_val (p::'a ptr)..+size_of TYPE('a)} 
      ptr_force_type p d x = d x"
  by (simp add: htd_upd_disj local.ptr_force_type_def)

lemma fold_ptr_force_type_other:
  fixes p::"'a::mem_type ptr"
  assumes a_notin: "a  {ptr_val p ..+ n * size_of TYPE('a)}"
  shows "(fold (λi. ptr_force_type (p +p int i)) [0..<n] d) a = d a"
  using a_notin 
proof (induct n arbitrary: d)
  case 0
  then show ?case by simp
next
  case (Suc n)
  from Suc.prems have a_notin: "a  {ptr_val p..+ Suc n * size_of TYPE('a)}" .
  from a_notin 
  have a_notin_n: "a  {ptr_val p..+ n * size_of TYPE('a)}" 
    by (metis add_leD2 intvlD intvlI linorder_not_less mult.commute mult_Suc_right)
  from a_notin 
  have a_notin_Suc: "a  ptr_span (p +p int n)"
    apply (clarsimp simp add: intvl_def ptr_add_def)
    by (metis (no_types, opaque_lifting) add.commute add_less_cancel_right mult.commute of_nat_add of_nat_mult)

  have fold_split: "(fold (λi. ptr_force_type (p +p int i)) ([0..<Suc n]) d) = 
        (fold (λi. ptr_force_type (p +p int i)) ([0..<n] @ [n]) d)"
    apply (simp only: Many_More.split_upt_on_n [where n=n])
    apply simp
    done
  show ?case 
    apply (simp only: fold_split)
    apply (simp only: fold_append)
    apply (simp)
    apply (subst (2) Suc.hyps [OF a_notin_n, symmetric])
    apply (rule ptr_force_type_d [OF a_notin_Suc])
    done
qed

lemma root_ptr_valid_domain:
  fixes p::"'a::mem_type ptr"
  assumes "root_ptr_valid d p"
  assumes "a. a  ptr_span p  d' a = d a"
  shows "root_ptr_valid d' p"
  by (metis (no_types, lifting) assms(1) assms(2) root_ptr_valid_def s_footprintD s_footprintI2 size_of_tag valid_root_footprint_def)

lemma root_ptr_valid_domain': 
  fixes p::"'a::mem_type ptr"
  assumes "a.  a  ptr_span p   d' a = d a"
  shows "root_ptr_valid d' p = root_ptr_valid d p"
  by (metis assms root_ptr_valid_domain)

lemma root_ptr_valid_range_not_NULL:
  "root_ptr_valid htd (p :: ('a :: c_type) ptr)
       0  {ptr_val p ..+ size_of TYPE('a)}"
  apply (clarsimp simp: root_ptr_valid_def)
  apply (metis c_guard_def c_null_guard_def)
  done

lemma intvl_no_overflow_nat':
  assumes no_overflow: "unat a + b  2 ^ len_of TYPE('a::len)"
  shows "(x  {(a :: 'a word) ..+ b}) = (unat a  unat x  unat x < (unat a + b))"
  apply (simp add: intvl_def)
  using no_overflow
  apply (intro iffI conjI)
    apply (smt (verit) add.commute nat_add_left_cancel_less nat_le_iff_add trans_less_add1 unat_of_nat_len word_arith_nat_add)
   apply (smt (verit) add.commute nat_add_left_cancel_less nat_le_iff_add trans_less_add1 unat_of_nat_len word_arith_nat_add)
  by (metis nat_add_left_cancel_less nat_le_iff_add of_nat_add unat_eq_of_nat unat_lt2p)

lemma intvl_no_overflow_nat:
  assumes no_overflow: "unat a + b  addr_card"
  shows "(x  {(a :: addr_bitsize word) ..+ b}) = (unat a  unat x  unat x < (unat a + b))"
  using no_overflow unfolding addr_card_def using intvl_no_overflow_nat' by (metis card_word)

lemma intvl_no_overflow_nat_conv:
  assumes no_overflow: "unat a + b  addr_card"
  shows "{(a :: addr_bitsize word) ..+ b} = {x. (unat a  unat x  unat x < (unat a + b))}"
  using intvl_no_overflow_nat [OF no_overflow] by blast

lemma zero_not_in_intvl_no_overflow:
  "0  {a :: 'a::len word ..+ b}  unat a + b  2 ^ len_of TYPE('a)"
  apply (rule ccontr)
  apply (simp add: intvl_def not_le)
  apply (drule_tac x="2 ^ len_of TYPE('a) - unat a" in spec)
  apply (clarsimp simp: not_less)
  by (smt (verit) Nat.le_diff_conv2 add.commute add.left_neutral add_uminus_conv_diff 
       cancel_comm_monoid_add_class.diff_cancel diff_zero linorder_not_less 
        nat_less_le of_nat_numeral semiring_1_class.of_nat_power unat_0 
        unat_lt2p unat_minus' word_arith_nat_add word_exp_length_eq_0)

lemma root_ptr_valid_last_byte_no_overflow:
  "root_ptr_valid htd (p :: ('a :: c_type) ptr)
       unat (ptr_val p) + size_of TYPE('a)  2 ^ len_of TYPE(addr_bitsize)"
  by (metis c_guard_def c_null_guard_def root_ptr_valid_def
        zero_not_in_intvl_no_overflow)

lemma root_ptr_valid_retyps_stack':
  fixes p::"'a::mem_type ptr"
  assumes stack: "a  {ptr_val p ..+ n * size_of TYPE('a)}. root_ptr_valid d (PTR (stack_byte) a)"
  assumes aligned: "ptr_aligned p"
  assumes i_bound: "i < n"
  shows "root_ptr_valid (fold (λi. ptr_retyp (p +p int i)) [0..<n] d) (p +p int i)"
  using stack i_bound 
proof (induct n arbitrary: d)
  case 0
  then show ?case by simp
next
  case (Suc n)

  have stack: "a{ptr_val p..+ Suc n * size_of TYPE('a)}. root_ptr_valid d (PTR(stack_byte) a)" by fact
  from stack have stack_n: "a{ptr_val p..+ n * size_of TYPE('a)}. root_ptr_valid d (PTR(stack_byte) a)" 
    by (metis add.commute intvlD intvlI mult_Suc trans_less_add1)
  from stack
  have stack_Suc: "a  ptr_span (p +p int n). root_ptr_valid d (PTR(stack_byte) a)" 
    apply (clarsimp simp add: intvl_def ptr_add_def)
    by (metis (no_types, opaque_lifting) add.assoc add.commute add_less_cancel_right 
        of_nat_add of_nat_mult)

  from stack have no_overflow: "0  {ptr_val p..+ Suc n * size_of TYPE('a)}"
    apply (clarsimp simp add: intvl_def)
    by (metis c_guard_NULL_simp root_ptr_valid_def)

  from stack have no_overflow': "a  {ptr_val p..+ Suc n * size_of TYPE('a)}. 0  ptr_span (PTR(stack_byte) a)"
    apply (clarsimp simp add: intvl_def)
    by (metis c_guard_NULL_simp root_ptr_valid_def)

  have i_bound: "i < Suc n" by fact

  have fold_split: "(fold (λi. ptr_retyp (p +p int i)) ([0..<Suc n]) d) = 
        (fold (λi. ptr_retyp (p +p int i)) ([0..<n] @ [n]) d)"
    apply (simp only: Many_More.split_upt_on_n [where n=n])
    apply simp
    done

  have sz_a: "0 < size_of (TYPE('a))"
    by simp
  hence sz_bound: "size_of TYPE(stack_byte)  size_of TYPE('a)"
    using size_of_stack_byte(1) by linarith

  from stack 
  have bound3: "unat (ptr_val p) + n * size_of (TYPE('a)) < addr_card"
    by (smt (verit, ccfv_SIG) add.commute add.left_neutral add_less_cancel_right c_guard_NULL_simp len_of_addr_card less_le mult.commute mult_Suc_right not_less root_ptr_valid_def stack_n sz_a zero_not_in_intvl_no_overflow)
  from bound3 have unat_dist1: "unat (ptr_val (p +p int n)) = unat (ptr_val p) + n * size_of (TYPE('a)) "
    by (smt (verit, ccfv_threshold) Abs_fnat_hom_add Abs_fnat_hom_mult CTypesDefs.ptr_add_def len_of_addr_card 
          of_int_of_nat_eq of_nat_inverse ptr_val.ptr_val_def word_unat.Rep_inverse')
  show ?case
  proof (cases "i=n")
    case True
    show ?thesis
      apply (simp only: fold_split)
      apply (simp only: fold_append)
      apply (simp add: True)
      apply (rule root_ptr_valid_retyp_stack')
      subgoal
        apply clarify
        apply (rule root_ptr_valid_domain)
         apply (rule stack_Suc [rule_format])
         apply assumption
        apply (rule fold_ptr_retyp_other)
        apply (subst (asm) intvl_no_overflow_nat_conv)
        subgoal
          apply (simp add: unat_dist1)
          using bound3
          by (metis c_guard_NULL_simp len_of_addr_card root_ptr_valid_def stack_Suc unat_dist1 zero_not_in_intvl_no_overflow)
        apply (subst (asm) intvl_no_overflow_nat_conv)
        subgoal
          apply simp
          using bound3
          by (metis Suc_leI len_of_addr_card unat_lt2p)
        apply (subst intvl_no_overflow_nat_conv)
        subgoal 
          using bound3
          by (simp add: mult.commute)
        subgoal using bound3 by (simp add: mult.commute unat_dist1)
        done
      subgoal 
        using aligned ptr_aligned_plus by blast
      done
  next
    case False
    with i_bound have i_bound_n: "i < n" by simp
    from no_overflow have bound1: "unat (ptr_val p + word_of_nat n * word_of_nat (size_of TYPE('a))) < addr_card"
      by (metis len_of_addr_card unsigned_less)
    from Suc.hyps [OF stack_n i_bound_n] 
    have hyp: "root_ptr_valid (fold (λi. ptr_retyp (p +p int i)) [0..<n] d) (p +p int i)" .
    from bound3 i_bound_n have unat_dist2: "unat (ptr_val (p +p int i)) = unat (ptr_val p) + i * size_of (TYPE('a)) "
      by (smt (verit, ccfv_threshold) Abs_fnat_hom_add Abs_fnat_hom_mult CTypesDefs.ptr_add_def add.commute 
          add_less_cancel_right len_of_addr_card mult_strict_right_mono nat_less_le of_int_of_nat_eq of_nat_inverse 
          order_less_le_trans ptr_val.ptr_val_def sz_nzero word_unat.Rep_inverse')
    show ?thesis 
      apply (simp only: fold_split)
      apply (simp only: fold_append)
      apply (simp)
      apply (rule root_ptr_valid_domain)
       apply (rule hyp)
      apply (rule ptr_retyp_d)

      apply (subst intvl_no_overflow_nat_conv)
      subgoal
        using bound1
        by (metis c_guard_NULL_simp len_of_addr_card root_ptr_valid_def stack_Suc zero_not_in_intvl_no_overflow)
      subgoal for a
        apply (subst (asm) intvl_no_overflow_nat_conv)
        subgoal using bound3 i_bound_n
          apply (simp add: ptr_add_def)
          by (metis (no_types, lifting) CTypesDefs.ptr_add_def hyp len_of_addr_card of_int_of_nat_eq 
              ptr_val.ptr_val_def root_ptr_valid_last_byte_no_overflow)
        subgoal using i_bound_n
          by (auto simp add: unat_dist1 unat_dist2 dest!: le_less_trans)
            (metis add.commute le_def less_le_mult_nat mem_type_class.mem_type_simps(3))
        done
      done
  qed  
qed

lemma root_ptr_valid_retyps_stack:
  fixes p::"'a::mem_type ptr"
  assumes stack: "a  {ptr_val p ..+ n * size_of TYPE('a)}. root_ptr_valid d (PTR (stack_byte) a)"
  assumes aligned: "ptr_aligned p"
  assumes i_bound: "i < n"
  shows "root_ptr_valid (fold (λi. ptr_force_type (p +p int i)) [0..<n] d) (p +p int i)"
  using stack i_bound 
proof (induct n arbitrary: d)
  case 0
  then show ?case by simp
next
  case (Suc n)

  have stack: "a{ptr_val p..+ Suc n * size_of TYPE('a)}. root_ptr_valid d (PTR(stack_byte) a)" by fact
  from stack have stack_n: "a{ptr_val p..+ n * size_of TYPE('a)}. root_ptr_valid d (PTR(stack_byte) a)" 
    by (metis add.commute intvlD intvlI mult_Suc trans_less_add1)
  from stack
  have stack_Suc: "a  ptr_span (p +p int n). root_ptr_valid d (PTR(stack_byte) a)" 
    apply (clarsimp simp add: intvl_def ptr_add_def)
    by (metis (no_types, opaque_lifting) add.assoc add.commute add_less_cancel_right 
        of_nat_add of_nat_mult)

  from stack have no_overflow: "0  {ptr_val p..+ Suc n * size_of TYPE('a)}"
    apply (clarsimp simp add: intvl_def)
    by (metis c_guard_NULL_simp root_ptr_valid_def)

  from stack have no_overflow': "a  {ptr_val p..+ Suc n * size_of TYPE('a)}. 0  ptr_span (PTR(stack_byte) a)"
    apply (clarsimp simp add: intvl_def)
    by (metis c_guard_NULL_simp root_ptr_valid_def)

  have i_bound: "i < Suc n" by fact

  have fold_split: "(fold (λi. ptr_force_type (p +p int i)) ([0..<Suc n]) d) = 
        (fold (λi. ptr_force_type (p +p int i)) ([0..<n] @ [n]) d)"
    apply (simp only: Many_More.split_upt_on_n [where n=n])
    apply simp
    done

  have sz_a: "0 < size_of (TYPE('a))"
    by simp
  hence sz_bound: "size_of TYPE(stack_byte)  size_of TYPE('a)"
    using size_of_stack_byte(1) by linarith

  from stack 
  have bound3: "unat (ptr_val p) + n * size_of (TYPE('a)) < addr_card"
    by (smt (verit, ccfv_SIG) add.commute add.left_neutral add_less_cancel_right c_guard_NULL_simp len_of_addr_card less_le mult.commute mult_Suc_right