From 4376ac7ff7c638298dc44323ec8f0f1a5ef08ab1 Mon Sep 17 00:00:00 2001
From: Ralf Jung <jung@mpi-sws.org>
Date: Thu, 11 Mar 2021 12:05:18 +0100
Subject: [PATCH] add BrandedVec proof by Joshua Yanovski

---
 _CoqProject                      |   1 +
 theories/lang/notation.v         |   2 +
 theories/typing/lib/brandedvec.v | 418 +++++++++++++++++++++++++++++++
 theories/typing/type.v           |  18 ++
 4 files changed, 439 insertions(+)
 create mode 100644 theories/typing/lib/brandedvec.v

diff --git a/_CoqProject b/_CoqProject
index ceef9efb..b4ce7fe5 100644
--- a/_CoqProject
+++ b/_CoqProject
@@ -69,6 +69,7 @@ theories/typing/lib/rc/weak.v
 theories/typing/lib/arc.v
 theories/typing/lib/swap.v
 theories/typing/lib/diverging_static.v
+theories/typing/lib/brandedvec.v
 theories/typing/lib/mutex/mutex.v
 theories/typing/lib/mutex/mutexguard.v
 theories/typing/lib/refcell/refcell.v
diff --git a/theories/lang/notation.v b/theories/lang/notation.v
index ca213604..d44fc14d 100644
--- a/theories/lang/notation.v
+++ b/theories/lang/notation.v
@@ -35,6 +35,8 @@ Notation "e1 - e2" := (BinOp MinusOp e1%E e2%E)
   (at level 50, left associativity) : expr_scope.
 Notation "e1 ≤ e2" := (BinOp LeOp e1%E e2%E)
   (at level 70) : expr_scope.
+Notation "e1 < e2" := (e1+#1 ≤ e2)%E
+  (at level 70) : expr_scope.
 Notation "e1 = e2" := (BinOp EqOp e1%E e2%E)
   (at level 70) : expr_scope.
 (* The unicode ← is already part of the notation "_ ← _; _" for bind. *)
diff --git a/theories/typing/lib/brandedvec.v b/theories/typing/lib/brandedvec.v
new file mode 100644
index 00000000..95685ec3
--- /dev/null
+++ b/theories/typing/lib/brandedvec.v
@@ -0,0 +1,418 @@
+From iris.algebra Require Import auth numbers.
+From iris.proofmode Require Import tactics.
+From lrust.lang Require Import proofmode notation lib.new_delete.
+From lrust.lifetime Require Import meta.
+From lrust.typing Require Import typing.
+From lrust.typing.lib Require Import option.
+Set Default Proof Using "Type".
+
+Definition brandidx_stR := max_natUR.
+Class brandidxG Σ := BrandedIdxG {
+  brandidx_inG :> inG Σ (authR brandidx_stR);
+  brandidx_gsingletonG :> lft_metaG Σ;
+}.
+
+Definition brandidxΣ : gFunctors
+  := #[GFunctor (authR brandidx_stR); lft_metaΣ].
+Instance subG_brandidxΣ {Σ} : subG brandidxΣ Σ → brandidxG Σ.
+Proof. solve_inG. Qed.
+
+Definition brandidxN := lrustN .@ "brandix".
+Definition brandvecN := lrustN .@ "brandvec".
+
+Section brandedvec.
+  Context `{!typeG Σ, !brandidxG Σ}.
+  Implicit Types (q : Qp) (α : lft) (γ : gname) (n m : nat).
+  Local Notation iProp := (iProp Σ).
+
+  Definition brandvec_inv α n : iProp :=
+    (∃ γ, lft_meta α γ ∗ own γ (● MaxNat n))%I.
+
+  Program Definition brandvec (α : lft) : type :=
+    {| ty_size        := int.(ty_size);
+       ty_own tid vl  :=
+        (∃ n, brandvec_inv α n ∗ ⌜vl = [ #n ]⌝)%I;
+       ty_shr κ tid l :=
+        (∃ n, &at{κ,brandvecN}(brandvec_inv α n) ∗ &frac{κ}(λ q, l ↦∗{q} [ #n ]))%I;
+    |}.
+  Next Obligation. iIntros "* H". iDestruct "H" as (?) "[_ %]". by subst. Qed.
+  Next Obligation.
+    iIntros (ty E κ l tid q ?) "#LFT Hown Hκ".
+    iMod (bor_exists with "LFT Hown") as (vl) "Hown"; first solve_ndisj.
+    iMod (bor_sep with "LFT Hown") as "[Hshr Hown]"; first solve_ndisj.
+    iMod (bor_exists with "LFT Hown") as (n) "Hown"; first solve_ndisj.
+    iMod (bor_sep with "LFT Hown") as "[Hghost Hown]"; first solve_ndisj.
+    iMod (bor_share _ brandvecN with "Hghost") as "Hghost"; first solve_ndisj.
+    iMod (bor_persistent with "LFT Hown Hκ") as "[> % $]"; first solve_ndisj.
+    subst. iExists n. iFrame.
+    by iApply (bor_fracture with "LFT"); first solve_ndisj.
+  Qed.
+  Next Obligation.
+    iIntros (ty ?? tid l) "#H⊑ H".
+    iDestruct "H" as (n) "[Hghost Hown]".
+    iExists n. iSplitR "Hown".
+    - by iApply (at_bor_shorten with "H⊑").
+    - by iApply (frac_bor_shorten with "H⊑").
+  Qed.
+
+  Global Instance brandvec_wf α : TyWf (brandvec α) :=
+    { ty_lfts := [α]; ty_wf_E := [] }.
+  Global Instance brandvec_ne : NonExpansive brandvec.
+  Proof. solve_proper. Qed.
+  Global Instance brandvec_mono E L :
+    Proper (lctx_lft_eq E L ==> subtype E L) brandvec.
+  Proof. apply lft_invariant_subtype. Qed.
+  Global Instance brandvec_equiv E L :
+    Proper (lctx_lft_eq E L ==> eqtype E L) brandvec.
+  Proof. apply lft_invariant_eqtype. Qed.
+
+  Global Instance brandvec_send α :
+    Send (brandvec α).
+  Proof. by unfold brandvec, Send. Qed.
+
+  Global Instance brandvec_sync α :
+    Sync (brandvec α).
+  Proof. by unfold brandvec, Sync. Qed.
+
+  (** [γ] is (a lower bound of) the length of the vector; as an in-bounds index,
+  that must be at least one more than the index value. *)
+  Definition brandidx_inv α m : iProp :=
+    (∃ γ, lft_meta α γ ∗ own γ (◯ MaxNat (m+1)%nat))%I.
+
+  Program Definition brandidx α :=
+    {| ty_size        := int.(ty_size);
+       ty_own tid vl  := (∃ m, brandidx_inv α m ∗ ⌜vl = [ #m]⌝)%I;
+       ty_shr κ tid l := (∃ m, &frac{κ}(λ q, l ↦∗{q} [ #m]) ∗ brandidx_inv α m)%I;
+    |}.
+  Next Obligation. iIntros "* H". iDestruct "H" as (?) "[_ %]". by subst. Qed.
+  Next Obligation.
+    iIntros (ty E κ l tid q ?) "#LFT Hown Hκ".
+    iMod (bor_exists with "LFT Hown") as (vl) "Hown"; first solve_ndisj.
+    iMod (bor_sep with "LFT Hown") as "[Hown Hghost]"; first solve_ndisj.
+    iMod (bor_persistent with "LFT Hghost Hκ") as "[>Hghost $]"; first solve_ndisj.
+    iDestruct "Hghost" as (m) "[Hghost %]". subst vl.
+    iExists m. iFrame.
+    by iApply (bor_fracture with "LFT"); first solve_ndisj.
+  Qed.
+  Next Obligation.
+    iIntros (ty ?? tid l) "#H⊑ H".
+    iDestruct "H" as (m) "[H ?]".
+    iExists m. iFrame.
+    by iApply (frac_bor_shorten with "H⊑").
+  Qed.
+
+  Global Instance brandidx_wf α : TyWf (brandidx α) :=
+    { ty_lfts := [α]; ty_wf_E := [] }.
+  Global Instance brandidx_ne : NonExpansive brandidx.
+  Proof. solve_proper. Qed.
+  Global Instance brandidx_mono E L :
+    Proper (lctx_lft_eq E L ==> subtype E L) brandidx.
+  Proof. apply lft_invariant_subtype. Qed.
+  Global Instance brandidx_equiv E L :
+    Proper (lctx_lft_eq E L ==> eqtype E L) brandidx.
+  Proof. apply lft_invariant_eqtype. Qed.
+
+  Global Instance brandidx_send α :
+    Send (brandidx α).
+  Proof. by unfold brandidx, Send. Qed.
+
+  Global Instance brandidx_sync α :
+    Sync (brandidx α).
+  Proof. by unfold brandidx, Sync. Qed.
+
+  Global Instance brandidx_copy α :
+    Copy (brandidx α).
+  Proof.
+    split; first by apply _.
+    iIntros (κ tid E ? l q ? HF) "#LFT #Hshr Htok Hlft".
+    iDestruct (na_own_acc with "Htok") as "[$ Htok]"; first solve_ndisj.
+    iDestruct "Hshr" as (γ) "[Hmem Hinv]".
+    iMod (frac_bor_acc with "LFT Hmem Hlft") as (q') "[>Hmt Hclose]"; first solve_ndisj.
+    iDestruct "Hmt" as "[Hmt1 Hmt2]".
+    iModIntro. iExists _.
+    iSplitL "Hmt1".
+    { iExists [_]. iNext. iFrame. iExists _. eauto with iFrame. }
+    iIntros "Htok2 Hmt1". iDestruct "Hmt1" as (vl') "[>Hmt1 #Hown']".
+    iDestruct ("Htok" with "Htok2") as "$".
+    iAssert (▷ ⌜1 = length vl'⌝)%I as ">%".
+    { iNext. iDestruct (ty_size_eq with "Hown'") as %->. done. }
+    destruct vl' as [|v' vl']; first done.
+    destruct vl' as [|]; last first. { simpl in *. lia. }
+    rewrite !heap_mapsto_vec_singleton.
+    iDestruct (heap_mapsto_agree with "[$Hmt1 $Hmt2]") as %->.
+    iCombine "Hmt1" "Hmt2" as "Hmt".
+    iApply "Hclose". done.
+  Qed.
+
+  Lemma brandinv_brandidx_lb α m n :
+    brandvec_inv α n -∗ brandidx_inv α m -∗ ⌜m < n⌝%nat.
+  Proof.
+    iIntros "Hn Hm".
+    iDestruct "Hn" as (γn) "(Hidx1 & Hn)".
+    iDestruct "Hm" as (γm) "(Hidx2 & Hm)".
+    iDestruct (lft_meta_agree with "Hidx1 Hidx2") as %<-. 
+    iDestruct (own_valid_2 with "Hn Hm") as %[?%max_nat_included ?]%auth_both_valid_discrete.
+    iPureIntro. simpl in *. lia.
+  Qed.
+
+End brandedvec.
+
+Section typing.
+  Context `{!typeG Σ, !brandidxG Σ}.
+  Implicit Types (q : Qp) (α : lft) (γ : gname) (n m : nat).
+  Local Notation iProp := (iProp Σ).
+
+  Definition brandvec_new (call_once : val) (R_F : type) : val :=
+    funrec: <> ["f"] :=
+      let: "call_once" := call_once in
+      letalloc: "n" <- #0 in
+      letcall: "r" := "call_once" ["f";"n"]%E in
+      letalloc: "r'" <-{ R_F.(ty_size)} !"r" in
+      delete [ #R_F.(ty_size); "r"];;
+      return: ["r'"].
+
+  Lemma brandvec_new_type F R_F call_once `{!TyWf F, !TyWf R_F} :
+    typed_val call_once (fn(∀ α, ∅; F, brandvec α) → R_F) →
+    typed_val (brandvec_new call_once R_F) (fn(∅; F) → R_F).
+  Proof.
+    iIntros (Hf E L). iApply type_fn; [solve_typing..|]. iIntros "/= !#".
+    iIntros (_ ϝ ret args). inv_vec args=> env. simpl_subst.
+    iApply type_let; [apply Hf|solve_typing|]. iIntros (f); simpl_subst.
+    iIntros (tid qmax) "#LFT #HE Hna HL Hc (Hf & Henv & _)".
+    wp_let.
+    wp_op.
+    wp_case.
+    wp_alloc n as "Hn" "Hdead".
+    wp_let.
+    rewrite !heap_mapsto_vec_singleton.
+    wp_write.
+    iAssert (∀ E : coPset, ⌜↑lftN ⊆ E⌝ →
+      |={E}=> ∃ α, tctx_elt_interp tid ((LitV n : expr) ◁ box (brandvec α)) ∗ 1.[α])%I
+      with "[Hn Hdead]" as "Hn'".
+    { iIntros (E' Hlft).
+      iMod (own_alloc (● (MaxNat 0))) as (γ) "Hown"; first by apply auth_auth_valid.
+      iMod (lft_create_meta γ with "LFT") as (α) "[#Hsing [Htok #Hα]]"; first done.
+      iExists α.
+      rewrite !tctx_hasty_val.
+      rewrite ownptr_own.
+      rewrite -heap_mapsto_vec_singleton.
+      iFrame "Htok".
+      iExists n, (Vector.cons (LitV 0) Vector.nil).
+      iSplitR; first done.
+      simpl.
+      rewrite freeable_sz_full.
+      iFrame.
+      iExists 0%nat.
+      iSplitL; last done.
+      iExists γ; iSplitR; by eauto. }
+    iMod ("Hn'" with "[%]") as (α) "[Hn Htok]"; [solve_typing..|].
+    wp_let.
+    iMod (lctx_lft_alive_tok ϝ with "HE HL")
+      as (qϝf) "(Hϝf & HL & Hclosef)"; [solve_typing..|].
+
+    iDestruct (lft_intersect_acc with "Htok Hϝf") as (?) "[Hαϝ Hcloseα]".
+    rewrite !tctx_hasty_val.
+    iApply (type_call_iris _ [α; ϝ] α [_;_]  _ _ _ _
+              with "LFT HE Hna [Hαϝ] Hf [Hn Henv]"); try solve_typing.
+    + by rewrite /= right_id.
+    + rewrite /= right_id.
+      rewrite !tctx_hasty_val.
+      by iFrame.
+    + iIntros (r) "Hna Hf Hown".
+      simpl_subst.
+      iDestruct ("Hcloseα" with "[Hf]") as "[Htok Hf]"; first by rewrite right_id.
+      iMod ("Hclosef" with "Hf HL") as "HL".
+      wp_let.
+      iApply (type_type _ _ _ [ r ◁ box R_F ] with "[] LFT HE Hna HL Hc");
+        try solve_typing; last by rewrite !tctx_interp_singleton tctx_hasty_val.
+      iApply type_letalloc_n; [solve_typing..|].
+      iIntros (r').
+      simpl_subst.
+      iApply type_delete; [solve_typing..|].
+      iApply type_jump; solve_typing.
+  Qed.
+
+  Definition brandvec_get_index : val :=
+    funrec: <> ["v"; "i"] :=
+      let: "r" := new [ #2 ] in
+      let: "v'" := !"v" in
+      let: "i'" := !"i" in
+      delete [ #1; "v" ];; delete [ #1; "i" ];;
+      let: "inbounds" := (if: #0 ≤ "i'" then "i'" < !"v'" else #false) in
+      if: "inbounds" then
+        "r" <-{Σ some} "i'";;
+         return: ["r"]
+      else
+        "r" <-{Σ none} ();;
+        return: ["r"].
+
+  Lemma brandvec_get_index_type :
+    typed_val brandvec_get_index (fn(∀ '(α, β), ∅; &shr{β} (brandvec α), int) → option (brandidx α))%T.
+  Proof.
+    intros E L. iApply type_fn; [solve_typing..|]. iIntros "/= !#".
+    iIntros ([α β] ϝ ret args). inv_vec args=>v i. simpl_subst.
+    iApply (type_new 2); [solve_typing..|]; iIntros (r); simpl_subst.
+    iApply type_deref; [solve_typing..|]. iIntros (v'); simpl_subst.
+    iApply type_deref; [solve_typing..|]. iIntros (i'); simpl_subst.
+    iApply type_delete; [solve_typing..|].
+    iApply type_delete; [solve_typing..|].
+    iIntros (tid qmax) "#LFT #HE Hna HL Hk (#Hi' & #Hv' & Hr & _)".
+    rewrite !tctx_hasty_val. clear.
+    destruct i' as [[| |i']|]; try done. iClear "Hi'".
+    wp_op.
+    rewrite bool_decide_decide.
+    destruct (decide (0 ≤ i')) as [Hpos|Hneg]; last first.
+    { wp_case. wp_let. wp_case.
+      iApply (type_type _ _ _ [ r ◁ box (uninit 2) ]
+            with "[] LFT HE Hna HL Hk [-]"); last first.
+      { rewrite tctx_interp_singleton tctx_hasty_val. done. }
+      iApply (type_sum_unit (option (brandidx _))); [solve_typing..|].
+      iApply type_jump; solve_typing. }
+    destruct (Z_of_nat_complete _ Hpos) as [i ->]. clear Hpos.
+    wp_case. wp_op.
+    iDestruct (shr_is_ptr with "Hv'") as % [l ?]. simplify_eq.
+    iDestruct "Hv'" as (m) "#[Hghost Hmem]".
+    iMod (lctx_lft_alive_tok β with "HE HL") as (qβ) "(Hβ & HL & Hclose)"; [solve_typing..|].
+    iMod (frac_bor_acc with "LFT Hmem Hβ") as (qβ') "[>Hn'↦ Hcloseβ]"; first done.
+    rewrite !heap_mapsto_vec_singleton.
+    wp_read.
+    iMod ("Hcloseβ" with "Hn'↦") as "Hβ".
+    wp_op.
+    rewrite bool_decide_decide.
+    destruct (decide (i + 1 ≤ m)) as [Hle|Hoob]; last first.
+    { wp_let. wp_case.
+      iMod ("Hclose" with "Hβ HL") as "HL".
+      iApply (type_type _ _ _ [ r ◁ box (uninit 2) ]
+            with "[] LFT HE Hna HL Hk [-]"); last first.
+      { rewrite tctx_interp_singleton tctx_hasty_val. done. }
+      iApply (type_sum_unit (option (brandidx _))); [solve_typing..|].
+      iApply type_jump; solve_typing. }
+    wp_let. wp_case.
+    iApply fupd_wp.
+    iMod (at_bor_acc_tok with "LFT Hghost Hβ") as "[>Hidx Hcloseg]"; [solve_ndisj..|].
+    iDestruct "Hidx" as (γ) "(#Hidx & Hown)".
+    iAssert (|==> own γ (● (MaxNat m)) ∗ own γ (◯ (MaxNat m)))%I with "[Hown]" as "> [Hown Hlb]".
+    { rewrite -own_op. iApply (own_update with "Hown"). apply auth_update_alloc.
+      by apply max_nat_local_update. }
+    iMod ("Hcloseg" with "[Hown]") as "Hβ".
+    { iExists _. eauto with iFrame. }
+    iMod ("Hclose" with "Hβ HL") as "HL".
+    iApply (type_type _ _ _ [ r ◁ box (uninit 2); #i ◁ brandidx _ ]
+            with "[] LFT HE Hna HL Hk [-]"); last first.
+      { rewrite tctx_interp_cons tctx_interp_singleton tctx_hasty_val. iFrame.
+        rewrite tctx_hasty_val'; last done.
+        iExists _. iSplit; last done.
+        iExists _. iFrame "Hidx".
+        iApply base_logic.lib.own.own_mono; last done.
+        apply: auth_frag_mono. apply max_nat_included. simpl. lia. }
+    iApply (type_sum_assign (option (brandidx _))); [solve_typing..|].
+    iApply type_jump; solve_typing.
+  Qed.
+
+  Definition brandidx_get : val :=
+    funrec: <> ["s";"c"] :=
+      let: "len" := !"s" in
+      let: "idx" := !"c" in
+      delete [ #1; "s" ];; delete [ #1; "c" ];;
+      if: !"idx" < !"len" then
+        let: "r" := new [ #0] in return: ["r"]
+      else
+        !#☠ (* stuck *).
+
+  Lemma brandidx_get_type :
+    typed_val brandidx_get (fn(∀ '(α, β), ∅; &shr{β} (brandvec α), &shr{β} (brandidx α)) → unit)%T.
+  Proof.
+    intros E L. iApply type_fn; [solve_typing..|]. iIntros "/= !#".
+    iIntros ([α β] ϝ ret args). inv_vec args=> s c. simpl_subst.
+    iApply type_deref; [solve_typing..|]. iIntros (n); simpl_subst.
+    iApply type_deref; [solve_typing..|]. iIntros (m); simpl_subst.
+    iApply type_delete; [solve_typing..|].
+    iApply type_delete; [solve_typing..|].
+    iIntros (tid qmax) "#LFT #HE Hna HL HC (Hm & Hn & _)".
+    rewrite !tctx_hasty_val.
+    iDestruct (shr_is_ptr with "Hm") as %[lm ?]. simplify_eq.
+    iDestruct (shr_is_ptr with "Hn") as %[ln ?]. simplify_eq.
+    simpl in *.
+    iDestruct "Hm" as (m) "[Hm Hmidx]".
+    iDestruct "Hn" as (n) "[Hnidx Hn]".
+    iMod (lctx_lft_alive_tok β with "HE HL") as (qβ) "((Hβ1 & Hβ2 & Hβ3) & HL & Hclose)"; [solve_typing..|].
+    iMod (frac_bor_acc with "LFT Hn Hβ1") as (qβn) "[>Hn↦ Hcloseβ1]"; first solve_ndisj.
+    iMod (frac_bor_acc with "LFT Hm Hβ2") as (qβm) "[>Hm↦ Hcloseβ2]"; first solve_ndisj.
+    rewrite !heap_mapsto_vec_singleton.
+    wp_read.
+    wp_op.
+    wp_read.
+    wp_op.
+    wp_case.
+    iApply fupd_wp.
+    iMod (at_bor_acc_tok with "LFT Hnidx Hβ3") as "[>Hnidx Hcloseβ3]"; [solve_ndisj..|].
+    iDestruct (brandinv_brandidx_lb with "Hnidx Hmidx") as %Hle.
+    iMod ("Hcloseβ3" with "Hnidx") as "Hβ3".
+    iMod ("Hcloseβ2" with "Hm↦") as "Hβ2".
+    iMod ("Hcloseβ1" with "Hn↦") as "Hβ1".
+    iCombine "Hβ2 Hβ3" as "Hβ2".
+    iMod ("Hclose" with "[$Hβ1 $Hβ2] HL") as "HL".
+    rewrite bool_decide_true; last by lia.
+    iApply (type_type _ _ _ []
+            with "[] LFT HE Hna HL HC []"); last by rewrite tctx_interp_nil.
+    iApply (type_new _); [solve_typing..|]; iIntros (r); simpl_subst.
+    iApply type_jump; solve_typing.
+  Qed.
+
+  Definition brandvec_push : val :=
+    funrec: <> ["s"] :=
+      let: "n" := !"s" in
+      delete [ #1; "s" ];;
+      let: "r" := new [ #1] in
+      let: "oldlen" := !"n" in
+      "n" <- "oldlen"+#1;;
+      "r" <- "oldlen";;
+      return: ["r"].
+
+  Lemma brandvec_push_type :
+    typed_val brandvec_push (fn(∀ '(α, β), ∅; &uniq{β} (brandvec α)) → brandidx α).
+  Proof.
+    intros E L. iApply type_fn; [solve_typing..|]. iIntros "/= !#".
+    iIntros ([α β] ϝ ret args). inv_vec args=>(*n *)s. simpl_subst.
+    iApply type_deref; [solve_typing..|]. iIntros (n); simpl_subst.
+    iApply type_delete; [solve_typing..|].
+    iApply (type_new _); [solve_typing..|]; iIntros (r); simpl_subst.
+    iIntros (tid qmax) "#LFT #HE Hna HL HC (Hr & Hn & _)".
+    rewrite !tctx_hasty_val.
+    iDestruct (uniq_is_ptr with "Hn") as %[ln H]. simplify_eq.
+    iMod (lctx_lft_alive_tok β with "HE HL") as (qβ) "(Hβ & HL & Hclose)"; [solve_typing..|].
+    iMod (bor_acc with "LFT Hn Hβ") as "[H↦ Hclose']"; first solve_ndisj.
+    iDestruct "H↦" as (vl) "[> H↦ Hn]".
+    iDestruct "Hn" as (n) "[Hn > %]". simplify_eq.
+    rewrite !heap_mapsto_vec_singleton.
+    wp_read.
+    wp_let.
+    wp_op.
+    wp_write.
+    iDestruct "Hn" as (γ) "[#Hidx Hown]".
+    iMod (own_update _ _ (● MaxNat (n+1) ⋅ _) with "Hown") as "[Hown Hlb]".
+    { apply auth_update_alloc.
+      apply max_nat_local_update.
+      simpl. lia.
+    }
+    iDestruct "Hlb" as "#Hlb".
+    iMod ("Hclose'" with "[H↦ Hidx Hown]") as "[Hn Hβ]".
+    { iExists (#(n+1)::nil).
+      rewrite heap_mapsto_vec_singleton.
+      iFrame "∗".
+      iIntros "!>".
+      iExists (n + 1)%nat.
+      iSplitL; last by (iPureIntro; do 3 f_equal; lia).
+      iExists γ. eauto with iFrame.
+    }
+    iMod ("Hclose" with "Hβ HL") as "HL".
+    iApply (type_type _ _ _ [ r ◁ box (uninit 1); #n ◁ brandidx _]
+            with "[] LFT HE Hna HL HC [Hr]"); last first.
+    { rewrite tctx_interp_cons tctx_interp_singleton tctx_hasty_val. iFrame.
+      rewrite tctx_hasty_val'; last done.
+      iExists _. iSplit; last done.
+      iExists _. eauto with iFrame. }
+    iApply type_assign; [solve_typing..|].
+    iApply type_jump; solve_typing.
+  Qed.
+End typing.
diff --git a/theories/typing/type.v b/theories/typing/type.v
index 6cfae951..aa1c630d 100644
--- a/theories/typing/type.v
+++ b/theories/typing/type.v
@@ -615,6 +615,20 @@ Section subtyping.
     iDestruct (llctx_interp_acc_noend with "HL") as "[$ _]".
   Qed.
 
+  Lemma lft_invariant_subtype E L T :
+    Proper (lctx_lft_eq E L ==> subtype E L) T.
+  Proof.
+    iIntros (x y [Hxy Hyx] qmax qL) "L".
+    iPoseProof (Hxy with "L") as "#Hxy".
+    iPoseProof (Hyx with "L") as "#Hyx".
+    iIntros "!> #E". clear Hxy Hyx.
+    iDestruct ("Hxy" with "E") as %Hxy.
+    iDestruct ("Hyx" with "E") as %Hyx.
+    iClear "Hyx Hxy".
+    rewrite (anti_symm _ _ _ Hxy Hyx).
+    iApply type_incl_refl.
+  Qed.
+
   Lemma type_equal_incl ty1 ty2 :
     type_equal ty1 ty2 ⊣⊢ type_incl ty1 ty2 ∗ type_incl ty2 ty1.
   Proof.
@@ -644,6 +658,10 @@ Section subtyping.
     - iApply (type_incl_trans _ ty2); done.
   Qed.
 
+  Lemma lft_invariant_eqtype E L T :
+    Proper (lctx_lft_eq E L ==> eqtype E L) T.
+  Proof. split; by apply lft_invariant_subtype. Qed.
+
   Lemma equiv_subtype E L ty1 ty2 : ty1 ≡ ty2 → subtype E L ty1 ty2.
   Proof. unfold subtype, type_incl=>EQ. setoid_rewrite EQ. apply subtype_refl. Qed.
 
-- 
GitLab