LogrelCC.logrel_unary

From iris.proofmode Require Import tactics.
From LogrelCC.program_logic Require Export weakestpre.
From LogrelCC Require Export lang rules_unary typing.
From iris.algebra Require Import list big_op.
From iris.base_logic.lib Require Import invariants.
Import uPred.

Definition logN : namespace := nroot .@ "logN".

interp : is a unary logical relation.
Section logrel.
  Context `{heapG Σ}.
  Notation D := (valC -n> iProp Σ).
  Implicit Types τi : D.
  Implicit Types Δ : listC D.
  Implicit Types interp : listC D D.

  Program Definition interp_ectx (interp : listC D -n> D) (K : ectx)
    : listC D -n> iProp Σ :=
    λne Δ, ( v, interp Δ v -∗ WP (fill K (of_val v)) {{_, True}})%I.
  Solve Obligations with repeat intros ?; simpl; solve_proper.

  Program Definition interp_cont (interp : listC D -n> D)
    : listC D -n> D :=
    λne Δ w, ( K, w = ContV K interp_ectx interp K Δ)%I.
  Solve Obligations with repeat intros ?; simpl; solve_proper.

  Program Definition interp_expr (interp : listC D -n> D) :
    listC D -n> (exprC -n> iProp Σ) :=
    λne Δ e, ( K, interp_ectx interp K Δ -∗ WP (fill K e) {{_, True}})%I.
  Solve Obligations with repeat intros ?; simpl; solve_proper.

  Program Definition env_lookup (x : var) : listC D -n> D := λne Δ,
    from_option id (cconst True)%I (Δ !! x).
  Solve Obligations with repeat intros ?; simpl; solve_proper.

  Definition interp_unit : listC D -n> D := λne Δ w, w = UnitV%I.
  Definition interp_nat : listC D -n> D := λne Δ w, n, w = #nv n%I.
  Definition interp_bool : listC D -n> D := λne Δ w, n, w = #♭v n%I.

  Program Definition interp_prod
      (interp1 interp2 : listC D -n> D) : listC D -n> D := λne Δ w,
    ( w1 w2, w = PairV w1 w2 interp1 Δ w1 interp2 Δ w2)%I.
  Solve Obligations with repeat intros ?; simpl; solve_proper.

  Program Definition interp_sum
      (interp1 interp2 : listC D -n> D) : listC D -n> D := λne Δ w,
    (( w1, w = InjLV w1 interp1 Δ w1)
     ( w2, w = InjRV w2 interp2 Δ w2))%I.
  Solve Obligations with repeat intros ?; simpl; solve_proper.

  Program Definition interp_arrow
      (interp1 interp2 : listC D -n> D) : listC D -n> D := λne Δ w,
    ( v, interp1 Δ v -∗
            interp_expr interp2 Δ (App (of_val w) (of_val v)))%I.
  Solve Obligations with repeat intros ?; simpl; solve_proper.

  Program Definition interp_forall
      (interp : listC D -n> D) : listC D -n> D := λne Δ w,
    ( τi : D,
       v, Persistent (τi v) -∗
        interp_expr interp (τi :: Δ) (TApp (of_val w)))%I.
  Solve Obligations with repeat intros ?; simpl; solve_proper.

  Definition interp_rec1
      (interp : listC D -n> D) (Δ : listC D) (τi : D) : D := λne w,
    ( ( v, w = FoldV v interp (τi :: Δ) v))%I.

  Global Instance interp_rec1_contractive
    (interp : listC D -n> D) (Δ : listC D) : Contractive (interp_rec1 interp Δ).
  Proof. by solve_contractive. Qed.

  Program Definition interp_rec (interp : listC D -n> D) : listC D -n> D :=
    λne Δ, fixpoint (interp_rec1 interp Δ).
  Next Obligation.
    intros interp n Δ1 Δ2 ; apply fixpoint_ne => τi w. solve_proper.
  Qed.

  Program Definition interp_ref_inv (l : loc) : D -n> iProp Σ := λne τi,
    ( v, l ↦ᵢ v τi v)%I.
  Solve Obligations with solve_proper.

  Program Definition interp_ref
      (interp : listC D -n> D) : listC D -n> D := λne Δ w,
    ( l, w = LocV l inv (logN .@ l) (interp_ref_inv l (interp Δ)))%I.
  Solve Obligations with solve_proper.

  Fixpoint interp (τ : type) : listC D -n> D :=
    match τ return _ with
    | TUnit => interp_unit
    | TNat => interp_nat
    | TBool => interp_bool
    | TProd τ1 τ2 => interp_prod (interp τ1) (interp τ2)
    | TSum τ1 τ2 => interp_sum (interp τ1) (interp τ2)
    | TArrow τ1 τ2 => interp_arrow (interp τ1) (interp τ2)
    | TVar x => env_lookup x
    | TForall τ' => interp_forall (interp τ')
    | TRec τ' => interp_rec (interp τ')
    | Tref τ' => interp_ref (interp τ')
    | TCont τ' => interp_cont (interp τ')
    end.
  Notation "⟦ τ ⟧" := (interp τ).

  Definition interp_env (Γ : list type)
      (Δ : listC D) (vs : list val) : iProp Σ :=
    (length Γ = length vs [∗] zip_with (λ τ, τ Δ) Γ vs)%I.
  Notation "⟦ Γ ⟧*" := (interp_env Γ).

  Class env_Persistent Δ :=
    env_persistent : Forall (λ τi, v, Persistent (τi v)) Δ.
  Global Instance env_persistent_nil : env_Persistent [].
  Proof. by constructor. Qed.
  Global Instance env_persistent_cons τi Δ :
    ( v, Persistent (τi v)) env_Persistent Δ env_Persistent (τi :: Δ).
  Proof. by constructor. Qed.
  Global Instance env_persistent_lookup Δ x v :
    env_Persistent Δ Persistent (env_lookup x Δ v).
  Proof. intros ; revert x; induction =>-[|?] /=; apply _. Qed.
  Global Instance interp_persistent τ Δ v :
    env_Persistent Δ Persistent (interp τ Δ v).
  Proof.
    revert v Δ; induction τ=> v Δ ; simpl; try apply _.
    rewrite /Persistent /interp_rec
            (fixpoint_unfold (interp_rec1 τ Δ) v) /interp_rec1 /=
    intuitionistically_into_persistently.
      by apply persistently_intro'.
  Qed.
  Global Instance interp_env_persistent Γ Δ vs :
    env_Persistent Δ Persistent ( Γ ⟧* Δ vs).
  Proof.
    intros .
    apply sep_persistent; first apply _.
    apply big_sepL_persistent_id.
    revert vs. induction Γ; simpl; first econstructor.
    destruct vs; first econstructor; simpl.
    econstructor; first apply _.
    eapply TCForall_ind; eauto; first econstructor.
    intros; econstructor; eauto.
  Qed.

  Lemma interp_weaken Δ1 Π Δ2 τ :
     τ.[upn (length Δ1) (ren (+ length Π))] (Δ1 ++ Π ++ Δ2)
     τ (Δ1 ++ Δ2).
  Proof.
    revert Δ1 Π Δ2. induction τ=> Δ1 Π Δ2; simpl; auto.
    - intros w; simpl; properness; auto. by apply IHτ1. by apply IHτ2.
    - intros w; simpl; properness; auto. by apply IHτ1. by apply IHτ2.
    - intros w; simpl; properness; auto. by apply IHτ1. by apply IHτ2.
    - apply fixpoint_proper=> τi w /=.
      properness; auto. apply (IHτ (_ :: _)).
    - rewrite iter_up; destruct lt_dec as [Hl | Hl]; simpl.
      { by rewrite !lookup_app_l. }
      (* going around a weird bug in lia or perhaps rewriting system!
      The following equalities are not solved by lia when they are
      generated by as side conditions for rewriting lemmas. *)

      assert (length Δ1 x) by lia.
      assert (length Π length Δ1 + (length Π + (x - length Δ1)) - length Δ1)
        by lia.
      assert (length Δ1 length Δ1 + (length Π + (x - length Δ1))) by lia.
      assert (length Δ1 + (length Π + (x - length Δ1)) - length Δ1 - length Π = x - length Δ1) by lia.
      rewrite !lookup_app_r //.
      by do 2 f_equiv.
    - intros w; simpl; properness; auto. apply (IHτ (_ :: _)).
    - intros w; simpl; properness; auto. by apply IHτ.
    - intros w; simpl; properness; auto. by apply IHτ.
  Qed.

  Lemma interp_subst_up Δ1 Δ2 τ τ' :
     τ (Δ1 ++ interp τ' Δ2 :: Δ2)
     τ.[upn (length Δ1) (τ' .: ids)] (Δ1 ++ Δ2).
  Proof.
    revert Δ1 Δ2; induction τ=> Δ1 Δ2; simpl; auto.
    - intros w; simpl; properness; auto. by apply IHτ1. by apply IHτ2.
    - intros w; simpl; properness; auto. by apply IHτ1. by apply IHτ2.
    - intros w; simpl; properness; auto. by apply IHτ1. by apply IHτ2.
    - apply fixpoint_proper=> τi w /=.
      properness; auto. apply (IHτ (_ :: _)).
    - rewrite iter_up; destruct lt_dec as [Hl | Hl]; simpl.
      { by rewrite !lookup_app_l. }
      (* going around a weird bug in lia or perhaps rewriting system!
      The following equalities are not solved by lia when they are
      generated by as side conditions for rewriting lemmas. *)

      assert (length Δ1 x) by lia.
      rewrite lookup_app_r //.
      destruct (x - length Δ1) as [|n] eqn:Heqn; rewrite /= Heqn.
      { symmetry. asimpl. apply (interp_weaken [] Δ1 Δ2 τ'). }
      (* going around a weird bug in lia or perhaps rewriting system!
      The following equalities are not solved by lia when they are
      generated by as side conditions for rewriting lemmas. *)

      assert (length Δ1 length Δ1 + n) by lia.
      assert (n = length Δ1 + n - length Δ1) by lia.
      rewrite !lookup_app_r //. by do 2 f_equiv /=.
    - intros w; simpl; properness; auto. apply (IHτ (_ :: _)).
    - intros w; simpl; properness; auto. by apply IHτ.
    - intros w; simpl; properness; auto. by apply IHτ.
  Qed.

  Lemma interp_subst Δ2 τ τ' : τ ( τ' Δ2 :: Δ2) τ.[τ'/] Δ2.
  Proof. apply (interp_subst_up []). Qed.

  Lemma interp_env_length Δ Γ vs : Γ ⟧* Δ vs length Γ = length vs.
  Proof. by iIntros "[% ?]". Qed.

  Lemma interp_env_Some_l Δ Γ vs x τ :
    Γ !! x = Some τ Γ ⟧* Δ vs v, vs !! x = Some v τ Δ v.
  Proof.
    iIntros (?) "[Hlen HΓ]"; iDestruct "Hlen" as %Hlen.
    destruct (lookup_lt_is_Some_2 vs x) as [v Hv].
    { by rewrite -Hlen; apply lookup_lt_Some with τ. }
    iExists v; iSplit. done. iApply (big_sepL_elem_of with "HΓ").
    apply elem_of_list_lookup_2 with x.
    rewrite lookup_zip_with; by simplify_option_eq.
  Qed.

  Lemma interp_env_nil Δ : True [] ⟧* Δ [].
  Proof. iIntros "_"; iSplit; simpl; auto. Qed.
  Lemma interp_env_cons Δ Γ vs τ v :
     τ :: Γ ⟧* Δ (v :: vs) ⊣⊢ τ Δ v Γ ⟧* Δ vs.
  Proof.
    rewrite /interp_env /= (assoc _ ( _ _ _)) -(comm _ ⌜(_ = _)⌝%I) -assoc.
    by apply sep_proper; [apply pure_proper; omega|].
  Qed.

  Lemma interp_env_ren Δ (Γ : list type) (vs : list val) τi :
     subst (ren (+1)) <$> Γ ⟧* (τi :: Δ) vs ⊣⊢ Γ ⟧* Δ vs.
  Proof.
    apply sep_proper; [apply pure_proper; by rewrite fmap_length|].
    revert Δ vs τi; induction Γ=> Δ [|v vs] τi; csimpl; auto.
    apply sep_proper; auto. apply (interp_weaken [] [τi] Δ).
  Qed.
End logrel.

Typeclasses Opaque interp_env.
Notation "⟦ τ ⟧" := (interp τ).
Notation "⟦ τ ⟧ₑ" := (interp_expr (interp τ)).
Notation "⟦ Γ ⟧*" := (interp_env Γ).