From Velus Require Import Common.
From compcert Require Import lib.Coqlib.
From compcert Require Import lib.Maps.
From compcert Require Import cfrontend.Clight.
From compcert Require Import cfrontend.Ctypes.
From compcert Require Import lib.Integers.
From compcert Require Import common.Values.
From compcert Require Import common.Errors.
From compcert Require Import common.Memory.
From Coq Require Import List.
Import List.ListNotations.
Open Scope list_scope.
From Coq Require Import Classes.RelationClasses.
Open Scope error_monad_scope.
Conversions.
Lemma NoDup_norepet:
forall {
A} (
l:
list A),
NoDup l <->
list_norepet l.
Proof.
induction l; split; constructor.
- now inversion H.
- apply IHl; now inversion H.
- now inversion H.
- apply IHl; now inversion H.
Qed.
Lemma NoDupMembers_disjoint:
forall l1 l2,
NoDupMembers (
l1 ++
l2) ->
list_disjoint (
var_names l1) (
var_names l2).
Proof.
Lemma list_drop_skipn:
forall {
A} (
xs:
list A)
n,
Coqlib.list_drop n xs =
skipn n xs.
Proof.
induction xs, n; simpl; auto.
Qed.
Lemma list_forall2_Forall2:
forall {
A B}
P (
xs:
list A) (
ys:
list B),
Coqlib.list_forall2 P xs ys <->
Forall2 P xs ys.
Proof.
induction xs as [|
x xs IH].
now split;
inversion 1;
auto using Coqlib.list_forall2_nil.
split;
intro HH;
inversion_clear HH;
constructor;
auto;
apply IH;
auto.
Qed.
The error monad.
Section Mmaps.
Context {
A B S:
Type}.
Definition mmaps (
f:
S ->
A ->
res (
S *
B)) :
S ->
list A ->
res (
S *
list B) :=
fix mmaps (
s:
S) (
xs:
list A) {
struct xs} :
res (
S *
list B) :=
match xs with
|
nil =>
OK (
s,
nil)
|
x ::
xs' =>
do (
s',
y) <-
f s x;
do (
s'',
ys) <-
mmaps s'
xs';
OK (
s'',
y ::
ys)
end.
Lemma mmaps_spec:
forall (
P:
S ->
S ->
B ->
Prop) (
R:
S ->
S ->
Prop)
(
I:
list B ->
Prop) (
IS:
S ->
Prop)
(
f:
S ->
A ->
res (
S *
B))
xs ys s s',
mmaps f s xs =
OK (
s',
ys) ->
Reflexive R ->
Transitive R ->
I [] ->
IS s ->
(
forall s s'
y,
P s s'
y ->
(
forall r,
R r s ->
P r s'
y)
/\ (
forall t',
R s'
t' ->
P s t'
y)) ->
(
forall x y s s',
In x xs ->
IS s ->
f s x =
OK (
s',
y) ->
P s s'
y /\
R s s' /\
IS s') ->
(
forall y ys s s'
s'',
P s s'
y ->
Forall (
P s'
s'')
ys ->
R s s' ->
R s'
s'' ->
I ys ->
I (
y ::
ys)) ->
Forall (
P s s')
ys /\
R s s' /\
I ys /\
IS s'.
Proof.
intros P R I IS f xs ys s s'
Hmm HRefl HTrans HInil HIS0 HPR Hf HPI.
revert xs ys s s'
Hf Hmm HIS0.
induction xs as [|
x xs IH];
simpl.
now inversion_clear 2;
auto.
intros ys s s'
Hf Hmm HIS.
monadInv Hmm.
match goal with H:
f _ _ =
OK (
_, ?
w) |-
_ =>
rename w into y;
apply Hf in H;
try destruct H as (
HP &
HR' &
HIS');
auto end.
match goal with H:
mmaps _ ?
s _ =
OK (
_, ?
ws) |-
_ =>
rename s into t,
ws into ys;
apply IH in H;
try destruct H as (
Hfa &
HR &
HI &
HIS'')
end;
auto.
2:
now intros;
eapply Hf;
eauto.
pose proof (
HTrans _ _ _ HR'
HR).
repeat split;
eauto.
constructor.
now apply HPR in HP;
destruct HP;
auto.
apply Forall_forall.
intros y'
Hin.
apply Forall_forall with (1:=
Hfa)
in Hin.
apply HPR in Hin.
destruct Hin as (
HP1 &
HP2).
now apply HP1.
Qed.
Lemma mmaps_weak_spec:
forall (
I:
S ->
Prop) (
R:
B ->
Prop)
(
f:
S ->
A ->
res (
S *
B))
xs s ys s',
mmaps f s xs =
OK (
s',
ys) ->
I s ->
(
forall x s y s',
I s ->
In x xs ->
f s x =
OK (
s',
y) ->
I s' /\
R y) ->
I s' /\
Forall R ys.
Proof.
induction xs as [|
x xs IH];
simpl;
intros *
Hmm HI Hone;
monadInv Hmm.
now auto.
match goal with Hf:
f _ _ =
OK _ |-
_ =>
apply Hone with (1:=
HI)
in Hf;
auto;
destruct Hf end.
rewrite Forall_cons2, (
and_comm (
R x1)), <-
and_assoc;
eauto.
Qed.
Lemma mmaps_weak_spec':
forall (
R:
B ->
Prop)
(
f:
S ->
A ->
res (
S *
B))
xs s ys s',
mmaps f s xs =
OK (
s',
ys) ->
(
forall x s y s',
In x xs ->
f s x =
OK (
s',
y) ->
R y) ->
Forall R ys.
Proof.
End Mmaps.
Definition mfoldl {
A B:
Type} (
f:
A ->
B ->
res A) :
A ->
list B ->
res A :=
fix mfoldl s xs {
struct xs} :=
match xs with
|
nil =>
OK s
|
x ::
xs' =>
do s' <-
f s x;
mfoldl s'
xs'
end.
Section IterError.
Context {
A:
Type}.
Variable f:
A ->
res unit.
Definition iter_error:
list A ->
res unit :=
mfoldl (
fun _ a =>
f a)
tt.
Lemma iter_error_ok:
forall l,
iter_error l =
OK tt <->
Forall (
fun a =>
f a =
OK tt)
l.
Proof.
unfold iter_error.
induction l;
simpl.
-
intuition.
-
split;
intros H.
+
monadInv H.
take (
unit)
and destruct it.
constructor;
auto.
apply IHl;
auto.
+
inversion_clear H as [|??
E].
rewrite E;
simpl.
apply IHl;
auto.
Qed.
End IterError.
Lemma OK_OK:
forall {
A} (
x:
A)
y,
OK x =
OK y <->
x =
y.
Proof.
intros; split; intro HH; subst; auto. now monadInv HH.
Qed.
Definition mmapacc {
A S T:
Type} (
acc:
S ->
T ->
S) (
f:
A ->
res T)
:
S ->
list A ->
res S :=
fix mmapacc (
s:
S) (
xs:
list A) {
struct xs} :
res S :=
match xs with
|
nil =>
OK s
|
x ::
xs' =>
do r <-
f x;
mmapacc (
acc s r)
xs'
end.
Lemma mmap_skipn:
forall {
A B} (
f:
A ->
res B)
xs ys n,
mmap f xs =
OK ys ->
mmap f (
skipn n xs) =
OK (
skipn n ys).
Proof.
induction xs as [|
x xs IH].
now inversion_clear 1;
setoid_rewrite skipn_nil;
auto.
intros ys n Hmm.
simpl in *.
monadInv Hmm.
destruct n;
simpl.
2:
now apply IH.
rewrite EQ;
rewrite EQ1.
auto.
Qed.
Open Scope nat.
Lemma mmapacc_plus_shift:
forall {
A}
f (
xs:
list A)
m1 m2 n,
mmapacc plus f (
m1 +
m2)
xs =
OK n ->
mmapacc plus f m1 xs =
OK (
n -
m2) /\
m2 <=
n.
Proof.
induction xs as [|
x xs IH];
intros m1 m2 n Hm;
simpl in *;
monadInv Hm.
now subst;
rewrite OK_OK;
lia.
rewrite EQ.
simpl.
rewrite Nat.add_comm,
Nat.add_assoc, (
Nat.add_comm x0 m1)
in EQ0.
apply IH in EQ0.
auto.
Qed.
More monad syntax and tactics
Definition bind22 {
A B C D:
Type} (
f:
res (
A * (
B *
C))) (
g:
A ->
B ->
C ->
res D) :
res D :=
match f with
|
OK (
x, (
y,
z)) =>
g x y z
|
Error msg =>
Error msg
end.
Module DoNotation.
Notation "'
do' (
X , (
Y ,
Z ) ) <-
A ;
B" :=
(
bind22 A (
fun X Y Z =>
B))
(
at level 200,
X name,
Y name,
Z name,
A at level 100,
B at level 200)
:
error_monad_scope.
End DoNotation.
Remark bind22_inversion:
forall {
A B C D:
Type} (
f:
res (
A*(
B*
C))) (
g:
A ->
B ->
C ->
res D) {
z:
D},
bind22 f g =
OK z ->
exists w,
exists x,
exists y,
f =
OK (
w, (
x,
y)) /\
g w x y =
OK z.
Proof.
intros until z. destruct f; simpl.
repeat destruct p; simpl; intros. exists a; exists b; exists c; auto.
intros; discriminate.
Qed.
Ltac monadInv2 H :=
match type of H with
| (
OK _ =
OK _) =>
inversion H;
clear H;
try subst
| (
Error _ =
OK _) =>
discriminate
| (
bind ?
F ?
G =
OK ?
X) =>
let x :=
fresh "
x"
in (
let EQ1 :=
fresh "
EQ"
in (
let EQ2 :=
fresh "
EQ"
in (
destruct (
bind_inversion F G H)
as [
x [
EQ1 EQ2]];
clear H;
try (
monadInv2 EQ2))))
| (
bind2 ?
F ?
G =
OK ?
X) =>
let x1 :=
fresh "
x"
in (
let x2 :=
fresh "
x"
in (
let EQ1 :=
fresh "
EQ"
in (
let EQ2 :=
fresh "
EQ"
in (
destruct (
bind2_inversion F G H)
as [
x1 [
x2 [
EQ1 EQ2]]];
clear H;
try (
monadInv2 EQ2)))))
| (
bind22 ?
F ?
G =
OK ?
X) =>
let x1 :=
fresh "
x"
in (
let x2 :=
fresh "
x"
in (
let x3 :=
fresh "
x"
in (
let EQ1 :=
fresh "
EQ"
in (
let EQ2 :=
fresh "
EQ"
in (
destruct (
bind22_inversion F G H)
as [
x1 [
x2 [
x3 [
EQ1 EQ2]]]];
clear H;
try (
monadInv2 EQ2))))))
| (
match ?
X with left _ =>
_ |
right _ =>
assertion_failed end =
OK _) =>
destruct X; [
try (
monadInv2 H) |
discriminate]
| (
match (
negb ?
X)
with true =>
_ |
false =>
assertion_failed end =
OK _) =>
destruct X as []
eqn:?; [
discriminate |
try (
monadInv2 H)]
| (
match ?
X with true =>
_ |
false =>
assertion_failed end =
OK _) =>
destruct X as []
eqn:?; [
try (
monadInv2 H) |
discriminate]
| (
mmap ?
F ?
L =
OK ?
M) =>
generalize (
mmap_inversion F L H);
intro
end.
Close Scope nat.
Close Scope error_monad_scope.
Lemma Int_Ptrofs_max_unsigned:
Int.max_unsigned <=
Ptrofs.max_unsigned.
Proof.
Lemma type_eq_refl:
forall {
A}
t (
T F:
A),
(
if type_eq t t then T else F) =
T.
Proof.
intros.
destruct (
type_eq t t)
as [|
Neq];
congruence.
Qed.
Definition mk_members (
flds :
list (
AST.ident *
type)) :
members :=
List.map (
fun '(
x,
ty) =>
Member_plain x ty)
flds.
Lemma mk_members_names :
forall flds,
map name_member (
mk_members flds) =
map fst flds.
Proof.
Remark field_offset_rec_mk_members :
forall env id flds k ofs bf,
field_offset_rec env id (
mk_members flds)
k =
OK (
ofs,
bf) ->
bf =
Full.
Proof.
induction flds as [|(?&?)];
simpl;
intros *
Hf.
-
discriminate.
-
destruct (
AST.ident_eq _ _);
simpl;
subst.
+
inv Hf;
auto.
+
eapply IHflds;
eauto.
Qed.
Corollary field_offset_mk_members :
forall env id flds ofs bf,
field_offset env id (
mk_members flds) =
OK (
ofs,
bf) ->
bf =
Full.
Proof.
Lemma two_power_nat_le_divides:
forall m n,
two_power_nat m <=
two_power_nat n ->
(
two_power_nat m |
two_power_nat n).
Proof.
Lemma two_power_nat_max:
forall m n,
(
two_power_nat m |
Z.max (
two_power_nat m) (
two_power_nat n)).
Proof.
Lemma co_members_alignof:
forall env co flds,
co_members co =
mk_members flds ->
composite_consistent env co ->
attr_alignas (
co_attr co) =
None ->
Forall (
fun x => (
alignof env (
type_member x) |
co_alignof co)) (
co_members co).
Proof.
Lemma align_noattr:
forall a,
align_attr noattr a =
a.
Proof.
intros.
unfold noattr.
reflexivity.
Qed.
Lemma in_field_type:
forall xs x ty,
NoDupMembers xs ->
In (
x,
ty)
xs ->
field_type x (
mk_members xs) =
Errors.OK ty.
Proof.
intros xs x ty Hndup Hin.
induction xs as [|
x'
xs IH]; [
now inversion Hin|].
destruct x'
as [
x'
ty'].
apply NoDupMembers_cons_inv in Hndup as [?
Hndup].
inversion Hin as [
Heq|
Heq].
-
injection Heq;
intros;
subst.
simpl.
rewrite peq_true.
reflexivity.
-
simpl.
rewrite peq_false.
+
now apply IH with (1:=
Hndup) (2:=
Heq).
+
intro;
subst.
apply NotInMembers_NotIn in Heq;
intuition.
Qed.
Lemma field_offset_type:
forall ge id ms d,
field_offset ge id ms =
Errors.OK d ->
exists ty,
field_type id ms =
Errors.OK ty.
Proof.
Remark field_offset_aligned':
forall gcenv id flds ofs ty bf,
field_offset gcenv id (
mk_members flds) =
OK (
ofs,
bf) ->
field_type id (
mk_members flds) =
OK ty ->
(
alignof gcenv ty |
ofs).
Proof.
Remark field_offset_in_range':
forall gcenv flds x ofs bf,
field_offset gcenv x (
mk_members flds) =
Errors.OK (
ofs,
bf) ->
0 <=
ofs.
Proof.
Lemma field_type_skip_app:
forall x ws xs,
~
InMembers x ws ->
field_type x (
mk_members (
ws ++
xs)) =
field_type x (
mk_members xs).
Proof.
induction ws as [|
w ws IH];
auto.
intros xs Hnin.
apply NotInMembers_cons in Hnin.
destruct Hnin as (
Hnin &
Hx).
destruct w.
simpl.
rewrite peq_false;
auto.
Qed.
Lemma sizeof_by_value:
forall ge ty chunk,
access_mode ty =
By_value chunk ->
sizeof ge ty =
Memdata.size_chunk chunk.
Proof.
destruct ty; try (intros; discriminate);
[destruct i, s, a|destruct s, a|destruct f, a|];
injection 1; intros; subst; reflexivity.
Qed.
Global Hint Resolve sizeof_by_value :
compcert.
Lemma sizeof_struct_pos:
forall env ms,
0 <=
sizeof_struct env ms.
Proof.
Lemma set_comm:
forall {
A}
x x' (
v v':
A)
m,
x <>
x' ->
PTree.set x v (
PTree.set x'
v'
m) =
PTree.set x'
v' (
PTree.set x v m).
Proof.
Remark bind_parameter_temps_comm:
forall xs vs s ts o to vself vout x t v le le',
x <>
o ->
x <>
s ->
(
bind_parameter_temps ((
s,
ts) :: (
o,
to) :: (
x,
t) ::
xs) (
vself ::
vout ::
v ::
vs)
le =
Some le' <->
bind_parameter_temps ((
x,
t) :: (
s,
ts) :: (
o,
to) ::
xs) (
v ::
vself ::
vout ::
vs)
le =
Some le').
Proof.
destruct xs as [|(
y,
ty)],
vs;
split;
intros *
Bind;
inv Bind;
simpl.
-
f_equal.
rewrite (
set_comm s x);
auto.
apply set_comm;
auto.
-
f_equal.
rewrite (
set_comm x o);
auto.
f_equal.
apply set_comm;
auto.
-
do 2
f_equal.
rewrite (
set_comm s x);
auto.
apply set_comm;
auto.
-
do 2
f_equal.
rewrite (
set_comm x o);
auto.
f_equal.
apply set_comm;
auto.
Qed.
Remark bind_parameter_temps_comm_noout:
forall xs vs s ts vself x t v le le',
x <>
s ->
(
bind_parameter_temps ((
s,
ts) :: (
x,
t) ::
xs) (
vself ::
v ::
vs)
le =
Some le' <->
bind_parameter_temps ((
x,
t) :: (
s,
ts) ::
xs) (
v ::
vself ::
vs)
le =
Some le').
Proof.
destruct xs as [|(
y,
ty)],
vs;
split;
intros *
Bind;
inv Bind;
simpl.
-
f_equal.
rewrite (
set_comm s x);
auto.
-
f_equal.
rewrite (
set_comm s x);
auto.
-
do 2
f_equal.
rewrite (
set_comm s x);
auto.
-
do 2
f_equal.
rewrite (
set_comm s x);
auto.
Qed.
Remark bind_parameter_temps_conservation:
forall xs vs x v le le',
~
InMembers x xs ->
le !
x =
v ->
bind_parameter_temps xs vs le =
Some le' ->
le' !
x =
v.
Proof.
induction xs as [|(
x',
t')];
destruct vs;
intros *
Notin Hin Bind;
inversion Bind.
-
subst;
auto.
-
apply NotInMembers_cons in Notin;
destruct Notin.
clear Bind;
eapply IHxs with (
le:=
PTree.set x'
v le);
eauto.
rewrite PTree.gso;
auto.
Qed.
Remark bind_parameter_temps_cons:
forall xs vs x ty v le le',
~
InMembers x xs ->
bind_parameter_temps xs vs le =
Some le' ->
bind_parameter_temps ((
x,
ty) ::
xs) (
v ::
vs)
le =
Some (
PTree.set x v le').
Proof.
induction xs as [|(
x',
t')],
vs;
simpl;
intros ?????
Notin Bind;
try discriminate.
-
now inversion Bind.
-
simpl in IHxs.
rewrite set_comm.
+
apply IHxs;
auto.
+
intro;
apply Notin;
now left.
Qed.
Remark bind_parameter_temps_implies_two:
forall xs vs s ts vself o to vout le le',
s <>
o ->
~
InMembers s xs ->
~
InMembers o xs ->
bind_parameter_temps ((
s,
ts) :: (
o,
to) ::
xs)
(
vself ::
vout ::
vs)
le =
Some le' ->
le' !
s =
Some vself /\
le' !
o =
Some vout.
Proof.
Remark bind_parameter_temps_implies:
forall xs vs s ts vself le le',
~
InMembers s xs ->
bind_parameter_temps ((
s,
ts) ::
xs)
(
vself ::
vs)
le =
Some le' ->
le' !
s =
Some vself.
Proof.
Remark alloc_implies:
forall tge vars x b t e m e'
m',
~
InMembers x vars ->
alloc_variables tge (
PTree.set x (
b,
t)
e)
m vars e'
m' ->
e' !
x =
Some (
b,
t).
Proof.
induction vars as [|(
x',
t')];
intros *
Notin H;
inversion_clear H as [|? ? ? ? ? ? ? ? ? ?
Halloc];
subst.
-
apply PTree.gss.
-
rewrite <-
set_comm in Halloc.
+
eapply IHvars;
eauto.
eapply NotInMembers_cons;
eauto.
+
intro;
subst x;
apply Notin;
apply InMembers_eq.
Qed.
Definition drop_block '((
x, (
_,
t)):
ident * (
block *
Ctypes.type)) : (
ident *
Ctypes.type) :=
(
x,
t).
Remark In_drop_block:
forall elts x t,
In (
x,
t) (
map drop_block elts) ->
exists b,
In (
x, (
b,
t))
elts.
Proof.
induction elts as [|(x', (b', t'))]; simpl; intros * Hin.
- contradiction.
- destruct Hin as [Eq|Hin].
+ inv Eq.
exists b'; now left.
+ apply IHelts in Hin.
destruct Hin as [b Hin].
exists b; now right.
Qed.
Remark drop_block_In:
forall elts x b t,
In (
x, (
b,
t))
elts ->
In (
x,
t) (
map drop_block elts).
Proof.
induction elts as [|(x', (b', t'))]; simpl; intros * Hin.
- contradiction.
- destruct Hin as [Eq|Hin].
+ inv Eq.
now left.
+ apply IHelts in Hin.
now right.
Qed.
Remark alloc_In:
forall tge vars e m e'
m',
alloc_variables tge e m vars e'
m' ->
NoDupMembers vars ->
(
forall x t,
In (
x,
t) (
map drop_block (
PTree.elements e')) <->
(
In (
x,
t) (
map drop_block (
PTree.elements e)) /\ (
forall t',
In (
x,
t')
vars ->
t =
t'))
\/
In (
x,
t)
vars).
Proof.
Remark alloc_permutation:
forall tge vars m e'
m',
alloc_variables tge empty_env m vars e'
m' ->
NoDupMembers vars ->
Permutation.Permutation vars (
map drop_block (
PTree.elements e')).
Proof.
intros *
Alloc Nodup.
pose proof (
alloc_In _ _ _ _ _ _ Alloc)
as H.
apply Permutation.NoDup_Permutation.
-
apply NoDupMembers_NoDup;
auto.
-
pose proof (
PTree.elements_keys_norepet e')
as Norep.
clear H.
induction (
PTree.elements e')
as [|(
x, (
b,
t))];
simpl in *;
constructor.
+
inversion_clear Norep as [|? ?
Notin Norep'].
clear IHl.
induction l as [|(
x', (
b',
t'))];
simpl in *.
*
intro;
contradiction.
*{
intros [
Eq |
Hin].
-
inv Eq.
apply Notin.
now left.
-
inv Norep'.
apply IHl;
auto.
}
+
inversion_clear Norep as [|? ?
Notin Norep'].
apply IHl;
auto.
-
intros (
x,
t).
specialize (
H Nodup x t).
intuition.
Qed.
Lemma Permutation_set:
forall {
A B}
x (
a:
A) (
b:
B)
e,
~
InMembers x (
PTree.elements e) ->
Permutation.Permutation (
PTree.elements (
PTree.set x (
a,
b)
e))
((
x, (
a,
b)) ::
PTree.elements e).
Proof.
Lemma set_NoDupMembers:
forall x (
e:
Clight.env)
b1 t,
NoDupMembers (
map snd (
PTree.elements e)) ->
~
InMembers x (
PTree.elements e) ->
~
InMembers b1 (
map snd (
PTree.elements e)) ->
NoDupMembers (
map snd (
PTree.elements (
PTree.set x (
b1,
t)
e))).
Proof.
Remark alloc_NoDupMembers:
forall tge vars e m e'
m',
alloc_variables tge e m vars e'
m' ->
NoDupMembers vars ->
NoDupMembers (
map snd (
PTree.elements e)) ->
Forall (
fun xv => ~
InMembers (
fst xv) (
PTree.elements e))
vars ->
(
forall b,
InMembers b (
map snd (
PTree.elements e)) ->
Mem.valid_block m b) ->
NoDupMembers (
map snd (
PTree.elements e')).
Proof.
Remark alloc_exists:
forall tge vars e m,
NoDupMembers vars ->
exists e'
m',
alloc_variables tge e m vars e'
m'.
Proof.
induction vars as [|(
x,
t)];
intros *
Nodup.
-
exists e,
m;
constructor.
-
inv Nodup.
destruct (
Mem.alloc m 0 (
Ctypes.sizeof tge t))
as (
m1 &
b)
eqn:
Eq.
edestruct IHvars with (
e:=
PTree.set x (
b,
t)
e) (
m:=
m1)
as (
e' &
m' &
Halloc);
eauto.
exists e',
m';
econstructor;
eauto.
Qed.
Remark Permutation_fst:
forall vars elems,
Permutation.Permutation vars elems ->
Permutation.Permutation (
var_names vars) (
map fst elems).
Proof.
intros *
Perm.
induction Perm;
simpl;
try constructor;
auto.
transitivity (
map fst l');
auto.
Qed.
Remark map_fst_drop_block:
forall elems,
map fst (
map drop_block elems) =
map fst elems.
Proof.
induction elems as [|(x, (b, t))]; simpl; auto with datatypes.
Qed.
Program Definition empty_co:
composite :=
{|
co_su :=
Struct;
co_members := [];
co_attr :=
noattr;
co_sizeof := 0;
co_alignof := 1;
co_rank := 0
|}.
Next Obligation.
Next Obligation.