(*************************************************************************)
(**                                                                      *)
(** * Typing: preservation and progress                                  *)
(**                                                                      *)
(*************************************************************************)
(*************************************************************************)
(** * Typing contexts *)
(*************************************************************************)
(** We represent contexts as association lists (lists of pairs of keys and values) whose keys are [atom]s.   *)
(** For STLC, contexts bind [atom]s to [typ]s.  We define an abbreviation [ctx] for the type of these contexts.  Lists are defined in Coq's standard library, with the constructors [nil] and [cons].  The list library includes the [::] notation for cons as well as standard list operations such as append, map, and fold. The infix operation "++" is list append.  The Metatheory library extends this reasoning by instantiating the AssocList library to provide support for association lists whose keys are [atom]s.  Everything in this library is polymorphic over the type of objects bound in the context.  Look in AssocList for additional details about the functions and predicates that we mention below.  *)
(** Context equality *)
(** When reasoning about contexts, we often need to talk about bindings in the "middle" of an context. Therefore, it is common for lemmas and definitions to use list append in their statements. Unfortunately, list append is associative, so two Coq expressions may denote the same context even though they are not equal.  The tactic [simpl_env] reassociates all concatenations of contexts to the right.  *)
(** To make contexts easy to read, instead of building them from [nil] and [cons], we prefer to build them from the following components: - [nil]: The empty list. - [one]: Lists consisting of exactly one item. - [++]:  List append. *)
(** The simpl_env tactic actually puts lists built from only nil, one and [++] into a "normal form". This process reassociates all appends to the right, removes extraneous nils and converts cons to singleton lists with an append. *)
(* /FULL *)
(** Context operations. *)
(** The ternary predicate [binds] holds when a given binding is present somewhere in an context. *)
(** The function [dom] computes the domain of an context, returning a finite set of [atom]s. Note that we cannot use Coq's equality for finite sets, we must instead use a defined relation [=] for atom set equality. *)
(** The unary predicate [uniq] holds when each atom is bound at most once in an context. *)
(*************************************************************************)
(** * Typing relation and cofinite quantification *)
(*************************************************************************)
(** The typing relation in the STLC definition states the typing rule for abstractions in a particular way.  << typing_abs : forall (L : atoms) (G : ctx) (T1 : typ) (e : exp) (T2 : typ), (forall x : atom, x `notin` L -> typing ([(x, T1)] ++ G) (open e (var_f x)) T2) -> typing G (abs e) (typ_arrow T1 T2) >>  To show that abstractions are well-typed, we must show that the body of the abstraction is well-typed for any choice of variable, except those from some unspecified set `L`.  This type of rule differs from the quantification in the [lc_abs] rule (which does not restrict the variables at all) or the [lc_abs_exists] version (which only applies for one particular variable. We call this version "co-finite" quantification.  To see why this version is necessary, we will start with a more traditional version of the rules that only uses a single variable in the abs rule and then see what goes wrong. (We say that the [typing_e] judgement below uses "exists-fresh" quantification in the abs rule.)  *)
Inductive typing_e : ctx -> exp -> typ -> Prop :=
| typing_e_var : forall E (x : atom) T,
uniq E ->
binds x T E ->
typing_e E (var_f x) T
| typing_e_abs : forall x E e T1 T2,
x `notin` dom E \u fv_exp e ->
typing_e ([(x, T1)] ++ E) (e ^ x) T2 ->
typing_e E (abs e) (typ_arrow T1 T2)
| typing_e_app : forall E e1 e2 T1 T2,
typing_e E e1 (typ_arrow T1 T2) ->
typing_e E e2 T1 ->
typing_e E (app e1 e2) T2.
Hint Constructors typing_e : core.
(*************************************************************************)
(** ** Weakening  *)
(*************************************************************************)
(** Weakening states that if an expression is typeable in some context, then it is typeable in any well-formed extension of that context.  This property is needed to prove the substitution lemma.  As stated below, this lemma is not directly proveable.  The natural way to try proving this lemma proceeds by induction on the typing derivation for [e]. *)
(** *** Demo [typing_weakening]  We can now prove our original statement of weakening.  The only interesting step is the use of the rewrite_env tactic. *)
(*************************************************************************)
(** ** Substitution *)
(*************************************************************************)
(** Having proved weakening, we can now prove the usual substitution lemma, which we state both in the form we need and in the strengthened form needed to make the proof go through.  << typing_subst_simple : forall E e u S T z, typing ([(z,S)] ++ E) e T -> typing E u S -> typing E ([z ~> u] e) T  typing_subst : forall E F e u S T z, typing (F ++ [(z,S)] ++ E) e T -> typing E u S -> typing (F ++ E) ([z ~> u] e) T >>  The proof of the strengthened statement proceeds by induction on the given typing derivation for [e].  The most involved case is the one for variables; the others follow from the induction hypotheses. *)
(** *** Exercise [typing_subst_var_case]  Below, we state what needs to be proved in the [typing_var] case of the substitution lemma.  Fill in the proof.  Proof sketch: The proof proceeds by a case analysis on [(x == z)], i.e., whether the two variables are the same or not.  - If [(x = z)], then we need to show [(typing (F ++ E) u T)]. This follows from the given typing derivation for [u] by weakening and the fact that [T] must equal [S].  - If [(x <> z)], then we need to show [(typing (F ++ E) x T)]. This follows by the typing rule for variables.  HINTS: Lemmas [binds_mid_eq], [uniq_remove_mid], and [binds_remove_mid] are useful.  *)

Lemma typing_subst_var_case : forall (E F : ctx) u S T (z x : atom),
binds x T (F ++ [(z,S)] ++ E) ->
uniq (F ++ [(z,S)] ++ E) ->
typing E u S ->
typing (F ++ E) ([z ~> u] (var_f x)) T.