clutch.foxtrot.unary_rel.unary_fundamental

Compatibility lemmas for the logical relation
From iris.base_logic Require Export invariants.
From iris.proofmode Require Import proofmode.
From clutch.prelude Require Import stdpp_ext.
From clutch.con_prob_lang Require Import metatheory notation lang.
From clutch.foxtrot Require Import primitive_laws proofmode.
From clutch.con_prob_lang.typing Require Import types.
From clutch.foxtrot.unary_rel Require Import unary_model unary_compatibility unary_rel_tactics unary_app_rel_rules unary_interp.

Section fundamental.
  Context `{!foxtrotGS Σ}.
  Implicit Types Δ : listO (lrelC Σ).
  Hint Resolve to_of_val : core.

  Local Ltac intro_clause := progress (iIntros (vs) "#Hvs /=").
  Local Ltac intro_clause' := progress (iIntros (?) "? /=").
  Local Ltac value_case := try intro_clause';
    try rel_pure_l; rel_values.

  Local Tactic Notation "rel_bind_ap" uconstr(e1) (* uconstr(e2)  *)constr(IH) ident(v) constr(Hv):=
    rel_bind_l (subst_map _ e1);
    (* rel_bind_r (subst_map _ e2); *)
    try iSpecialize (IH with "Hvs");
    iApply (refines_bind with IH);
    iIntros (v) Hv; simpl.

  Lemma bin_log_related_var Δ Γ x τ :
    Γ !! x = Some τ
     Δ;Γ Var x : τ.
  Proof.
    intros Hx. intro_clause. simpl.
    rewrite (env_ltyped2_lookup _ vs x); last first.
    { rewrite lookup_fmap Hx //. }
    iDestruct "Hvs" as (v1 ->) "HA". simpl.
    by iApply refines_ret.
  Qed.

  Lemma bin_log_related_pair Δ Γ e1 e2 (τ1 τ2 : type) :
    (Δ;Γ e1 : τ1) -∗
    (Δ;Γ e2 : τ2) -∗
    Δ;Γ Pair e1 e2 : TProd τ1 τ2.
  Proof.
    iIntros "IH1 IH2".
    intro_clause.
    iApply (refines_pair with "[IH1] [IH2]").
    - by iApply "IH1".
    - by iApply "IH2".
  Qed.

  Lemma bin_log_related_fst Δ Γ e τ1 τ2 :
    (Δ;Γ e : τ1 * τ2) -∗
    Δ;Γ Fst e : τ1.
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v "IH".
    iDestruct "IH" as (v1 v2 ) "(% & IHw & IHw')". simplify_eq/=.
    value_case.
  Qed.

  Lemma bin_log_related_snd Δ Γ e τ1 τ2 :
    (Δ;Γ e : τ1 * τ2) -∗
    Δ;Γ Snd e : τ2.
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v "IH".
    iDestruct "IH" as (v1 w1) "(% & IHw & IHw')". simplify_eq/=.
    value_case.
  Qed.

  Lemma bin_log_related_app Δ Γ e1 e2 τ1 τ2 :
    (Δ;Γ e1 : τ1 τ2) -∗
    (Δ;Γ e2 : τ1) -∗
    Δ;Γ App e1 e2 : τ2.
  Proof.
    iIntros "IH1 IH2".
    intro_clause.
    iApply (refines_app with "[IH1] [IH2]").
    - by iApply "IH1".
    - by iApply "IH2".
  Qed.

  Lemma bin_log_related_rec Δ (Γ : stringmap type) (f x : binder) (e : expr) τ1 τ2 :
     (Δ;<[f:=TArrow τ1 τ2]>(<[x:=τ1]>Γ) e : τ2) -∗
    Δ;Γ Rec f x e : τ1 τ2.
  Proof.
    iIntros "#Ht".
    intro_clause.
    rel_pure_l.
    iApply refines_arrow_val.
    iModIntro. iLöb as "IH". iIntros (v1) "#Hτ1".
    rel_pure_l.

    set (r := (RecV f x (subst_map (binder_delete x (binder_delete f (vs))) e))).
    set (vvs' := binder_insert f r (binder_insert x (v1) vs)).
    iSpecialize ("Ht" $! vvs' with "[#]").
    { rewrite !binder_insert_fmap.
      iApply (env_ltyped2_insert with "[IH]").
      - iApply "IH".
      - iApply (env_ltyped2_insert with "Hτ1").
        by iFrame. }

    unfold vvs'.
    destruct x as [|x], f as [|f];
      rewrite /= ?fmap_insert ?subst_map_insert //.
      try by iApply "H".
    destruct (decide (x = f)) as [->|]; iSimpl in "Ht".
    - rewrite !delete_insert_eq !subst_subst !delete_delete_eq.
      by iApply "Ht".
    - rewrite !delete_insert_ne // subst_map_insert.
      rewrite !(subst_subst_ne _ x f) // subst_map_insert.
  Qed.

  Lemma bin_log_related_tlam Δ Γ (e : expr) τ :
    ( (A : lrel Σ),
       (〈(A::Δ);Γ e : τ)) -∗
    Δ;Γ (Λ: e) : ∀: τ.
  Proof.
    iIntros "#H".
    intro_clause. rel_pure_l.
    iApply refines_forall.
    iModIntro. iIntros (A).
    iDestruct ("H" $! A) as "#H1".
    iApply "H1".
    by rewrite (interp_ren A Δ Γ).
  Qed.

  Lemma bin_log_related_tapp' Δ Γ e τ τ' :
    (Δ;Γ e : ∀: τ) -∗
    Δ;Γ (TApp e) : τ.[τ'/].
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v "IH".
    iSpecialize ("IH" $! (interp τ' Δ)).
    iDestruct "IH" as "#IH".
    iSpecialize ("IH" $! #() with "[//]").
    by rewrite -interp_subst.
  Qed.

  Lemma bin_log_related_tapp (τi : lrel Σ) Δ Γ e τ :
    (Δ;Γ e : ∀: τ) -∗
    τi::Δ;Γ (TApp e) : τ.
  Proof.
    iIntros "IH". intro_clause.
    iApply (bin_log_related_app _ _ e #() () τ with "[IH] [] Hvs").
    - iClear (vs) "Hvs". intro_clause.
      rewrite interp_ren.
      iSpecialize ("IH" with "Hvs").
      iApply (refines_wand with "IH").
      eauto with iFrame.
    - value_case.
  Qed.

  Lemma bin_log_related_seq R Δ Γ e1 e2 τ1 τ2 :
    (〈(R::Δ);Γ e1 : τ1) -∗
    (Δ;Γ e2 : τ2) -∗
    Δ;Γ (e1;; e2) : τ2.
  Proof.
    iIntros "He1 He2".
    intro_clause.
    iApply (refines_seq (interp τ1 (R::Δ)) with "[He1]").
    - iApply ("He1" with "[Hvs]").
      by rewrite interp_ren.
    - by iApply "He2".
  Qed.

  Lemma bin_log_related_seq' Δ Γ e1 e2 τ1 τ2 :
    (Δ;Γ e1 : τ1) -∗
    (Δ;Γ e2 : τ2) -∗
    Δ;Γ (e1;; e2) : τ2.
  Proof.
    iIntros "He1 He2".
    iApply (bin_log_related_seq lrel_true _ _ _ _ τ1.[ren (+1)] with "[He1] He2").
    intro_clause.
    rewrite interp_ren -(interp_ren_up [] Δ τ1).
    by iApply "He1".
  Qed.

  Lemma bin_log_related_injl Δ Γ e τ1 τ2 :
    (Δ;Γ e : τ1) -∗
    Δ;Γ InjL e : τ1 + τ2.
  Proof.
    iIntros "IH".
    intro_clause.
    iApply refines_injl.
    by iApply "IH".
  Qed.

  Lemma bin_log_related_injr Δ Γ e τ1 τ2 :
    (Δ;Γ e : τ2) -∗
    Δ;Γ InjR e : τ1 + τ2.
  Proof.
    iIntros "IH".
    intro_clause.
    iApply refines_injr. by iApply "IH".
  Qed.

  Lemma bin_log_related_case Δ Γ e0 e1 e2 τ1 τ2 τ3 :
    (Δ;Γ e0 : τ1 + τ2) -∗
    (Δ;Γ e1 : τ1 τ3) -∗
    (Δ;Γ e2 : τ2 τ3) -∗
    Δ;Γ Case e0 e1 e2 : τ3.
  Proof.
    iIntros "IH1 IH2 IH3".
    intro_clause.
    rel_bind_ap e0 "IH1" v0 "IH1".
    iDestruct "IH1" as (w) "[(% & #Hw)|(% & #Hw)]"; simplify_eq/=;
      rel_pures_l.
    - iApply (bin_log_related_app Δ Γ _ w with "IH2 [] Hvs").
      value_case.
    - iApply (bin_log_related_app Δ Γ _ w with "IH3 [] Hvs").
      value_case.
  Qed.

  Lemma bin_log_related_if Δ Γ e0 e1 e2 τ :
    (Δ;Γ e0 : TBool) -∗
    (Δ;Γ e1 : τ) -∗
    (Δ;Γ e2 : τ) -∗
    Δ;Γ If e0 e1 e2 : τ.
  Proof.
    iIntros "IH1 IH2 IH3".
    intro_clause.
    rel_bind_ap e0 "IH1" v0 "IH1".
    iDestruct "IH1" as ([]) "%"; simplify_eq/=;
      rel_pures_l.
    - by iApply "IH2".
    - by iApply "IH3".
  Qed.

  Lemma bin_log_related_load Δ Γ e τ :
    (Δ;Γ e : (TRef τ)) -∗
    Δ;Γ Load e : τ.
  Proof.
    iIntros "IH".
    intro_clause.
    iApply refines_load.
    by iApply "IH".
  Qed.

  Lemma bin_log_related_store Δ Γ e1 e2 τ :
    (Δ;Γ e1 : TRef τ) -∗
    (Δ;Γ e2 : τ) -∗
    Δ;Γ Store e1 e2 : ().
  Proof.
    iIntros "IH1 IH2".
    intro_clause.
    iApply (refines_store with "[IH1] [IH2]").
    - by iApply "IH1".
    - by iApply "IH2".
  Qed.

  Lemma bin_log_related_alloc Δ Γ e τ :
    (Δ;Γ e : τ) -∗
    Δ;Γ Alloc e : TRef τ.
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v "IH".
    rel_alloc_l l as "Hl".
    (* rel_alloc_r k as "Hk". *)
    iMod (inv_alloc (logN .@ (l)) _ ( w1,
      l w1 interp τ Δ w1)%I with "[Hl IH]") as "HN"; eauto.
    (* { iNext. iExists v; simpl; iFrame. } *)
    rel_values. iExists l. eauto.
  Qed.

  Lemma bin_log_related_alloctape Δ Γ e :
    (Δ;Γ e : TNat) -∗
    Δ;Γ alloc e : TTape.
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v "IH".
    iDestruct "IH" as (N) "%H2".
    subst.
    rel_alloctape_l α as "Hα".
    (* rel_alloctape_r β as "Hβ". *)
    iPoseProof (tapeN_to_empty with "Hα") as "Hα".
    (* iPoseProof (spec_tapeN_to_empty with "Hβ") as "Hβ". *)
    iMod (inv_alloc (logN .@ (α)) _ (α (_; []) )%I
           with "[$Hα]") as "HN".
    rel_values. iExists α, N. auto.
  Qed.

  Lemma bin_log_related_rand_tape Δ Γ e1 e2 :
    (Δ; Γ e1 : TNat) -∗
    (Δ; Γ e2 : TTape) -∗
    Δ; Γ rand(e2) e1 : TNat.
  Proof.
    iIntros "IH1 IH2".
    intro_clause.
    rel_bind_ap e2 "IH2" v2 "#IH2".
    rel_bind_ap e1 "IH1" v1 "#IH1".
    iDestruct "IH1" as (N) "%H".
    subst.
    iApply refines_rand_tape; value_case.
  Qed.

  Lemma bin_log_related_rand_unit Δ Γ e1 e2 :
    (Δ; Γ e1 : TNat) -∗
    (Δ; Γ e2 : TUnit) -∗
    Δ; Γ rand(e2) e1 : TNat.
  Proof.
    iIntros "IH1 IH2".
    intro_clause.
    rel_bind_ap e2 "IH2" v2 "#IH2".
    rel_bind_ap e1 "IH1" v1 "#IH1".
    iDestruct "IH1" as (N) "%H".
    subst.
    (* destruct H as -> ->. *)
    iDestruct "IH2" as "%H".
    subst.
    (* destruct H as -> ->. *)
    iApply refines_rand_unit.
    value_case.
  Qed.

  Lemma bin_log_related_subsume_int_nat Δ Γ e :
  (Δ; Γ e : TNat) -∗
  (Δ; Γ e: TInt).
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v "#IH".
    iDestruct "IH" as (N) "->".
    value_case.
  Qed.

  Lemma bin_log_related_unboxed_eq Δ Γ e1 e2 τ :
    UnboxedType τ
    (Δ;Γ e1 : τ) -∗
    (Δ;Γ e2 : τ) -∗
    Δ;Γ BinOp EqOp e1 e2: TBool.
  Proof.
    iIntros (Hτ) "IH1 IH2".
    intro_clause.
    rel_bind_ap e2 "IH2" v2 "#IH2".
    rel_bind_ap e1 "IH1" v1 "#IH1".
    iAssert (val_is_unboxed v1)%I as "%".
    { rewrite !unboxed_type_sound //. }
    (*   iDestruct "IH1" as "$ _". } *)
    (* iAssert (⌜val_is_unboxed v2'⌝)". *)
    (* { rewrite !unboxed_type_sound //. *)
    (*   iDestruct "IH2" as "_ $". } *)
    (* iMod (unboxed_type_eq with "IH1 IH2") as "*)
    rel_pures_l.
    case_bool_decide; first [by rel_values | naive_solver].
  Qed.

  Lemma bin_log_related_int_binop Δ Γ op e1 e2 τ :
    binop_int_res_type op = Some τ
    (Δ;Γ e1 : TInt) -∗
    (Δ;Γ e2 : TInt) -∗
    Δ;Γ BinOp op e1 e2 : τ.
  Proof.
    iIntros (Hopτ) "IH1 IH2".
    intro_clause.
    rel_bind_ap e2 "IH2" v2 "IH2".
    rel_bind_ap e1 "IH1" v1 "IH1".
    iDestruct "IH1" as (n) "->"; simplify_eq/=.
    iDestruct "IH2" as (n') "->"; simplify_eq/=.
    destruct (binop_int_typed_safe op n n' _ Hopτ) as [v' Hopv'].
    rel_pures_l; eauto.
    (* rel_pures_r; eauto. *)
    value_case.
    destruct op; inversion Hopv'; simplify_eq/=; try case_match; eauto.
  Qed.

  Lemma bin_log_related_bool_binop Δ Γ op e1 e2 τ :
    binop_bool_res_type op = Some τ
    (Δ;Γ e1 : TBool) -∗
    (Δ;Γ e2 : TBool) -∗
    Δ;Γ BinOp op e1 e2 : τ.
  Proof.
    iIntros (Hopτ) "IH1 IH2".
    intro_clause.
    rel_bind_ap e2 "IH2" v2 "IH2".
    rel_bind_ap e1 "IH1" v1 "IH1".
    iDestruct "IH1" as (n) "->"; simplify_eq/=.
    iDestruct "IH2" as (n') "->"; simplify_eq/=.
    destruct (binop_bool_typed_safe op n n' _ Hopτ) as [v' Hopv'].
    rel_pures_l; eauto.
    (* rel_pures_r; eauto. *)
    value_case.
    destruct op; inversion Hopv'; simplify_eq/=; eauto.
  Qed.

  Lemma bin_log_related_int_unop Δ Γ op e τ :
    unop_int_res_type op = Some τ
    (Δ;Γ e : TInt) -∗
    Δ;Γ UnOp op e : τ.
  Proof.
    iIntros (Hopτ) "IH".
    intro_clause.
    rel_bind_ap e "IH" v "IH".
    iDestruct "IH" as (n) "->"; simplify_eq/=.
    destruct (unop_int_typed_safe op n _ Hopτ) as [v' Hopv'].
    rel_pure_l. (* rel_pure_r. *)
    value_case.
    destruct op; inversion Hopv'; simplify_eq/=; try case_match; eauto.
  Qed.

  Lemma bin_log_related_bool_unop Δ Γ op e τ :
    unop_bool_res_type op = Some τ
    (Δ;Γ e : TBool) -∗
    Δ;Γ UnOp op e : τ.
  Proof.
    iIntros (Hopτ) "IH".
    intro_clause.
    rel_bind_ap e "IH" v "IH".
    iDestruct "IH" as (n) "->"; simplify_eq/=.
    destruct (unop_bool_typed_safe op n _ Hopτ) as [v' Hopv'].
    rel_pure_l. (* rel_pure_r. *)
    value_case.
    destruct op; inversion Hopv'; simplify_eq/=; try case_match; eauto.
  Qed.

  Lemma bin_log_related_unfold Δ Γ e τ :
    (Δ;Γ e : μ: τ) -∗
    Δ;Γ rec_unfold e : τ.[(TRec τ)/].
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v"IH".
    iEval (rewrite lrel_rec_unfold /lrel_car /=) in "IH".
    change (lrel_rec _) with (interp (TRec τ) Δ).
    rel_rec_l. (* rel_rec_r. *)
    value_case. by rewrite -interp_subst.
  Qed.

  Lemma bin_log_related_fold Δ Γ e τ :
    (Δ;Γ e : τ.[(TRec τ)/]) -∗
    Δ;Γ e : μ: τ.
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v "IH".
    value_case.
    iModIntro.
    iEval (rewrite lrel_rec_unfold /lrel_car /=).
    change (lrel_rec _) with (interp (TRec τ) Δ).
    by rewrite -interp_subst.
  Qed.

  Lemma bin_log_related_pack' Δ Γ e (τ τ' : type) :
    (Δ;Γ e : τ.[τ'/]) -∗
    Δ;Γ e : ∃: τ.
  Proof.
    iIntros "IH".
    intro_clause.
    rel_bind_ap e "IH" v "#IH".
    value_case.
    iModIntro. iExists (interp τ' Δ).
    by rewrite interp_subst.
  Qed.

  Lemma bin_log_related_pack (τi : lrel Σ) Δ Γ e τ :
    (τi::Δ;Γ e : τ) -∗
    Δ;Γ e : ∃: τ.
  Proof.
    iIntros "IH".
    intro_clause.
    iSpecialize ("IH" $! vs with "[Hvs]").
    { by rewrite interp_ren. }
    rel_bind_ap e "IH" v "#IH".
    value_case.
    iModIntro. by iExists τi.
  Qed.

  Lemma bin_log_related_unpack Δ Γ x e1 e2 τ τ2 :
    (Δ;Γ e1 : ∃: τ) -∗
    ( τi : lrel Σ,
      τi::Δ;<[x:=τ]>(⤉Γ)
        e2 : (Autosubst_Classes.subst (ren (+1)) τ2)) -∗
    Δ;Γ (unpack: x := e1 in e2) : τ2.
  Proof.
    iIntros "IH1 IH2".
    intro_clause.
    rel_pure_l. (* rel_pure_r. *)
    rel_bind_ap e1 "IH1" v "#IH1".
    iDestruct "IH1" as (A) "#IH".
    rel_rec_l. rel_pure_l. rel_pure_l.
    (* rel_rec_r. rel_pure_r. rel_pure_r. *)
    rel_pure_l. (* rel_pure_r. *)
    iSpecialize ("IH2" $! A (binder_insert x (v) vs) with "[Hvs]").
    { rewrite -(interp_ren A).
      rewrite binder_insert_fmap.
      iApply (env_ltyped2_insert with "IH Hvs"). }
    rewrite !subst_map_binder_insert /=.
    iApply (refines_wand with "IH2").
    iIntros (v1). rewrite (interp_ren_up [] Δ τ2 A).
    asimpl. eauto.
  Qed.

  Lemma bin_log_related_fork Δ Γ e :
    (Δ;Γ e : ()) -∗
    Δ;Γ Fork e : ().
  Proof.
    iIntros "IH".
    intro_clause.
    iApply refines_fork.
    by iApply "IH".
  Qed.

  Lemma bin_log_related_CmpXchg_EqType Δ Γ e1 e2 e3 τ :
    EqType τ ->
    UnboxedType τ ->
    (Δ;Γ e1 : TRef τ) -∗
    (Δ;Γ e2 : τ) -∗
    (Δ;Γ e3 : τ) -∗
    Δ;Γ CmpXchg e1 e2 e3 : TProd τ TBool.
  Proof.
    intros Hτ Hτ'.
    iIntros "IH1 IH2 IH3".
    intro_clause.
    rel_bind_ap e3 "IH3" v3 "#IH3".
    rel_bind_ap e2 "IH2" v2 "#IH2".
    rel_bind_ap e1 "IH1" v1 "#IH1".
    iDestruct "IH1" as (l) "(% & Hinv)"; simplify_eq/=.
    iDestruct (unboxed_type_sound with "IH2") as "%"; try fast_done.
    (* iDestruct (eq_type_sound with "IH2") as "*)
    (* iDestruct (eq_type_sound with "IH3") as "*)
    subst.
    rewrite -(fill_empty (CmpXchg #l _ _)).
    iApply refines_atomic_l.
    (* iIntros (? j) "Hspec". *)
    iInv (logN .@ (l)) as (v1) "[>Hv1 #Hv]" "Hclose".
    iModIntro.
    destruct (decide (v1 = v2)) as [|Hneq]; subst.
    - iApply wp_pupd.
      wp_cmpxchg_suc.
      iModIntro.
      (* iDestruct (eq_type_sound with "Hv") as "*)
      (* tp_cmpxchg_suc j. *)
      iModIntro.
      iMod ("Hclose" with "[-]").
      { iNext; by iFrame. }
      iModIntro.
      iFrame.
      rel_values. subst.
      iExists _, _. do 2 (iSplitL; first done).
      by iExists _.
    - iApply wp_pupd.
      wp_cmpxchg_fail.
      iModIntro.
      (* iDestruct (eq_type_sound with "Hv") as "*)
      (* tp_cmpxchg_fail j. *)
      iModIntro.
      iMod ("Hclose" with "[-]").
      { iNext; iExists _; by iFrame. }
      iModIntro. iFrame.
      rel_values. iExists _, _. do 2 (iSplitL; first done).
      by iExists _.
  Qed.


  Lemma bin_log_related_CmpXchg Δ Γ e1 e2 e3 τ:
    UnboxedType τ ->
    (Δ;Γ e1 : TRef τ) -∗
    (Δ;Γ e2 : τ) -∗
    (Δ;Γ e3 : τ) -∗
    Δ;Γ CmpXchg e1 e2 e3 : TProd τ TBool.
  Proof.
    intros.
    cut (EqType τ τ', τ = TRef τ').
    { intros [Hτ | [τ' ->]].
      - by iApply bin_log_related_CmpXchg_EqType.
      - iIntros "H1 H2 H3". intro_clause.
        iSpecialize ("H1" with "Hvs").
        iSpecialize ("H2" with "Hvs").
        iSpecialize ("H3" with "Hvs").
        iApply (refines_cmpxchg_ref with "H1 H2 H3"). }
    by apply unboxed_type_ref_or_eqtype.
  Qed.

  Lemma bin_log_related_xchg Δ Γ e1 e2 τ :
    (Δ;Γ e1 : TRef τ) -∗
    (Δ;Γ e2 : τ) -∗
    Δ;Γ Xchg e1 e2 : τ.
  Proof.
    iIntros "IH1 IH2".
    intro_clause.
    iApply (refines_xchg with "[IH1] [IH2]").
    - by iApply "IH1".
    - by iApply "IH2".
  Qed.

  Lemma bin_log_related_FAA Δ Γ e1 e2 :
    (Δ;Γ e1 : TRef TNat) -∗
    (Δ;Γ e2 : TNat) -∗
    Δ;Γ FAA e1 e2 : TNat.
  Proof.
    iIntros "IH1 IH2".
    intro_clause.
    rel_bind_ap e2 "IH2" v2 "#IH2".
    rel_bind_ap e1 "IH1" v1 "#IH1".
    iDestruct "IH1" as (l) "(% & Hinv)"; simplify_eq/=.
    iDestruct "IH2" as (n) "->". simplify_eq.
    rewrite -(fill_empty (FAA #l _)).
    iApply refines_atomic_l.
    (* iIntros (? j) "Hspec". *)
    iInv (logN.@ (l)) as (v1) "[>Hv1 #>Hv]" "Hclose".
    iDestruct "Hv" as (n1) "->"; simplify_eq.
    iApply wp_pupd.
    iModIntro.
    wp_faa.
    iModIntro.
    (* tp_faa j. *)
    iModIntro.
    iMod ("Hclose" with "[-]") as "_".
    { iNext. iExists _. iFrame. rewrite -Nat2Z.inj_add. by iExists _. }
    iFrame. iModIntro. simpl. rel_values.
  Qed.

  Theorem fundamental Δ Γ e τ :
    Γ ⊢ₜ e : τ Δ;Γ e : τ
  with fundamental_val Δ v τ :
    ⊢ᵥ v : τ interp τ Δ v.
  Proof.
    - intros Ht. destruct Ht.
      + by iApply bin_log_related_var.
      + iIntros (γ) "#H". simpl. rel_values.
        iModIntro. by iApply fundamental_val.
      + iApply bin_log_related_int_binop; first done;
          by iApply fundamental.
      + iApply bin_log_related_bool_binop; first done;
          by iApply fundamental.
      + iApply bin_log_related_int_unop; first done.
        by iApply fundamental.
      + iApply bin_log_related_bool_unop; first done.
        by iApply fundamental.
      + iApply bin_log_related_unboxed_eq; try done;
          by iApply fundamental.
      + iApply bin_log_related_pair;
          by iApply fundamental.
      + iApply bin_log_related_fst;
          by iApply fundamental.
      + iApply bin_log_related_snd;
          by iApply fundamental.
      + iApply bin_log_related_injl;
          by iApply fundamental.
      + iApply bin_log_related_injr;
          by iApply fundamental.
      + iApply bin_log_related_case;
          by iApply fundamental.
      + iApply bin_log_related_if;
          by iApply fundamental.
      + iApply bin_log_related_rec.
        iModIntro. by iApply fundamental.
      + iApply bin_log_related_app;
          by iApply fundamental.
      + iApply bin_log_related_tlam.
        iIntros (A). iModIntro. by iApply fundamental.
      + iApply bin_log_related_tapp'; by iApply fundamental.
      + iApply bin_log_related_fold; by iApply fundamental.
      + iApply bin_log_related_unfold; by iApply fundamental.
      + iApply bin_log_related_pack'; by iApply fundamental.
      + iApply bin_log_related_unpack; try by iApply fundamental.
        iIntros (A). by iApply fundamental.
      + iApply bin_log_related_alloc; by iApply fundamental.
      + iApply bin_log_related_load; by iApply fundamental.
      + iApply bin_log_related_store; by iApply fundamental.
      + iApply bin_log_related_alloctape. by iApply fundamental.
      + iApply bin_log_related_rand_tape; by iApply fundamental.
      + iApply bin_log_related_rand_unit; by iApply fundamental.
      + iApply bin_log_related_subsume_int_nat ; by iApply fundamental.
      + iApply bin_log_related_fork ; by iApply fundamental.
      + iApply bin_log_related_CmpXchg; first done; by iApply fundamental.
      + iApply bin_log_related_xchg; by iApply fundamental.
      + iApply bin_log_related_FAA ; by iApply fundamental.
    - intros Hv. destruct Hv; simpl.
      + eauto.
      + iExists _; eauto.
      + iExists _; eauto.
      + iExists _; eauto.
      + iExists _,_.
        repeat iSplit; eauto; by iApply fundamental_val.
      + iExists _. iLeft.
        repeat iSplit; eauto; by iApply fundamental_val.
      + iExists _. iRight.
        repeat iSplit; eauto; by iApply fundamental_val.
      + iLöb as "IH". iModIntro.
        iIntros (v1) "#Hv".
        pose (Γ := (<[f:=(τ1 τ2)%ty]> (<[x:=τ1]> ∅)):stringmap type).
        pose (γ := (binder_insert f (rec: f x := e)%V
                     (binder_insert x (v1) )):stringmap (val)).
        rel_pure_l.
        iPoseProof (fundamental Δ Γ e τ2 $! γ with "[]") as "H"; eauto.
        { rewrite /γ /Γ. rewrite !binder_insert_fmap fmap_empty.
          iApply (env_ltyped2_insert with "IH").
          iApply (env_ltyped2_insert with "Hv").
          iApply env_ltyped2_empty. }
        rewrite /γ /=.
        by rewrite !subst_map_binder_insert_2_empty.
      + iIntros (A). iModIntro. iIntros (v1) "_".
        rel_pures_l. (* rel_pures_r. *)
        iPoseProof (fundamental (A::Δ) e τ $! with "[]") as "H"; eauto.
        { rewrite fmap_empty. iApply env_ltyped2_empty. }
        by rewrite subst_map_empty.
  Qed.

  Theorem refines_typed τ Δ e :
     ⊢ₜ e : τ
     REL e : interp τ Δ.
  Proof.
    move=> /fundamental Hty.
    iPoseProof (Hty Δ with "[]") as "H".
    { rewrite fmap_empty. iApply env_ltyped2_empty. }
    by rewrite !subst_map_empty.
  Qed.

  Theorem typed_safe τ Δ e :
     ⊢ₜ e : τ
     WP e {{ interp τ Δ }}.
  Proof.
    iIntros (H).
    iPoseProof refines_typed as "H"; first done.
    by unfold_rel.
  Qed.

End fundamental.