(*************************************************************************)
(** * Tactic support *)
(*************************************************************************)
(** When picking a fresh var or applying a rule that uses cofinite quantification, choosing a set of vars to be fresh for can be tedious.  In practice, it is simpler to use a tactic to choose the set to be as large as possible.  The tactic [gather_atoms] is used to collect together all the atoms in the context.  It relies on an auxiliary tactic, [gather_atoms_with] (from MetatheoryAtom), which maps a function that returns a finite set of atoms over all hypotheses with the appropriate type. *)
Ltac gather_atoms ::=
let A := gather_atoms_with (fun x : atoms => x) in
let B := gather_atoms_with (fun x : atom => singleton x) in
let C := gather_atoms_with (fun x : list (var * typ) => dom x) in
let D := gather_atoms_with (fun x : exp => fv_exp x) in
constr:(A `union` B `union` C `union` D).
(** A number of other, useful tactics are defined by the Metatheory library, and each depends on [gather_atoms].  By redefining [gather_atoms], denoted by the [::=] in its definition below, we automatically update these tactics so that they use the proper notion of "all atoms in the context."  For example, the tactic [(pick fresh x)] chooses an atom fresh for "everything" in the context.  It is the same as [(pick fresh x for L)], except where [L] has been computed by [gather_atoms].  *)
(*************************************************************************)
(** * Renaming *)
(*************************************************************************)
(** Substitution and weakening together give us a property we call renaming: (see [typing_rename below] that we can change the name of the variable used to open an expression in a typing derivation. In practice, this means that if a variable is not "fresh enough" during a proof, we can use this lemma to rename it to one with additional freshness constraints.  Renaming is used below to show the correspondence between the exists-fresh version of the rules with the cofinite version, and also to show that typechecking is decidable. *)
(** However, before we prove renaming, we need an auxiliary lemma about typing judgments which says that terms are well-typed only in unique contexts. *)

(** Demo: the proof of renaming.  Note that this proof does not proceed by induction: even if we add new typing rules to the language, as long as the weakening and substution properties hold we can use this proof. *)
Lemma typing_rename : forall (x y : atom) E e T1 T2,
x `notin` fv_exp e ->
y `notin` dom E ->
typing ([(x, T1)] ++ E) (e ^ x) T2 ->
typing ([(y, T1)] ++ E) (e ^ y) T2.