Section LINKER_PROG.
Context {F V: Type} {LF: Linker F} {LV: Linker V} (p1 p2: program F V).
Let dm1 := prog_defmap p1.
Let dm2 := prog_defmap p2.
Definition link_prog_check (x: ident) (gd1: globdef F V) :=
match dm2!x with
| None => true
| Some gd2 =>
In_dec peq x p1.(prog_public)
&& In_dec peq x p2.(prog_public)
&& match link gd1 gd2 with Some _ => true | None => false end
end.
Definition link_prog_merge (o1 o2: option (globdef F V)) :=
match o1, o2 with
| None, _ => o2
| _, None => o1
| Some gd1, Some gd2 => link gd1 gd2
end.
Definition link_prog :=
if ident_eq p1.(prog_main) p2.(prog_main)
&& PTree_Properties.for_all dm1 link_prog_check then
Some {| prog_main := p1.(prog_main);
prog_public := p1.(prog_public) ++ p2.(prog_public);
prog_defs := PTree.elements (PTree.combine link_prog_merge dm1 dm2) |}
else
None.
End LINKER_PROG.
Program Instance Linker_prog (F V: Type) {LF: Linker F} {LV: Linker V} : Linker (program F V) := {
link := link_prog;
linkorder := fun p1 p2 =>
p1.(prog_main) = p2.(prog_main)
/\ incl p1.(prog_public) p2.(prog_public)
/\ forall id gd1,
(prog_defmap p1)!id = Some gd1 ->
exists gd2,
(prog_defmap p2)!id = Some gd2
/\ linkorder gd1 gd2
/\ (~In id p2.(prog_public) -> gd2 = gd1)
}.
Next Obligation.
split; auto. split. apply incl_refl. intros.
exists gd1; split; auto. split; auto. apply linkorder_refl.
Defined.
Next Obligation.
split. congruence. split. red; eauto.
intros. exploit H4; eauto. intros (gd2 & P & Q & R).
exploit H2; eauto. intros (gd3 & U & X & Y).
exists gd3. split; auto. split. eapply linkorder_trans; eauto.
intros. transitivity gd2. apply Y. auto. apply R. red; intros; elim H0; auto.
Defined.
Next Obligation.
apply link_prog_inv in H. destruct H as (L1 & L2 & L3).
subst z; simpl. intuition auto.
+ red; intros; apply in_app_iff; auto.
+ rewrite prog_defmap_elements, PTree.gcombine, H by auto.
destruct (prog_defmap y)!id as [gd2|] eqn:GD2; simpl.
* exploit L2; eauto. intros (P & Q & gd & R).
exists gd; split. auto. split. apply link_linkorder in R; tauto.
rewrite in_app_iff; tauto.
* exists gd1; split; auto. split. apply linkorder_refl. auto.
+ red; intros; apply in_app_iff; auto.
+ rewrite prog_defmap_elements, PTree.gcombine, H by auto.
destruct (prog_defmap x)!id as [gd2|] eqn:GD2; simpl.
* exploit L2; eauto. intros (P & Q & gd & R).
exists gd; split. auto. split. apply link_linkorder in R; tauto.
rewrite in_app_iff; tauto.
* exists gd1; split; auto. split. apply linkorder_refl. auto.
Defined.

Lemma prog_defmap_linkorder:
forall {F V: Type} {LF: Linker F} {LV: Linker V} (p1 p2: program F V) id gd1,
linkorder p1 p2 ->
(prog_defmap p1)!id = Some gd1 ->
exists gd2, (prog_defmap p2)!id = Some gd2 /\ linkorder gd1 gd2.