Built with Alectryon, running Coq+coqc-time vThe Rocq Prover, version 9.1.1 compiled with OCaml 5.2.1. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
From Stdlib Require Import Lia.
From Stdlib Require Import List.
From Stdlib Require String.
Require Import Stdlib.Sets.Ensembles.
Require Import Stdlib.Classes.RelationClasses.
Import ListNotations.

Require Import Syntax Typing Subtyping ViewpointAdaptation Helpers Reachability.

(* ------------------RUNTIME H ELPER FUNCTION------------------*)
(* The first element should also be a Loc because that is the receiver type*)
Definition get_this_var_mapping (vm : var_mapping) : option Loc :=
  match vm with
  | [] => None
  | ι :: _ =>
    match ι with
    | Null_a => None
    | Iot loc => Some loc
    end
  end.

Lemma get_this_var_mapping_app_null_last : forall vs,
  get_this_var_mapping (vs ++ [Null_a]) = get_this_var_mapping vs.
Proof.
  intros vs. destruct vs; reflexivity.
Qed.

Lemma get_this_var_mapping_update_vars_app_null : forall ,
  get_this_var_mapping (vars (set_vars rΓ (vars rΓ ++ [Null_a])))
  = get_this_var_mapping (vars rΓ).
Proof.
  intro rΓ. simpl. apply get_this_var_mapping_app_null_last.
Qed.

(* Get the runtime mutability type of a Loc *)
Definition r_muttype (h: heap) (ι: Loc) : option q_r :=
  match runtime_getObj h ι with
  | None => None
  | Some o => Some (rqtype (rt_type o))
  end.

(* Get the runtime class name of a Loc *)
Definition r_basetype (h: heap) (ι: Loc) : option class_name :=
  match runtime_getObj h ι with
  | None => None
  | Some o => Some (rctype (rt_type o))
  end.

(* Get the runtime type of a Loc *)
Definition r_type (h: heap) (ι: Loc) : option runtime_type :=
  match runtime_getObj h ι with
  | None => None
  | Some o => Some (rt_type o)
  end.

Definition update_field (h: heap) (ι: Loc) (f: var) (v: value) : heap :=
  match runtime_getObj h ι with
  | None => h
  | Some o =>
      let new_fields := update f v o.(fields_map) in
      let new_obj := (set_fields_map o (new_fields))
      in update ι new_obj h
  end.

Lemma update_field_length : forall h ι f v,
  dom (update_field h ι f v) = dom h.
Proof.
  intros.
  unfold update_field.
  destruct (runtime_getObj h ι); [apply update_length | reflexivity].
Qed.

Definition vpa_mutability_runtime_bound_agree (q1: q_r)(q2 : q_c) : bool :=
  match (q1, q2) with
    | (Imm_r, RDM_c) => true
    | (Mut_r, RDM_c) => true
    | (Imm_r, Imm_c) => true
    | (Mut_r, Mut_c) => true
    | (_, _) => false
    end.

(* ------------------RUNTIME WELLFORMEDNESS RULES------------------*)
(* Wellformed Runtime Type use  *)
Definition wf_rtypeuse (CT: class_table) (q: q_r) (c: class_name) : Prop :=
  match (bound CT c) with
  | None => False
  | Some q' => c < dom CT /\ vpa_mutability_runtime_bound_agree q q'
  end.

Definition qualifier_typable_context (qr: q_r) (qs: q) (qcontext: q_r): Prop :=
  match qr with
  | Imm_r =>
    match vpa_mutability_runtime qcontext qs with
    | Imm => True
    | RO => True
    | Lost => True
    | _ => False
    end
  | Mut_r =>
    match vpa_mutability_runtime qcontext qs with
    | Mut => True
    | RO => True
    | Lost => True
    | _ => False
    end
  end.

(* heap typable is different than environment typable; I sepearate it in the proof but uses the same in the written up *)
Definition qualifier_typable_heap (qr: q_r) (qs: q): Prop :=
  match qr with
  | Imm_r =>
    match qs with
    | Imm => True
    | RO => True
    | _ => False
    end
  | Mut_r =>
    match qs with
    | Mut => True
    | RO => True
    | _ => False
    end
  end.

(* Wellformed Runtime Object: an object is well-formed if itself and its fields' type are well formed *)
Definition wf_obj (CT: class_table) (h: heap) (ι: Loc) : Prop :=
  match runtime_getObj h ι with
  | None => False
  | Some o =>
      (* The runtime type of the object is well-formed *)
      wf_rtypeuse CT (rt_type o).(rqtype) (rt_type o).(rctype) /\
      (* All field values are well-formed and have correct types *)
      exists field_defs, CollectFields CT (rt_type o).(rctype) field_defs /\
      List.length (fields_map o) = List.length field_defs /\
      Forall2 (fun v fdef =>
        match v with
        | Null_a => True
        | Iot loc =>
          match runtime_getObj h loc with
          | Some _ =>
            (* Field value exists and has correct type *)
            exists rqt, r_type h loc = Some rqt /\
            base_subtype CT (rctype rqt) (f_base_type (ftype fdef)) /\
            qualifier_typable_heap (rqtype rqt) (vpa_mutability_rec_fld (rqtype (rt_type o)) (mutability (ftype fdef)))
          | None => False
          end
        end) (fields_map o) field_defs
  end.

(* Wellformed Runtime environment: a rΓ is well formed if for all variable in its domain, it maps to null_a or a value in the domin of heap *)
Definition wf_renv (CT: class_table) (: r_env) (h: heap) : Prop :=
  (* The first variable is the receiver and should always be present as non-null value *)
  dom rΓ.(vars) > 0 /\
  (exists iot, get_this_var_mapping rΓ.(vars) = Some iot /\ iot < dom h) /\
  Forall (fun value =>
    match value with
    | Null_a => True
    | Iot loc =>
        match runtime_getObj h loc with
        | None => False
        | Some _ => True
        end
    end) rΓ.(vars).

(* Wellformed Runtime Heap: a heap is well-formed if all objects in it are well-formed *)
Definition wf_heap (CT: class_table) (h: heap) : Prop :=
    forall (ι : Loc),
    ι < (List.length h) ->
    wf_obj CT h ι.

Definition wf_r_typable (CT: class_table) (h: heap) (ι: Loc)
  (sqt: qualified_type) (qcontext: q_r) : Prop :=
  match r_type h ι with
  | Some rqt =>
      base_subtype CT (rctype rqt) (sctype sqt) /\
      qualifier_typable_context (rqtype rqt) (sqtype sqt) qcontext
  | _ => False
  end.
A runtime configuration is well formed when its class table, heap, and environments are well formed and the static and runtime environments correspond pointwise.
Definition wf_r_config (CT: class_table) (: s_env) (: r_env) (h: heap) : Prop :=
  (* CT is well-formed *)
  wf_class_table CT /\
  (* Heap is well-formed *)
  wf_heap CT h /\
  (* Runtime environment is well-formed *)
  wf_renv CT rΓ h /\
  (* Static environment is well-formed *)
  wf_senv CT sΓ /\
  (* Static and runtime environment correspond *)
  List.length sΓ = List.length rΓ.(vars) /\
  forall ι qcontext,
  get_this_var_mapping (vars rΓ) = Some ι ->
  (r_muttype h ι) = Some qcontext ->
  forall i, i < List.length sΓ ->
  forall sqt,
    (* Static lookup at index [i]. *)
    nth_error sΓ i = Some sqt ->
    match runtime_getVal rΓ i with
    | Some (Iot loc) => wf_r_typable CT h loc sqt qcontext
    | Some Null_a => True
    | None => False
    end.

(* ------------------EVALUATION RULES------------------*)

(* Evaluation resulting state *)
Inductive eval_result :=
| OK : eval_result
| MUTATIONEXP: eval_result
| NPE : eval_result.

Definition runtime_vpa_assignability (q1: q_r) (a1: a) : a :=
  match q1, a1 with
    | _, Assignable => Assignable
    | Mut_r, RDA => Assignable
    | _, _ => Final
  end.

Definition reachable_locations_from_initial_env
  (h : heap) ( : r_env) : Ensembles.Ensemble Loc :=
  fun l_target =>
    exists x l_root ,
      runtime_getVal rΓ x = Some (Iot l_root) /\
      reachable h l_root l_target.

Fixpoint find_in_list_dec {A : Type} (P : A -> Prop)
  (P_dec : forall x, {P x} + {~ P x}) (xs : list A) :
  {x | List.In x xs /\ P x} +
  {forall x, List.In x xs -> ~ P x}.
Proof.
  destruct xs as [|x xs].
  - right. intros y Hin. inversion Hin.
  - destruct (P_dec x) as [HP|HnotP].
    + left. exists x. split; [left; reflexivity|exact HP].
    + destruct (@find_in_list_dec A P P_dec xs) as [Hex|Hnone].
      * left. destruct Hex as [y [Hin HPy]].
        exists y. split; [right; exact Hin|exact HPy].
      * right. intros y [Heq|Hin] HPy.
        -- subst y. contradiction.
        -- exact (Hnone y Hin HPy).
Defined.

(* PICO expression evaluation *)
Inductive eval_expr : class_table -> r_env -> heap -> expr -> value -> eval_result -> r_env -> heap -> Prop :=
  (* evalutate null expression  *)
  | EBS_Null : forall CT  h,
      eval_expr CT rΓ h ENull Null_a OK rΓ h

  (* evaluate value expression *)
  | EBS_Val : forall CT  h x v
      (Hval : runtime_getVal rΓ x = Some v),
      eval_expr CT rΓ h (EVar x) v OK rΓ h

  (* evaluate field access expression *)
  | EBS_Field : forall CT  h x f v o v1
      (Hval   : runtime_getVal rΓ x = Some (Iot v))
      (Hobj   : runtime_getObj h v = Some o)
      (Hfield : getVal o.(fields_map) f = Some v1),
      eval_expr CT rΓ h (EField x f) v1 OK rΓ h

  (* evaluate field access expression yields NPE *)
  | EBS_Field_NPE : forall CT  h x f
      (Hnull : runtime_getVal rΓ x = Some (Null_a)),
      eval_expr CT rΓ h (EField x f) Null_a NPE rΓ h
  .
(* Determinism of eval_expr.

   Every well-formed expression evaluation from a given starting state
   produces the same result value, outcome tag, environment, and heap. *)
Lemma eval_expr_deterministic :
  forall CT  h e v1 r1 rΓ1' h1' v2 r2 rΓ2' h2',
    eval_expr CT rΓ h e v1 r1 rΓ1' h1' ->
    eval_expr CT rΓ h e v2 r2 rΓ2' h2' ->
    v1 = v2 /\ r1 = r2 /\ rΓ1' = rΓ2' /\ h1' = h2'.
Proof.
  intros CT rΓ h e v1 r1 rΓ1' h1' v2 r2 rΓ2' h2' H1 H2.
  inversion H1; subst; inversion H2; subst.
  - (* EBS_Null vs EBS_Null *)
    repeat split; reflexivity.
  - (* EBS_Val vs EBS_Val *)
    rewrite Hval0 in Hval; injection Hval as ?; subst.
    repeat split; reflexivity.
  - (* EBS_Field vs EBS_Field *)
    rewrite Hval0 in Hval; injection Hval as ?; subst.
    rewrite Hobj0 in Hobj; injection Hobj as ?; subst.
    rewrite Hfield0 in Hfield; injection Hfield as ?; subst.
    repeat split; reflexivity.
  - (* EBS_Field vs EBS_Field_NPE: Iot vs Null contradiction *)
    rewrite Hnull in Hval; discriminate.
  - (* EBS_Field_NPE vs EBS_Field: Null vs Iot contradiction *)
    rewrite Hnull in Hval; discriminate.
  - (* EBS_Field_NPE vs EBS_Field_NPE *)
    repeat split; reflexivity.
Qed.

(* PICO Statement evaluation *)
Inductive eval_stmt : class_table -> r_env -> heap -> stmt -> eval_result -> r_env -> heap -> Prop :=
  (* evaluate skip statement *)
  | SBS_Skip : forall CT  h,
      eval_stmt CT rΓ h SSkip OK rΓ h

  (* evaluate local variable declaration statement *)
  | SBS_Local : forall CT  h T x
      (Hnone : runtime_getVal rΓ x = None),
      eval_stmt CT rΓ h (SLocal T x) OK
      (set_vars rΓ (rΓ.(vars)++[Null_a]) )
      h

  (* evaluate variable assignment statement *)
  | SBS_Assign : forall CT  h x e v1 v2
      (Hval   : runtime_getVal rΓ x = Some v1)
      (Heval  : eval_expr CT rΓ h e v2 OK rΓ h),
      eval_stmt CT rΓ h (SVarAss x e) OK
      (set_vars rΓ (update x v2 rΓ.(vars)))
      h

  | SBS_Assign_NPE : forall CT  h x e v1 v2
      (Hval   : runtime_getVal rΓ x = Some v1)
      (Heval  : eval_expr CT rΓ h e v2 NPE rΓ h),
      eval_stmt CT rΓ h (SVarAss x e) NPE
      rΓ
      h

  (* evaluate field write statement *)
  | SBS_FldWrite : forall CT  h x f y loc_x o a vf val_y h'
      (Hval_x  : runtime_getVal rΓ x = Some (Iot loc_x))
      (Hobj    : runtime_getObj h loc_x = Some o)
      (Hfield  : getVal o.(fields_map) f = Some vf)
      (Hassign : sf_assignability_rel CT (rctype (rt_type o)) f a)
      (Hval_y  : runtime_getVal rΓ y = Some val_y)
      (Hruntime_assignable : runtime_vpa_assignability (rqtype (rt_type o)) a = Assignable)
      (Hupdate : h' = update_field h loc_x f val_y),
      eval_stmt CT rΓ h (SFldWrite x f y) OK rΓ h'

  (* evaluate field write statement NPE *)
  | SBS_FldWrite_NPE : forall CT  h x f y
      (Hnull : runtime_getVal rΓ x = Some (Null_a)),
      eval_stmt CT rΓ h (SFldWrite x f y) NPE rΓ h

  | SBS_FldWrite_MUTATIONEXP : forall CT  h x f y loc_x o a vf val_y
      (Hval_x  : runtime_getVal rΓ x = Some (Iot loc_x))
      (Hobj    : runtime_getObj h loc_x = Some o)
      (Hfield  : getVal o.(fields_map) f = Some vf)
      (Hassign : sf_assignability_rel CT (rctype (rt_type o)) f a)
      (Hval_y  : runtime_getVal rΓ y = Some val_y)
      (Hfinal  : runtime_vpa_assignability (rqtype (rt_type o)) a = Final),
      eval_stmt CT rΓ h (SFldWrite x f y) MUTATIONEXP rΓ h

  (* evaluate object creation statement *)
  | SBS_New : forall CT  h x (q_c:q_c) c ys l1 qthisr vals o qadapted rΓ' h'
      (Hthis    : runtime_getVal rΓ 0 = Some (Iot l1))
      (Hargs    : runtime_lookup_list rΓ ys = Some vals)
      (Hmut     : r_muttype h l1 = Some qthisr)
      (Hadapt   : vpa_mutability_object_creation qthisr q_c = qadapted)
      (Hobj     : o = mkObj (mkruntime_type qadapted c) (vals))
      (Hheap    : h' = h++[o])
      (Henv     : rΓ' = (set_vars rΓ (update x (Iot (dom h)) rΓ.(vars)))),
      eval_stmt CT rΓ h (SNew x q_c c ys) OK rΓ' h'

  (* evaluate method call statement *)
  | SBS_Call : forall CT  h x y m zs vals ly cy mdef mbody mstmt mret retval h' rΓ' rΓ'' rΓ'''
      (Hval_y      : runtime_getVal rΓ y = Some (Iot ly))
      (Hbase       : r_basetype h ly = Some cy)
      (Hfind       : FindMethodWithName CT cy m mdef /\ mbody = Syntax.mbody mdef)
      (Hstmt       : mstmt = mbody.(mbody_stmt))
      (Hret        : mret = mbody.(mreturn))
      (Hargs       : runtime_lookup_list rΓ zs = Some vals)
      (Hframe      : rΓ' = mkr_env (Iot ly :: vals))
      (Heval_body  : eval_stmt CT rΓ' h mstmt OK rΓ'' h')
      (Hretval     : runtime_getVal rΓ'' mret = Some retval)
      (Henv        : rΓ''' = (set_vars rΓ (update x retval rΓ.(vars)))),
      eval_stmt CT rΓ h (SCall x m y zs) OK rΓ''' h'

  (* evaluate method call statement NPE *)
  | SBS_Call_NPE : forall CT  h x y m zs
      (Hnull : runtime_getVal rΓ y = Some (Null_a)),
      eval_stmt CT rΓ h (SCall x m y zs) NPE rΓ h

  | SBS_Call_NPE_Body : forall CT  h x y m zs vals ly cy mdef mbody mstmt mret h' rΓ' rΓ''
      (Hval_y     : runtime_getVal rΓ y = Some (Iot ly))
      (Hbase      : r_basetype h ly = Some cy)
      (Hfind      : FindMethodWithName CT cy m mdef /\ mbody = Syntax.mbody mdef)
      (Hstmt      : mstmt = mbody.(mbody_stmt))
      (Hret       : mret = mbody.(mreturn))
      (Hargs      : runtime_lookup_list rΓ zs = Some vals)
      (Hframe     : rΓ' = mkr_env (Iot ly :: vals))
      (Heval_body : eval_stmt CT rΓ' h mstmt NPE rΓ'' h'),
      eval_stmt CT rΓ h (SCall x m y zs) NPE rΓ'' h'

  | SBS_Call_MUTATIONEXP_Body : forall CT  h x y m zs vals ly cy mdef mbody mstmt mret h' rΓ' rΓ''
      (Hval_y     : runtime_getVal rΓ y = Some (Iot ly))
      (Hbase      : r_basetype h ly = Some cy)
      (Hfind      : FindMethodWithName CT cy m mdef /\ mbody = Syntax.mbody mdef)
      (Hstmt      : mstmt = mbody.(mbody_stmt))
      (Hret       : mret = mbody.(mreturn))
      (Hargs      : runtime_lookup_list rΓ zs = Some vals)
      (Hframe     : rΓ' = mkr_env (Iot ly :: vals))
      (Heval_body : eval_stmt CT rΓ' h mstmt MUTATIONEXP rΓ'' h'),
      eval_stmt CT rΓ h (SCall x m y zs) MUTATIONEXP rΓ'' h'
  (* evaluate sequence of statements *)
  | SBS_Seq : forall CT  h s1 s2 rΓ' h' rΓ'' h''
      (Heval1 : eval_stmt CT rΓ h s1 OK rΓ' h')
      (Heval2 : eval_stmt CT rΓ' h' s2 OK rΓ'' h''),
      eval_stmt CT rΓ h (SSeq s1 s2) OK rΓ'' h''

  | SBS_Seq_NPE_first : forall CT  h s1 s2 rΓ' h'
      (Heval1 : eval_stmt CT rΓ h s1 NPE rΓ' h'),
      eval_stmt CT rΓ h (SSeq s1 s2) NPE rΓ' h'

  | SBS_Seq_NPE_second : forall CT  h s1 s2 rΓ' h' rΓ'' h''
      (Heval1 : eval_stmt CT rΓ h s1 OK rΓ' h')
      (Heval2 : eval_stmt CT rΓ' h' s2 NPE rΓ'' h''),
      eval_stmt CT rΓ h (SSeq s1 s2) NPE rΓ'' h''

  | SBS_Seq_MUTATIONEXP_first : forall CT  h s1 s2 rΓ' h'
      (Heval1 : eval_stmt CT rΓ h s1 MUTATIONEXP rΓ' h'),
      eval_stmt CT rΓ h (SSeq s1 s2) MUTATIONEXP rΓ' h'

  | SBS_Seq_MUTATIONEXP_second : forall CT  h s1 s2 rΓ' h' rΓ'' h''
      (Heval1 : eval_stmt CT rΓ h s1 OK rΓ' h')
      (Heval2 : eval_stmt CT rΓ' h' s2 MUTATIONEXP rΓ'' h''),
      eval_stmt CT rΓ h (SSeq s1 s2) MUTATIONEXP rΓ'' h''
  .

(* Determinism of eval_stmt.

   Every well-formed statement evaluation from a given starting state
   produces the same outcome tag, environment, and heap. *)
Lemma eval_stmt_deterministic :
  forall CT  h s res1 rΓ1' h1' res2 rΓ2' h2',
    eval_stmt CT rΓ h s res1 rΓ1' h1' ->
    eval_stmt CT rΓ h s res2 rΓ2' h2' ->
    res1 = res2 /\ rΓ1' = rΓ2' /\ h1' = h2'.
Proof.
  intros CT rΓ h s res1 rΓ1' h1' res2 rΓ2' h2' H1.
  generalize dependent h2'.
  generalize dependent rΓ2'.
  generalize dependent res2.
  induction H1; intros res2 rΓ2' h2' H2.
  - (* SBS_Skip *)
    inversion H2; subst.
    repeat split; reflexivity.
  - (* SBS_Local *)
    inversion H2; subst.
    repeat split; reflexivity.
  - (* SBS_Assign *)
    inversion H2; subst.
    + (* H2 : SBS_Assign *)
      pose proof (eval_expr_deterministic
        _ _ _ _ _ _ _ _ _ _ _ _ Heval Heval0)
        as Hdet.
      destruct Hdet as [Hveq _].
      subst.
      repeat split; reflexivity.
    + (* H2 : SBS_Assign_NPE — eval_expr vs NPE *)
      pose proof (eval_expr_deterministic
        _ _ _ _ _ _ _ _ _ _ _ _ Heval Heval0)
        as Hdet.
      destruct Hdet as [_ [Hreq _]].
      discriminate.
  - (* SBS_Assign_NPE *)
    inversion H2; subst.
    + (* H2 : SBS_Assign — eval_expr NPE vs OK *)
      pose proof (eval_expr_deterministic
        _ _ _ _ _ _ _ _ _ _ _ _ Heval Heval0)
        as Hdet.
      destruct Hdet as [_ [Hreq _]].
      discriminate.
    + (* H2 : SBS_Assign_NPE *)
      repeat split; reflexivity.
  - (* SBS_FldWrite *)
    inversion H2; subst.
    + (* H2 : SBS_FldWrite *)
      rewrite Hval_x0 in Hval_x; injection Hval_x as Hloc_eq; subst loc_x0.
      rewrite Hobj0 in Hobj; injection Hobj as Ho_eq; subst o0.
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hval_y_eq; subst val_y0.
      pose proof (sf_assignability_deterministic_rel _ _ _ _ _ Hassign Hassign0) as Haeq.
      subst a0.
      repeat split; reflexivity.
    + (* H2 : SBS_FldWrite_NPE — Iot vs Null *)
      rewrite Hnull in Hval_x; discriminate.
    + (* H2 : SBS_FldWrite_MUTATIONEXP — same field, Assignable vs Final *)
      rewrite Hval_x0 in Hval_x; injection Hval_x as Hloc_eq; subst loc_x0.
      rewrite Hobj0 in Hobj; injection Hobj as Ho_eq; subst o0.
      pose proof (sf_assignability_deterministic_rel _ _ _ _ _ Hassign Hassign0) as Haeq.
      subst a0.
      rewrite Hruntime_assignable in Hfinal; discriminate.
  - (* SBS_FldWrite_NPE *)
    inversion H2; subst.
    + (* H2 : SBS_FldWrite — Null vs Iot *)
      rewrite Hval_x in Hnull; discriminate.
    + (* H2 : SBS_FldWrite_NPE *)
      repeat split; reflexivity.
    + (* H2 : SBS_FldWrite_MUTATIONEXP — Null vs Iot *)
      rewrite Hval_x in Hnull; discriminate.
  - (* SBS_FldWrite_MUTATIONEXP *)
    inversion H2; subst.
    + (* H2 : SBS_FldWrite — Final vs Assignable *)
      rewrite Hval_x0 in Hval_x; injection Hval_x as Hloc_eq; subst loc_x0.
      rewrite Hobj0 in Hobj; injection Hobj as Ho_eq; subst o0.
      pose proof (sf_assignability_deterministic_rel _ _ _ _ _ Hassign Hassign0) as Haeq.
      subst a0.
      rewrite Hfinal in Hruntime_assignable; discriminate.
    + (* H2 : SBS_FldWrite_NPE — Iot vs Null *)
      rewrite Hnull in Hval_x; discriminate.
    + (* H2 : SBS_FldWrite_MUTATIONEXP *)
      repeat split; reflexivity.
  - (* SBS_New *)
    inversion H2; subst.
    rewrite Hthis0 in Hthis; injection Hthis as Hl_eq; subst l0.
    rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst vals0.
    rewrite Hmut0 in Hmut; injection Hmut as Hqthisr_eq; subst qthisr0.
    repeat split; reflexivity.
  - (* SBS_Call *)
    inversion H2; subst.
    + (* H2 : SBS_Call *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [_ [Henv_eq Hheap_eq]].
      subst.
      rewrite Hretval0 in Hretval; injection Hretval as Hretval_eq; subst.
      repeat split; reflexivity.
    + (* H2 : SBS_Call_NPE — Iot vs Null *)
      rewrite Hnull in Hval_y; discriminate.
    + (* H2 : SBS_Call_NPE_Body — IH: OK = NPE *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Call_MUTATIONEXP_Body — IH: OK = MUTATIONEXP *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [Hres_eq _].
      discriminate.
  - (* SBS_Call_NPE *)
    inversion H2; subst.
    + (* H2 : SBS_Call — Null vs Iot *)
      rewrite Hval_y in Hnull; discriminate.
    + (* H2 : SBS_Call_NPE *)
      repeat split; reflexivity.
    + (* H2 : SBS_Call_NPE_Body — Null vs Iot *)
      rewrite Hval_y in Hnull; discriminate.
    + (* H2 : SBS_Call_MUTATIONEXP_Body — Null vs Iot *)
      rewrite Hval_y in Hnull; discriminate.
  - (* SBS_Call_NPE_Body *)
    inversion H2; subst.
    + (* H2 : SBS_Call — IH: NPE = OK *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Call_NPE — Iot vs Null *)
      rewrite Hnull in Hval_y; discriminate.
    + (* H2 : SBS_Call_NPE_Body *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [_ [Henv_eq Hheap_eq]].
      subst.
      repeat split; reflexivity.
    + (* H2 : SBS_Call_MUTATIONEXP_Body — IH: NPE = MUTATIONEXP *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [Hres_eq _].
      discriminate.
  - (* SBS_Call_MUTATIONEXP_Body *)
    inversion H2; subst.
    + (* H2 : SBS_Call — IH: MUTATIONEXP = OK *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Call_NPE — Iot vs Null *)
      rewrite Hnull in Hval_y; discriminate.
    + (* H2 : SBS_Call_NPE_Body — IH: MUTATIONEXP = NPE *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Call_MUTATIONEXP_Body *)
      rewrite Hval_y0 in Hval_y; injection Hval_y as Hly_eq; subst.
      rewrite Hbase0 in Hbase; injection Hbase as Hcy_eq; subst.
      destruct Hfind as [Hfmn1 Hmbody1].
      destruct Hfind0 as [Hfmn2 Hmbody2].
      pose proof (find_method_with_name_deterministic _ _ _ _ _ Hfmn1 Hfmn2) as Hmdef_eq.
      subst.
      rewrite Hargs0 in Hargs; injection Hargs as Hvals_eq; subst.
      destruct (IHeval_stmt _ _ _ Heval_body) as [_ [Henv_eq Hheap_eq]].
      subst.
      repeat split; reflexivity.
  - (* SBS_Seq *)
    inversion H2; subst.
    + (* H2 : SBS_Seq *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst.
      exact (IHeval_stmt2 _ _ _ Heval2).
    + (* H2 : SBS_Seq_NPE_first — IH on s1: OK = NPE *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_NPE_second — IH on s2: OK = NPE *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst.
      destruct (IHeval_stmt2 _ _ _ Heval2) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_MUTATIONEXP_first — IH on s1: OK = MUTATIONEXP *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_MUTATIONEXP_second — IH on s2: OK = MUTATIONEXP *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst.
      destruct (IHeval_stmt2 _ _ _ Heval2) as [Hres_eq _].
      discriminate.
  - (* SBS_Seq_NPE_first *)
    inversion H2; subst.
    + (* H2 : SBS_Seq — IH on s1: NPE = OK *)
      destruct (IHeval_stmt _ _ _ Heval1) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_NPE_first *)
      exact (IHeval_stmt _ _ _ Heval1).
    + (* H2 : SBS_Seq_NPE_second — IH on s1: NPE = OK *)
      destruct (IHeval_stmt _ _ _ Heval1) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_MUTATIONEXP_first — IH on s1: NPE = MUTATIONEXP *)
      destruct (IHeval_stmt _ _ _ Heval1) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_MUTATIONEXP_second — IH on s1: NPE = OK *)
      destruct (IHeval_stmt _ _ _ Heval1) as [Hres_eq _].
      discriminate.
  - (* SBS_Seq_NPE_second *)
    inversion H2; subst.
    + (* H2 : SBS_Seq — IH on s2: NPE = OK *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst.
      destruct (IHeval_stmt2 _ _ _ Heval2) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_NPE_first — IH on s1: OK = NPE *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_NPE_second *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst.
      exact (IHeval_stmt2 _ _ _ Heval2).
    + (* H2 : SBS_Seq_MUTATIONEXP_first — IH on s1: OK = MUTATIONEXP *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [Hres_eq _].
      discriminate.
    + (* H2 : SBS_Seq_MUTATIONEXP_second — IH on s2: NPE = MUTATIONEXP *)
      destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst.
      destruct (IHeval_stmt2 _ _ _ Heval2) as [Hres_eq _].
      discriminate.
  - (* SBS_Seq_MUTATIONEXP_first *)
    inversion H2; subst.
    + destruct (IHeval_stmt _ _ _ Heval1) as [Hres_eq _]; discriminate.
    + destruct (IHeval_stmt _ _ _ Heval1) as [Hres_eq _]; discriminate.
    + destruct (IHeval_stmt _ _ _ Heval1) as [Hres_eq _]; discriminate.
    + exact (IHeval_stmt _ _ _ Heval1).
    + destruct (IHeval_stmt _ _ _ Heval1) as [Hres_eq _]; discriminate.
  - (* SBS_Seq_MUTATIONEXP_second *)
    inversion H2; subst.
    + destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst. destruct (IHeval_stmt2 _ _ _ Heval2) as [Hres_eq _]; discriminate.
    + destruct (IHeval_stmt1 _ _ _ Heval1) as [Hres_eq _]; discriminate.
    + destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst. destruct (IHeval_stmt2 _ _ _ Heval2) as [Hres_eq _]; discriminate.
    + destruct (IHeval_stmt1 _ _ _ Heval1) as [Hres_eq _]; discriminate.
    + destruct (IHeval_stmt1 _ _ _ Heval1) as [_ [Hrenv1 Hh1]].
      subst. exact (IHeval_stmt2 _ _ _ Heval2).
Qed.

Lemma qualifier_typable_subtype : forall CT qr T1 T2 qcontext
  (Hsub   : qualified_type_subtype CT T1 T2)
  (Hqual1 : qualifier_typable_context qr (sqtype T1) qcontext),
  qualifier_typable_context qr (sqtype T2) qcontext.
Proof.
  intros CT qr T1 T2 qcontext Hsub Hqual1.
  apply qualified_type_subtype_q_subtype in Hsub.
  unfold qualifier_typable_context in *.
  destruct qr as [|].
  - (* Goal 1: Mut_r case *)
  destruct T1 as [q1 c1], T2 as [q2 c2].
  simpl in *.
  destruct q1, q2, qcontext; simpl in *; auto;
  try (inversion Hsub; subst; simpl in *; auto).

  - (* Goal 2: Imm_r case *)
    destruct T1 as [q1 c1], T2 as [q2 c2].
    simpl in *.
    destruct q1, q2, qcontext; simpl in *; auto;
    try (inversion Hsub; subst; simpl in *; auto).
Qed.

(* Subtyping Preservation for wf_r_typable *)
Lemma wf_r_typable_subtype : forall CT h loc T1 T2 qcontext
  (Hwfheap : wf_heap CT h)
  (Hwf     : wf_r_typable CT h loc T1 qcontext)
  (Hsub    : qualified_type_subtype CT T1 T2),
  wf_r_typable CT h loc T2 qcontext.
Proof.
  intros CT h loc T1 T2 qcontext hwfheap Hwf Hsub.
  unfold wf_r_typable in *.
  destruct (r_type h loc) as [rqt|] eqn:Hrtype; [|contradiction].
  split.
- (* Base type equality *)
  destruct Hwf as [Hbase _].
  apply qualified_type_subtype_base_subtype in Hsub.
  inversion Hsub; subst.
  exact Hbase.
  eapply base_trans; eauto.
  eapply base_trans; eauto.
  - destruct Hwf as [_ Hqualifier].
  eapply qualifier_typable_subtype; [ exact Hsub | exact Hqualifier].
Qed.

Lemma get_this_qualified_type_nth_error : forall  Tthis
  (Hthis : get_this_qualified_type sΓ = Some Tthis),
  nth_error sΓ 0 = Some Tthis.
Proof.
  intros sΓ Tthis H.
  unfold get_this_qualified_type in H.
  destructas [|T sΓ']; [discriminate|].
  injection H as H. subst. reflexivity.
Qed.

Lemma get_this_var_mapping_runtime_getVal : forall  loc
  (Hthis : get_this_var_mapping (vars rΓ) = Some loc),
  runtime_getVal rΓ 0 = Some (Iot loc).
Proof.
  intros rΓ loc H.
  unfold get_this_var_mapping in H.
  unfold runtime_getVal.
  destruct (vars rΓ) as [|v vs] eqn:Hvars.
  - discriminate H.
  - destruct v as [|l].
    + discriminate H.
    + injection H as Heq. subst l.
      reflexivity.
Qed.

Lemma vpa_mutability_tt_sctype_abstract_state : forall Tthis T,
  sctype (vpa_mutability_tt_abstract_state Tthis T) = sctype T.
Proof.
  intros Tthis T.
  unfold vpa_mutability_tt_abstract_state.
  destruct T as [q c]. simpl.
  destruct (sqtype Tthis); destruct q; simpl; reflexivity.
Qed.

Lemma vpa_mutability_tt_sctype_readonly_state : forall Tthis T,
  sctype (vpa_mutability_tt_readonly_state Tthis T) = sctype T.
Proof.
  intros Tthis T.
  unfold vpa_mutability_tt_readonly_state.
  destruct T as [q c]. simpl.
  destruct (sqtype Tthis); destruct q; simpl; reflexivity.
Qed.

(* Both directions cannot be proved here. *)
Lemma Forall2_nth_error_prop : forall {A B : Type} (P : A -> B -> Prop) (l1 : list A) (l2 : list B) (n : nat) (a : A) (b : B)
  (Hforall2 : Forall2 P l1 l2)
  (Hnth1    : nth_error l1 n = Some a)
  (Hnth2    : nth_error l2 n = Some b),
  P a b.
Proof.
  intros A B P l1 l2 n a b Hforall2 Hnth1 Hnth2.
  revert l1 l2 Hforall2 Hnth1 Hnth2.
  induction n; intros l1 l2 Hforall2 Hnth1 Hnth2.
  - (* n = 0 *)
    destruct l1 as [|a1 l1']; [discriminate|].
    destruct l2 as [|b1 l2']; [discriminate|].
    inversion Hforall2; subst.
    simpl in Hnth1, Hnth2.
    injection Hnth1 as Ha_eq.
    injection Hnth2 as Hb_eq.
    subst. exact H2.
  - (* n = S n' *)
    destruct l1 as [|a1 l1']; [discriminate|].
    destruct l2 as [|b1 l2']; [discriminate|].
    inversion Hforall2; subst.
    simpl in Hnth1, Hnth2.
    apply IHn with l1' l2'; assumption.
Qed.

Lemma nth_error_update_neq : forall {A : Type} (l : list A) (i j : nat) (v : A)
  (Hneq : i <> j),
  nth_error (update i v l) j = nth_error l j.
Proof.
  intros A l i j v Hneq.
  apply update_diff.
  exact Hneq.
Qed.

(* 1. Static-Runtime Correspondence Lemmas *)
Lemma runtime_lookup_list_preserves_length : forall  args vals
  (Hlookup : runtime_lookup_list rΓ args = Some vals),
  List.length vals = List.length args.
Proof.
  intros rΓ args vals H.
  unfold runtime_lookup_list in H.
  generalize dependent vals.
  induction args as [|a args' IH]; intros vals H.
  - simpl in H. injection H as H. subst. reflexivity.
  - simpl in H.
    destruct (runtime_getVal rΓ a) as [v|] eqn:Hval; [|discriminate].
    destruct (mapM (fun x => runtime_getVal rΓ x) args') as [vs|] eqn:Hmap; [|discriminate].
    injection H as H. subst.
    simpl. f_equal. apply IH. reflexivity.
Qed.

Lemma runtime_lookup_list_preserves_typing : forall CT   h args vals argtypes ι qcontext
  (Hreceiveraddr  : get_this_var_mapping (vars rΓ) = Some ι)
  (Hreceiverrmut  : (r_muttype h ι) = Some qcontext)
  (Hwf            : wf_r_config CT sΓ rΓ h)
  (Hstatic        : static_getType_list sΓ args = Some argtypes)
  (Hruntime       : runtime_lookup_list rΓ args = Some vals),
  Forall2 (fun v T => match v with
    | Null_a => True
    | Iot loc => wf_r_typable CT h loc T qcontext
    end) vals argtypes.
Proof.
  intros CT sΓ rΓ h args vals argtypes ι qcontext Hreceiveraddr Hreceiverrmut Hwf Hstatic Hruntime.
  unfold wf_r_config in Hwf.
  destruct Hwf as [_ [_ [_ [_ [Hlen Hcorr]]]]].
  generalize dependent vals. generalize dependent argtypes.
  induction args as [|a args' IH]; intros argtypes Hstatic vals Hruntime.
  - (* Base case: empty list *)
    unfold static_getType_list, runtime_lookup_list in *.
    simpl in Hstatic, Hruntime.
    injection Hstatic as Hstatic. injection Hruntime as Hruntime.
    subst. constructor.
  - (* Inductive case: a :: args' *)
    unfold static_getType_list, runtime_lookup_list in *.
    simpl in Hstatic, Hruntime.
    destruct (static_getType sΓ a) as [T|] eqn:HstaticT; [|discriminate].
    destruct (mapM (static_getType sΓ) args') as [Ts|] eqn:HstaticTs; [|discriminate].
    destruct (runtime_getVal rΓ a) as [v|] eqn:HruntimeV; [|discriminate].
    destruct (mapM (runtime_getVal rΓ) args') as [vs|] eqn:HruntimeVs; [|discriminate].
    injection Hstatic as Hstatic. injection Hruntime as Hruntime.
    subst. constructor.
    + (* Show v is well-typed with T *)
      assert (Ha_bound : a < List.length sΓ) by (apply static_getType_dom in HstaticT; exact HstaticT).
      specialize (Hcorr ι qcontext Hreceiveraddr Hreceiverrmut a Ha_bound T HstaticT).
      rewrite HruntimeV in Hcorr.
      destruct v as [|loc]; [trivial | exact Hcorr].
    + (* Apply IH to the tail *)
      apply IH.
      * unfold static_getType_list. reflexivity.
      * unfold runtime_lookup_list. reflexivity.
Qed.

(* 2. Heap Extension Preservation *)
Lemma heap_extension_preserves_objects : forall h obj loc
  (Hloc : loc < dom h),
  runtime_getObj (h ++ [obj]) loc = runtime_getObj h loc.
Proof.
  intros h obj loc Hloc.
  unfold runtime_getObj.
  apply nth_error_app1. exact Hloc.
Qed.

(* evaluation preserves runtime type on heap. *)
Lemma runtime_preserves_r_type_heap : forall CT  h loc C h' vals s rΓ'
  (Hobj  : runtime_getObj h loc = Some {| rt_type := C; fields_map := vals |})
  (Heval : eval_stmt CT rΓ h s OK rΓ' h'),
  exists vals', runtime_getObj h' loc = Some {| rt_type := C; fields_map := vals' |}.
Proof.
  intros. remember OK as ok. generalize dependent vals.
  induction Heval; intros; subst; try discriminate.
  1-3: exists vals; assumption.
  - (* SBS_FldWrite *)
    destruct (Nat.eq_dec loc loc_x).
    + subst loc_x. rewrite Hobj in Hobj0. inversion Hobj0; subst.
      exists (update f val_y vals). unfold runtime_getObj.
      unfold update_field. rewrite Hobj. simpl.
      rewrite update_same; auto. apply runtime_getObj_dom in Hobj; auto.
    + exists vals. unfold runtime_getObj. unfold update_field. rewrite Hobj.
      rewrite update_diff; auto.
  - (* SBS_New *)
    exists vals0.
    apply runtime_getObj_dom in Hobj0 as Hloc_dom.
    rewrite runtime_getObj_last2; auto.
  - (* SBS_Call *)
    eapply IHHeval; eauto.
  - (* SBS_Seq *)
    destruct (IHHeval1 Heqok vals Hobj) as [vals' Hobj'].
    destruct (IHHeval2 Heqok vals' Hobj') as [vals'' Hobj''].
    exists vals''. exact Hobj''.
Qed.

Lemma Forall2_length : forall {A B} (P : A -> B -> Prop) l1 l2
  (H : Forall2 P l1 l2),
  List.length l1 = List.length l2.
Proof.
  intros A B P l1 l2 H.
  induction H; [reflexivity | simpl; f_equal; assumption].
Qed.

(* Expression Evaluation Preservation *)
The receiver premises supply the runtime qualifier context used in the non-null result.
Lemma expr_eval_preservation : forall CT  mt  h e v rΓ' h' T ι qcontext
  (Hreceiveraddr : get_this_var_mapping (vars rΓ) = Some ι)
  (Hreceiverrmut : (r_muttype h ι) = Some qcontext)
  (Hwf           : wf_r_config CT sΓ rΓ h)
  (Htype         : expr_has_type CT sΓ mt e T)
  (Heval         : eval_expr CT rΓ h e v OK rΓ' h'),
  match v with
  | Null_a => True
  | Iot loc => wf_r_typable CT h loc T qcontext
  end.
Proof.
  intros CT sΓ mt rΓ h e v rΓ' h' T ι qcontext Hreceiveraddr Hreceiverrmut Hwf Htype Heval.
  have Hevalcopy := Heval.
  remember OK as ok.
  induction Heval; inversion Htype; subst; try discriminate.
  - (* EBS_Null *) trivial.
  - (* EBS_Val *)
    unfold wf_r_config in Hwf.
    destruct Hwf as [_ [_ [_ [_ [Hlen Hcorr]]]]].
    assert (Hx_bound : x < List.length sΓ) by (apply static_getType_dom in Hget; exact Hget).
    specialize (Hcorr ι qcontext Hreceiveraddr Hreceiverrmut x Hx_bound T Hget).
    rewrite Hval in Hcorr.
    destruct v as [|loc]; [trivial | exact Hcorr].
  - (* EBS_Field *)
  destruct v1 as [|loc]; [trivial|].
  (* Need to show: wf_r_typable CT h loc (vpa_type_to_type T0 ...) *)
  unfold wf_r_config in Hwf.
  destruct Hwf as [Hwfclass [Hwf_heap [Hwf_renv [Hwf_senv [_ Hcorr]]]]].
  (* Use heap well-formedness to get field typing *)
  assert (Hobj_wf : wf_obj CT h v).
  {
    apply Hwf_heap.
    apply runtime_getObj_dom in Hobj.
    exact Hobj.
  }
  unfold wf_obj in Hobj_wf.
  rewrite Hobj in Hobj_wf.
  destruct Hobj_wf as [_ Hfields_wf].
  destruct Hfields_wf as [Hdom_eq Hforall2].
  destruct Hforall2 as [Hcollect [Hdom_eq_test Hforall2]].

  assert (Hfield_lookup : exists fdef,
    nth_error Hdom_eq f = Some fdef /\
    nth_error (fields_map o) f = Some (Iot loc)).
  {
    (* Use H1 and Hdom_eq to establish this *)
    (* Convert getVal to nth_error using domain equality *)
    assert (Hf_in_dom : f < dom (fields_map o)).
    {
      apply getVal_dom in Hfield.
      exact Hfield.
    }
    (* Use domain equality to get f in collect_fields domain *)
    rewrite Hdom_eq_test in Hf_in_dom.
    assert (Hfdef_exists : exists fdef, nth_error Hdom_eq f = Some fdef).
    {
      destruct (nth_error Hdom_eq f) as [fdef|] eqn:Hfdef_lookup.
      - exists fdef. reflexivity.
      - exfalso.
        apply nth_error_None in Hfdef_lookup.
        lia.
    }
    destruct Hfdef_exists as [fdef Hfdef_lookup].
    (* Convert H1 from getVal to nth_error *)
    assert (Hfield_nth : nth_error (fields_map o) f = Some (Iot loc)).
    {
      unfold getVal in Hfield.
      exact Hfield.
    }
    exists fdef.
    split; [exact Hfdef_lookup | exact Hfield_nth].
  }
  destruct Hfield_lookup as [fdef [Hfdef_lookup Hfield_nth]].
  (* Apply Forall2 property *)
  eapply Forall2_nth_error_prop in Hforall2; eauto.
  simpl in Hforall2.
  (* Now check if loc exists in heap *)
  destruct (runtime_getObj h loc) as [o_loc|] eqn:Hloc_obj.
  * (* loc exists in heap *)
    destruct Hforall2 as [rqt [Hrtype_loc Hsubtype]].
    (* Now you have the typing for loc *)
    unfold wf_r_typable.
    rewrite Hrtype_loc.
    (* Get this variable mapping *)
    destruct (get_this_var_mapping (vars rΓ)) as [ι'|] eqn:Hthis.
    2:{
      (* Use wf_r_config to show that this variable mapping must exist *)
      destruct Hwf_renv as [Hwf_this [Hwf_this_addr Hwf_renv]].
      (* Hwf_this should guarantee that get_this_var_mapping succeeds *)
      unfold get_this_var_mapping in Hthis.
      (* get_this_var_mapping typically looks at vars[0] *)
      assert (H0_bound : 0 < dom (vars rΓ)) by exact Hwf_this.
      (* unfold dom in H0_bound. *)
      (* Since length > 0, nth_error 0 must succeed *)
      destruct (nth_error (vars rΓ) 0) as [v0|] eqn:Hv0.
      - (* vars[0] exists, so get_this_var_mapping should succeed *)
        simpl in Hthis.
        destruct (vars rΓ) as [|v1 rest] eqn:Hvars.
        simpl in Hv0.
        discriminate.
        simpl in Hv0.
        injection Hv0 as Hv0_eq.
        subst v0.
        (* So v1 = v0, and from Hthis we know v1 = Null_a *)
        destruct v1 as [|loc'].
        + (* v1 = Null_a, consistent with Hthis *)
          (* But this contradicts well-formedness - need stronger condition *)
          (* For now, this might be an allowed case *)
          destruct Hwf_this_addr as [iot Hiot].
          (* gget (Null_a :: rest) 0 should return Null_a, not Iot iot *)
          simpl in Hiot.
          destruct Hiot as [Hiot Hthisdom].
          (* gget is likely nth_error or similar, so gget (Null_a :: rest) 0 = Some Null_a *)
          discriminate Hiot.
        + (* v1 = Iot loc', should make get_this_var_mapping return Some loc' *)
          simpl in Hthis.
          discriminate Hthis.
      - (* vars[0] doesn't exist, contradicts length > 0 *)
        apply nth_error_None in Hv0.
        simpl in H0_bound.
        lia.
    }
    destruct (r_muttype h ι') as [q|] eqn:Hmut.
    2:{
      assert (Hι'_in_heap : ι' < dom h).
      {
        (* ι' comes from get_this_var_mapping, so it must be in heap *)
        (* Use the third component of Hwf_renv *)
        destruct Hwf_renv as [Hwf_this [Hwf_this_addr Hwf_renv]].
        destruct Hwf_this_addr as [iot Hiot].
        destruct Hiot as [Hiot Hthisdom].
        unfold get_this_var_mapping in Hthis.
        assert (Hconnect : ι' = iot).
        {
          unfold get_this_var_mapping in Hthis.
          destruct (vars rΓ) as [|vtest rest] eqn:Hvars.
          - (* Empty list case *)
            discriminate Hthis.
          - (* Non-empty list case *)
            destruct vtest as [|loctest] eqn:Hv.
            + (* Null_a case *)
              discriminate Hthis.
            + (* Iot loc case *)
              injection Hthis as Heq.
              subst ι'.
              simpl in Hiot.
              injection Hiot as Heq2.
              exact Heq2.
        }
        rewrite Hconnect. exact Hthisdom.
      }
      (* Now use heap well-formedness *)
      apply Hwf_heap in Hι'_in_heap.
      unfold wf_obj in Hι'_in_heap.
        destruct Hwf_renv as [Hwf_this [Hwf_this_addr Hwf_renv]].
        destruct Hwf_this_addr as [iot Hiot].
        destruct Hiot as [Hiot Hthisdom].
      assert (Hconnect : ι' = iot).
      {
        unfold get_this_var_mapping in Hthis.
        destruct (vars rΓ) as [|vtest rest] eqn:Hvars.
        - discriminate Hthis.
        - destruct vtest as [|loctest] eqn:Hv.
          + discriminate Hthis.
          + injection Hthis as Heq.
            subst ι'.
            simpl in Hiot.
            injection Hiot as Heq2.
            exact Heq2.
      }
      rewrite Hconnect in Hmut.
      unfold r_muttype in Hmut.
      apply runtime_getObj_Some in Hthisdom.
      destruct Hthisdom as [C [ω Ho']].
      rewrite Ho' in Hmut.
      discriminate Hmut.
    }
  assert (Hfdef_eq : fdef = fDef).
  {
    unfold sf_def_rel in Hfld_def.

    assert (Hfield_lookup_o : FieldLookup CT (rctype (rt_type o)) f fdef).
    {
      apply FL_Found with Hdom_eq.
      - exact Hcollect.
      - exact Hfdef_lookup.
    }

    (* Use wf_r_typable for x *)
    assert (Hx_wf : wf_r_typable CT h v T0 qcontext).
    {
      assert (Hx_bound : x < dom sΓ) by (apply static_getType_dom in Hget_x; exact Hget_x).
      specialize (Hcorr ι qcontext Hreceiveraddr Hreceiverrmut x Hx_bound T0 Hget_x).
      rewrite Hval in Hcorr.
      exact Hcorr.
    }

    (* Extract base subtyping from wf_r_typable *)
    unfold wf_r_typable in Hx_wf.
    unfold r_type in Hx_wf.
    rewrite Hobj in Hx_wf.
    simpl in Hx_wf.
    eapply field_lookup_deterministic_rel.
    - exact Hfield_lookup_o.
    - destruct Hx_wf as [Hbase _].
      eapply field_inheritance_subtyping; eauto.
  }
  subst fdef.
  split.
  -- (* Base subtyping *)
    simpl.
    destruct Hsubtype as [Hbasesubtyp _].
    exact Hbasesubtyp.
  -- (* Qualifier typability *)
    destruct Hsubtype as [Hbasesubtyp Hqualifiertypable].
    unfold qualifier_typable_context.
    inversion Hreceiveraddr; subst ι'.
    rewrite Hmut in Hreceiverrmut.
    inversion Hreceiverrmut; subst q.
    unfold vpa_mutability_runtime; unfold vpa_mutability_stype_fld_abstract_state in *; unfold vpa_mutability_rec_fld in Hqualifiertypable;
    destruct (rqtype rqt) eqn: Hrqttype;
    destruct (sqtype T0) eqn: HsqtypeT0;
    destruct (mutability (ftype fDef)) eqn: Hfieldqualifier;
    destruct qcontext eqn: Hqcontext;
    simpl;
    try (inversion Hsubtype; auto);
    destruct T0 as [q0 c0] eqn: HT0type;
    try discriminate;
    try trivial.
    all:
    try destruct (rqtype (rt_type o)) eqn: HreceiverRuntimeQualifier;
    unfold qualifier_typable_heap in Hqualifiertypable;
    try easy.
    all: try simpl in HsqtypeT0; subst q0.
    all: try have H7copy := Hget_x; try apply static_getType_dom in Hget_x.
    all: try unfold static_getType in H7copy.
    all: try specialize (Hcorr ι Mut_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Imm; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Imm_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Imm; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Mut_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := RDM; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Imm_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := RDM; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Mut_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Bot; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Imm_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Bot; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Mut_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Mut; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Imm_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Mut; sctype :=
    c0
    |} H7copy).
    all: try rewrite Hval in Hcorr.
    all: try unfold wf_r_typable in Hcorr.
    all: try unfold r_type in Hcorr.
    all: try rewrite Hobj in Hcorr.
    all: try destruct Hcorr as [Hobasetype Hoqualifier].
    all: try simpl in Hoqualifier.
    all: try rewrite HreceiverRuntimeQualifier in Hoqualifier.
    all: try unfold qualifier_typable in Hoqualifier.
    all: try unfold vpa_mutability_runtime; try unfold vpa_mutability_stype_fld in *; try unfold vpa_mutability_rec_fld in Hqualifiertypable; try easy.
  *  exfalso. exact Hforall2.
  -
  destruct v1 as [|loc]; [trivial|].
  (* Need to show: wf_r_typable CT h loc (vpa_type_to_type T0 ...) *)
  unfold wf_r_config in Hwf.
  destruct Hwf as [Hwfclass [Hwf_heap [Hwf_renv [Hwf_senv [_ Hcorr]]]]].
  (* Use heap well-formedness to get field typing *)
  assert (Hobj_wf : wf_obj CT h v).
  {
    apply Hwf_heap.
    apply runtime_getObj_dom in Hobj.
    exact Hobj.
  }
  unfold wf_obj in Hobj_wf.
  rewrite Hobj in Hobj_wf.
  destruct Hobj_wf as [_ Hfields_wf].
  destruct Hfields_wf as [Hdom_eq Hforall2].
  destruct Hforall2 as [Hcollect [Hdom_eq_test Hforall2]].

  assert (Hfield_lookup : exists fdef,
    nth_error Hdom_eq f = Some fdef /\
    nth_error (fields_map o) f = Some (Iot loc)).
  {
    (* Use H1 and Hdom_eq to establish this *)
    (* Convert getVal to nth_error using domain equality *)
    assert (Hf_in_dom : f < dom (fields_map o)).
    {
      apply getVal_dom in Hfield.
      exact Hfield.
    }
    (* Use domain equality to get f in collect_fields domain *)
    rewrite Hdom_eq_test in Hf_in_dom.
    assert (Hfdef_exists : exists fdef, nth_error Hdom_eq f = Some fdef).
    {
      destruct (nth_error Hdom_eq f) as [fdef|] eqn:Hfdef_lookup.
      - exists fdef. reflexivity.
      - exfalso.
        apply nth_error_None in Hfdef_lookup.
        lia.
    }
    destruct Hfdef_exists as [fdef Hfdef_lookup].
    (* Convert H1 from getVal to nth_error *)
    assert (Hfield_nth : nth_error (fields_map o) f = Some (Iot loc)).
    {
      unfold getVal in Hfield.
      exact Hfield.
    }
    exists fdef.
    split; [exact Hfdef_lookup | exact Hfield_nth].
  }
  destruct Hfield_lookup as [fdef [Hfdef_lookup Hfield_nth]].
  (* Apply Forall2 property *)
  eapply Forall2_nth_error_prop in Hforall2; eauto.
  simpl in Hforall2.
  (* Now check if loc exists in heap *)
  destruct (runtime_getObj h loc) as [o_loc|] eqn:Hloc_obj.
  * (* loc exists in heap *)
    destruct Hforall2 as [rqt [Hrtype_loc Hsubtype]].
    (* Now you have the typing for loc *)
    unfold wf_r_typable.
    rewrite Hrtype_loc.
    (* Get this variable mapping *)
    destruct (get_this_var_mapping (vars rΓ)) as [ι'|] eqn:Hthis.
    2:{
      (* Use wf_r_config to show that this variable mapping must exist *)
      destruct Hwf_renv as [Hwf_this [Hwf_this_addr Hwf_renv]].
      (* Hwf_this should guarantee that get_this_var_mapping succeeds *)
      unfold get_this_var_mapping in Hthis.
      (* get_this_var_mapping typically looks at vars[0] *)
      assert (H0_bound : 0 < dom (vars rΓ)) by exact Hwf_this.
      (* unfold dom in H0_bound. *)
      (* Since length > 0, nth_error 0 must succeed *)
      destruct (nth_error (vars rΓ) 0) as [v0|] eqn:Hv0.
      - (* vars[0] exists, so get_this_var_mapping should succeed *)
        simpl in Hthis.
        destruct (vars rΓ) as [|v1 rest] eqn:Hvars.
        simpl in Hv0.
        discriminate.
        simpl in Hv0.
        injection Hv0 as Hv0_eq.
        subst v0.
        (* So v1 = v0, and from Hthis we know v1 = Null_a *)
        destruct v1 as [|loc'].
        + (* v1 = Null_a, consistent with Hthis *)
          (* But this contradicts well-formedness - need stronger condition *)
          (* For now, this might be an allowed case *)
          destruct Hwf_this_addr as [iot Hiot].
          (* gget (Null_a :: rest) 0 should return Null_a, not Iot iot *)
          simpl in Hiot.
          destruct Hiot as [Hiot Hthisdom].
          (* gget is likely nth_error or similar, so gget (Null_a :: rest) 0 = Some Null_a *)
          discriminate Hiot.
        + (* v1 = Iot loc', should make get_this_var_mapping return Some loc' *)
          simpl in Hthis.
          discriminate Hthis.
      - (* vars[0] doesn't exist, contradicts length > 0 *)
        apply nth_error_None in Hv0.
        simpl in H0_bound.
        lia.
    }
    destruct (r_muttype h ι') as [q|] eqn:Hmut.
    2:{
      assert (Hι'_in_heap : ι' < dom h).
      {
        (* ι' comes from get_this_var_mapping, so it must be in heap *)
        (* Use the third component of Hwf_renv *)
        destruct Hwf_renv as [Hwf_this [Hwf_this_addr Hwf_renv]].
        destruct Hwf_this_addr as [iot Hiot].
        destruct Hiot as [Hiot Hthisdom].
        unfold get_this_var_mapping in Hthis.
        assert (Hconnect : ι' = iot).
        {
          unfold get_this_var_mapping in Hthis.
          destruct (vars rΓ) as [|vtest rest] eqn:Hvars.
          - (* Empty list case *)
            discriminate Hthis.
          - (* Non-empty list case *)
            destruct vtest as [|loctest] eqn:Hv.
            + (* Null_a case *)
              discriminate Hthis.
            + (* Iot loc case *)
              injection Hthis as Heq.
              subst ι'.
              simpl in Hiot.
              injection Hiot as Heq2.
              exact Heq2.
        }
        rewrite Hconnect. exact Hthisdom.
      }
      (* Now use heap well-formedness *)
      apply Hwf_heap in Hι'_in_heap.
      unfold wf_obj in Hι'_in_heap.
      destruct Hwf_renv as [Hwf_this [Hwf_this_addr Hwf_renv]].
      destruct Hwf_this_addr as [iot Hiot].
      destruct Hiot as [Hiot Hthisdom].
      assert (Hconnect : ι' = iot).
      {
        unfold get_this_var_mapping in Hthis.
        destruct (vars rΓ) as [|vtest rest] eqn:Hvars.
        - discriminate Hthis.
        - destruct vtest as [|loctest] eqn:Hv.
          + discriminate Hthis.
          + injection Hthis as Heq.
            subst ι'.
            simpl in Hiot.
            injection Hiot as Heq2.
            exact Heq2.
      }
      rewrite Hconnect in Hmut.
      unfold r_muttype in Hmut.
      apply runtime_getObj_Some in Hthisdom.
      destruct Hthisdom as [C [ω Ho']].
      rewrite Ho' in Hmut.
      discriminate Hmut.
    }
    assert (Hfdef_eq : fdef = fDef).
    {
      unfold sf_def_rel in Hfld_def.

      (* Next, get FieldLookup from CollectFields *)
      assert (Hfield_lookup_o : FieldLookup CT (rctype (rt_type o)) f fdef).
      {
        apply FL_Found with Hdom_eq.
        - exact Hcollect.
        - exact Hfdef_lookup.
      }

      (* Use wf_r_typable for x *)
      assert (Hx_wf : wf_r_typable CT h v T0 qcontext).
      {
        assert (Hx_bound : x < dom sΓ) by (apply static_getType_dom in Hget_x; exact Hget_x).
        specialize (Hcorr ι qcontext Hreceiveraddr Hreceiverrmut x Hx_bound T0 Hget_x).
        rewrite Hval in Hcorr.
        exact Hcorr.
      }

      (* Extract base subtyping from wf_r_typable *)
      unfold wf_r_typable in Hx_wf.
      unfold r_type in Hx_wf.
      rewrite Hobj in Hx_wf.
      simpl in Hx_wf.
      eapply field_lookup_deterministic_rel.
      - exact Hfield_lookup_o.
      - destruct Hx_wf as [Hbase _].
        eapply field_inheritance_subtyping; eauto.
    }
    subst fdef.
    split.
  -- (* Base subtyping *)
    simpl.
    destruct Hsubtype as [Hbasesubtyp _].
    exact Hbasesubtyp.
  -- (* Qualifier typability *)
    destruct Hsubtype as [Hbasesubtyp Hqualifiertypable].
    unfold qualifier_typable_context.
    inversion Hreceiveraddr; subst ι'.
    rewrite Hmut in Hreceiverrmut.
    inversion Hreceiverrmut; subst q.
    unfold vpa_mutability_runtime; unfold vpa_mutability_stype_fld_abstract_state in *; unfold vpa_mutability_rec_fld in Hqualifiertypable;
    destruct (rqtype rqt) eqn: Hrqttype;
    destruct (sqtype T0) eqn: HsqtypeT0;
    destruct (mutability (ftype fDef)) eqn: Hfieldqualifier;
    destruct qcontext eqn: Hqcontext;
    simpl;
    try (inversion Hsubtype; auto);
    destruct T0 as [q0 c0] eqn: HT0type;
    try discriminate;
    try trivial.
    all:
    try destruct (rqtype (rt_type o)) eqn: HreceiverRuntimeQualifier;
    unfold qualifier_typable_heap in Hqualifiertypable;
    try easy.
    all: try simpl in HsqtypeT0; subst q0.
    all: try have H7copy := Hget_x; try apply static_getType_dom in Hget_x.
    all: try unfold static_getType in H7copy.
    all: try specialize (Hcorr ι Mut_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Imm; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Imm_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Imm; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Mut_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := RDM; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Imm_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := RDM; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Mut_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Bot; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Imm_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Bot; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Mut_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Mut; sctype :=
    c0
    |} H7copy).
    all: try specialize (Hcorr ι Imm_r Hreceiveraddr Hmut x Hget_x {|
    sqtype := Mut; sctype :=
    c0
    |} H7copy).
    all: try rewrite Hval in Hcorr.
    all: try unfold wf_r_typable in Hcorr.
    all: try unfold r_type in Hcorr.
    all: try rewrite Hobj in Hcorr.
    all: try destruct Hcorr as [Hobasetype Hoqualifier].
    all: try simpl in Hoqualifier.
    all: try rewrite HreceiverRuntimeQualifier in Hoqualifier.
    all: try unfold qualifier_typable in Hoqualifier.
    all: try unfold vpa_mutability_runtime; try unfold vpa_mutability_stype_fld in *; try unfold vpa_mutability_rec_fld in Hqualifiertypable; try easy.
  *  exfalso. exact Hforall2.
Qed.

Lemma runtime_lookup_list_preserves_wf_values : forall CT  h zs vals0
  (Hwf_renv : wf_renv CT rΓ h)
  (Hlookup  : runtime_lookup_list rΓ zs = Some vals0),
  Forall (fun v => match v with
    | Null_a => True
    | Iot loc => match runtime_getObj h loc with Some _ => True | None => False end
    end) vals0.
Proof.
  intros CT rΓ h zs vals0 Hwf_renv Hlookup.
  unfold runtime_lookup_list in Hlookup.
  unfold wf_renv in Hwf_renv.
  destruct Hwf_renv as [_ [_ Hallvals]].
  (* Prove by induction on zs and vals0 simultaneously *)
  generalize dependent vals0.
  induction zs as [|z zs' IH]; intros vals0 Hlookup.
  - (* Base case: zs = [] *)
    simpl in Hlookup.
    injection Hlookup as Hlookup.
    subst vals0.
    constructor.
  - (* Inductive case: zs = z :: zs' *)
    simpl in Hlookup.
    destruct (runtime_getVal rΓ z) as [v|] eqn:Hv; [|discriminate].
    destruct (mapM (runtime_getVal rΓ) zs') as [vs|] eqn:Hvs; [|discriminate].
    injection Hlookup as Hlookup.
    subst vals0.
    constructor.
    + (* Show v is well-formed *)
      destruct v as [|loc].
      * (* Case: Null_a *)
        trivial.
      * (* Case: Iot loc *)
        assert (Hloc_bound : z < dom (vars rΓ)).
        {
          apply runtime_getVal_dom in Hv.
          exact Hv.
        }
        assert (Hloc_wf := Forall_nth_error _ _ _ _ Hallvals Hv).
        simpl in Hloc_wf.
        exact Hloc_wf.
    + (* Show vs is well-formed *)
      apply IH.
      reflexivity.
Qed.