(* We would prefer to let the queue hold arbitrarily complex data, not necessarily associated with a C type. For now, the mpred embedding can't handle type application, so we have to default to data_at instead of taking a general predicate for the queue, and that means the data is always of a C type. *)
Definition q_lock_pred' (t : type) P p (vals : list (val * reptype t)) head (addc remc : val) lock gsh h :=
!!(Zlength vals <= MAX /\ 0 <= head < MAX /\ consistent h [] vals) &&
(data_at Tsh tqueue (rotate (complete MAX (map fst vals)) head MAX, (vint (Zlength vals),
(vint head, (vint ((head + Zlength vals) mod MAX), (addc, remc))))) p *
cond_var Tsh addc * cond_var Tsh remc * malloc_token Tsh (sizeof tqueue_t) p *
malloc_token Tsh (sizeof tcond) addc * malloc_token Tsh (sizeof tcond) remc *
malloc_token Tsh (sizeof tlock) lock * ghost gsh (Tsh, h) p *
fold_right sepcon emp (map (fun x => let '(p, v) := x in
!!(P v) && (data_at Tsh t v p * malloc_token Tsh (sizeof t) p)) vals)).
Definition q_lock_pred A P p lock gsh := EX vals : list (val * reptype A), EX head : Z,
EX addc : val, EX remc : val, EX h : hist _, q_lock_pred' A P p vals head addc remc lock gsh h.
Definition lqueue lsh t P p lock gsh1 gsh2 (h : hist (reptype t)) :=
!!(sepalg.join gsh1 gsh2 Tsh /\ field_compatible tqueue_t [] p) &&
(field_at lsh tqueue_t [StructField _lock] lock p *
lock_inv lsh lock (q_lock_pred t P p lock gsh2) * ghost gsh1 (lsh, h) p).

Lemma lqueue_feasible : forall A P p lock gsh1 gsh2 (h : hist (reptype A)),
lqueue Tsh A P p lock gsh1 gsh2 h = !!(feasible h) && lqueue Tsh A P p lock gsh1 gsh2 h.