(** * Commutation with linking *)
Remark link_def_either:
forall (gd1 gd2 gd: globdef fundef unit),
link_def gd1 gd2 = Some gd -> gd = gd1 \/ gd = gd2.
Proof with (try discriminate).
intros until gd.
Local Transparent Linker_def Linker_fundef Linker_varinit Linker_vardef Linker_unit.
destruct gd1 as [f1|v1], gd2 as [f2|v2]...
(* Two fundefs *)
destruct f1 as [f1|ef1], f2 as [f2|ef2]; simpl...
destruct ef2; intuition congruence.
destruct ef1; intuition congruence.
destruct (external_function_eq ef1 ef2); intuition congruence.
(* Two vardefs *)
simpl. unfold link_vardef. destruct v1 as [info1 init1 ro1 vo1], v2 as [info2 init2 ro2 vo2]; simpl.
destruct (link_varinit init1 init2) as [init|] eqn:LI...
destruct (eqb ro1 ro2) eqn:RO...
destruct (eqb vo1 vo2) eqn:VO...
simpl.
destruct info1, info2.
assert (EITHER: init = init1 \/ init = init2).
{ revert LI. unfold link_varinit.
destruct (classify_init init1), (classify_init init2); intro EQ; inv EQ; auto.
destruct (zeq sz (Z.max sz0 0 + 0)); inv H0; auto.
destruct (zeq sz (init_data_list_size il)); inv H0; auto.
destruct (zeq sz (init_data_list_size il)); inv H0; auto. }
apply eqb_prop in RO. apply eqb_prop in VO.
intro EQ; inv EQ. destruct EITHER; subst init; auto.
Qed.
Remark used_not_defined:
forall p used id,
valid_used_set p used ->
(prog_defmap p)!id = None ->
IS.mem id used = false \/ id = prog_main p.
Remark used_not_defined_2:
forall p used id,
valid_used_set p used ->
id <> prog_main p ->
(prog_defmap p)!id = None ->
~IS.In id used.

(* Specifications: *)
Lemma link_valid_used_set:
forall p1 p2 p used1 used2,
link p1 p2 = Some p ->
valid_used_set p1 used1 ->
valid_used_set p2 used2 ->
valid_used_set p (IS.union used1 used2).
Theorem link_match_program:
forall p1 p2 tp1 tp2 p,
link p1 p2 = Some p ->
match_prog p1 tp1 -> match_prog p2 tp2 ->
exists tp, link tp1 tp2 = Some tp /\ match_prog p tp.