(* *********************************************************************)
(*                                                                     *)
(*              The Compcert verified compiler                         *)
(*                                                                     *)
(*          Xavier Leroy, INRIA Paris-Rocquencourt                     *)
(*                                                                     *)
(*  Copyright Institut National de Recherche en Informatique et en     *)
(*  Automatique.  All rights reserved.  This file is distributed       *)
(*  under the terms of the INRIA Non-Commercial License Agreement.     *)
(*                                                                     *)
(* *********************************************************************)

(** Typing rules and a type inference algorithm for RTL. *)

Require Import Coqlib.
Require Import Errors.
Require Import Unityping.
Require Import Maps.
Require Import AST.
Require Import Op.
Require Import Registers.
Require Import Globalenvs.
Require Import Values.
Require Import Integers.
Require Import Memory.
Require Import Events.
Require Import RTL RTLmach.
Require Import Conventions.

(** * The type system *)

(** Like Cminor and all intermediate languages, RTL can be equipped with
  a simple type system that statically guarantees that operations
  and addressing modes are applied to the right number of arguments
  and that the arguments are of the correct types.   The type algebra
  is very simple, consisting of the four types [Tint] (for integers
  and pointers), [Tfloat] (for double-precision floats), [Tlong]
  (for 64-bit integers) and [Tsingle] (for single-precision floats).

  Additionally, we impose that each pseudo-register has the same type
  throughout the function.  This requirement helps with register allocation,
  enabling each pseudo-register to be mapped to a single hardware register
  or stack location of the correct type.

  Finally, we also check that the successors of instructions
  are valid, i.e. refer to non-empty nodes in the CFG.

  The typing judgement for instructions is of the form [wt_instr f env
  instr], where [f] is the current function (used to type-check
  [Ireturn] instructions) and [env] is a typing environment
  associating types to pseudo-registers.  Since pseudo-registers have
  unique types throughout the function, the typing environment does
  not change during type-checking of individual instructions.  One
  point to note is that we have one polymorphic operator, [Omove],
  which can work over both integers and floats.
*)

Definition regenv := reg -> typ.

Section WT_INSTR.

Variable funct: function.
Variable env: regenv.

Definition valid_successor (s: node) : Prop :=
  exists i, funct.(fn_code)!s = Some i.

Definition type_of_builtin_arg (a: builtin_arg reg) : typ :=
  match a with
  | BA r => env r
  | BA_int _ => Tint
  | BA_long _ => Tlong
  | BA_float _ => Tfloat
  | BA_single _ => Tsingle
  | BA_loadstack chunk ofs => type_of_chunk chunk
  | BA_addrstack ofs => Tptr
  | BA_loadglobal chunk id ofs => type_of_chunk chunk
  | BA_addrglobal id ofs => Tptr
  | BA_splitlong hi lo => Tlong
  | BA_addptr a1 a2 => Tptr
  end.

Definition type_of_builtin_res (r: builtin_res reg) : typ :=
  match r with
  | BR r => env r
  | _    => Tint
  end.

Inductive wt_instr : instruction -> Prop :=
  | wt_Inop:
      forall s,
      valid_successor s ->
      wt_instr (Inop s)
  | wt_Iopmove:
      forall r1 r s,
      env r = env r1 ->
      valid_successor s ->
      wt_instr (Iop Omove (r1 :: nil) r s)
  | wt_Iop:
      forall op args res s,
      op <> Omove ->
      map env args = fst (type_of_operation op) ->
      env res = snd (type_of_operation op) ->
      valid_successor s ->
      wt_instr (Iop op args res s)
  | wt_Iload:
      forall chunk addr args dst s,
      map env args = type_of_addressing addr ->
      env dst = type_of_chunk chunk ->
      valid_successor s ->
      wt_instr (Iload chunk addr args dst s)
  | wt_Istore:
      forall chunk addr args src s,
      map env args = type_of_addressing addr ->
      env src = type_of_chunk chunk ->
      valid_successor s ->
      wt_instr (Istore chunk addr args src s)
  | wt_Icall:
      forall sig ros args res s,
      match ros with inl r => env r = Tptr | inr s => True end ->
      map env args = sig.(sig_args) ->
      env res = proj_sig_res sig ->
      valid_successor s ->
      wt_instr (Icall sig ros args res s)
  | wt_Itailcall:
      forall sig ros args,
      match ros with inl r => env r = Tptr | inr s => True end ->
      map env args = sig.(sig_args) ->
      sig.(sig_res) = funct.(fn_sig).(sig_res) ->
      tailcall_possible sig ->
      wt_instr (Itailcall sig ros args)
  | wt_Ibuiltin:
      forall ef args res s,
      match ef with
      | EF_annot _ _ _ | EF_debug _ _ _ => True
      | _ => map type_of_builtin_arg args = (ef_sig ef).(sig_args)
      end ->
      type_of_builtin_res res = proj_sig_res (ef_sig ef) ->
      valid_successor s ->
      wt_instr (Ibuiltin ef args res s)
  | wt_Icond:
      forall cond args s1 s2,
      map env args = type_of_condition cond ->
      valid_successor s1 ->
      valid_successor s2 ->
      wt_instr (Icond cond args s1 s2)
  | wt_Ijumptable:
      forall arg tbl,
      env arg = Tint ->
      (forall s, In s tbl -> valid_successor s) ->
      list_length_z tbl * 4 <= Int.max_unsigned ->
      wt_instr (Ijumptable arg tbl)
  | wt_Ireturn_none:
      funct.(fn_sig).(sig_res) = Tvoid ->
      wt_instr (Ireturn None)
  | wt_Ireturn_some:
      forall arg ty,
      funct.(fn_sig).(sig_res) <> Tvoid ->
      env arg = proj_sig_res funct.(fn_sig) ->
      env arg = ty ->
      wt_instr (Ireturn (Some arg)).

End WT_INSTR.

(** A function [f] is well-typed w.r.t. a typing environment [env],
   written [wt_function env f], if all instructions are well-typed,
   parameters agree in types with the function signature, and
   parameters are pairwise distinct. *)

Record wt_function (f: function) (env: regenv): Prop :=
  mk_wt_function {
    wt_params:
      map env f.(fn_params) = f.(fn_sig).(sig_args);
    wt_norepet:
      list_norepet f.(fn_params);
    wt_instrs:
      forall pc instr,
      f.(fn_code)!pc = Some instr -> wt_instr f env instr;
    wt_entrypoint:
      valid_successor f f.(fn_entrypoint)
}.

Inductive wt_fundef: fundef -> Prop :=
  | wt_fundef_external: forall ef,
      wt_fundef (External ef)
  | wt_function_internal: forall f env,
      wt_function f env ->
      wt_fundef (Internal f).

Definition wt_program (p: program): Prop :=
  forall i f, In (i, Gfun f) (prog_defs p) -> wt_fundef f.

(** * Type inference *)

(** Type inference reuses the generic solver for unification constraints
  defined in module [Unityping]. *)

Module RTLtypes <: TYPE_ALGEBRA.

Definition t := typ.
Definition eq := typ_eq.
Definition default := Tint.

End RTLtypes.

Module S := UniSolver(RTLtypes).

Section INFERENCE.

Local Open Scope error_monad_scope.

Variable f: function.

(** Checking the validity of successor nodes. *)

Definition check_successor (s: node): res unit :=
  match f.(fn_code)!s with
  | None => Error (MSG "bad successor " :: POS s :: nil)
  | Some i => OK tt
  end.

Fixpoint check_successors (sl: list node): res unit :=
  match sl with
  | nil => OK tt
  | s1 :: sl' => do x <- check_successor s1; check_successors sl'
  end.

(** Check structural constraints and process / record all type constraints. *)

Definition type_ros (e: S.typenv) (ros: reg + ident) : res S.typenv :=
  match ros with
  | inl r => S.set e r Tptr
  | inr s => OK e
  end.

Definition is_move (op: operation) : bool :=
  match op with Omove => true | _ => false end.

Definition type_expect (e: S.typenv) (t1 t2: typ) : res S.typenv :=
  if typ_eq t1 t2 then OK e else Error(msg "unexpected type").

Definition type_builtin_arg (e: S.typenv) (a: builtin_arg reg) (ty: typ) : res S.typenv :=
  match a with
  | BA r => S.set e r ty
  | BA_int _ => type_expect e ty Tint
  | BA_long _ => type_expect e ty Tlong
  | BA_float _ => type_expect e ty Tfloat
  | BA_single _ => type_expect e ty Tsingle
  | BA_loadstack chunk ofs => type_expect e ty (type_of_chunk chunk)
  | BA_addrstack ofs => type_expect e ty Tptr
  | BA_loadglobal chunk id ofs => type_expect e ty (type_of_chunk chunk)
  | BA_addrglobal id ofs => type_expect e ty Tptr
  | BA_splitlong hi lo => type_expect e ty Tlong
  | BA_addptr a1 a2 => type_expect e ty Tptr
  end.

Fixpoint type_builtin_args (e: S.typenv) (al: list (builtin_arg reg)) (tyl: list typ) : res S.typenv :=
  match al, tyl with
  | nil, nil => OK e
  | a1 :: al, ty1 :: tyl =>
      do e1 <- type_builtin_arg e a1 ty1; type_builtin_args e1 al tyl
  | _, _ =>
      Error (msg "builtin arity mismatch")
  end.

Definition type_builtin_res (e: S.typenv) (a: builtin_res reg) (ty: typ) : res S.typenv :=
  match a with
  | BR r => S.set e r ty
  | _    => type_expect e ty Tint
  end.

Definition type_instr (e: S.typenv) (i: instruction) : res S.typenv :=
  match i with
  | Inop s =>
      do x <- check_successor s; OK e
  | Iop op args res s =>
      do x <- check_successor s;
      if is_move op then
        match args with
        | arg :: nil => do (changed, e') <- S.move e res arg; OK e'
        | _ => Error (msg "ill-formed move")
        end
      else
       (let (targs, tres) := type_of_operation op in
        do e1 <- S.set_list e args targs; S.set e1 res tres)
  | Iload chunk addr args dst s =>
      do x <- check_successor s;
      do e1 <- S.set_list e args (type_of_addressing addr);
      S.set e1 dst (type_of_chunk chunk)
  | Istore chunk addr args src s =>
      do x <- check_successor s;
      do e1 <- S.set_list e args (type_of_addressing addr);
      S.set e1 src (type_of_chunk chunk)
  | Icall sig ros args res s =>
      do x <- check_successor s;
      do e1 <- type_ros e ros;
      do e2 <- S.set_list e1 args sig.(sig_args);
      S.set e2 res (proj_sig_res sig)
  | Itailcall sig ros args =>
      do e1 <- type_ros e ros;
      do e2 <- S.set_list e1 args sig.(sig_args);
      if rettype_eq sig.(sig_res) f.(fn_sig).(sig_res) then
        if tailcall_is_possible sig
        then OK e2
        else Error(msg "tailcall not possible")
      else Error(msg "bad return type in tailcall")
  | Ibuiltin ef args res s =>
      let sig := ef_sig ef in
      do x <- check_successor s;
      do e1 <-
        match ef with
        | EF_annot _ _ _ | EF_debug _ _ _ => OK e
        | _ => type_builtin_args e args sig.(sig_args)
        end;
      type_builtin_res e1 res (proj_sig_res sig)
 | Icond cond args s1 s2 =>
      do x1 <- check_successor s1;
      do x2 <- check_successor s2;
      S.set_list e args (type_of_condition cond)
 | Ijumptable arg tbl =>
      do x <- check_successors tbl;
      do e1 <- S.set e arg Tint;
      if zle (list_length_z tbl * 4) Int.max_unsigned
      then OK e1
      else Error(msg "jumptable too big")
  | Ireturn optres =>
      match optres, rettype_eq f.(fn_sig).(sig_res) Tvoid with
      | None, left _ => OK e
      | Some r, right _ => S.set e r (proj_sig_res f.(fn_sig))
      | _, _ => Error(msg "bad return")
      end
  end.

Definition type_code (e: S.typenv): res S.typenv :=
  PTree.fold (fun re pc i =>
    match re with
    | Error _ => re
    | OK e =>
        match type_instr e i with
        | Error msg => Error(MSG "At PC " :: POS pc :: MSG ": " :: msg)
        | OK e' => OK e'
        end
    end)
  f.(fn_code) (OK e).

(** Solve remaining constraints *)

Definition check_params_norepet (params: list reg): res unit :=
  if list_norepet_dec Reg.eq params
  then OK tt
  else Error(msg "duplicate parameters").

Definition type_function : res regenv :=
  do e1 <- type_code S.initial;
  do e2 <- S.set_list e1 f.(fn_params) f.(fn_sig).(sig_args);
  do te <- S.solve e2;
  do x1 <- check_params_norepet f.(fn_params);
  do x2 <- check_successor f.(fn_entrypoint);
  OK te.

(** ** Soundness proof *)

Remark type_ros_incr:
  forall e ros e' te, type_ros e ros = OK e' -> S.satisf te e' -> S.satisf te e.
Proof.
  unfold type_ros; intros. destruct ros. eauto with ty. inv H; auto with ty.
Qed.

Hint Resolve type_ros_incr: ty.

Lemma type_ros_sound:
  forall e ros e' te, type_ros e ros = OK e' -> S.satisf te e' ->
  match ros with inl r => te r = Tptr | inr s => True end.
Proof.
  unfold type_ros; intros. destruct ros.
  eapply S.set_sound; eauto.
  auto.
Qed.

Lemma check_successor_sound:
  forall s x, check_successor s = OK x -> valid_successor f s.
Proof.
  unfold check_successor, valid_successor; intros.
  destruct (fn_code f)!s; inv H. exists i; auto.
Qed.

Hint Resolve check_successor_sound: ty.

Lemma check_successors_sound:
  forall sl x, check_successors sl = OK x -> forall s, In s sl -> valid_successor f s.
Proof.
  induction sl; simpl; intros.
  contradiction.
  monadInv H. destruct H0. subst a; eauto with ty. eauto.
Qed.

Remark type_expect_incr:
  forall e ty1 ty2 e' te, type_expect e ty1 ty2 = OK e' -> S.satisf te e' -> S.satisf te e.
Proof.
  unfold type_expect; intros. destruct (typ_eq ty1 ty2); inv H. auto.
Qed.

Hint Resolve type_expect_incr: ty.

Lemma type_expect_sound:
  forall e ty1 ty2 e', type_expect e ty1 ty2 = OK e' -> ty1 = ty2.
Proof.
  unfold type_expect; intros. destruct (typ_eq ty1 ty2); inv H. auto.
Qed.

Lemma type_builtin_arg_incr:
  forall e a ty e' te, type_builtin_arg e a ty = OK e' -> S.satisf te e' -> S.satisf te e.
Proof.
  unfold type_builtin_arg; intros; destruct a; eauto with ty.
Qed.

Lemma type_builtin_args_incr:
  forall a ty e e' te, type_builtin_args e a ty = OK e' -> S.satisf te e' -> S.satisf te e.
Proof.
  induction a; destruct ty; simpl; intros; try discriminate.
  inv H; auto.
  monadInv H. eapply type_builtin_arg_incr; eauto.
Qed.

Lemma type_builtin_res_incr:
  forall e a ty e' te, type_builtin_res e a ty = OK e' -> S.satisf te e' -> S.satisf te e.
Proof.
  unfold type_builtin_res; intros; destruct a; inv H; eauto with ty.
Qed.

Hint Resolve type_builtin_args_incr type_builtin_res_incr: ty.

Lemma type_builtin_arg_sound:
  forall e a ty e' te,
  type_builtin_arg e a ty = OK e' -> S.satisf te e' -> type_of_builtin_arg te a = ty.
Proof.
  intros. destruct a; simpl in *; try (symmetry; eapply type_expect_sound; eassumption).
  eapply S.set_sound; eauto.
Qed.

Lemma type_builtin_args_sound:
  forall al tyl e e' te,
  type_builtin_args e al tyl = OK e' -> S.satisf te e' -> List.map (type_of_builtin_arg te) al = tyl.
Proof.
  induction al as [|a al]; destruct tyl as [|ty tyl]; simpl; intros; try discriminate.
- auto.
- monadInv H. f_equal.
  eapply type_builtin_arg_sound; eauto with ty.
  eauto.
Qed.

Lemma type_builtin_res_sound:
  forall e a ty e' te,
  type_builtin_res e a ty = OK e' -> S.satisf te e' -> type_of_builtin_res te a = ty.
Proof.
  intros. destruct a; simpl in *.
  eapply S.set_sound; eauto.
  symmetry; eapply type_expect_sound; eauto.
  symmetry; eapply type_expect_sound; eauto.
Qed.

Lemma type_instr_incr:
  forall e i e' te,
  type_instr e i = OK e' -> S.satisf te e' -> S.satisf te e.
Proof.
  intros; destruct i; try (monadInv H); eauto with ty.
- (* op *)
  destruct (is_move o) eqn:ISMOVE.
  destruct l; try discriminate. destruct l; monadInv EQ0. eauto with ty.
  destruct (type_of_operation o) as [targs tres] eqn:TYOP. monadInv EQ0. eauto with ty.
- (* tailcall *)
  destruct (rettype_eq (sig_res s) (sig_res (fn_sig f))); try discriminate.
  destruct (tailcall_is_possible s) eqn:TCIP; inv EQ2.
  eauto with ty.
- (* builtin *)
  destruct e0; try monadInv EQ1; eauto with ty.
- (* jumptable *)
  destruct (zle (list_length_z l * 4) Int.max_unsigned); inv EQ2.
  eauto with ty.
- (* return *)
  simpl in H.
  destruct o as [r|] eqn: RET; destruct (rettype_eq (sig_res (fn_sig f)) Tvoid); try discriminate.
  eauto with ty.
  inv H; auto with ty.
Qed.

Lemma type_instr_sound:
  forall e i e' te,
  type_instr e i = OK e' -> S.satisf te e' -> wt_instr f te i.
Proof.
  intros; destruct i; try (monadInv H); simpl.
- (* nop *)
  constructor; eauto with ty.
- (* op *)
  destruct (is_move o) eqn:ISMOVE.
  (* move *)
  + assert (o = Omove) by (unfold is_move in ISMOVE; destruct o; congruence).
    subst o.
    destruct l; try discriminate. destruct l; monadInv EQ0.
    constructor. eapply S.move_sound; eauto. eauto with ty.
  + destruct (type_of_operation o) as [targs tres] eqn:TYOP. monadInv EQ0.
    apply wt_Iop.
    unfold is_move in ISMOVE; destruct o; congruence.
    rewrite TYOP. eapply S.set_list_sound; eauto with ty.
    rewrite TYOP. eapply S.set_sound; eauto with ty.
    eauto with ty.
- (* load *)
  constructor.
  eapply S.set_list_sound; eauto with ty.
  eapply S.set_sound; eauto with ty.
  eauto with ty.
- (* store *)
  constructor.
  eapply S.set_list_sound; eauto with ty.
  eapply S.set_sound; eauto with ty.
  eauto with ty.
- (* call *)
  constructor.
  eapply type_ros_sound; eauto with ty.
  eapply S.set_list_sound; eauto with ty.
  eapply S.set_sound; eauto with ty.
  eauto with ty.
- (* tailcall *)
  destruct (rettype_eq (sig_res s) (sig_res (fn_sig f))); try discriminate.
  destruct (tailcall_is_possible s) eqn:TCIP; inv EQ2.
  constructor.
  eapply type_ros_sound; eauto with ty.
  eapply S.set_list_sound; eauto with ty.
  auto.
  apply tailcall_is_possible_correct; auto.
- (* builtin *)
  constructor.
  destruct e0; auto; eapply type_builtin_args_sound; eauto with ty.
  eapply type_builtin_res_sound; eauto.
  eauto with ty.
- (* cond *)
  constructor.
  eapply S.set_list_sound; eauto with ty.
  eauto with ty.
  eauto with ty.
- (* jumptable *)
  destruct (zle (list_length_z l * 4) Int.max_unsigned); inv EQ2.
  constructor.
  eapply S.set_sound; eauto.
  eapply check_successors_sound; eauto.
  auto.
- (* return *)
  simpl in H.
  destruct o as [r|] eqn: RET; destruct (rettype_eq (sig_res (fn_sig f)) Tvoid); try discriminate.
  econstructor. auto. eapply S.set_sound; eauto with ty. eauto.
  inv H. constructor. auto.
Qed.

Lemma type_code_sound:
  forall pc i e e' te,
  type_code e = OK e' ->
  f.(fn_code)!pc = Some i -> S.satisf te e' -> wt_instr f te i.
Proof.
  intros pc i e0 e1 te TCODE.
  set (P := fun c opte =>
         match opte with
         | Error _ => True
         | OK e' => c!pc = Some i -> S.satisf te e' -> wt_instr f te i
         end).
  change (P f.(fn_code) (OK e1)).
  rewrite <- TCODE. unfold type_code. apply PTree_Properties.fold_rec; unfold P; intros.
  - (* extensionality *)
    destruct a; auto; intros. rewrite <- H in H1. eapply H0; eauto.
  - (* base case *)
    rewrite PTree.gempty in H; discriminate.
  - (* inductive case *)
    destruct a as [e|?]; auto.
    destruct (type_instr e v) as [e'|?] eqn:TYINSTR; auto.
    intros. rewrite PTree.gsspec in H2. destruct (peq pc k).
    inv H2. eapply type_instr_sound; eauto.
    eapply H1; eauto. eapply type_instr_incr; eauto.
Qed.

Theorem type_function_correct:
  forall env, type_function = OK env -> wt_function f env.
Proof.
  unfold type_function; intros. monadInv H.
  assert (SAT0: S.satisf env x0) by (eapply S.solve_sound; eauto).
  assert (SAT1: S.satisf env x) by (eauto with ty).
  constructor.
- (* type of parameters *)
  eapply S.set_list_sound; eauto.
- (* parameters are unique *)
  unfold check_params_norepet in EQ2.
  destruct (list_norepet_dec Reg.eq (fn_params f)); inv EQ2; auto.
- (* instructions are well typed *)
  intros. eapply type_code_sound; eauto.
- (* entry point is valid *)
  eauto with ty.
Qed.

(** ** Completeness proof *)

Lemma type_ros_complete:
  forall te ros e,
  S.satisf te e ->
  match ros with inl r => te r = Tptr | inr s => True end ->
  exists e', type_ros e ros = OK e' /\ S.satisf te e'.
Proof.
  intros; destruct ros; simpl.
  eapply S.set_complete; eauto.
  exists e; auto.
Qed.

Lemma check_successor_complete:
  forall s, valid_successor f s -> check_successor s = OK tt.
Proof.
  unfold valid_successor, check_successor; intros.
  destruct H as [i EQ]; rewrite EQ; auto.
Qed.

Lemma type_expect_complete:
  forall e ty, type_expect e ty ty = OK e.
Proof.
  unfold type_expect; intros. rewrite dec_eq_true; auto.
Qed.

Lemma type_builtin_arg_complete:
  forall te a e,
  S.satisf te e ->
  exists e', type_builtin_arg e a (type_of_builtin_arg te a) = OK e' /\ S.satisf te e'.
Proof.
  intros. destruct a; simpl; try (exists e; split; [apply type_expect_complete|assumption]).
  apply S.set_complete; auto.
Qed.

Lemma type_builtin_args_complete:
  forall te al e,
  S.satisf te e ->
  exists e', type_builtin_args e al (List.map (type_of_builtin_arg te) al) = OK e' /\ S.satisf te e'.
Proof.
  induction al; simpl; intros.
- exists e; auto.
- destruct (type_builtin_arg_complete te a e) as (e1 & A & B); auto.
  destruct (IHal e1) as (e2 & C & D); auto.
  exists e2; split; auto. rewrite A. auto.
Qed.

Lemma type_builtin_res_complete:
  forall te a e,
  S.satisf te e ->
  exists e', type_builtin_res e a (type_of_builtin_res te a) = OK e' /\ S.satisf te e'.
Proof.
  intros. destruct a; simpl.
  apply S.set_complete; auto.
  exists e; auto.
  exists e; auto.
Qed.

Lemma type_instr_complete:
  forall te e i,
  S.satisf te e ->
  wt_instr f te i ->
  exists e', type_instr e i = OK e' /\ S.satisf te e'.
Proof.
  induction 2; simpl.
- (* nop *)
  econstructor; split. rewrite check_successor_complete; simpl; eauto. auto.
- (* move *)
  exploit S.move_complete; eauto. intros (changed & e' & A & B).
  exists e'; split. rewrite check_successor_complete by auto; simpl. rewrite A; auto. auto.
- (* other op *)
  destruct (type_of_operation op) as [targ tres]. simpl in *.
  exploit S.set_list_complete. eauto. eauto. intros [e1 [A B]].
  exploit S.set_complete. eexact B. eauto. intros [e2 [C D]].
  exists e2; split; auto.
  rewrite check_successor_complete by auto; simpl.
  replace (is_move op) with false. rewrite A; simpl; rewrite C; auto.
  destruct op; reflexivity || congruence.
- (* load *)
  exploit S.set_list_complete. eauto. eauto. intros [e1 [A B]].
  exploit S.set_complete. eexact B. eauto. intros [e2 [C D]].
  exists e2; split; auto.
  rewrite check_successor_complete by auto; simpl.
  rewrite A; simpl; rewrite C; auto.
- (* store *)
  exploit S.set_list_complete. eauto. eauto. intros [e1 [A B]].
  exploit S.set_complete. eexact B. eauto. intros [e2 [C D]].
  exists e2; split; auto.
  rewrite check_successor_complete by auto; simpl.
  rewrite A; simpl; rewrite C; auto.
- (* call *)
  exploit type_ros_complete. eauto. eauto. intros [e1 [A B]].
  exploit S.set_list_complete. eauto. eauto. intros [e2 [C D]].
  exploit S.set_complete. eexact D. eauto. intros [e3 [E F]].
  exists e3; split; auto.
  rewrite check_successor_complete by auto; simpl.
  rewrite A; simpl; rewrite C; simpl; rewrite E; auto.
- (* tailcall *)
  exploit type_ros_complete. eauto. eauto. intros [e1 [A B]].
  exploit S.set_list_complete. eauto. eauto. intros [e2 [C D]].
  exists e2; split; auto.
  rewrite A; simpl; rewrite C; simpl.
  rewrite H2; rewrite dec_eq_true.
  replace (tailcall_is_possible sig) with true; auto.
  symmetry. unfold tailcall_is_possible. apply forallb_forall.
  intros. apply H3 in H4. destruct x; intuition auto.
- (* builtin *)
  exploit type_builtin_args_complete; eauto. instantiate (1 := args). intros [e1 [A B]].
  exploit type_builtin_res_complete; eauto. instantiate (1 := res). intros [e2 [C D]].
  exploit type_builtin_res_complete. eexact H. instantiate (1 := res). intros [e3 [E F]].
  rewrite check_successor_complete by auto. simpl.
  exists (match ef with EF_annot _ _ _ | EF_debug _ _ _ => e3 | _ => e2 end); split.
  rewrite H1 in C, E.
  destruct ef; try (rewrite <- H0; rewrite A); simpl; auto.
  destruct ef; auto.
- (* cond *)
  exploit S.set_list_complete. eauto. eauto. intros [e1 [A B]].
  exists e1; split; auto.
  rewrite check_successor_complete by auto; simpl.
  rewrite check_successor_complete by auto; simpl.
  auto.
- (* jumptbl *)
  exploit S.set_complete. eauto. eauto. intros [e1 [A B]].
  exists e1; split; auto.
  replace (check_successors tbl) with (OK tt). simpl.
  rewrite A; simpl. apply zle_true; auto.
  revert H1. generalize tbl. induction tbl0; simpl; intros. auto.
  rewrite check_successor_complete by auto; simpl.
  apply IHtbl0; intros; auto.
- (* return none *)
  rewrite H0, dec_eq_true. exists e; auto.
- (* return some *)
  rewrite dec_eq_false by auto. apply S.set_complete; auto.
Qed.

Lemma type_code_complete:
  forall te e,
  (forall pc instr, f.(fn_code)!pc = Some instr -> wt_instr f te instr) ->
  S.satisf te e ->
  exists e', type_code e = OK e' /\ S.satisf te e'.
Proof.
  intros te e0 WTC SAT0.
  set (P := fun c res =>
        (forall pc i, c!pc = Some i -> wt_instr f te i) ->
        exists e', res = OK e' /\ S.satisf te e').
  assert (P f.(fn_code) (type_code e0)).
  {
    unfold type_code. apply PTree_Properties.fold_rec; unfold P; intros.
    - apply H0. intros. apply H1 with pc. rewrite <- H; auto.
    - exists e0; auto.
    - destruct H1 as [e [A B]].
      intros. apply H2 with pc. rewrite PTree.gso; auto. congruence.
      subst a.
      destruct (type_instr_complete te e v) as [e' [C D]].
      auto. apply H2 with k. apply PTree.gss.
      exists e'; split; auto. rewrite C; auto.
  }
  apply H; auto.
Qed.

Theorem type_function_complete:
  forall te, wt_function f te -> exists te, type_function = OK te.
Proof.
  intros. destruct H.
  destruct (type_code_complete te S.initial) as (e1 & A & B).
  auto. apply S.satisf_initial.
  destruct (S.set_list_complete te f.(fn_params) f.(fn_sig).(sig_args) e1) as (e2 & C & D); auto.
  destruct (S.solve_complete te e2) as (te' & E); auto.
  exists te'; unfold type_function.
  rewrite A; simpl. rewrite C; simpl. rewrite E; simpl.
  unfold check_params_norepet. rewrite pred_dec_true; auto. simpl.
  rewrite check_successor_complete by auto. auto.
Qed.

End INFERENCE.

(** * Type preservation during evaluation *)

(** The type system for RTL is not sound in that it does not guarantee
  progress: well-typed instructions such as [Icall] can fail because
  of run-time type tests (such as the equality between callee and caller's
  signatures).  However, the type system guarantees a type preservation
  property: if the execution does not fail because of a failed run-time
  test, the result values and register states match the static
  typing assumptions.  This preservation property will be useful
  later for the proof of semantic equivalence between [Linear] and [Mach].
  Even though we do not need it for [RTL], we show preservation for [RTL]
  here, as a warm-up exercise and because some of the lemmas will be
  useful later. *)

Definition wt_regset (env: regenv) (rs: regset) : Prop :=
  forall r, Val.has_type (rs#r) (env r).

Lemma wt_regset_assign:
  forall env rs v r,
  wt_regset env rs ->
  Val.has_type v (env r) ->
  wt_regset env (rs#r <- v).
Proof.
  intros; red; intros.
  rewrite Regmap.gsspec.
  case (peq r0 r); intro.
  subst r0. assumption.
  apply H.
Qed.

Lemma wt_regset_list:
  forall env rs,
  wt_regset env rs ->
  forall rl, Val.has_type_list (rs##rl) (List.map env rl).
Proof.
  induction rl; simpl.
  auto.
  split. apply H. apply IHrl.
Qed.

Lemma wt_regset_setres:
  forall env rs v res,
  wt_regset env rs ->
  Val.has_type v (type_of_builtin_res env res) ->
  wt_regset env (regmap_setres res v rs).
Proof.
  intros. destruct res; simpl in *; auto. apply wt_regset_assign; auto.
Qed.

Lemma wt_init_regs:
  forall env rl args,
  Val.has_type_list args (List.map env rl) ->
  wt_regset env (init_regs args rl).
Proof.
  induction rl; destruct args; simpl; intuition.
  red; intros. rewrite Regmap.gi. simpl; auto.
  apply wt_regset_assign; auto.
Qed.

Lemma wt_exec_Iop:
  forall (ge: genv) env f sp op args res s rs m v,
  wt_instr f env (Iop op args res s) ->
  eval_operation ge sp op rs##args m = Some v ->
  wt_regset env rs ->
  wt_regset env (rs#res <- v).
Proof.
  intros. inv H.
  simpl in H0. inv H0. apply wt_regset_assign; auto.
  rewrite H4; auto.
  eapply wt_regset_assign; auto.
  rewrite H8. eapply type_of_operation_sound; eauto.
Qed.

Lemma wt_exec_Iload:
  forall env f chunk addr args dst s m a v rs,
  wt_instr f env (Iload chunk addr args dst s) ->
  Mem.loadv chunk m a = Some v ->
  wt_regset env rs ->
  wt_regset env (rs#dst <- v).
Proof.
  intros. destruct a; simpl in H0; try discriminate. inv H.
  eapply wt_regset_assign; eauto. rewrite H8; eapply Mem.load_type; eauto.
Qed.

Lemma wt_exec_Ibuiltin:
  forall env f ef (ge: genv) args res s vargs m t vres m' rs,
  wt_instr f env (Ibuiltin ef args res s) ->
  external_call ef ge vargs m t vres m' ->
  wt_regset env rs ->
  wt_regset env (regmap_setres res vres rs).
Proof.
  intros. inv H.
  eapply wt_regset_setres; eauto.
  rewrite H7. eapply external_call_well_typed; eauto.
Qed.

Lemma wt_instr_at:
  forall f env pc i,
  wt_function f env -> f.(fn_code)!pc = Some i -> wt_instr f env i.
Proof.
  intros. inv H. eauto.
Qed.

Inductive wt_stackframes: list stackframe -> signature -> Prop :=
  | wt_stackframes_nil: forall sg,
      sg.(sig_res) = Tint ->
      wt_stackframes nil sg
  | wt_stackframes_cons:
      forall s res f sp pc rs env sg,
      wt_function f env ->
      wt_regset env rs ->
      env res = proj_sig_res sg ->
      wt_stackframes s (fn_sig f) ->
      wt_stackframes (Stackframe res f sp pc rs :: s) sg.

Inductive wt_state: state -> Prop :=
  | wt_state_intro:
      forall s f sp pc rs m env
        (WT_STK: wt_stackframes s (fn_sig f))
        (WT_FN: wt_function f env)
        (WT_RS: wt_regset env rs),
      wt_state (State s f sp pc rs m)
  | wt_state_call:
      forall s f args m id,
      wt_stackframes s (funsig f) ->
      wt_fundef f ->
      Val.has_type_list args (sig_args (funsig f)) ->
      wt_state (Callstate s f args m id)
  | wt_state_return:
      forall s v m sg,
      wt_stackframes s sg ->
      Val.has_type v (proj_sig_res sg) ->
      wt_state (Returnstate s v m).

Remark wt_stackframes_change_sig:
  forall s sg1 sg2,
  sg1.(sig_res) = sg2.(sig_res) -> wt_stackframes s sg1 -> wt_stackframes s sg2.
Proof.
  intros. inv H0.
- constructor; congruence.
- econstructor; eauto. rewrite H3. unfold proj_sig_res. rewrite H. auto.
Qed.

Section SUBJECT_REDUCTION.

Variable p: program.

Hypothesis wt_p: wt_program p.

Let ge := Genv.globalenv p.

Variable fn_stack_requirements : ident -> Z.

Lemma subject_reduction:
  forall st1 t st2, step fn_stack_requirements ge st1 t st2 ->
  forall (WT: wt_state st1), wt_state st2.
Proof.
  induction 1; intros; inv WT;
  try (generalize (wt_instrs _ _ WT_FN pc _ H); intros WTI).
  (* Inop *)
  econstructor; eauto.
  (* Iop *)
  econstructor; eauto. eapply wt_exec_Iop; eauto.
  (* Iload *)
  econstructor; eauto. eapply wt_exec_Iload; eauto.
  (* Istore *)
  econstructor; eauto.
  (* Icall *)
  try (generalize (wt_instrs _ _ WT_FN pc _ H0); intros WTI).
  assert (wt_fundef fd).
    destruct ros; simpl in H1.
    pattern fd. apply Genv.find_funct_prop with fundef unit p (rs#r).
    exact wt_p. exact H1.
    caseEq (Genv.find_symbol ge i); intros; rewrite H2 in H1.
    pattern fd. apply Genv.find_funct_ptr_prop with fundef unit p b.
    exact wt_p. exact H1.
    discriminate.
  econstructor; eauto.
  econstructor; eauto. inv WTI; auto.
  inv WTI. rewrite <- H9. apply wt_regset_list. auto.
  (* Itailcall *)
  try (generalize (wt_instrs _ _ WT_FN pc _ H0); intros WTI).
  assert (wt_fundef fd).
    destruct ros; simpl in H1.
    pattern fd. apply Genv.find_funct_prop with fundef unit p (rs#r).
    exact wt_p. exact H1.
    caseEq (Genv.find_symbol ge i); intros; rewrite H2 in H1.
    pattern fd. apply Genv.find_funct_ptr_prop with fundef unit p b.
    exact wt_p. exact H1.
    discriminate.
  econstructor; eauto.
  inv WTI. apply wt_stackframes_change_sig with (fn_sig f); auto.
  inv WTI. rewrite <- H10. apply wt_regset_list. auto.
  (* Ibuiltin *)
  econstructor; eauto. eapply wt_exec_Ibuiltin; eauto.
  (* Icond *)
  econstructor; eauto.
  (* Ijumptable *)
  econstructor; eauto.
  (* Ireturn *)
  econstructor; eauto.
  inv WTI; simpl. auto. rewrite <- H5. auto.
  (* internal function *)
  simpl in *. inv H8.
  econstructor; eauto.
  inv H3. apply wt_init_regs; auto. rewrite wt_params0. auto.
  (* external function *)
  econstructor; eauto.
  eapply external_call_well_typed; eauto.
  (* return *)
  inv H1. econstructor; eauto.
  apply wt_regset_assign; auto. rewrite H10; auto.
Qed.

Lemma wt_initial_state:
  forall S, initial_state p S -> wt_state S.
Proof.
  intros. inv H. constructor. constructor. rewrite H3; auto.
  pattern f. apply Genv.find_funct_ptr_prop with fundef unit p b.
  exact wt_p. exact H2.
  rewrite H3. constructor.
Qed.

Lemma wt_instr_inv:
  forall s f sp pc rs m i,
  wt_state (State s f sp pc rs m) ->
  f.(fn_code)!pc = Some i ->
  exists env, wt_instr f env i /\ wt_regset env rs.
Proof.
  intros. inv H. exists env; split; auto.
  inv WT_FN. eauto.
Qed.

End SUBJECT_REDUCTION.


Require Import VST.floyd.base2.
Require Import VST.floyd.client_lemmas.
Require Import VST.floyd.type_induction.
(*Require Import VST.floyd.fieldlist.*)
Require Import VST.floyd.compact_prod_sum.
(*Require Import VST.floyd.aggregate_type.*)
Require Import VST.floyd.mapsto_memory_block.
Require Import VST.floyd.nested_pred_lemmas.
Require Import VST.floyd.jmeq_lemmas.
Require Import VST.floyd.sublist.

Require Export VST.floyd.fieldlist.
Require Export VST.floyd.aggregate_type.

Open Scope Z.
Open Scope logic.

(******************************************

Definition and lemmas about rangespec

******************************************)

Fixpoint rangespec (lo: Z) (n: nat) (P: Z -> val -> mpred): val -> mpred :=
  match n with
  | O => fun _ => emp
  | S n' => P lo * rangespec (Z.succ lo) n' P
 end.

Fixpoint fold_range' {A: Type} (f: Z -> A -> A) (zero: A) (lo: Z) (n: nat) : A :=
 match n with
  | O => zero
  | S n' => f lo (fold_range' f  zero (Z.succ lo) n')
 end.

Definition fold_range {A: Type} (f: Z -> A -> A) (zero: A) (lo hi: Z) : A :=
  fold_range' f zero lo (Z.to_nat (hi-lo)).

Lemma rangespec_shift_derives: forall lo lo' len P P' p p',
  (forall i i', lo <= i < lo + Z_of_nat len -> i - lo = i' - lo' -> P i p |-- P' i' p') ->
  rangespec lo len P p |-- rangespec lo' len P' p'.
Proof.
  intros.
  revert lo lo' H;
  induction len; intros.
  + simpl. auto.
  + simpl.
    apply sepcon_derives.
    - apply H; [| omega].
      rewrite Nat2Z.inj_succ.
      rewrite <- Z.add_1_r.
      omega.
    - apply IHlen. intros.
      apply H; [| omega].
      rewrite Nat2Z.inj_succ.
      rewrite <- Z.add_1_r.
      pose proof Zle_0_nat (S len).
      omega.
Qed.

Lemma rangespec_ext_derives: forall lo len P P' p,
  (forall i, lo <= i < lo + Z_of_nat len -> P i p |-- P' i p) ->
  rangespec lo len P p |-- rangespec lo len P' p.
Proof.
  intros.
  apply rangespec_shift_derives.
  intros.
  assert (i = i') by omega.
  subst.
  apply H.
  auto.
Qed.

Lemma rangespec_shift: forall lo lo' len P P' p p',
  (forall i i', lo <= i < lo + Z_of_nat len -> i - lo = i' - lo' -> P i p = P' i' p') ->
  rangespec lo len P p = rangespec lo' len P' p'.
Proof.
  intros; apply pred_ext; apply rangespec_shift_derives;
  intros.
  + erewrite H; eauto.
      apply derives_refl.
  + erewrite H; eauto.
      apply derives_refl.
    omega.
Qed.

Lemma rangespec_ext: forall lo len P P' p,
  (forall i, lo <= i < lo + Z_of_nat len -> P i p = P' i p) ->
  rangespec lo len P p = rangespec lo len P' p.
Proof.
  intros; apply pred_ext; apply rangespec_ext_derives;
  intros; rewrite H; auto.
Qed.

Lemma rangespec_sepcon: forall lo len P Q p,
  rangespec lo len P p * rangespec lo len Q p = rangespec lo len (P * Q) p.
Proof.
  intros.
  revert lo; induction len; intros.
  + simpl.
    rewrite sepcon_emp; auto.
  + simpl.
    rewrite !sepcon_assoc.
    f_equal.
    rewrite <- sepcon_assoc, (sepcon_comm _ (Q lo p)), sepcon_assoc.
    f_equal.
    rewrite IHlen.
    reflexivity.
Qed.

Lemma rangespec_elim: forall lo len P i,
  lo <= i < lo + Z_of_nat len -> rangespec lo len P |-- P i * TT.
Proof.
  intros. revert lo i H; induction len; intros.
  + simpl in H. omega.
  + simpl. intros; destruct (Z.eq_dec i lo).
    - subst. cancel.
    - replace (P i x * !!True) with (TT * (P i x * TT)) by (apply pred_ext; cancel).
      apply sepcon_derives; [cancel |].
      apply IHlen.
      rewrite Nat2Z.inj_succ in H.
      rewrite <- Z.add_1_l in *.
      omega.
Qed.

Inductive Forallz {A} (P: Z -> A->Prop) : Z -> list A -> Prop :=
 | Forallz_nil : forall i, Forallz P i nil
 | Forallz_cons : forall i x l, P i x -> Forallz P (Z.succ i) l -> Forallz P i (x::l).

(******************************************

Definition of aggregate predicates.

******************************************)

Definition array_pred {A: Type}{d: Inhabitant A}  (lo hi: Z) (P: Z -> A -> val -> mpred) (v: list A) (p: val) : mpred :=
  !! (Zlength v = hi - lo) &&
  rangespec lo (Z.to_nat (hi-lo)) (fun i => P i (Znth (i-lo) v)) p.

Definition struct_pred (m: members) {A: ident * type -> Type} (P: forall it, A it -> val -> mpred) (v: compact_prod (map A m)) (p: val): mpred.
Proof.
  destruct m as [| (i0, t0) m]; [exact emp |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros ? ? v.
  + simpl in v.
    exact (P _ v p).
  + simpl in v.
    exact ((P _ (fst v) p) * IHm i0 t0 (snd v)).
Defined.

(* when unfold, do cbv [struct_pred list_rect]. *)

Definition union_pred (m: members) {A: ident * type -> Type} (P: forall it, A it -> val -> mpred) (v: compact_sum (map A m)) (p: val): mpred.
Proof.
  destruct m as [| (i0, t0) m]; [exact emp |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros ? ? v.
  + simpl in v.
    exact (P _ v p).
  + simpl in v.
    destruct v as [v | v].
    - exact (P _ v p).
    - exact (IHm i0 t0 v).
Defined.

Definition array_Prop {A: Type} (d:A) (lo hi: Z) (P: Z -> A -> Prop) (v: list A) : Prop :=
   Zlength v = hi-lo /\ Forallz P 0 v.

Definition struct_Prop (m: members) {A: ident * type -> Type}
                             (P: forall it, A it -> Prop) (v: compact_prod (map A m)) : Prop.
Proof.
  destruct m as [| (i0, t0) m]; [exact True |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros ? ? v.
  + simpl in v.
    exact (P _ v).
  + simpl in v.
    exact ((P _ (fst v)) /\ IHm i0 t0 (snd v)).
Defined.

Definition union_Prop (m: members) {A: ident * type -> Type}
               (P: forall it, A it -> Prop) (v: compact_sum (map A m)): Prop.
Proof.
  destruct m as [| (i0, t0) m]; [exact True |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros ? ? v.
  + simpl in v.
    exact (P _ v).
  + simpl in v.
    destruct v as [v | v].
    - exact (P _ v).
    - exact (IHm i0 t0 v).
Defined.

(******************************************

Properties

******************************************)

Lemma array_pred_len_0: forall {A}{d: Inhabitant A} lo hi P p,
  hi = lo ->
  array_pred lo hi P nil p = emp.
Proof.
  intros.
  unfold array_pred.
  replace (Z.to_nat (hi - lo)) with 0%nat by (symmetry; apply nat_of_Z_neg; omega).
  simpl.
  rewrite prop_true_andp by (unfold Zlength; simpl; omega).
  reflexivity.
Qed.

Lemma array_pred_len_1: forall {A}{d: Inhabitant A} i P v p,
  array_pred i (i + 1) P (v :: nil) p = P i v p.
Proof.
  intros.
  unfold array_pred.
  replace (i + 1 - i) with 1 by omega.
  simpl. rewrite sepcon_emp.
  rewrite prop_true_andp by (unfold Zlength; simpl; omega).
  unfold Znth. rewrite Z.sub_diag. rewrite if_false by omega. change (Z.to_nat 0) with 0%nat. auto.
Qed.

Lemma split_array_pred: forall {A}{d: Inhabitant A} lo mid hi P v p,
  lo <= mid <= hi ->
  Zlength v = hi - lo ->
  array_pred lo hi P v p =
  array_pred lo mid P (sublist 0 (mid-lo) v) p *
  array_pred mid hi P (sublist (mid-lo) (hi-lo) v) p.
Proof.
  intros.
  unfold array_pred.
  normalize.
  rewrite prop_true_andp by (rewrite !Zlength_sublist by omega; omega).
  clear H0.
  remember (Z.to_nat (mid-lo)) as n.
  replace (Z.to_nat (hi-lo)) with (n + Z.to_nat (hi-mid))%nat in *
    by (subst n; rewrite <- Z2Nat.inj_add by omega; f_equal; omega).
  assert (lo = mid - Z.of_nat n)
    by (rewrite Heqn; rewrite Z2Nat.id by omega; omega).
  clear Heqn.
  revert lo v H H0; induction n; intros.
  + subst lo.
    change (Z.of_nat 0) with 0 in *.
    simpl rangespec at 2. rewrite emp_sepcon.
    rewrite Z.sub_0_r, Z.sub_diag, plus_0_l.
    apply rangespec_ext; intros.
    rewrite Z2Nat.id in H0 by omega.
    f_equal.
    rewrite Znth_sublist, Z.add_0_r by omega.
    reflexivity.
  + simpl plus at 1.
    unfold rangespec; fold rangespec.
    repeat match goal with |- context [(?A * ?B) p] => change ((A*B)p) with (A p * B p) end.
    rewrite !sepcon_assoc.
    f_equal.
    - f_equal.
      rewrite Z.sub_diag.
      subst lo.
      rewrite Znth_sublist by (try rewrite Nat2Z.inj_succ; omega).
      reflexivity.
    - replace (rangespec (Z.succ lo) (n + Z.to_nat (hi - mid))
              (fun i : Z => P i (Znth (i - lo) v)) p)
      with (rangespec (Z.succ lo) (n + Z.to_nat (hi - mid))
              (fun i : Z => P i (Znth (i - Z.succ lo) (skipn 1 v))) p).
      2:{
        apply rangespec_ext; intros.
        f_equal.
        rewrite <- Znth_succ by omega; auto.
      }
      rewrite Nat2Z.inj_succ in H0.
      rewrite IHn by omega.
      f_equal.
      * apply rangespec_ext; intros.
        f_equal.
        rewrite Znth_sublist, Z.add_0_r by omega.
        rewrite <- Znth_succ by omega; auto.
        rewrite Znth_sublist, Z.add_0_r by omega.
        reflexivity.
      * apply rangespec_ext; intros.
        f_equal.
        rewrite Z2Nat.id in H1 by omega.
        rewrite Znth_sublist by omega.
        rewrite Znth_sublist by omega.
        replace (i - mid + (mid - Z.succ lo)) with (i - Z.succ lo) by omega.
        rewrite <- Znth_succ by omega; auto.
         f_equal; omega.
Qed.

Lemma array_pred_shift: forall {A}{d: Inhabitant A} (lo hi lo' hi' mv : Z) P' P v p,
  lo - lo' = mv ->
  hi - hi' = mv ->
 (forall i i', lo <= i < hi -> i - i' = mv -> P' i' (Znth (i-lo) v) p = P i (Znth (i-lo) v) p) ->
  array_pred lo' hi' P' v p = array_pred lo hi P v p.
Proof.
  intros.
  unfold array_pred.
  apply andp_prop_ext; [omega | intros].
  replace (hi' - lo') with (hi - lo) by omega.
  destruct (zlt hi lo). rewrite Z2Nat_neg by omega. reflexivity.
  apply pred_ext; apply rangespec_shift_derives; intros.
  rewrite H4; rewrite Z2Nat.id in H3 by omega.
  rewrite H1; auto; omega.
  rewrite <- H4; rewrite Z2Nat.id in H3 by omega.
  rewrite H1; auto; omega.
Qed.

Lemma array_pred_ext_derives: forall {A B} (dA: Inhabitant A) (dB: Inhabitant B)
         lo hi P0 P1 (v0: list A) v1 p,
  (Zlength v0 = hi - lo -> Zlength v1 = hi - lo) ->
  (forall i, lo <= i < hi ->
    P0 i (Znth (i-lo) v0) p |-- P1 i (Znth (i-lo) v1) p) ->
  array_pred  lo hi P0 v0 p |-- array_pred lo hi P1 v1 p.
Proof.
  intros.
  unfold array_pred.
  normalize.
  rewrite prop_true_andp by omega.
  apply rangespec_ext_derives.
  intros.
  destruct (zlt hi lo).
  + rewrite Z2Nat_neg  in H2 by omega.
    change (Z.of_nat 0) with 0 in H2. omega.
  + rewrite Z2Nat.id in H2 by omega.
    apply H0. omega.
Qed.

Lemma array_pred_ext: forall {A B} (dA: Inhabitant A) (dB: Inhabitant B) lo hi P0 P1
        (v0: list A) (v1: list B) p,
  Zlength v0 = Zlength v1 ->
  (forall i, lo <= i < hi ->
    P0 i (Znth (i-lo) v0) p = P1 i (Znth (i-lo) v1) p) ->
  array_pred lo hi P0 v0 p = array_pred lo hi P1 v1 p.
Proof.
  intros; apply pred_ext; apply array_pred_ext_derives; intros; try omega;
  rewrite H0; auto.
Qed.

Lemma at_offset_array_pred: forall  {A} {d: Inhabitant A} lo hi P v ofs p,
  at_offset (array_pred lo hi P v) ofs p = array_pred lo hi (fun i v => at_offset (P i v) ofs) v p.
Proof.
  intros.
  rewrite at_offset_eq.
  unfold array_pred.
  f_equal.
  apply rangespec_shift.
  intros.
  assert (i = i') by omega; subst i'; clear H0.
  rewrite at_offset_eq.
  auto.
Qed.

Lemma array_pred_sepcon: forall  {A} {d: Inhabitant A} lo hi P Q v p,
  array_pred lo hi P v p * array_pred lo hi Q v p = array_pred lo hi (P * Q) v p.
Proof.
  intros.
  unfold array_pred.
  normalize.
  apply andp_prop_ext; [omega | intros].
  rewrite rangespec_sepcon.
  auto.
Qed.

Opaque member_dec.

Lemma struct_pred_ext_derives: forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall i d0 d1, in_members i m ->
     P0 _ (proj_struct i m v0 d0) p |-- P1 _ (proj_struct i m v1 d1) p) ->
  struct_pred m P0 v0 p |-- struct_pred m P1 v1 p.
Proof.
  unfold proj_struct, field_type.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  revert i0 t0 v0 v1 H H0; induction m as [| (i1, t1) m]; intros.
  + specialize (H0 i0).
    simpl in H0.
    if_tac in H0; [| congruence].
    specialize (H0 v0 v1).
    spec H0; [left; reflexivity |].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r in H0; rewrite <- !eq_rect_eq in H0.
    simpl.
    exact H0.
  + change (struct_pred ((i0, t0) :: (i1, t1) :: m) P0 v0 p) with
      (P0 (i0, t0) (fst v0) p * struct_pred ((i1, t1) :: m) P0 (snd v0) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P1 v1 p) with
      (P1 (i0, t0) (fst v1) p * struct_pred ((i1, t1) :: m) P1 (snd v1) p).
    apply sepcon_derives.
    - specialize (H0 i0).
      simpl in H0.
      if_tac in H0; [| congruence].
      specialize (H0 (fst v0) (fst v1)).
      spec H0; [left; reflexivity |].
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r in H0; rewrite <- !eq_rect_eq in H0.
      simpl.
      exact H0.
    - rewrite members_no_replicate_ind in H.
      apply IHm; [tauto |].
      intros.
      specialize (H0 i).
      simpl in H0.
      if_tac in H0.
      * clear - H H1 H2.
        subst.
        tauto.
      * specialize (H0 d0 d1).
        spec H0; [right; auto |].
        change (if ident_eq i i1
                then Errors.OK t1
                else Ctypes.field_type i m) with (Ctypes.field_type i ((i1, t1) :: m)) in H0.
        destruct (member_dec
             (i,
             match Ctypes.field_type i ((i1, t1) :: m) with
             | Errors.OK t => t
             | Errors.Error _ => Tvoid
             end) (i0, t0)); [congruence |].
        exact H0.
Qed.

Lemma struct_pred_ext: forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall i d0 d1, in_members i m ->
     P0 _ (proj_struct i m v0 d0) p = P1 _ (proj_struct i m v1 d1) p) ->
  struct_pred m P0 v0 p = struct_pred m P1 v1 p.
Proof.
  intros.
  apply pred_ext; eapply struct_pred_ext_derives; eauto;
  intros; erewrite H0 by eauto; auto;      apply derives_refl.
Qed.

Lemma struct_pred_not_member: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p,
  let P' it := if ident_eq i (fst it) then fun _ _ => emp else P it in
  ~ in_members i m ->
  struct_pred m P v p = struct_pred m P' v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [auto |].
  unfold proj_struct, field_type in *.
  revert i0 t0 H v; induction m as [| (i1, t1) m]; intros i0 t0 H.
  + intros; subst P'; simpl.
    rewrite if_false; auto.
    intro; apply H; subst; left; auto.
  + set (M := (i1, t1) :: m).
    simpl compact_prod; simpl Ctypes.field_type.
    intros v.
    subst M.
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P _ (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P' v p)
      with (P' _ (fst v) p * struct_pred ((i1, t1) :: m) P' (snd v) p).
    destruct (ident_eq i i0).
    - subst P'; intros; subst.
      exfalso; apply H.
      left; auto.
    - intros.
      f_equal.
      * unfold P'.
        rewrite if_false by auto.
        auto.
      * apply IHm.
        intro; apply H; right; auto.
Qed.

Lemma struct_pred_proj: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  let P' it := if ident_eq i (fst it) then fun _ _ => emp else P it in
  members_no_replicate m = true ->
  in_members i m ->
  struct_pred m P v p = P _ (proj_struct i m v d) p * struct_pred m P' v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inv H0 |].
  unfold proj_struct, field_type in *.
  revert i0 t0 H v d H0; induction m as [| (i1, t1) m]; intros.
  + subst P'; simpl in *.
    destruct H0; [simpl in H0; subst i | tauto].
    destruct (ident_eq i0 i0); [| congruence].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    rewrite sepcon_emp; auto.
  + pose proof H.
    apply members_no_replicate_ind in H1; destruct H1.
    set (M := (i1, t1) :: m).
    simpl compact_prod in v |- *; simpl Ctypes.field_type in d |- *.
    subst M.
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P _ (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P' v p)
      with (P' _ (fst v) p * struct_pred ((i1, t1) :: m) P' (snd v) p).
    destruct (ident_eq i i0).
    - subst i0.
      f_equal.
      * simpl.
        destruct (member_dec (i, t0) (i, t0)); [| congruence].
        unfold eq_rect_r; rewrite <- eq_rect_eq.
        auto.
      * erewrite struct_pred_not_member by eauto.
        unfold P' at 1.
        rewrite if_true by auto.
        rewrite emp_sepcon; auto.
    - intros.
      destruct H0; [simpl in H0; congruence |].
      rewrite <- sepcon_assoc, (sepcon_comm _ (P' _ _ _)), sepcon_assoc.
      f_equal.
      * unfold P'.
        rewrite if_false by (simpl; congruence).
        auto.
      * erewrite IHm by eauto.
        f_equal.
        simpl.
        match goal with
        | |- context [member_dec (i, ?t') (i0, t0)] =>
                destruct (member_dec (i, t') (i0, t0));
                  [inversion e; congruence |]
        end.
        auto.
Qed.

Lemma struct_pred_upd: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p v0,
  let P' it := if ident_eq i (fst it) then fun _ _ => emp else P it in
  members_no_replicate m = true ->
  in_members i m ->
  struct_pred m P (upd_struct i m v v0) p = P _ v0 p * struct_pred m P' v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inv H0 |].
  unfold proj_struct, field_type in *.
  revert i0 t0 H v v0 H0; induction m as [| (i1, t1) m]; intros.
  + subst P'; simpl in *.
    destruct H0; [simpl in H0; subst i | tauto].
    destruct (ident_eq i0 i0); [| congruence].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    rewrite sepcon_emp; auto.
  + pose proof H.
    apply members_no_replicate_ind in H1; destruct H1.
    simpl compact_prod in v |- *; simpl Ctypes.field_type in v0 |- *.
    set (v' := (upd_struct i ((i0, t0) :: (i1, t1) :: m) v v0)).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v' p)
      with (P _ (fst v') p * struct_pred ((i1, t1) :: m) P (snd v') p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P' v p)
      with (P' _ (fst v) p * struct_pred ((i1, t1) :: m) P' (snd v) p).
    subst v'.
    simpl upd_struct.
    destruct (ident_eq i i0).
    - subst i0.
      destruct (member_dec (i, t0) (i, t0)); [| congruence].
      f_equal.
      * simpl.
        unfold eq_rect_r; rewrite <- eq_rect_eq.
        auto.
      * unfold eq_rect_r; rewrite <- eq_rect_eq.
        change (snd (v0, snd v)) with (snd v).
        erewrite struct_pred_not_member by eauto.
        unfold P' at 1.
        rewrite if_true by auto.
        rewrite emp_sepcon; auto.
    - destruct H0; [simpl in H0; congruence |].
      rewrite <- sepcon_assoc, (sepcon_comm _ (P' _ _ _)), sepcon_assoc.
      match goal with
      | |- context [member_dec (i, ?t') (i0, t0)] =>
              destruct (member_dec (i, t') (i0, t0));
                [inversion e; congruence |]
      end.
      f_equal.
      * unfold P'; simpl.
        rewrite if_false by (simpl; congruence).
        auto.
      * simpl snd.
        simpl in IHm |- *; erewrite IHm by auto.
        reflexivity.
Qed.

Lemma struct_pred_ramif: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  in_members i m ->
  members_no_replicate m = true ->
  struct_pred m P v p |--
    P _ (proj_struct i m v d) p *
     (ALL v0: _, P _ v0 p -* struct_pred m P (upd_struct i m v v0) p).
Proof.
  intros.
  set (P' it := if ident_eq i (fst it) then fun _ _ => emp else P it).
  apply RAMIF_Q.solve with (struct_pred m P' v p).
  + apply derives_refl'.
    apply struct_pred_proj; auto.
  + intro v0.
    apply derives_refl'.
    symmetry; rewrite sepcon_comm.
    apply struct_pred_upd; auto.
Qed.

Lemma at_offset_struct_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p ofs,
  at_offset (struct_pred m P v) ofs p = struct_pred m (fun it v => at_offset (P it v) ofs) v p.
Proof.
  intros.
  rewrite at_offset_eq.
  destruct m as [| (i0, t0) m]; [auto |].
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl.
    rewrite at_offset_eq.
    auto.
  + simpl.
    rewrite at_offset_eq.
    f_equal.
Qed.

Lemma corable_andp_struct_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p Q,
  corable Q ->
  Q && struct_pred m P v p =
  match m with
  | nil => Q && emp
  | _ => struct_pred m (fun it v p => Q && P it v p) v p
  end.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [auto |].
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl.
    auto.
  + change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    pattern Q at 1; rewrite <- (andp_dup Q).
    rewrite andp_assoc.
    rewrite <- corable_sepcon_andp1 by auto.
    rewrite IHm.
    rewrite <- corable_andp_sepcon1 by auto.
    reflexivity.
Qed.

Lemma struct_pred_sepcon: forall m {A} (P Q: forall it, A it -> val -> mpred) v p,
  struct_pred m P v p * struct_pred m Q v p = struct_pred m (fun it => P it * Q it) v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [| revert i0 t0 v; induction m as [| (i1, t1) m]; intros].
  + simpl.
    rewrite emp_sepcon; auto.
  + simpl.
    auto.
  + change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) Q v p)
      with (Q (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) Q (snd v) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) (fun it => P it * Q it) v p)
      with (P (i0, t0) (fst v) p * Q (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) (fun it => P it * Q it) (snd v) p).
    rewrite !sepcon_assoc; f_equal.
    rewrite <- sepcon_assoc, (sepcon_comm _ (Q _ _ _)), sepcon_assoc; f_equal.
    apply IHm.
Qed.

Lemma compact_sum_inj_eq_spec: forall {A} a0 a1 (l: list A) F0 F1 (v0: compact_sum (map F0 (a0 :: a1 :: l))) (v1: compact_sum (map F1 (a0 :: a1 :: l))) H,
  ~ In a0 (a1 :: l) ->
 ((forall a, compact_sum_inj v0 a H <-> compact_sum_inj v1 a H) <->
  match v0, v1 with
  | inl _, inl _ => True
  | inr v0, inr v1 => forall a, compact_sum_inj (v0: compact_sum (map F0 (a1 :: l))) a H <-> compact_sum_inj (v1: compact_sum (map F1 (a1 :: l))) a H
  | _, _ => False
  end).
Proof.
  intros.
  rename H0 into H_not_in.
  destruct v0, v1.
  + simpl.
    firstorder.
  + assert (~ (forall a : A,
      iff
        (@compact_sum_inj A F0 (@cons A a0 (@cons A a1 l))
           (@inl (F0 a0) (compact_sum (@map A Type F0 (@cons A a1 l))) f) a H)
        (@compact_sum_inj A F1 (@cons A a0 (@cons A a1 l))
           (@inr (F1 a0) (compact_sum (@map A Type F1 (@cons A a1 l))) c) a H))); [| tauto].
    intro.
    specialize (H0 a0).
    simpl in H0.
    destruct (H a0 a0); [| congruence].
    tauto.
  + assert (~ (forall a : A,
      iff
        (@compact_sum_inj A F0 (@cons A a0 (@cons A a1 l))
           (@inr (F0 a0) (compact_sum (@map A Type F0 (@cons A a1 l))) c) a H)
        (@compact_sum_inj A F1 (@cons A a0 (@cons A a1 l))
           (@inl (F1 a0) (compact_sum (@map A Type F1 (@cons A a1 l))) f) a H))); [| tauto].
    intro.
    specialize (H0 a0).
    simpl in H0.
    destruct (H a0 a0); [| congruence].
    tauto.
  + split; intros HH a; specialize (HH a).
    - pose proof compact_sum_inj_in c a H.
      pose proof compact_sum_inj_in c0 a H.
Opaque In.
      simpl in HH, H0, H1 |- *.
Transparent In.
      destruct (H a a0).
      * subst.
        tauto.
      * tauto.
    - simpl in HH |- *.
      destruct (H a a0).
      * tauto.
      * tauto.
Qed.

Lemma union_pred_ext_derives: forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall it, members_union_inj v0 it <-> members_union_inj v1 it) ->
  (forall i d0 d1, members_union_inj v0 (i, field_type i m) -> members_union_inj v1 (i, field_type i m) ->
     P0 _ (proj_union i m v0 d0) p |-- P1 _ (proj_union i m v1 d1) p) ->
  union_pred m P0 v0 p |-- union_pred m P1 v1 p.
Proof.
  unfold members_union_inj, proj_union, field_type.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  revert i0 t0 v0 v1 H H0 H1; induction m as [| (i1, t1) m]; intros.
  + specialize (H1 i0).
    simpl in H1.
    if_tac in H1; [| congruence].
    specialize (H1 v0 v1).
    spec H1; [if_tac; [auto | congruence] |].
    spec H1; [if_tac; [auto | congruence] |].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r in H1; rewrite <- !eq_rect_eq in H1.
    simpl.
    exact H1.
  + rewrite compact_sum_inj_eq_spec in H0.
    2:{
      clear - H.
      pose proof in_members_tail_no_replicate i0 _ _ _ H.
      intro HH; apply in_map with (f := fst) in HH.
      apply H0 in HH.
      tauto.
    }
    destruct v0 as [v0 | v0], v1 as [v1 | v1]; try solve [inversion H0].
    - specialize (H1 i0).
      simpl in H1.
      if_tac in H1; [| congruence].
      specialize (H1 v0 v1).
      spec H1; [if_tac; [auto | congruence] |].
      spec H1; [if_tac; [auto | congruence] |].
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r in H1; rewrite <- !eq_rect_eq in H1.
      simpl.
      exact H1.
    - rewrite members_no_replicate_ind in H.
      apply IHm; [tauto | tauto |].
      intros.
      specialize (H1 i).
      simpl in H1.
      if_tac in H1. (* i = i0 vs i <> i0 *)
      * clear - H H2 H4 t0.
        pose proof compact_sum_inj_in v0 (i, field_type i ((i1, t1) :: m)) member_dec.
        spec H0; [exact H2 |].
        subst.
        apply in_map with (f := fst) in H0.
        unfold fst at 1 in H0.
        tauto.
      * specialize (H1 d0 d1).
        change (if ident_eq i i1
                then Errors.OK t1
                else Ctypes.field_type i m) with (Ctypes.field_type i ((i1, t1) :: m)) in H1.
        destruct (member_dec
             (i,
             match Ctypes.field_type i ((i1, t1) :: m) with
             | Errors.OK t => t
             | Errors.Error _ => Tvoid
             end) (i0, t0)); [congruence |].
        spec H1; [auto |].
        spec H1; [auto |].
        exact H1.
Qed.

Lemma union_pred_ext: forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall it, members_union_inj v0 it <-> members_union_inj v1 it) ->
  (forall i d0 d1, members_union_inj v0 (i, field_type i m) -> members_union_inj v1 (i, field_type i m) ->
     P0 _ (proj_union i m v0 d0) p = P1 _ (proj_union i m v1 d1) p) ->
  union_pred m P0 v0 p = union_pred m P1 v1 p.
Proof.
  intros.
  assert (forall it, members_union_inj v1 it <-> members_union_inj v0 it)
    by (intro it; specialize (H0 it); tauto).
  apply pred_ext; eapply union_pred_ext_derives; auto;
  intros; erewrite H1 by eauto; apply derives_refl.
Qed.

Lemma union_pred_derives_const: forall m {A} (P: forall it, A it -> val -> mpred) p v R,
  members_no_replicate m = true ->
  m <> nil ->
  (forall i (v: A (i, field_type i m)), in_members i m -> P _ v p |-- R) ->
  union_pred m P v p |-- R.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [congruence |].
  clear H0.
  revert i0 t0 v H H1; induction m as [| (i1, t1) m]; intros.
  + simpl.
    specialize (H1 i0); simpl in H1.
    destruct (ident_eq i0 i0); [| congruence].
    apply H1; left; auto.
  + destruct v; simpl.
    - specialize (H1 i0); simpl in H1.
      destruct (ident_eq i0 i0); [| congruence].
      apply H1; left; auto.
    - pose proof H.
      rewrite members_no_replicate_ind in H; destruct H.
      apply (IHm i1 t1); auto.
      intros.
      specialize (H1 i).
      pose proof in_members_tail_no_replicate _ _ _ _ H0 H3.
      simpl in H1; destruct (ident_eq i i0); [congruence |].
      apply H1.
      right; auto.
Qed.

Lemma union_pred_proj: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  members_no_replicate m = true ->
  in_members i m ->
  (forall i' (v': A (i', field_type i' m)), in_members i' m -> P _ v' p |-- P _ d p) ->
  union_pred m P v p |-- P _ (proj_union i m v d) p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inv H0 |].
  revert i0 t0 v d H H0 H1; induction m as [| (i1, t1) m]; intros.
  + destruct H0; [simpl in H0; subst i | tauto].
    simpl in *.
    destruct (ident_eq i0 i0); [| congruence].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    auto.
  + pose proof H.
    rewrite members_no_replicate_ind in H; destruct H.
    simpl in *; destruct (ident_eq i i0), v.
    - subst i.
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      auto.
    - subst i.
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      apply (union_pred_derives_const ((i1, t1) :: m) P p y); [auto | congruence |].
      intros.
      specialize (H1 i).
      pose proof in_members_tail_no_replicate _ _ _ _ H2 H4.
      simpl in H1; destruct (ident_eq i i0); [congruence |].
      apply H1.
      right; auto.
    - match goal with
      | |- context [member_dec (i, ?t') (i0, t0)] =>
              destruct (member_dec (i, t') (i0, t0));
                [inversion e; congruence |]
      end.
      specialize (H1 i0).
      destruct (ident_eq i0 i0); [| congruence].
      apply H1.
      left; auto.
    - match goal with
      | |- context [member_dec (i, ?t') (i0, t0)] =>
              destruct (member_dec (i, t') (i0, t0));
                [inversion e; congruence |]
      end.
      apply (IHm i1 t1); auto.
      * destruct H0; [| auto].
        simpl in H0; congruence.
      * intros.
        specialize (H1 i').
        pose proof in_members_tail_no_replicate _ _ _ _ H2 H4.
        simpl in H1; destruct (ident_eq i' i0); [congruence |].
        apply H1.
        right; auto.
Qed.

Lemma union_pred_upd: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p v0,
  members_no_replicate m = true ->
  in_members i m ->
  union_pred m P (upd_union i m v v0) p = P _ v0 p.
Proof.
  intros.
  intros.
  unfold upd_union, upd_compact_sum.
  destruct (in_dec member_dec (i, field_type i m) m) as [?H | ?H];
    [| apply in_members_field_type in H0; tauto].
  clear v.
  destruct m as [| (i0, t0) m]; [inv H0 |].
  revert i0 t0 v0 H H0 H1; induction m as [| (i1, t1) m]; intros.
  + simpl in *.
    destruct H0; [simpl in H0; subst i | tauto].
    destruct (ident_eq i0 i0); [| congruence].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    auto.
  + destruct H0; [simpl in H0; subst i |].
    - simpl in *.
      destruct (ident_eq i0 i0); [| congruence].
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      auto.
    - simpl in *.
      pose proof in_members_tail_no_replicate _ _ _ _ H H0.
      destruct (ident_eq i i0); [congruence |].
      match goal with
      | |- context [member_dec (i, ?t') (i0, t0)] =>
              destruct (member_dec (i, t') (i0, t0));
                [inversion e; congruence |]
      end.
      rewrite members_no_replicate_ind in H; destruct H.
      apply (IHm i1 t1); auto.
Qed.

Lemma union_pred_ramif: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  (forall i' (v': A (i', field_type i' m)), in_members i' m -> P _ v' p |-- P _ d p) ->
  in_members i m ->
  members_no_replicate m = true ->
  union_pred m P v p |--
    P _ (proj_union i m v d) p *
     (ALL v0: _, P _ v0 p -* union_pred m P (upd_union i m v v0) p).
Proof.
  intros.
  apply RAMIF_Q.solve with emp.
  + rewrite sepcon_emp.
    apply union_pred_proj; auto.
  + intro v0.
    rewrite emp_sepcon.
    apply derives_refl'.
    symmetry.
    apply union_pred_upd; auto.
Qed.

Lemma at_offset_union_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p ofs,
  at_offset (union_pred m P v) ofs p = union_pred m (fun it v => at_offset (P it v) ofs) v p.
Proof.
  intros.
  rewrite at_offset_eq.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl.
    rewrite at_offset_eq.
    auto.
  + simpl.
    destruct v.
    - rewrite at_offset_eq.
      auto.
    - apply IHm.
Qed.

Lemma andp_union_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p Q,
  Q && union_pred m P v p =
  match m with
  | nil => Q && emp
  | _ => union_pred m (fun it v p => Q && P it v p) v p
  end.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [auto |].
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl.
    auto.
  + destruct v.
    - simpl.
      auto.
    - simpl.
      apply IHm.
Qed.

Lemma union_pred_sepcon: forall m {A} (P Q: forall it, A it -> val -> mpred) v p,
  union_pred m P v p * union_pred m Q v p = union_pred m (fun it => P it * Q it) v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [| revert i0 t0 v; induction m as [| (i1, t1) m]; intros].
  + simpl.
    rewrite sepcon_emp; auto.
  + simpl.
    auto.
  + destruct v.
    - simpl; auto.
    - apply IHm.
Qed.

Lemma struct_Prop_compact_prod_gen: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) (f: forall it, F it),
  members_no_replicate m = true ->
  (forall i, in_members i m -> P (i, field_type i m) (f (i, field_type i m))) ->
  struct_Prop m P (compact_prod_gen f m).
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  revert i0 t0 H H0; induction m as [| (i1, t1) m]; intros.
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
  + change (struct_Prop ((i0, t0) :: (i1, t1) :: m) P
             (compact_prod_gen f ((i0, t0) :: (i1, t1) :: m)))
    with (P (i0, t0) (f (i0, t0)) /\
            struct_Prop ((i1, t1) :: m) P (compact_prod_gen f ((i1, t1) :: m))).
    split.
    - specialize (H0 i0).
      simpl in H0.
      rewrite if_true in H0 by auto.
      apply H0; left; auto.
    - rewrite members_no_replicate_ind in H; destruct H.
      apply (IHm i1 t1); auto.
      intros.
      specialize (H0 i).
      simpl in H0.
      destruct (ident_eq i i0); [subst; tauto |].
      apply H0; right; auto.
Qed.

Lemma struct_Prop_proj: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) v i d,
  in_members i m ->
  struct_Prop m P v ->
  P (i, field_type i m) (proj_struct i m v d).
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inversion H |].
  revert i0 t0 v d H H0; induction m as [| (i1, t1) m]; intros.
  + inversion H; [simpl in H0; subst| tauto].
    simpl in *.
    destruct (ident_eq i0 i0); [| tauto].
    destruct (member_dec (i0, t0) (i0, t0)); [| tauto].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    auto.
  + destruct (ident_eq i i0).
    - subst.
      simpl in *.
      destruct (ident_eq i0 i0); [| tauto].
      destruct (member_dec (i0, t0) (i0, t0)); [| tauto].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      exact (proj1 H0).
    - assert (in_members i ((i1, t1) :: m)) by (inversion H; [subst; tauto | auto]).
      simpl in *.
      destruct (ident_eq i i0); [tauto |].
      destruct (member_dec
         (i,
         match
           (if ident_eq i i1 then Errors.OK t1 else Ctypes.field_type i m)
         with
         | Errors.OK t => t
         | Errors.Error _ => Tvoid
         end) (i0, t0)); [inversion e; subst; tauto |].
      apply IHm; auto.
      exact (proj2 H0).
Qed.

Lemma union_Prop_compact_sum_gen: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) (f: forall it, F it),
  members_no_replicate m = true ->
  (forall i, in_members i m -> P (i, field_type i m) (f (i, field_type i m))) ->
  union_Prop m P (compact_sum_gen (fun _ => true) f m).
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  destruct m as [| (i1, t1) m].
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
Qed.

Lemma union_Prop_proj: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) v i d,
  members_union_inj v (i, field_type i m) ->
  union_Prop m P v ->
  P (i, field_type i m) (proj_union i m v d).
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inversion H |].
  revert i0 t0 v d H H0; induction m as [| (i1, t1) m]; intros.
  + simpl in H. if_tac in H; [| tauto].
    inversion H1; subst i.
    clear H1 H4.
    simpl in *.
    destruct (ident_eq i0 i0); [| tauto].
    destruct (member_dec (i0, t0) (i0, t0)); [| tauto].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    auto.
  + destruct (ident_eq i i0).
    - subst.
      simpl in *.
      destruct (ident_eq i0 i0); [| tauto].
      destruct (member_dec (i0, t0) (i0, t0)); [| tauto].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      destruct v; [| inversion H].
      auto.
    - assert (members_union_inj v (i, field_type i ((i1, t1) :: m))) by (simpl in H; destruct (ident_eq i i0); [tauto | auto]).
      simpl in *.
      destruct (ident_eq i i0); [tauto |].
      destruct (member_dec
         (i,
         match
           (if ident_eq i i1 then Errors.OK t1 else Ctypes.field_type i m)
         with
         | Errors.OK t => t
         | Errors.Error _ => Tvoid
         end) (i0, t0)); [inversion e; subst; tauto |].
      destruct v; [tauto |].
      apply IHm; auto.
Qed.

Lemma array_pred_local_facts: forall {A}{d: Inhabitant A} lo hi P v p Q,
  (forall i x, lo <= i < hi -> P i x p |-- !! Q x) ->
  array_pred lo hi P v p |-- !! (Zlength v = hi - lo /\ Forall Q v).
Proof.
  intros.
  unfold array_pred.
  normalize.
  rewrite prop_and; apply andp_right; [normalize |].
  pose proof ZtoNat_Zlength v.
  rewrite H0 in H1; symmetry in H1; clear H0.
  revert hi lo H H1; induction v; intros.
  + apply prop_right; constructor.
  + replace (hi - lo) with (Z.succ (hi - Z.succ lo)) in * by omega.
    assert (hi - Z.succ lo >= 0).
    {
      destruct (zlt (hi - Z.succ lo) 0); auto.
      assert (Z.succ (hi - Z.succ lo) <= 0) by omega.
      simpl length in H1.
      destruct (zeq (Z.succ (hi - Z.succ lo)) 0);
       [rewrite e in H1 | rewrite Z2Nat_neg in H1 by omega]; inv H1.
    }
    rewrite Z2Nat.inj_succ in H1 |- * by omega.
    inv H1.
    simpl rangespec.
    replace (rangespec (Z.succ lo) (length v)
              (fun i : Z => P i (Znth (i - lo) (a :: v))) p)
    with (rangespec (Z.succ lo) (length v)
            (fun i : Z => P i (Znth (i - Z.succ lo) v)) p).
    2:{
      apply rangespec_ext; intros.
      change v with (skipn 1 (a :: v)) at 1.
      rewrite <- Znth_succ by omega.
      auto.
    }
    rewrite H3.
    eapply derives_trans; [apply sepcon_derives; [apply H | apply IHv; auto] |].
    - omega.
    - intros; apply H; omega.
    - rewrite sepcon_prop_prop.
      apply prop_derives; intros.
      rewrite Z.sub_diag in H1; cbv in H1.
      constructor; tauto.
Qed.

Lemma struct_pred_local_facts: forall m {A} (P: forall it, A it -> val -> mpred)v p (R: forall it, A it -> Prop),
  members_no_replicate m = true ->
  (forall i v0, in_members i m -> P (i, field_type i m) v0 p |-- !! R _ v0) ->
  struct_pred m P v p |-- !! struct_Prop m R v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; apply prop_right; auto |].
  revert i0 t0 v H H0; induction m as [| (i1, t1) m]; intros.
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
  + change (struct_Prop ((i0, t0) :: (i1, t1) :: m) R v)
      with (R (i0, t0) (fst v) /\ struct_Prop ((i1, t1) :: m) R (snd v)).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    rewrite members_no_replicate_ind in H.

    pose proof H0 i0.
    simpl in H1.
    if_tac in H1; [| congruence].
    specialize (H1 (fst v)).
    spec H1; [left; auto |].

    specialize (IHm i1 t1 (snd v)).
    spec IHm; [tauto |].
    eapply derives_trans; [apply sepcon_derives; [apply H1 | apply IHm] |].
    - intros.
      specialize (H0 i).
      simpl in H0.
      destruct (ident_eq i i0); [subst; tauto |].
      apply H0; right; auto.
    - rewrite sepcon_prop_prop; normalize.
Qed.

Lemma union_pred_local_facts: forall m {A} (P: forall it, A it -> val -> mpred)v p (R: forall it, A it -> Prop),
  members_no_replicate m = true ->
  (forall i v0, in_members i m -> P (i, field_type i m) v0 p |-- !! R _ v0) ->
  union_pred m P v p |-- !! union_Prop m R v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; apply prop_right; auto |].
  revert i0 t0 v H H0; induction m as [| (i1, t1) m]; intros.
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
  + rewrite members_no_replicate_ind in H.
    destruct v.
    - simpl.
      pose proof H0 i0.
      simpl in H1.
      if_tac in H1; [| congruence].
      specialize (H1 a).
      apply H1; left; auto.
    - specialize (IHm i1 t1 c).
      spec IHm; [tauto |].
      apply IHm.
      intros.
      specialize (H0 i).
      simpl in H0.
      destruct (ident_eq i i0); [subst; tauto |].
      apply H0; right; auto.
Qed.

Section MEMORY_BLOCK_AGGREGATE.

Context {cs: compspecs}.

Lemma memory_block_array_pred: forall  {A}{d: Inhabitant A} sh t lo hi v b ofs,
  0 <= ofs + sizeof t * lo /\ ofs + sizeof t * hi < Ptrofs.modulus ->
  0 <= lo <= hi ->
  Zlength v = hi - lo ->
  array_pred lo hi
    (fun i _ p => memory_block sh (sizeof t) (offset_val (sizeof t * i) p)) v
    (Vptr b (Ptrofs.repr ofs)) =
   memory_block sh (sizeof t * (hi - lo)) (Vptr b (Ptrofs.repr (ofs + sizeof t * lo))).
Proof.
  intros.
  unfold array_pred.
  rewrite prop_true_andp by auto; clear H1.
  f_equal.
  remember (Z.to_nat (hi - lo)) as n eqn:HH.
  revert lo HH H H0 v; induction n; intros.
  + simpl.
    pose proof arith_aux00 _ _ (proj2 H0) HH.
    rewrite H1, Z.mul_0_r, memory_block_zero_Vptr.
    reflexivity.
  + simpl.
    pose proof arith_aux01 _ _ _ HH.
    solve_mod_modulus.
    pose_size_mult cenv_cs t (0 :: hi - Z.succ lo :: hi - lo :: nil).
    rewrite IHn; [| apply arith_aux02; auto | omega | omega | exact v].
    replace (ofs + sizeof  t * Z.succ lo) with (ofs + sizeof t * lo + sizeof t) by omega.
    rewrite <- memory_block_split by (auto; omega).
    f_equal.
    omega.
Qed.

Lemma memory_block_array_pred': forall {A}{d: Inhabitant A} (a: A) sh t z b ofs,
  0 <= z ->
  0 <= ofs /\ ofs + sizeof t * z < Ptrofs.modulus ->
  array_pred 0 z
     (fun i _ p =>
      memory_block sh (sizeof t) (offset_val (sizeof t * i) p))
             (list_repeat (Z.to_nat z) a)
     (Vptr b (Ptrofs.repr ofs))  =
  memory_block sh (sizeof t * z) (Vptr b (Ptrofs.repr ofs)).
Proof.
  intros.
  rewrite memory_block_array_pred.
  f_equal. f_equal. omega. f_equal. f_equal. rewrite Z.mul_0_r. omega.
  rewrite Z.mul_0_r. split; omega. omega.
  rewrite Z.sub_0_r. auto. rewrite Zlength_list_repeat', Z2Nat.id by omega.
  omega.
Qed.

Lemma memory_block_struct_pred: forall sh m sz {A} (v: compact_prod (map A m)) b ofs,
  (m = nil -> sz = 0) ->
  members_no_replicate m = true ->
  sizeof_struct cenv_cs 0 m <= sz < Ptrofs.modulus ->
  0 <= ofs /\ ofs + sz < Ptrofs.modulus ->
  struct_pred m
   (fun it _ p =>
     (memory_block sh (field_offset_next cenv_cs (fst it) m sz - field_offset cenv_cs (fst it) m))
     (offset_val (field_offset cenv_cs (fst it) m) p)) v (Vptr b (Ptrofs.repr ofs)) =
  memory_block sh sz (Vptr b (Ptrofs.repr ofs)).
Proof.
  unfold field_offset, Ctypes.field_offset, field_offset_next.
  intros sh m sz A v b ofs NIL_CASE NO_REPLI; intros.
  destruct m as [| (i0, t0) m].
  1: rewrite (NIL_CASE eq_refl), memory_block_zero; simpl; normalize.
  assert (align 0 (alignof t0) = 0) by apply align_0, alignof_pos.
  revert H0; pattern ofs at 1 4; replace ofs with (ofs + align 0 (alignof t0)) by omega; intros.
  revert H; pattern sz at 2 4; replace sz with (sz - align 0 (alignof t0)) by omega; intros.
  pattern 0 at 1; rewrite <- H1.
  clear NIL_CASE H1.
  revert H H0; generalize 0 at 1 2 4 5 6 8 10 11; revert i0 t0 v NO_REPLI;
  induction m as [| (i1, t1) m]; intros.
  + simpl.
    if_tac; [| congruence].
    solve_mod_modulus.
    reflexivity.
  + match goal with
    | |- struct_pred ((i0, t0) :: (i1, t1) :: m) ?P v ?p = _ =>
           change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p) with
             (P (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p);
           simpl (P (i0, t0) (fst v) p)
    end.
    if_tac; [| congruence].
    solve_mod_modulus.
    erewrite struct_pred_ext.
    - rewrite members_no_replicate_ind in NO_REPLI; destruct NO_REPLI as [NOT_IN NO_REPLI].
      rewrite IHm with (z := align z (alignof t0) + sizeof t0);
        [| now auto
         | simpl in H |- *; pose_align_le; pose_sizeof_pos; omega
         | pose_align_le; pose_sizeof_pos; omega].
      replace (ofs + align (align z (alignof t0) + sizeof t0) (alignof t1)) with
        (ofs + align z (alignof t0) +
         (align (align z (alignof t0) + sizeof t0) (alignof t1) -
          align z (alignof t0))) by omega.
      rewrite <- memory_block_split by
        (simpl in H; revert H; pose_align_le; pose_sizeof_pos; intros; omega).
      f_equal; omega.
    - rewrite members_no_replicate_ind in NO_REPLI; destruct NO_REPLI as [NOT_IN NO_REPLI].
      auto.
    - intros. instantiate (1 := (snd v)).
      solve_mod_modulus.
      unfold fst.
      pose proof in_members_tail_no_replicate _ _ _ _ NO_REPLI H2.
      rewrite (neq_field_offset_rec_cons cenv_cs i i0 t0) by auto.
      rewrite (neq_field_offset_next_rec_cons cenv_cs i i0 t0) by auto.
      reflexivity.
Qed.

Lemma memory_block_union_pred: forall sh m sz {A} (v: compact_sum (map A m)) b ofs,
  (m = nil -> sz = 0) ->
  union_pred m (fun it _ => memory_block sh sz) v (Vptr b (Ptrofs.repr ofs)) =
  memory_block sh sz (Vptr b (Ptrofs.repr ofs)).
Proof.
  intros sh m sz A v b ofs NIL_CASE; intros.
  destruct m as [| (i0, t0) m].
  1: rewrite (NIL_CASE eq_refl), memory_block_zero; simpl; normalize.
  clear NIL_CASE.
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl; auto.
  + destruct v.
    - simpl; auto.
    - apply IHm.
Qed.

End MEMORY_BLOCK_AGGREGATE.

Module aggregate_pred.

Open Scope Z.
Open Scope logic.

Definition array_pred: forall {A: Type}{d: Inhabitant A} (lo hi: Z) (P: Z -> A -> val -> mpred) (v: list A),
    val -> mpred := @array_pred.

Definition struct_pred: forall (m: members) {A: ident * type -> Type} (P: forall it, A it -> val -> mpred) (v: compact_prod (map A m)) (p: val), mpred := @struct_pred.

Definition union_pred: forall (m: members) {A: ident * type -> Type} (P: forall it, A it -> val -> mpred) (v: compact_sum (map A m)) (p: val), mpred := @union_pred.

Definition array_Prop: forall {A: Type} (d:A) (lo hi: Z) (P: Z -> A -> Prop) (v: list A), Prop := @array_Prop.

Definition struct_Prop: forall (m: members) {A: ident * type -> Type} (P: forall it, A it -> Prop) (v: compact_prod (map A m)), Prop := @struct_Prop.

Definition union_Prop: forall (m: members) {A: ident * type -> Type} (P: forall it, A it -> Prop) (v: compact_sum (map A m)), Prop := union_Prop.

Definition array_pred_len_0: forall {A}{d: Inhabitant A} lo hi P p,
  hi = lo ->
  array_pred lo hi P nil p = emp
:= @array_pred_len_0.

Definition array_pred_len_1: forall {A}{d: Inhabitant A} i P v p,
  array_pred i (i + 1) P (v :: nil) p =  P i v p
:= @array_pred_len_1.

Definition split_array_pred: forall  {A}{d: Inhabitant A} lo mid hi P v p,
  lo <= mid <= hi ->
  Zlength v = (hi-lo) ->
  array_pred lo hi P v p =
  array_pred lo mid P (sublist 0 (mid-lo) v) p *
  array_pred mid hi P (sublist (mid-lo) (hi-lo) v) p
:= @split_array_pred.

Definition array_pred_shift: forall {A} {d: Inhabitant A} lo hi lo' hi' mv
              P' P v p,
  lo - lo' = mv ->
  hi - hi' = mv ->
  (forall i i', lo <= i < hi -> i - i' = mv ->
           P' i' (Znth (i - lo) v) p = P i (Znth (i - lo) v) p) ->
  array_pred lo' hi' P' v p = array_pred lo hi P v p
:= @array_pred_shift.

Definition array_pred_ext_derives:
  forall {A B} {dA: Inhabitant A} {dB: Inhabitant B} lo hi P0 P1
            (v0: list A) (v1: list B) p,
  (Zlength v0 = hi - lo -> Zlength v1 = hi - lo) ->
  (forall i, lo <= i < hi ->
      P0 i (Znth (i-lo) v0) p |-- P1 i (Znth (i-lo) v1) p) ->
  array_pred lo hi P0 v0 p |-- array_pred lo hi P1 v1 p
:= @array_pred_ext_derives.

Definition array_pred_ext:
  forall {A B} {dA: Inhabitant A} {dB: Inhabitant B} lo hi P0 P1 (v0: list A) (v1: list B)  p,
  Zlength v0 = Zlength v1 ->
  (forall i, lo <= i < hi ->
     P0 i (Znth (i - lo) v0) p = P1 i (Znth (i - lo) v1) p) ->
  array_pred lo hi P0 v0 p = array_pred lo hi P1 v1 p
:= @array_pred_ext.

Definition at_offset_array_pred: forall {A} {d: Inhabitant A} lo hi P v ofs p,
  at_offset (array_pred lo hi P v) ofs p = array_pred lo hi (fun i v => at_offset (P i v) ofs) v p
:= @at_offset_array_pred.

Definition array_pred_sepcon: forall  {A} {d: Inhabitant A} lo hi P Q v p,
  array_pred lo hi P v p * array_pred lo hi Q v p = array_pred lo hi (P * Q) v p
:= @array_pred_sepcon.

Definition struct_pred_ramif: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  in_members i m ->
  members_no_replicate m = true ->
  struct_pred m P v p |--
    P _ (proj_struct i m v d) p *
     allp ((fun v0: _ => P _ v0 p) -* (fun v0: _ => struct_pred m P (upd_struct i m v v0) p))
:= @struct_pred_ramif.

Definition struct_pred_ext_derives:
  forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall i d0 d1, in_members i m ->
     P0 _ (proj_struct i m v0 d0) p |-- P1 _ (proj_struct i m v1 d1) p) ->
  struct_pred m P0 v0 p |-- struct_pred m P1 v1 p
:= @struct_pred_ext_derives.

Definition struct_pred_ext:
  forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall i d0 d1, in_members i m ->
     P0 _ (proj_struct i m v0 d0) p = P1 _ (proj_struct i m v1 d1) p) ->
  struct_pred m P0 v0 p = struct_pred m P1 v1 p
:= @struct_pred_ext.

Definition at_offset_struct_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p ofs,
  at_offset (struct_pred m P v) ofs p = struct_pred m (fun it v => at_offset (P it v) ofs) v p
:= @at_offset_struct_pred.

Definition andp_struct_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p Q,
  corable Q ->
  Q && struct_pred m P v p =
  match m with
  | nil => Q && emp
  | _ => struct_pred m (fun it v p => Q && P it v p) v p
  end
:= @corable_andp_struct_pred.

Definition struct_pred_sepcon: forall m {A} (P Q: forall it, A it -> val -> mpred) v p,
  struct_pred m P v p * struct_pred m Q v p = struct_pred m (fun it => P it * Q it) v p
:= @struct_pred_sepcon.

Definition union_pred_ramif: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  (forall i' (v': A (i', field_type i' m)), in_members i' m -> P _ v' p |-- P _ d p) ->
  in_members i m ->
  members_no_replicate m = true ->
  union_pred m P v p |--
    P _ (proj_union i m v d) p *
     allp ((fun v0: _ => P _ v0 p) -* (fun v0 =>union_pred m P (upd_union i m v v0) p))
:= @union_pred_ramif.

Definition union_pred_ext_derives:
  forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall it, members_union_inj v0 it <-> members_union_inj v1 it) ->
  (forall i d0 d1, members_union_inj v0 (i, field_type i m) -> members_union_inj v1 (i, field_type i m) ->
     P0 _ (proj_union i m v0 d0) p |-- P1 _ (proj_union i m v1 d1) p) ->
  union_pred m P0 v0 p |-- union_pred m P1 v1 p
:= @union_pred_ext_derives.

Definition union_pred_ext:
  forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall it, members_union_inj v0 it <-> members_union_inj v1 it) ->
  (forall i d0 d1, members_union_inj v0 (i, field_type i m) -> members_union_inj v1 (i, field_type i m) ->
     P0 _ (proj_union i m v0 d0) p = P1 _ (proj_union i m v1 d1) p) ->
  union_pred m P0 v0 p = union_pred m P1 v1 p
:= @union_pred_ext.

Definition at_offset_union_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p ofs,
  at_offset (union_pred m P v) ofs p = union_pred m (fun it v => at_offset (P it v) ofs) v p
:= at_offset_union_pred.

Definition andp_union_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p Q,
  Q && union_pred m P v p =
  match m with
  | nil => Q && emp
  | _ => union_pred m (fun it v p => Q && P it v p) v p
  end
:= @andp_union_pred.

Definition union_pred_sepcon: forall m {A} (P Q: forall it, A it -> val -> mpred) v p,
  union_pred m P v p * union_pred m Q v p = union_pred m (fun it => P it * Q it) v p
:= @union_pred_sepcon.

Definition struct_Prop_compact_prod_gen: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) (f: forall it, F it),
  members_no_replicate m = true ->
  (forall i, in_members i m -> P (i, field_type i m) (f (i, field_type i m))) ->
  struct_Prop m P (compact_prod_gen f m)
:= @struct_Prop_compact_prod_gen.

Definition struct_Prop_proj: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) v i d,
  in_members i m ->
  struct_Prop m P v ->
  P (i, field_type i m) (proj_struct i m v d)
:= @struct_Prop_proj.

Definition union_Prop_compact_sum_gen: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) (f: forall it, F it),
  members_no_replicate m = true ->
  (forall i, in_members i m -> P (i, field_type i m) (f (i, field_type i m))) ->
  union_Prop m P (compact_sum_gen (fun _ => true) f m)
:= @union_Prop_compact_sum_gen.

Definition union_Prop_proj: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) v i d,
  members_union_inj v (i, field_type i m) ->
  union_Prop m P v ->
  P (i, field_type i m) (proj_union i m v d)
:= @union_Prop_proj.

Definition array_pred_local_facts: forall {A} {d: Inhabitant A} lo hi P v p Q,
  (forall i x, lo <= i < hi -> P i x p |-- !! Q x) ->
  array_pred lo hi P v p |-- !! (Zlength v = hi - lo /\ Forall Q v)
:= @array_pred_local_facts.

Definition struct_pred_local_facts: forall m {A} (P: forall it, A it -> val -> mpred)v p (R: forall it, A it -> Prop),
  members_no_replicate m = true ->
  (forall i v0, in_members i m -> P (i, field_type i m) v0 p |-- !! R _ v0) ->
  struct_pred m P v p |-- !! struct_Prop m R v
:= @struct_pred_local_facts.

Definition union_pred_local_facts: forall m {A} (P: forall it, A it -> val -> mpred)v p (R: forall it, A it -> Prop),
  members_no_replicate m = true ->
  (forall i v0, in_members i m -> P (i, field_type i m) v0 p |-- !! R _ v0) ->
  union_pred m P v p |-- !! union_Prop m R v
:= @union_pred_local_facts.

End aggregate_pred.

Require Import VST.floyd.reptype_lemmas.

(******************************************

Auxiliary predicates

******************************************)

Section AUXILIARY_PRED.

Context {cs: compspecs}.

Variable sh: share.

Definition struct_data_at_rec_aux (m m0: members) (sz: Z) (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> (val -> mpred)) m)) (v: compact_prod (map (fun it => reptype (field_type (fst it) m0)) m)) : (val -> mpred).
Proof.
  destruct m as [| (i0, t0) m]; [exact (fun _ => emp) |].
  revert i0 t0 v P; induction m as [| (i0, t0) m]; intros ? ? v P.
  + simpl in v, P.
    inversion P; subst.
    exact (withspacer sh
            (field_offset cenv_cs i0 m0 + sizeof (field_type i0 m0))
            (field_offset_next cenv_cs i0 m0 sz)
            (at_offset (a v) (field_offset cenv_cs i0 m0))).
  + simpl in v, P.
    destruct (ident_eq i1 i1); [| congruence].
    inversion P; subst.
    exact (withspacer sh
            (field_offset cenv_cs i1 m0 + sizeof (field_type i1 m0))
            (field_offset_next cenv_cs i1 m0 sz)
            (at_offset (a (fst v)) (field_offset cenv_cs i1 m0)) * IHm i0 t0 (snd v) b)%logic.
Defined.

Definition union_data_at_rec_aux (m m0: members) (sz: Z) (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> (val -> mpred)) m)) (v: compact_sum (map (fun it => reptype (field_type (fst it) m0)) m)) : (val -> mpred).
Proof.
  destruct m as [| (i0, t0) m]; [exact (fun _ => emp) |].
  revert i0 t0 v P; induction m as [| (i0, t0) m]; intros ? ? v P.
  + simpl in v, P.
    inversion P; subst.
    exact (withspacer sh (sizeof (field_type i0 m0)) sz (a v)).
  + simpl in v, P.
    inversion P; subst.
    destruct v as [v | v].
    - exact (withspacer sh (sizeof (field_type i1 m0)) sz (a v)).
    - exact (IHm i0 t0 v b).
Defined.

Lemma struct_data_at_rec_aux_spec: forall m m0 sz v P,
  struct_data_at_rec_aux m m0 sz
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> val -> mpred)
     P m) v =
  struct_pred m
   (fun it v =>
      withspacer sh
       (field_offset cenv_cs (fst it) m0 + sizeof (field_type (fst it) m0))
       (field_offset_next cenv_cs (fst it) m0 sz)
       (at_offset (P it v) (field_offset cenv_cs (fst it) m0))) v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [reflexivity |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros.
  + simpl; reflexivity.
  + replace
     (struct_data_at_rec_aux ((i1, t1) :: (i0, t0) :: m) m0 sz
     (ListTypeGen (fun it : ident * type => reptype (field_type (fst it) m0) -> val -> mpred)
        P ((i1, t1) :: (i0, t0) :: m)) v) with
     (withspacer sh
       (field_offset cenv_cs i1 m0 + sizeof (field_type i1 m0))
         (field_offset_next cenv_cs i1 m0 sz)
           (at_offset (P (i1, t1) (fst v)) (field_offset cenv_cs i1 m0)) *
      struct_data_at_rec_aux ((i0, t0) :: m) m0 sz
     (ListTypeGen (fun it : ident * type => reptype (field_type (fst it) m0) -> val -> mpred)
        P ((i0, t0) :: m)) (snd v))%logic.
    - rewrite IHm.
      reflexivity.
    - simpl.
      destruct (ident_eq i1 i1); [| congruence].
      reflexivity.
Qed.

Lemma union_data_at_rec_aux_spec: forall m m0 sz v P,
  union_data_at_rec_aux m m0 sz
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> val -> mpred)
     P m) v =
  union_pred m
   (fun it v =>
      withspacer sh
       (sizeof (field_type (fst it) m0))
       sz
       (P it v)) v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [reflexivity |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros.
  + simpl. unfold union_pred. simpl. reflexivity.
  + destruct v as [v | v].
    - reflexivity.
    - match goal with
      | _ => apply IHm
      | _ => simpl ; f_equal ; apply IHm
      end.
Qed.

Definition struct_value_fits_aux (m m0: members)
      (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> Prop) m))
      (v: compact_prod (map (fun it => reptype (field_type (fst it) m0)) m)) : Prop.
Proof.
  destruct m as [| (i0, t0) m]; [exact True |].
  revert i0 t0 v P; induction m as [| (i0, t0) m]; intros ? ? v P.
  + simpl in v, P.
    inversion P; subst.
    apply (a v).
  + simpl in v, P.
    destruct (ident_eq i1 i1); [| congruence].
    inversion P; subst.
    apply (a (fst v) /\ IHm i0 t0 (snd v) b).
Defined.

Definition union_value_fits_aux (m m0: members)
      (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> Prop) m))
      (v: compact_sum (map (fun it => reptype (field_type (fst it) m0)) m)) : Prop.
Proof.
  destruct m as [| (i0, t0) m]; [exact True |].
  revert i0 t0 v P; induction m as [| (i0, t0) m]; intros ? ? v P.
  + simpl in v, P.
    inversion P; subst.
    exact (a v).
  + simpl in v, P.
    inversion P; subst.
    destruct v as [v | v].
    - exact (a v).
    - exact (IHm i0 t0 v b).
Defined.

Lemma struct_value_fits_aux_spec: forall m m0 v P,
  struct_value_fits_aux m m0
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> Prop)
     P m) v =
  struct_Prop m P v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [reflexivity |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros.
  + simpl; reflexivity.
  + replace
     (struct_value_fits_aux ((i1, t1) :: (i0, t0) :: m) m0
     (ListTypeGen (fun it : ident * type => reptype (field_type (fst it) m0) -> Prop)
        P ((i1, t1) :: (i0, t0) :: m)) v) with
     (P (i1, t1) (fst v) /\  struct_value_fits_aux ((i0, t0) :: m) m0
     (ListTypeGen (fun it : ident * type => reptype (field_type (fst it) m0) -> Prop)
        P ((i0, t0) :: m)) (snd v)).
    - rewrite IHm.
      reflexivity.
    - simpl.
      destruct (ident_eq i1 i1); [| congruence].
      reflexivity.
Qed.

Lemma union_value_fits_aux_spec: forall m m0 v P,
  union_value_fits_aux m m0
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> Prop)
     P m) v =
  union_Prop m P v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [reflexivity |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros.
  + simpl. unfold union_Prop. simpl. reflexivity.
  + destruct v as [v | v].
    - reflexivity.
    - match goal with
      | _ => apply IHm
      | _ => simpl ; f_equal ; apply IHm
      end.
Qed.

End AUXILIARY_PRED.

Module auxiliary_pred.

Import aggregate_pred.

Definition struct_data_at_rec_aux:
   forall {cs: compspecs} (sh: share) (m m0: members) (sz: Z) (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> (val -> mpred)) m)) (v: compact_prod (map (fun it => reptype (field_type (fst it) m0)) m)), (val -> mpred)
:= @struct_data_at_rec_aux.

Definition union_data_at_rec_aux:
  forall {cs: compspecs} (sh: share) (m m0: members) (sz: Z) (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> (val -> mpred)) m)) (v: compact_sum (map (fun it => reptype (field_type (fst it) m0)) m)), (val -> mpred)
:= @union_data_at_rec_aux.

Definition struct_data_at_rec_aux_spec: forall {cs: compspecs} (sh: share) m m0 sz v P,
  struct_data_at_rec_aux sh m m0 sz
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> val -> mpred)
     P m) v =
  struct_pred m
   (fun it v =>
      withspacer sh
       (field_offset cenv_cs (fst it) m0 + sizeof (field_type (fst it) m0))
       (field_offset_next cenv_cs (fst it) m0 sz)
       (at_offset (P it v) (field_offset cenv_cs (fst it) m0))) v
:= @struct_data_at_rec_aux_spec.

Definition union_data_at_rec_aux_spec: forall {cs: compspecs} sh m m0 sz v P,
  union_data_at_rec_aux sh m m0 sz
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> val -> mpred)
     P m) v =
  union_pred m
   (fun it v =>
      withspacer sh
       (sizeof (field_type (fst it) m0))
       sz
       (P it v)) v
:= @union_data_at_rec_aux_spec.

Definition struct_value_fits_aux:
  forall {cs: compspecs} (m m0: members)
      (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> Prop) m))
      (v: compact_prod (map (fun it => reptype (field_type (fst it) m0)) m)), Prop
:= @struct_value_fits_aux.

Definition union_value_fits_aux:
  forall {cs: compspecs} (m m0: members)
      (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> Prop) m))
      (v: compact_sum (map (fun it => reptype (field_type (fst it) m0)) m)), Prop
:= @union_value_fits_aux.

Definition struct_value_fits_aux_spec: forall {cs: compspecs} m m0 v P,
  struct_value_fits_aux m m0
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> Prop)
     P m) v =
  struct_Prop m P v
:= @struct_value_fits_aux_spec.

Definition union_value_fits_aux_spec: forall {cs: compspecs} m m0 v P,
  union_value_fits_aux m m0
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> Prop)
     P m) v =
  union_Prop m P v
:= @union_value_fits_aux_spec.

Definition memory_block_array_pred:
  forall {cs: compspecs} {A : Type} {d : Inhabitant A} (a: A) sh t z b ofs,
  0 <= z ->
  0 <= ofs /\ ofs + sizeof t * z < Ptrofs.modulus ->
  array_pred 0 z
     (fun i _ p =>
      memory_block sh (sizeof t)
        (offset_val (sizeof t * i) p)) (list_repeat (Z.to_nat z) a)
     (Vptr b (Ptrofs.repr ofs))  =
  memory_block sh (sizeof t * z) (Vptr b (Ptrofs.repr ofs))
:= @memory_block_array_pred'.

Definition memory_block_struct_pred:
  forall {cs: compspecs} sh m sz {A} (v: compact_prod (map A m)) b ofs,
  (m = nil -> sz = 0) ->
  members_no_replicate m = true ->
  sizeof_struct cenv_cs 0 m <= sz < Ptrofs.modulus ->
  0 <= ofs /\ ofs + sz < Ptrofs.modulus ->
  struct_pred m
   (fun it _ p =>
     (memory_block sh (field_offset_next cenv_cs (fst it) m sz - field_offset cenv_cs (fst it) m))
     (offset_val (field_offset cenv_cs (fst it) m) p)) v (Vptr b (Ptrofs.repr ofs)) =
  memory_block sh sz (Vptr b (Ptrofs.repr ofs))
:= @memory_block_struct_pred.

Definition memory_block_union_pred:
  forall sh m sz {A} (v: compact_sum (map A m)) b ofs,
  (m = nil -> sz = 0) ->
  union_pred m (fun it _ => memory_block sh sz) v (Vptr b (Ptrofs.repr ofs)) =
  memory_block sh sz (Vptr b (Ptrofs.repr ofs))
:= @memory_block_union_pred.

End auxiliary_pred.