From iris.heap_lang Require Import proofmode notation.


(** In class, linked lists where represented by a null location for the empty
list, and a non-null location p such that p+0 points to the head of the list and
p+1 points to the tail of the list. In this file we try to stick to this
convention by assuming a null location, but this is not idiomatic in iris's toy
language Heaplang *)

Definition null : loc := Loc 0.


(** Syntax of [mlength] program *)

Definition mlength : val :=
  rec: "mlength" "p" :=
    if: "p" = #null then
      #0
    else
      let: "n'" := "mlength" !("p" +ₗ #1) in
      #1 + "n'".



(** Incantation asking Iris for the heap mapsto assertions *)
Section mlist_examples. Context `{!heapGS Σ}.


(** null is not special, so we wouldn't be able to prove p ⟼ v -∗ ⌜p ≠ null⌝.
Below, we bake it into the [Mlist] predicate instead of adding an axiom such as:

[Axiom mapsto_not_null : ∀ p v, p ↦ v ⊢ ⌜p ≠ null⌝.]

When allocating, we might get the null pointer, in which case we can just
allocate again to get another pointer, which must be not null this time *)

Fixpoint Mlist (L : list Z) (p : loc) : iProp Σ :=
  match L with
  | nil => ⌜ p = null ⌝
  | x :: L' => ∃ p' : loc, ⌜ p ≠ null ⌝ ∗ p ↦ #x ∗ (p +ₗ 1) ↦ #p' ∗ Mlist L' p'
  end%I.


(** Proof of [mlength] function *)

Lemma mlength_spec L (p : loc) :
  {{{ Mlist L p }}}
    mlength #p
  {{{ RET #(length L); Mlist L p }}}.
Proof.
  (* Iris's postcondition-passing style, we ignore [Φ] and [Hreturn] for now. *)
  (* [Hp] is the name of the important spatial hypothesis *)
  iIntros (Φ) "Hp Hreturn".

  (* We proceed by induction on [L], but we generalize on [p] by adding [∀ (p)] *)
  iInduction L as [ | x L' ] "IH" forall (p Φ).
  - (* rule [fix] in iris's heaplang is implemented by [wp_rec] *)
    wp_rec.

    (* [Mlist [] p] means that p is null, we can use it as a normal Rocq hypothesis *)
    simpl Mlist.
    iDestruct "Hp" as %Hp.

    (* pure deterministic reduction *)
    wp_pure.

    (* rule for if *)
    case_bool_decide as Hif; wp_if.
    (* the case for false is a contradiction *)
    2: congruence.

    (* case for true *)

    (* we need to return [length []] which is 0 *)
    iApply "Hreturn".
    iPureIntro.
    auto.

  - (* Inductive case: [L = x :: L'] *)

    (* unfolding Mlist *)
    simpl (Mlist (x :: L') p) at 1.

    (* Destruct the existential: rule EXISTS, introduces a Rocq variable *)
    iDestruct "Hp" as (p') "Hp".
    (* Destruct the hypothesis: rule PROP, introduces a Rocq hypothesis *)
    iDestruct "Hp" as (Hp) "Hp".
    (* Destruct the separating conjunction by naming each component *)
    iDestruct "Hp" as "(Hp0 & Hp1 & Hp')".

    (* Rule for fix *)
    wp_rec.
    (* Bind rule: same as using the A-normal form + using the rule for let *)
    wp_bind (#p = #null)%E. wp_pure.

    (* rule for if *)
    case_bool_decide as Hif; wp_if. congruence.

    (* rule for load *)
    wp_load.
    
    (* Induction hypothesis + frame *)
    (* Hp' is passed to IH, the rest is framed *)
    (* Hp'_post is the name we give to the postcondition *)
    wp_apply ("IH" with "Hp'") as "Hp'_post".

    (* again, applying rules for pure reductions: *)
    wp_pures.

    (* ignore this *)
    iModIntro.

    (* massaging goal to match the expected return value *)
    replace (1 + length L')%Z with (S (length L') : Z) by lia.
    iApply "Hreturn".

    (* reconstructing [Mlist (x :: L') p] *)
    simpl Mlist.
    iExists p'.

    (* applying the frame rule several times *)
    iFrame "Hp0".
    iFrame "Hp1".
    iFrame "Hp'_post".
    iPureIntro.
    auto.
Qed.

(** Proof of list_incr *)

Definition list_incr : val :=
  rec: "list_incr" "p" :=
    if: "p" = #null then
      #()
    else
      "p" <- #1 + !"p";;
      "list_incr" (!("p" +ₗ #1)).

Lemma list_incr_spec L (p : loc) :
  {{{ Mlist L p }}}
    list_incr #p
  {{{ RET #(); Mlist (map (Z.add 1) L) p }}}.
Proof.
  iIntros (Φ) "Hp Hreturn".
  iInduction L as [ | x L' ] "IH" forall (p Φ).
  - wp_rec.
    simpl.
    iDestruct "Hp" as %->.
    wp_pure.
    wp_if.
    iModIntro.
    iApply "Hreturn".
    iPureIntro.
    reflexivity.
  - simpl.
    iDestruct "Hp" as (p') "(% & Hp0 & Hp1 & Hp')".
    wp_rec.
    wp_pure.
    rewrite bool_decide_eq_false_2.
    2: now injection 1 as ->.
    wp_if.
    wp_load.
    wp_pure.
    wp_store.
    wp_load.
    wp_apply ("IH" with "Hp'") as "Hp'_post".
    iApply "Hreturn".
    iExists p'.
    iFrame. auto.
Qed.

End mlist_examples.
