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