Theory JMM_JVM_Typesafe

(*  Title:      JinjaThreads/MM/JMM_JVM_Typesafe.thy
    Author:     Andreas Lochbihler
*)

section ‹JMM type safety for bytecode›

theory JMM_JVM_Typesafe
imports
  JMM_Typesafe2
  DRF_JVM
begin

locale JVM_allocated_heap_conf' = 
  h: JVM_heap_conf 
    addr2thread_id thread_id2addr
    spurious_wakeups
    empty_heap allocate "λ_. typeof_addr" heap_read heap_write hconf
    P
  +
  h: JVM_allocated_heap 
    addr2thread_id thread_id2addr
    spurious_wakeups
    empty_heap allocate "λ_. typeof_addr" heap_read heap_write
    allocated
    P
  +
  heap''
    addr2thread_id thread_id2addr
    spurious_wakeups
    empty_heap allocate typeof_addr heap_read heap_write
    P
  for addr2thread_id :: "('addr :: addr)  'thread_id"
  and thread_id2addr :: "'thread_id  'addr"
  and spurious_wakeups :: bool
  and empty_heap :: "'heap"
  and allocate :: "'heap  htype  ('heap × 'addr) set"
  and typeof_addr :: "'addr  htype"
  and heap_read :: "'heap  'addr  addr_loc  'addr val  bool"
  and heap_write :: "'heap  'addr  addr_loc  'addr val  'heap  bool"
  and hconf :: "'heap  bool"
  and allocated :: "'heap  'addr set"
  and P :: "'addr jvm_prog"

sublocale JVM_allocated_heap_conf' < h: JVM_allocated_heap_conf
  addr2thread_id thread_id2addr
  spurious_wakeups
  empty_heap allocate "λ_. typeof_addr" heap_read heap_write hconf allocated
  P
by(unfold_locales)

context JVM_allocated_heap_conf' begin

lemma exec_instr_New_type_match:
  " (ta, s')  h.exec_instr i P t h stk loc C M pc frs; NewHeapElem ad CTn  set tao; typeof_addr ad  None 
   typeof_addr ad = CTn"
by(cases i)(auto split: if_split_asm prod.split_asm dest: allocate_typeof_addr_SomeD red_external_aggr_New_type_match)

lemma mexecd_New_type_match:
  " h.mexecd P t (xcpfrs, h) ta (xcpfrs', h'); NewHeapElem ad CTn  set tao; typeof_addr ad  None 
   typeof_addr ad = CTn"
apply(cases xcpfrs)
apply(cases xcpfrs')
apply(simp add: split_beta)
apply(erule h.jvmd_NormalE)
apply(cases "fst xcpfrs")
apply(auto 4 3 dest: exec_instr_New_type_match)
done

lemma mexecd_known_addrs_typing':
  assumes wf: "wf_jvm_prog⇘ΦP"
  and ok: "h.start_heap_ok"
  shows "known_addrs_typing' addr2thread_id thread_id2addr empty_heap allocate typeof_addr heap_write allocated h.jvm_known_addrs JVM_final (h.mexecd P) (λt (xcp, frs) h. h.correct_state Φ t (xcp, h, frs)) P"
proof -
  interpret known_addrs_typing
    addr2thread_id thread_id2addr 
    spurious_wakeups
    empty_heap allocate "λ_. typeof_addr" heap_read heap_write
    allocated h.jvm_known_addrs
    JVM_final "h.mexecd P" "λt (xcp, frs) h. h.correct_state Φ t (xcp, h, frs)"
    P
    using assms by(rule h.mexecd_known_addrs_typing)

  show ?thesis by(unfold_locales)(erule mexecd_New_type_match)
qed

lemma JVM_weakly_legal_read_value_typeable:
  assumes wf: "wf_jvm_prog⇘ΦP"
  and wf_start: "h.wf_start_state P C M vs"
  and legal: "weakly_legal_execution P (h.JVMd_ℰ P C M vs status) (E, ws)"
  and a: "enat a < llength E"
  and read: "action_obs E a = NormalAction (ReadMem ad al v)"
  shows "T. P  ad@al : T  P  v :≤ T"
proof -
  note wf
  moreover from wf_start have "h.start_heap_ok" by cases
  moreover from wf wf_start
  have "ts_ok (λt (xcp, frs) h. h.correct_state Φ t (xcp, h, frs)) (thr (h.JVM_start_state P C M vs)) h.start_heap"
    using h.correct_jvm_state_initial[OF wf wf_start]
    by(simp add: h.correct_jvm_state_def h.start_state_def split_beta)
  moreover from wf obtain wf_md where wf': "wf_prog wf_md P" by(blast dest: wt_jvm_progD)
  hence "wf_syscls P" by(rule wf_prog_wf_syscls) 
  ultimately show ?thesis using legal a read
    by(rule known_addrs_typing'.weakly_legal_read_value_typeable[OF mexecd_known_addrs_typing'])
qed

end


abbreviation jmm_JVMd_ℰ
  :: "addr jvm_prog  cname  mname  addr val list  status  (addr × (addr, addr) obs_event action) llist set"
where 
  "jmm_JVMd_ℰ P  
  JVM_heap_base.JVMd_ℰ addr2thread_id thread_id2addr jmm_spurious_wakeups jmm_empty jmm_allocate (jmm_typeof_addr P) jmm_heap_read jmm_heap_write P"

abbreviation jmm'_JVMd_ℰ
  :: "addr jvm_prog  cname  mname  addr val list  status  (addr × (addr, addr) obs_event action) llist set"
where 
  "jmm'_JVMd_ℰ P  
  JVM_heap_base.JVMd_ℰ addr2thread_id thread_id2addr jmm_spurious_wakeups jmm_empty jmm_allocate (jmm_typeof_addr P) (jmm_heap_read_typed P) jmm_heap_write P"

abbreviation jmm_JVM_start_state
  :: "addr jvm_prog  cname  mname  addr val list  (addr,thread_id,addr jvm_thread_state,JMM_heap,addr) state"
where "jmm_JVM_start_state  JVM_heap_base.JVM_start_state addr2thread_id jmm_empty jmm_allocate"

lemma jmm_JVM_heap_conf:
  "JVM_heap_conf addr2thread_id thread_id2addr jmm_empty jmm_allocate (jmm_typeof_addr P) jmm_heap_write jmm_hconf P"
by(unfold_locales)

lemma jmm_JVMd_allocated_heap_conf':
  "JVM_allocated_heap_conf' addr2thread_id thread_id2addr jmm_empty jmm_allocate (jmm_typeof_addr' P) jmm_heap_write jmm_hconf jmm_allocated P"
apply(rule JVM_allocated_heap_conf'.intro)
apply(unfold jmm_typeof_addr'_conv_jmm_typeof_addr)
apply(unfold_locales)
done


lemma exec_instr_heap_read_typed:
  "(ta, xcphfrs')  JVM_heap_base.exec_instr addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) (heap_base.heap_read_typed (λ_ :: 'heap. typeof_addr) heap_read P) heap_write i P t h stk loc C M pc frs 
   (ta, xcphfrs')  JVM_heap_base.exec_instr addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) heap_read heap_write i P t h stk loc C M pc frs 
   (ad al v T. ReadMem ad al v  set tao  heap_base'.addr_loc_type TYPE('heap) typeof_addr P ad al T  heap_base'.conf TYPE('heap) typeof_addr P v T)"
apply(cases i)
apply(simp_all add: JVM_heap_base.exec_instr.simps split_beta cong: conj_cong)

apply(auto dest: heap_base.heap_read_typed_into_heap_read del: disjCI)[5]
apply(blast dest:  heap_base.heap_read_typed_typed heap_base'.addr_loc_type_conv_addr_loc_type[THEN fun_cong, THEN fun_cong, THEN fun_cong, THEN iffD2] heap_base'.conf_conv_conf[THEN fun_cong, THEN fun_cong, THEN iffD1])
apply(auto dest: heap_base'.addr_loc_type_conv_addr_loc_type[THEN fun_cong, THEN fun_cong, THEN fun_cong, THEN iffD1] intro: heap_base'.conf_conv_conf[THEN fun_cong, THEN fun_cong, THEN iffD2] heap_base.heap_read_typedI)[1]
apply(blast dest:  heap_base.heap_read_typed_typed heap_base'.addr_loc_type_conv_addr_loc_type[THEN fun_cong, THEN fun_cong, THEN fun_cong, THEN iffD2] heap_base'.conf_conv_conf[THEN fun_cong, THEN fun_cong, THEN iffD1])
apply(auto dest: heap_base'.addr_loc_type_conv_addr_loc_type[THEN fun_cong, THEN fun_cong, THEN fun_cong, THEN iffD1] intro: heap_base'.conf_conv_conf[THEN fun_cong, THEN fun_cong, THEN iffD2] heap_base.heap_read_typedI)[1]
apply(blast dest:  heap_base.heap_read_typed_typed heap_base'.addr_loc_type_conv_addr_loc_type[THEN fun_cong, THEN fun_cong, THEN fun_cong, THEN iffD2] heap_base'.conf_conv_conf[THEN fun_cong, THEN fun_cong, THEN iffD1])
subgoal by(auto dest: heap_base.heap_read_typed_into_heap_read)
apply(blast dest:  heap_base.heap_read_typed_typed heap_base'.addr_loc_type_conv_addr_loc_type[THEN fun_cong, THEN fun_cong, THEN fun_cong, THEN iffD2] heap_base'.conf_conv_conf[THEN fun_cong, THEN fun_cong, THEN iffD1])
subgoal by(auto dest: heap_base'.addr_loc_type_conv_addr_loc_type[THEN fun_cong, THEN fun_cong, THEN fun_cong, THEN iffD1] intro: heap_base'.conf_conv_conf[THEN fun_cong, THEN fun_cong, THEN iffD2] heap_base.heap_read_typedI)[1]
subgoal by(auto 4 3 dest: heap_base.heap_read_typed_into_heap_read intro: heap_base.heap_read_typedI simp add: heap_base'.conf_conv_conf heap_base'.addr_loc_type_conv_addr_loc_type)

apply(subst red_external_aggr_heap_read_typed)
apply(fastforce)
apply auto
done

lemma exec_heap_read_typed:
  "(ta, xcphfrs')  JVM_heap_base.exec addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) (heap_base.heap_read_typed (λ_. typeof_addr) heap_read P) heap_write P t xcphfrs 
   (ta, xcphfrs')  JVM_heap_base.exec addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) heap_read heap_write P t xcphfrs 
   (ad al v T. ReadMem ad al v  set tao  heap_base'.addr_loc_type TYPE('heap) typeof_addr P ad al T  heap_base'.conf TYPE('heap) typeof_addr P v T)"
apply(cases xcphfrs)
apply(cases "fst xcphfrs")
apply(case_tac [!] "snd (snd xcphfrs)")
apply(auto simp add: JVM_heap_base.exec.simps exec_instr_heap_read_typed)
done

lemma exec_1_d_heap_read_typed:
  "JVM_heap_base.exec_1_d addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) (heap_base.heap_read_typed (λ_. typeof_addr) heap_read P) heap_write P t (Normal xcphfrs) ta (Normal xcphfrs') 
   JVM_heap_base.exec_1_d addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) heap_read heap_write P t (Normal xcphfrs) ta (Normal xcphfrs') 
   (ad al v T. ReadMem ad al v  set tao  heap_base'.addr_loc_type TYPE('heap) typeof_addr P ad al T  heap_base'.conf TYPE('heap) typeof_addr P v T)"
by(auto elim!: JVM_heap_base.jvmd_NormalE intro: JVM_heap_base.exec_1_d_NormalI simp add: exec_heap_read_typed JVM_heap_base.exec_d_def)

lemma mexecd_heap_read_typed:
  "JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) (heap_base.heap_read_typed (λ_ :: 'heap. typeof_addr) heap_read P) heap_write P t xcpfrsh ta xcpfrsh' 
   JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) heap_read heap_write P t xcpfrsh ta xcpfrsh' 
   (ad al v T. ReadMem ad al v  set tao  heap_base'.addr_loc_type TYPE('heap) typeof_addr P ad al T  heap_base'.conf TYPE('heap) typeof_addr P v T)"
by(simp add: split_beta exec_1_d_heap_read_typed)

lemma if_mexecd_heap_read_typed:
  "multithreaded_base.init_fin JVM_final (JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) (heap_base.heap_read_typed (λ_ :: 'heap. typeof_addr) heap_read P) heap_write P) t xh ta x'h' 
   if_heap_read_typed JVM_final (JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) heap_read heap_write P) typeof_addr P t xh ta x'h'"
unfolding multithreaded_base.init_fin.simps
by(subst mexecd_heap_read_typed) fastforce

lemma JVMd_ℰ_heap_read_typedI:
  " E  JVM_heap_base.JVMd_ℰ addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) heap_read heap_write P C M vs status;
     ad al v T.  NormalAction (ReadMem ad al v)  snd ` lset E; heap_base'.addr_loc_type TYPE('heap) typeof_addr P ad al T   heap_base'.conf TYPE('heap) typeof_addr P v T 
   E  JVM_heap_base.JVMd_ℰ addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_ :: 'heap. typeof_addr) (heap_base.heap_read_typed (λ_ :: 'heap. typeof_addr) heap_read P) heap_write P C M vs status"
apply(erule imageE, hypsubst)
apply(rule imageI)
apply(erule multithreaded_base.ℰ.cases, hypsubst)
apply(rule multithreaded_base.ℰ.intros)
apply(subst if_mexecd_heap_read_typed[abs_def])
apply(erule if_mthr_Runs_heap_read_typedI)
apply(auto simp add: image_Un lset_lmap[symmetric] lmap_lconcat llist.map_comp o_def split_def simp del: lset_lmap)
done

lemma jmm'_exec_instrI:
  " (ta, xcphfrs)  JVM_heap_base.exec_instr addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr jmm_heap_read jmm_heap_write i P t h stk loc C M pc frs; 
     final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta 
   ta xcphfrs. (ta, xcphfrs)  JVM_heap_base.exec_instr addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr (heap_base.heap_read_typed typeof_addr jmm_heap_read P) jmm_heap_write i P t h stk loc C M pc frs  final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta"
apply(cases i)
apply(auto simp add: JVM_heap_base.exec_instr.simps split_beta final_thread.actions_ok_iff intro!: jmm_heap_read_typed_default_val rev_image_eqI simp del: split_paired_Ex split: if_split_asm)
subgoal for F D
  apply(cases "hd (tl stk) = (default_val (THE T. heap_base.addr_loc_type typeof_addr P h (the_Addr (hd (tl (tl stk)))) (CField D F) T))")
  subgoal by(auto simp add: JVM_heap_base.exec_instr.simps split_beta final_thread.actions_ok_iff intro!: jmm_heap_read_typed_default_val rev_image_eqI simp del: split_paired_Ex split: if_split_asm del: disjCI intro!: disjI1 exI)
  subgoal by(auto simp add: JVM_heap_base.exec_instr.simps split_beta final_thread.actions_ok_iff intro!: jmm_heap_read_typed_default_val rev_image_eqI simp del: split_paired_Ex split: if_split_asm del: disjCI intro!: disjI2 exI)
  done
subgoal for F D
  apply(cases "hd (tl stk) = (default_val (THE T. heap_base.addr_loc_type typeof_addr P h (the_Addr (hd (tl (tl stk)))) (CField D F) T))")
  subgoal by(auto simp add: JVM_heap_base.exec_instr.simps split_beta final_thread.actions_ok_iff intro!: jmm_heap_read_typed_default_val rev_image_eqI simp del: split_paired_Ex split: if_split_asm del: disjCI intro!: disjI1 exI jmm_heap_write.intros)
  subgoal by(rule exI conjI disjI2 refl jmm_heap_read_typed_default_val|assumption)+ auto
  done
subgoal by(drule red_external_aggr_heap_read_typedI)(fastforce simp add: final_thread.actions_ok_iff simp del: split_paired_Ex)+
done

lemma jmm'_execI:
  " (ta, xcphfrs')  JVM_heap_base.exec addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr jmm_heap_read jmm_heap_write P t xcphfrs;
     final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta 
   ta xcphfrs'. (ta, xcphfrs')  JVM_heap_base.exec addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr (heap_base.heap_read_typed typeof_addr jmm_heap_read P) jmm_heap_write P t xcphfrs  final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta"
apply(cases xcphfrs)
apply(cases "snd (snd xcphfrs)")
 apply(simp add: JVM_heap_base.exec.simps)
apply(cases "fst xcphfrs")
apply(fastforce simp add: JVM_heap_base.exec.simps dest!: jmm'_exec_instrI)+
done

lemma jmm'_execdI:
  " JVM_heap_base.exec_1_d addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr jmm_heap_read jmm_heap_write P t (Normal xcphfrs) ta (Normal xcphfrs');
     final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta 
   ta xcphfrs'. JVM_heap_base.exec_1_d addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr (heap_base.heap_read_typed typeof_addr jmm_heap_read P) jmm_heap_write P t (Normal xcphfrs) ta (Normal xcphfrs')  final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta"
apply(erule JVM_heap_base.jvmd_NormalE)
apply(drule (1) jmm'_execI)
apply(force intro: JVM_heap_base.exec_1_d_NormalI simp add: JVM_heap_base.exec_d_def)
done

lemma jmm'_mexecdI:
  " JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr jmm_heap_read jmm_heap_write P t xcpfrsh ta xcpfrsh';
     final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta 
   ta xcpfrsh'. JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr (heap_base.heap_read_typed typeof_addr jmm_heap_read P) jmm_heap_write P t xcpfrsh ta xcpfrsh'  final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta"
by(simp add: split_beta)(drule (1) jmm'_execdI, auto 4 10)

lemma if_mexecd_heap_read_not_stuck:
  " multithreaded_base.init_fin JVM_final (JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr jmm_heap_read jmm_heap_write P) t xh ta x'h';
     final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta 
   ta x'h'. multithreaded_base.init_fin JVM_final (JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr (heap_base.heap_read_typed typeof_addr jmm_heap_read P) jmm_heap_write P) t xh ta x'h'  final_thread.actions_ok (final_thread.init_fin_final JVM_final) s t ta"
apply(erule multithreaded_base.init_fin.cases)
  apply hypsubst
  apply(drule jmm'_mexecdI)
   apply(simp add: final_thread.actions_ok_iff)
  apply clarify
  apply(subst (2) split_paired_Ex)
  apply(subst (2) split_paired_Ex)
  apply(subst (2) split_paired_Ex)
  apply(rule exI conjI)+
   apply(rule multithreaded_base.init_fin.intros)
   apply simp
  apply(simp add: final_thread.actions_ok_iff)
 apply(blast intro: multithreaded_base.init_fin.intros)
apply(blast intro: multithreaded_base.init_fin.intros)
done

lemma if_mExecd_heap_read_not_stuck:
  "multithreaded_base.redT (final_thread.init_fin_final JVM_final) (multithreaded_base.init_fin JVM_final (JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr jmm_heap_read jmm_heap_write P)) convert_RA' s tta s'
   tta s'. multithreaded_base.redT (final_thread.init_fin_final JVM_final) (multithreaded_base.init_fin JVM_final (JVM_heap_base.mexecd addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate typeof_addr (heap_base.heap_read_typed typeof_addr jmm_heap_read P) jmm_heap_write P)) convert_RA' s tta s'"
apply(erule multithreaded_base.redT.cases)
 apply hypsubst
 thm if_mexecd_heap_read_not_stuck
 apply(drule (1) if_mexecd_heap_read_not_stuck)
 apply(erule exE)+
 apply(rename_tac ta' x'h')
 apply(insert redT_updWs_total)
 apply(erule_tac x="t" in meta_allE)
 apply(erule_tac x="wset s" in meta_allE)
 apply(erule_tac x="ta'w" in meta_allE)
 apply clarsimp
 apply(rule exI)+
 apply(auto intro!: multithreaded_base.redT.intros)[1]
apply hypsubst
apply(rule exI conjI)+
apply(rule multithreaded_base.redT.redT_acquire)
apply assumption+
done

lemma JVM_legal_typesafe1:
  assumes wfP: "wf_jvm_prog P"
  and ok: "jmm_wf_start_state P C M vs"
  and legal: "legal_execution P (jmm_JVMd_ℰ P C M vs status) (E, ws)"
  shows "legal_execution P (jmm'_JVMd_ℰ P C M vs status) (E, ws)"
proof -
  let ?ℰ = "jmm_JVMd_ℰ P C M vs status"
  let ?ℰ' = "jmm'_JVMd_ℰ P C M vs status"
  from legal obtain J 
    where justified: "P  (E, ws) justified_by J"
    and range: "range (justifying_exec  J)  ?ℰ"
    and E: "E  ?ℰ" and wf: "P  (E, ws) " by(auto simp add: gen_legal_execution.simps)
  let ?J = "J(0 := committed = {}, justifying_exec = justifying_exec (J 1), justifying_ws = justifying_ws (J 1), action_translation = id)"

  from wfP obtain Φ where Φ: "wf_jvm_prog⇘ΦP" by(auto simp add: wf_jvm_prog_def)
  hence wf_sys: "wf_syscls P" by(auto dest: wt_jvm_progD intro: wf_prog_wf_syscls)

  from justified have "P  (justifying_exec (J 1), justifying_ws (J 1)) " by(simp add: justification_well_formed_def)
  with justified have "P  (E, ws) justified_by ?J" by(rule drop_0th_justifying_exec)
  moreover have "range (justifying_exec  ?J)  ?ℰ'"
  proof
    fix ξ
    assume "ξ  range (justifying_exec  ?J)"
    then obtain n where "ξ = justifying_exec (?J n)" by auto
    then obtain n where ξ: "ξ = justifying_exec (J n)" and n: "n > 0" by(auto split: if_split_asm)
    from range ξ have "ξ  ?ℰ" by auto
    thus "ξ  ?ℰ'" unfolding jmm_typeof_addr'_conv_jmm_type_addr[symmetric, abs_def]
    proof(rule JVMd_ℰ_heap_read_typedI)
      fix ad al v T
      assume read: "NormalAction (ReadMem ad al v)  snd ` lset ξ"
        and adal: "P ⊢jmm ad@al : T"
      from read obtain a where a: "enat a < llength ξ" "action_obs ξ a = NormalAction (ReadMem ad al v)"
        unfolding lset_conv_lnth by(auto simp add: action_obs_def)

      have "ts_ok (λt (xcp, frs) h. JVM_heap_conf_base.correct_state addr2thread_id jmm_empty jmm_allocate (λ_. jmm_typeof_addr' P) jmm_hconf P Φ t (xcp, h, frs)) (thr (jmm_JVM_start_state P C M vs)) jmm.start_heap"

        using JVM_heap_conf.correct_jvm_state_initial[OF jmm_JVM_heap_conf Φ ok]
        by(simp add: JVM_heap_conf_base.correct_jvm_state_def jmm_typeof_addr'_conv_jmm_typeof_addr heap_base.start_state_def split_beta)
      with JVM_allocated_heap_conf'.mexecd_known_addrs_typing'[OF jmm_JVMd_allocated_heap_conf' Φ jmm_start_heap_ok]
      have "T. P ⊢jmm ad@al : T  P ⊢jmm v :≤ T"
        using wf_sys is_justified_by_imp_is_weakly_justified_by[OF justified wf] range n a
        unfolding jmm_typeof_addr'_conv_jmm_type_addr[symmetric, abs_def] ξ
        by(rule known_addrs_typing'.read_value_typeable_justifying)
      thus "P ⊢jmm v :≤ T" using adal
        by(auto dest: jmm.addr_loc_type_fun[unfolded jmm_typeof_addr_conv_jmm_typeof_addr', unfolded heap_base'.addr_loc_type_conv_addr_loc_type])
    qed
  qed
  moreover from E have "E  ?ℰ'"
    unfolding jmm_typeof_addr'_conv_jmm_type_addr[symmetric, abs_def]
  proof(rule JVMd_ℰ_heap_read_typedI)
    fix ad al v T
    assume read: "NormalAction (ReadMem ad al v)  snd ` lset E"
      and adal: "P ⊢jmm ad@al : T"
    from read obtain a where a: "enat a < llength E" "action_obs E a = NormalAction (ReadMem ad al v)"
      unfolding lset_conv_lnth by(auto simp add: action_obs_def)
    with jmm_JVMd_allocated_heap_conf' Φ ok legal_imp_weakly_legal_execution[OF legal]
    have "T. P ⊢jmm ad@al : T  P ⊢jmm v :≤ T"
      unfolding jmm_typeof_addr'_conv_jmm_typeof_addr[symmetric, abs_def]
      by(rule JVM_allocated_heap_conf'.JVM_weakly_legal_read_value_typeable)
    thus "P ⊢jmm v :≤ T" using adal
      by(auto dest: jmm.addr_loc_type_fun[unfolded jmm_typeof_addr_conv_jmm_typeof_addr', unfolded heap_base'.addr_loc_type_conv_addr_loc_type])
  qed
  ultimately show ?thesis using wf unfolding gen_legal_execution.simps by blast
qed

lemma JVM_weakly_legal_typesafe1:
  assumes wfP: "wf_jvm_prog P"
  and ok: "jmm_wf_start_state P C M vs"
  and legal: "weakly_legal_execution P (jmm_JVMd_ℰ P C M vs status) (E, ws)"
  shows "weakly_legal_execution P (jmm'_JVMd_ℰ P C M vs status) (E, ws)"
proof -
  let ?ℰ = "jmm_JVMd_ℰ P C M vs status"
  let ?ℰ' = "jmm'_JVMd_ℰ P C M vs status"
  from legal obtain J 
    where justified: "P  (E, ws) weakly_justified_by J"
    and range: "range (justifying_exec  J)  ?ℰ"
    and E: "E  ?ℰ" and wf: "P  (E, ws) " by(auto simp add: gen_legal_execution.simps)
  let ?J = "J(0 := committed = {}, justifying_exec = justifying_exec (J 1), justifying_ws = justifying_ws (J 1), action_translation = id)"

  from wfP obtain Φ where Φ: "wf_jvm_prog⇘ΦP" by(auto simp add: wf_jvm_prog_def)
  hence wf_sys: "wf_syscls P" by(auto dest: wt_jvm_progD intro: wf_prog_wf_syscls)

  from justified have "P  (justifying_exec (J 1), justifying_ws (J 1)) " by(simp add: justification_well_formed_def)
  with justified have "P  (E, ws) weakly_justified_by ?J" by(rule drop_0th_weakly_justifying_exec)
  moreover have "range (justifying_exec  ?J)  ?ℰ'"
  proof
    fix ξ
    assume "ξ  range (justifying_exec  ?J)"
    then obtain n where "ξ = justifying_exec (?J n)" by auto
    then obtain n where ξ: "ξ = justifying_exec (J n)" and n: "n > 0" by(auto split: if_split_asm)
    from range ξ have "ξ  ?ℰ" by auto
    thus "ξ  ?ℰ'" unfolding jmm_typeof_addr'_conv_jmm_type_addr[symmetric, abs_def]
    proof(rule JVMd_ℰ_heap_read_typedI)
      fix ad al v T
      assume read: "NormalAction (ReadMem ad al v)  snd ` lset ξ"
        and adal: "P ⊢jmm ad@al : T"
      from read obtain a where a: "enat a < llength ξ" "action_obs ξ a = NormalAction (ReadMem ad al v)"
        unfolding lset_conv_lnth by(auto simp add: action_obs_def)

      have "ts_ok (λt (xcp, frs) h. JVM_heap_conf_base.correct_state addr2thread_id jmm_empty jmm_allocate (λ_. jmm_typeof_addr' P) jmm_hconf P Φ t (xcp, h, frs)) (thr (jmm_JVM_start_state P C M vs)) jmm.start_heap"

        using JVM_heap_conf.correct_jvm_state_initial[OF jmm_JVM_heap_conf Φ ok]
        by(simp add: JVM_heap_conf_base.correct_jvm_state_def jmm_typeof_addr'_conv_jmm_typeof_addr heap_base.start_state_def split_beta)
      with JVM_allocated_heap_conf'.mexecd_known_addrs_typing'[OF jmm_JVMd_allocated_heap_conf' Φ jmm_start_heap_ok]
      have "T. P ⊢jmm ad@al : T  P ⊢jmm v :≤ T"
        using wf_sys justified range n a
        unfolding jmm_typeof_addr'_conv_jmm_type_addr[symmetric, abs_def] ξ
        by(rule known_addrs_typing'.read_value_typeable_justifying)
      thus "P ⊢jmm v :≤ T" using adal
        by(auto dest: jmm.addr_loc_type_fun[unfolded jmm_typeof_addr_conv_jmm_typeof_addr', unfolded heap_base'.addr_loc_type_conv_addr_loc_type])
    qed
  qed
  moreover from E have "E  ?ℰ'"
    unfolding jmm_typeof_addr'_conv_jmm_type_addr[symmetric, abs_def]
  proof(rule JVMd_ℰ_heap_read_typedI)
    fix ad al v T
    assume read: "NormalAction (ReadMem ad al v)  snd ` lset E"
      and adal: "P ⊢jmm ad@al : T"
    from read obtain a where a: "enat a < llength E" "action_obs E a = NormalAction (ReadMem ad al v)"
      unfolding lset_conv_lnth by(auto simp add: action_obs_def)
    with jmm_JVMd_allocated_heap_conf' Φ ok legal
    have "T. P ⊢jmm ad@al : T  P ⊢jmm v :≤ T"
      unfolding jmm_typeof_addr'_conv_jmm_typeof_addr[symmetric, abs_def]
      by(rule JVM_allocated_heap_conf'.JVM_weakly_legal_read_value_typeable)
    thus "P ⊢jmm v :≤ T" using adal
      by(auto dest: jmm.addr_loc_type_fun[unfolded jmm_typeof_addr_conv_jmm_typeof_addr', unfolded heap_base'.addr_loc_type_conv_addr_loc_type])
  qed
  ultimately show ?thesis using wf unfolding gen_legal_execution.simps by blast
qed

lemma JVMd_ℰ_heap_read_typedD:
  "E  JVM_heap_base.JVMd_ℰ addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_. typeof_addr) (heap_base.heap_read_typed (λ_. typeof_addr) jmm_heap_read P) jmm_heap_write P C M vs status
   E  JVM_heap_base.JVMd_ℰ addr2thread_id thread_id2addr spurious_wakeups empty_heap allocate (λ_. typeof_addr) jmm_heap_read jmm_heap_write P C M vs status"
apply(erule imageE, hypsubst)
apply(rule imageI)
apply(erule multithreaded_base.ℰ.cases, hypsubst)
apply(rule multithreaded_base.ℰ.intros)
apply(subst (asm) if_mexecd_heap_read_typed[abs_def])
apply(erule if_mthr_Runs_heap_read_typedD)
apply(erule if_mExecd_heap_read_not_stuck[where typeof_addr="λ_. typeof_addr", unfolded if_mexecd_heap_read_typed[abs_def]])
done

lemma JVMd_ℰ_typesafe_subset: "jmm'_JVMd_ℰ P C M vs status  jmm_JVMd_ℰ P C M vs status"
unfolding jmm_typeof_addr_def[abs_def]
by(rule subsetI)(erule JVMd_ℰ_heap_read_typedD)

lemma JVMd_legal_typesafe2:
  assumes legal: "legal_execution P (jmm'_JVMd_ℰ P C M vs status) (E, ws)"
  shows "legal_execution P (jmm_JVMd_ℰ P C M vs status) (E, ws)"
proof -
  let ?ℰ = "jmm_JVMd_ℰ P C M vs status"
  let ?ℰ' = "jmm'_JVMd_ℰ P C M vs status"
  from legal obtain J 
    where justified: "P  (E, ws) justified_by J"
    and range: "range (justifying_exec  J)  ?ℰ'"
    and E: "E  ?ℰ'" and wf: "P  (E, ws) " by(auto simp add: gen_legal_execution.simps)
  from range E have "range (justifying_exec  J)  ?ℰ" "E  ?ℰ"
    using JVMd_ℰ_typesafe_subset[of P status C M vs] by blast+
  with justified wf
  show ?thesis by(auto simp add: gen_legal_execution.simps)
qed

theorem JVMd_weakly_legal_typesafe2:
  assumes legal: "weakly_legal_execution P (jmm'_JVMd_ℰ P C M vs status) (E, ws)"
  shows "weakly_legal_execution P (jmm_JVMd_ℰ P C M vs status) (E, ws)"
proof -
  let ?ℰ = "jmm_JVMd_ℰ P C M vs status"
  let ?ℰ' = "jmm'_JVMd_ℰ P C M vs status"
  from legal obtain J 
    where justified: "P  (E, ws) weakly_justified_by J"
    and range: "range (justifying_exec  J)  ?ℰ'"
    and E: "E  ?ℰ'" and wf: "P  (E, ws) " by(auto simp add: gen_legal_execution.simps)
  from range E have "range (justifying_exec  J)  ?ℰ" "E  ?ℰ"
    using JVMd_ℰ_typesafe_subset[of P status C M vs] by blast+
  with justified wf
  show ?thesis by(auto simp add: gen_legal_execution.simps)
qed

theorem JVMd_weakly_legal_typesafe:
  assumes "wf_jvm_prog P"
  and "jmm_wf_start_state P C M vs"
  shows "weakly_legal_execution P (jmm_JVMd_ℰ P C M vs status) = weakly_legal_execution P (jmm'_JVMd_ℰ P C M vs status)"
apply(rule ext iffI)+
 apply(clarify, erule JVM_weakly_legal_typesafe1[OF assms])
apply(clarify, erule JVMd_weakly_legal_typesafe2)
done

theorem JVMd_legal_typesafe:
  assumes "wf_jvm_prog P"
  and "jmm_wf_start_state P C M vs"
  shows "legal_execution P (jmm_JVMd_ℰ P C M vs status) = legal_execution P (jmm'_JVMd_ℰ P C M vs status)"
apply(rule ext iffI)+
 apply(clarify, erule JVM_legal_typesafe1[OF assms])
apply(clarify, erule JVMd_legal_typesafe2)
done

end