Library coqrel.Transport
The transport tactic
Transportable hypotheses
Class Transport {A B} (R: rel A B) (a: A) (b: B) (PA: Prop) (PB: Prop) :=
transport: R a b → PA → PB.
The stereotypical example is option_rel, which we can use to
  transport hypotheses as per the following instances. 
Global Instance option_rel_transport_eq_some {A B} (R: rel A B) x y a:
Transport (option_rel R) x y (x = Some a) (∃ b, y = Some b ∧ R a b).
Proof.
intros Hxy Hx.
subst; inversion Hxy; eauto.
Qed.
Global Instance option_rel_transport_eq_none {A B} (R: rel A B) x y:
Transport (option_rel R) x y (x = None) (y = None).
Proof.
intros Hxy Hx.
subst; inversion Hxy; eauto.
Qed.
A similar approach can be used to transport hypotheses which assert
  a element of a product type is equal to a specific pair. 
Global Instance prod_rel_transport_eq_pair {A1 B1 A2 B2} (R1: rel A1 B1) (R2: rel A2 B2) x y a1 a2:
Transport (prod_rel R1 R2) x y (x = (a1, a2)) (∃ b1 b2, y = (b1, b2) ∧ R1 a1 b1 ∧ R2 a2 b2).
Proof.
intros [Hxy1 Hxy2] Hx.
destruct y.
subst.
simpl in ×.
eauto.
Qed.
For set_rel the situation is slightly more involved, for two
  reasons. First, a regular eapply set_rel_transport fails to unify
  the parameter B of Transport against the _ → Prop provied by
  the instance below. This can be worked around by pre-unifying that
  specific parameter. Second, because set_rel_transport is
  potentially applicable to virtually any hypothesis (since there is
  no strongly distinguishing syntactic format in our target
  hypotheses), it makes transport_hyps very slow.
 
  To address this, we explicitely register hints based on the
  set_rel_transport tactic, which looks for "keywords" before doing
  anything, then applies the lemma with the required unification
  preparation. For example, set_rel_transport is used in the
  following way in the SimClight library:
    Hint Extern 1 (Transport _ _ _ _ _) =>
      set_rel_transport @assign_loc : typeclass_instances.
  Note that it's necessary to use @ because assign_loc is
  parametrized by typeclasses, and we want to avoid undue
  specialization at hint registration time. 
Lemma set_rel_transport {A B} (R: rel A B) sA sB a:
Transport (set_rel R) sA sB (sA a) (∃ b, sB b ∧ R a b).
Proof.
intros HsAB Ha.
edestruct HsAB; eauto.
Qed.
Ltac set_rel_transport keyword :=
lazymatch goal with
| |- @Transport ?A ?B ?R ?a ?b ?PA ?PB ⇒
lazymatch PA with
| context [keyword] ⇒
let Xv := fresh "X" in evar (Xv: Type);
let X := eval red in Xv in clear Xv;
unify B (X → Prop);
eapply set_rel_transport
end
end.
Lemma rel_curry_set_rel_transport {A1 A2 B1 B2} R sA sB (a1: A1) (a2: A2):
Transport (% set_rel R) sA sB
(sA a1 a2)
(∃ (b1: B1) (b2: B2), sB b1 b2 ∧ R (a1, a2) (b1, b2)).
Proof.
intros HsAB Ha.
destruct (HsAB (a1, a2)) as ([b1 b2] & Hb & Hab); eauto.
Qed.
Ltac rel_curry_set_rel_transport keyword :=
lazymatch goal with
| |- @Transport ?A ?B ?R ?a ?b ?PA ?PB ⇒
lazymatch PA with
| context [keyword] ⇒
let Xv := fresh "X" in evar (Xv: Type);
let X := eval red in Xv in clear Xv;
let Yv := fresh "Y" in evar (Yv: Type);
let Y := eval red in Yv in clear Yv;
unify B (X → Y → Prop);
eapply rel_curry_set_rel_transport
end
end.
Lemma rel_curry2_set_rel_transport {A1 A2 A3 B1 B2 B3} R sA sB (a1: A1) (a2: A2) (a3: A3):
Transport (% % set_rel R) sA sB
(sA a1 a2 a3)
(∃ (b1: B1) (b2: B2) (b3: B3), sB b1 b2 b3 ∧ R (a1, a2, a3) (b1, b2, b3)).
Proof.
intros HsAB Ha.
destruct (HsAB (a1, a2, a3)) as ([[b1 b2] b3] & Hb & Hab); eauto.
Qed.
Ltac rel_curry2_set_rel_transport keyword :=
lazymatch goal with
| |- @Transport ?A ?B ?R ?a ?b ?PA ?PB ⇒
lazymatch PA with
| context [keyword] ⇒
let Xv := fresh "X" in evar (Xv: Type);
let X := eval red in Xv in clear Xv;
let Yv := fresh "Y" in evar (Yv: Type);
let Y := eval red in Yv in clear Yv;
let Zv := fresh "Y" in evar (Yv: Type);
let Z := eval red in Yv in clear Yv;
unify B (X → Y → Z → Prop);
eapply rel_curry2_set_rel_transport
end
end.
The situation for impl is similar: since this transport instance
  can apply to pretty much anything, we need to register it on a
  case-by-case basis. Here is an example used in the CertiKOS proof
  for hypotheses of the form writable_block ge b:
    Hint Extern 10 (Transport _ _ _ (writable_block _ _) _) =>
      eapply impl_transport : typeclass_instances.
 
Often, the target hypotheses declared using the Transport class
  have existential quantifiers, and need to be broken up to get to the
  actual relational hypotheses we're interested in. The split_hyp
  tactic does that. We also use that opportunity to split up
  prod_rel and rel_incr when appropriate. For prod_rel this is
  especially useful when rel_curry is involved. Likewise we
  generally want to split up rel_incr as soon as possible, so that
  the existentially quantified world therein can be used to
  instantiate evars that have been spawned from that point on.
 
  As a generally useful complement, the split_hyps tactic applies
  the same process to all hypotheses. 
Ltac split_hyp H :=
lazymatch type of H with
| ex _ ⇒
destruct H as [? H];
split_hyp H
| _ ∧ _ ⇒
let H1 := fresh in
let H2 := fresh in
destruct H as [H1 H2];
split_hyp H1;
split_hyp H2
| prod_rel ?Rx ?Ry (?x1, ?y1) (?x2, ?y2) ⇒
change (Rx x1 x2 ∧ Ry y1 y2) in H;
split_hyp H
| rel_incr ?acc ?R ?w ?x ?y ⇒
let w' := fresh w "'" in
let Hw' := fresh "H" w' in
destruct H as (w' & Hw' & H);
split_hyp H
| _ ⇒
idtac
end.
Ltac split_hyps :=
repeat
lazymatch goal with
| H: ex _ |- _ ⇒
destruct H
| H: _ ∧ _ |- _ ⇒
destruct H
| H: prod_rel ?Rx ?Ry (?x1, ?y1) (?x2, ?y2) |- _ ⇒
change (Rx x1 x2 ∧ Ry y1 y2) in H
end.
We're now ready to defined the transport tactic, which
  essentially looks up a Transport instance and applies it the the
  hypothesis to be transported, using RAuto to solve the relational
  subgoal. Note that the relation and right-hand side will usually
  contain existential variables, but the proof search can hopefully
  proceed by following the structure of the left-hand side. Once the
  Transport instance has been applied, we use split_hyp on the
  modified hypothesis as post-processing.
 
  Because we have many open-ended evars involved, it is easy for
  trivial relations such as eq to kick in and sabotage us,
  overriding the more interesting properties that we actually want to
  use and preventing any progress from actually being made. In order
  to attenuate this issue, we require that RAuto yield unconvertible
  related elements.
 
  Another pitfall we want to avoid is illustrated by the option_rel
  case. When we have a hypothesis of the form H: m = Some a, but no
  interesting way to relate m to something else, then the search for
  (option_rel ?R) m ?n can end up using H itself (because
  option_rel eq is reflexive, hence subrel_eq applies). To prevent
  such cases we need to make sure that the hypothesis being
  transported is not used to discharge the relational premise, and so
  we revert it into the conclusing before we proceed, and use the
  following lemma to work on a goal of that form directly. 
Lemma transport_in_goal `{Transport} `{!RAuto (R a b)} `{!Unconvertible a b}:
∀ (Q: Prop), (PB → Q) → (PA → Q).
Proof.
firstorder.
Qed.
Ltac transport H :=
revert H;
lazymatch goal with
| |- ?PA → ?Q ⇒
apply (transport_in_goal (PA:=PA) Q)
end;
intro H;
split_hyp H.
Again we provide a tactic which attempts to transport all
  hypotheses. Notice that earlier transportations may provide new
  hypotheses making later transportations possible. Hence it would be
  hard to provide a much more effective tactic, even though this one
  may retry failing transportations many times. 
Ltac transport_hyps :=
repeat
match goal with
| H: _ |- _ ⇒ transport H
end.