Module UTyping

From Velus Require Import Common.
From Velus Require Import Operators Environment.
From Velus Require Import Clocks.
From Velus Require Import Fresh.
From Coq Require Import List. Import List.ListNotations. Open Scope list_scope.
From Velus Require Import Lustre.StaticEnv.
From Velus Require Import Lustre.LSyntax Lustre.LTyping.
From Velus Require Import Lustre.Unnesting.Unnesting.

Preservation of Typing through Unnesting


Module Type UTYPING
       (Import Ids : IDS)
       (Import Op : OPERATORS)
       (Import OpAux : OPERATORS_AUX Ids Op)
       (Import Cks : CLOCKS Ids Op OpAux)
       (Import Senv : STATICENV Ids Op OpAux Cks)
       (Import Syn : LSYNTAX Ids Op OpAux Cks Senv)
       (Import Typ : LTYPING Ids Op OpAux Cks Senv Syn)
       (Import Un : UNNESTING Ids Op OpAux Cks Senv Syn).

  Import Fresh Facts Tactics.

Preservation of typeof


  Fact unnest_noops_exps_typesof: forall cks es es' eqs' st st',
      length cks = length es ->
      Forall (fun e => numstreams e = 1) es ->
      unnest_noops_exps cks es st = (es', eqs', st') ->
      typesof es' = typesof es.
  Proof.

  Fact unnest_exp_typeof : forall G e is_control es' eqs' st st',
      wl_exp G e ->
      unnest_exp G is_control e st = (es', eqs', st') ->
      typesof es' = typeof e.
  Proof with

  Corollary mmap2_unnest_exp_typesof'' : forall G is_control es es' eqs' st st',
      Forall (wl_exp G) es ->
      mmap2 (unnest_exp G is_control) es st = (es', eqs', st') ->
      Forall2 (fun e ty => typeof e = [ty]) (concat es') (typesof es).
  Proof.

  Corollary mmap2_mmap2_unnest_exp_typesof : forall G is_control tys (es : list (enumtag * list exp)) es' eqs' st st',
      Forall (fun es => Forall (wl_exp G) (snd es)) es ->
      Forall (fun es => typesof (snd es) = tys) es ->
      mmap2 (fun '(i, es) => bind2 (mmap2 (unnest_exp G is_control) es)
                                  (fun es' eqs' => ret (i, concat es', concat eqs'))) es st = (es', eqs', st') ->
      Forall (fun es0 => Forall2 (fun e ty => typeof e = [ty]) (snd es0) tys) es'.
  Proof.

  Corollary mmap2_unnest_exp_typesof :
    forall G is_control es es' eqs' st st',
      Forall (wl_exp G) es ->
      mmap2 (unnest_exp G is_control) es st = (es', eqs', st') ->
      typesof (concat es') = typesof es.
  Proof.
  Local Hint Resolve mmap2_unnest_exp_typesof : norm.

  Corollary unnest_exps_typesof : forall G es es' eqs' st st',
      Forall (wl_exp G) es ->
      unnest_exps G es st = (es', eqs', st') ->
      typesof es' = typesof es.
  Proof.

  Fact unnest_rhs_typeof : forall G e es' eqs' st st',
      wl_exp G e ->
      unnest_rhs G e st = (es', eqs', st') ->
      typesof es' = typeof e.
  Proof with

  Corollary unnest_rhss_typesof : forall G es es' eqs' st st',
      Forall (wl_exp G) es ->
      unnest_rhss G es st = (es', eqs', st') ->
      typesof es' = typesof es.
  Proof with

A few additional tactics


  Fact idents_for_anns_incl_ty : forall anns ids st st',
    idents_for_anns anns st = (ids, st') ->
    forall x ty ck, In (x, (ty, ck)) ids -> HasType (st_senv st') x ty.
  Proof.

  Ltac solve_incl :=
    match goal with
    | Hiface : global_iface_incl ?G1 ?G2, H : wt_clock (types ?G1) _ ?ck |- wt_clock (types ?G2) _ ?ck =>
        eapply iface_incl_wt_clock; eauto
    | H : wt_clock _ ?l1 ?cl |- wt_clock _ ?l2 ?cl =>
        eapply wt_clock_incl; [|eauto]; intros
    | Hiface : global_iface_incl ?G1 ?G2, H : wt_exp ?G1 _ ?e |- wt_exp ?G2 _ ?e =>
        eapply iface_incl_wt_exp; eauto
    | H : wt_exp ?G ?l1 ?e |- wt_exp ?G ?l2 ?e =>
        eapply wt_exp_incl; [| |eauto]; intros
    | H : wt_equation ?G ?l1 ?eq |- wt_equation ?G ?l2 ?eq =>
        eapply wt_equation_incl; [| |eauto]; intros
    | Hiface : global_iface_incl ?G1 ?G2, H : wt_block ?G1 _ ?e |- wt_block ?G2 _ ?e =>
        eapply iface_incl_wt_block; eauto
    | H : wt_block ?G ?l1 ?eq |- wt_block ?G ?l2 ?eq =>
        eapply wt_block_incl; [| |eauto]; intros
    | |- incl ?l1 ?l1 => reflexivity
    | |- incl ?l1 (?l1 ++ ?l2) =>
        eapply incl_appl; reflexivity
    | |- incl (?l1 ++ ?l2) (?l1 ++ ?l3) =>
        eapply incl_app
    | |- incl ?l1 (?l2 ++ ?l3) =>
        eapply incl_appr
    | |- incl ?l1 (?a::?l2) =>
        eapply incl_tl
    | |- incl (st_anns ?st1) (st_anns _) =>
        eapply st_follows_incl; repeat solve_st_follows
    | |- incl (st_senv ?st1) (st_senv _) =>
        apply incl_map; repeat solve_st_follows
    | H : In ?x ?l1 |- In ?x ?l2 =>
        assert (incl l1 l2); eauto
    | H : HasType ?l1 ?x ?ty |- HasType ?l2 ?x ?ty =>
        eapply HasType_incl; [|eauto]
    | H : IsLast ?l1 ?x |- IsLast ?l2 ?x =>
        eapply IsLast_incl; [|eauto]
    end; auto with datatypes.

  Local Hint Resolve in_combine_l in_combine_r : datatypes.
  Local Hint Resolve incl_tl incl_appl incl_appr incl_app incl_refl : datatypes.

Preservation of wt through the first pass


  Section unnest_node_wt.
    Variable G1 : @global nolocal local_prefs.
    Variable G2 : @global unnested norm1_prefs.

    Hypothesis Hiface : global_iface_incl G1 G2.

    Fact idents_for_anns_wt : forall vars anns ids st st',
        idents_for_anns anns st = (ids, st') ->
        Forall (fun '(ty, cl) => wt_clock G2.(types) (vars++st_senv st) cl) anns ->
        Forall (wt_exp G2 (vars++st_senv st')) (map (fun '(x, ann) => Evar x ann) ids).
    Proof.

    Fact unnest_fby_wt_exp : forall vars e0s es anns,
        Forall (wt_clock G2.(types) vars) (map snd anns) ->
        Forall (wt_exp G2 vars) e0s ->
        Forall (wt_exp G2 vars) es ->
        Forall2 (fun e0 a => typeof e0 = [a]) e0s (map fst anns) ->
        Forall2 (fun e a => typeof e = [a]) es (map fst anns) ->
        Forall (wt_exp G2 vars) (unnest_fby e0s es anns).
    Proof.

    Fact unnest_arrow_wt_exp : forall vars e0s es anns,
        Forall (wt_clock G2.(types) vars) (map snd anns) ->
        Forall (wt_exp G2 vars) e0s ->
        Forall (wt_exp G2 vars) es ->
        Forall2 (fun e0 a => typeof e0 = [a]) e0s (map fst anns) ->
        Forall2 (fun e a => typeof e = [a]) es (map fst anns) ->
        Forall (wt_exp G2 vars) (unnest_arrow e0s es anns).
    Proof.

    Fact unnest_when_wt_exp : forall vars k ckid tx tn es tys ck,
        In (Tenum tx tn) G2.(types) ->
        HasType vars ckid (Tenum tx tn) ->
        k < length tn ->
        wt_clock G2.(types) vars ck ->
        Forall (wt_exp G2 vars) es ->
        Forall2 (fun e ty => typeof e = [ty]) es tys ->
        Forall (wt_exp G2 vars) (unnest_when (ckid, Tenum tx tn) k es tys ck).
    Proof.

    Import Permutation.

    Fact unnest_merge_wt_exp : forall vars ckid tx tn es tys ck,
        In (Tenum tx tn) G2.(types) ->
        HasType vars ckid (Op.Tenum tx tn) ->
        Permutation (map fst es) (seq 0 (length tn)) ->
        es <> [] ->
        wt_clock G2.(types) vars ck ->
        Forall (fun es => Forall (wt_exp G2 vars) (snd es)) es ->
        Forall (fun es => Forall2 (fun e ty => typeof e = [ty]) (snd es) tys) es ->
        Forall (wt_exp G2 vars) (unnest_merge (ckid, Tenum tx tn) es tys ck).
    Proof with

    Fact unnest_case_Total_wt_exp : forall vars e tx tn es tys ck,
        wt_exp G2 vars e ->
        typeof e = [Tenum tx tn] ->
        In (Tenum tx tn) G2.(types) ->
        Permutation (map fst es) (seq 0 (length tn)) ->
        es <> [] ->
        wt_clock G2.(types) vars ck ->
        Forall (fun es => Forall (wt_exp G2 vars) (snd es)) es ->
        Forall (fun es => Forall2 (fun e ty => typeof e = [ty]) (snd es) tys) es ->
        Forall (wt_exp G2 vars) (unnest_case e es None tys ck).
    Proof with

    Fact unnest_case_Default_wt_exp : forall vars e tx tn es d tys ck,
        wt_exp G2 vars e ->
        typeof e = [Tenum tx tn] ->
        In (Tenum tx tn) G2.(types) ->
        incl (map fst es) (seq 0 (length tn)) ->
        NoDupMembers es ->
        es <> [] ->
        wt_clock G2.(types) vars ck ->
        Forall (fun es => Forall (wt_exp G2 vars) (snd es)) es ->
        Forall (fun es => Forall2 (fun e ty => typeof e = [ty]) (snd es) tys) es ->
        Forall (wt_exp G2 vars) d ->
        Forall2 (fun d ty => typeof d = [ty]) d tys ->
        Forall (wt_exp G2 vars) (unnest_case e es (Some d) tys ck).
    Proof with

    Lemma unnest_noops_exps_wt : forall vars cks es es' eqs' st st' ,
        length es = length cks ->
        Forall normalized_lexp es ->
        Forall (fun e => numstreams e = 1) es ->
        Forall (wt_exp G2 (vars++st_senv st)) es ->
        unnest_noops_exps cks es st = (es', eqs', st') ->
        Forall (wt_exp G2 (vars++st_senv st')) es' /\
        Forall (wt_equation G2 (vars++st_senv st')) eqs'.
    Proof.

    Fact mmap2_wt {pref A B} :
      forall vars (k : A -> Fresh pref (list exp * list equation) B) a es' eqs' st st',
        mmap2 k a st = (es', eqs', st') ->
        (forall st st' a es eqs', k a st = (es, eqs', st') -> st_follows st st') ->
        Forall (fun a => forall es' eqs' st0 st0',
                    k a st0 = (es', eqs', st0') ->
                    st_follows st st0 ->
                    st_follows st0' st' ->
                    Forall (wt_exp G2 vars) es' /\
                    Forall (wt_equation G2 vars) eqs') a ->
        Forall (wt_exp G2 vars) (concat es') /\
        Forall (wt_equation G2 vars) (concat eqs').
    Proof.

    Fact mmap2_wt' {pref A B} :
      forall vars (k : A -> Fresh pref (enumtag * list exp * list equation) B) a es' eqs' st st',
        mmap2 k a st = (es', eqs', st') ->
        (forall st st' a es eqs', k a st = (es, eqs', st') -> st_follows st st') ->
        Forall (fun a => forall n es' eqs' st0 st0',
                    k a st0 = (n, es', eqs', st0') ->
                    st_follows st st0 ->
                    st_follows st0' st' ->
                    Forall (wt_exp G2 vars) es' /\
                    Forall (wt_equation G2 vars) eqs') a ->
        Forall (wt_exp G2 vars) (concat (map snd es')) /\
        Forall (wt_equation G2 vars) (concat eqs').
    Proof with

    Import Permutation.

    Local Hint Resolve iface_incl_wt_clock iface_incl_wt_exp : norm.

    Fact unnest_reset_wt : forall vars e e' eqs' st st' ,
        (forall es' eqs' st0',
            st_follows st0' st' ->
            unnest_exp G1 true e st = (es', eqs', st0') ->
            Forall (wt_exp G2 (vars++st_senv st0')) es' /\
            Forall (wt_equation G2 (vars++st_senv st0')) eqs') ->
        wt_exp G1 (vars++st_senv st) e ->
        typeof e = [OpAux.bool_velus_type] ->
        unnest_reset (unnest_exp G1 true) e st = (e', eqs', st') ->
        typeof e' = [OpAux.bool_velus_type] /\
        wt_exp G2 (vars++st_senv st') e' /\
        Forall (wt_equation G2 (vars++st_senv st')) eqs'.
    Proof.

    Corollary unnest_resets_wt : forall vars es es' eqs' st st' ,
        Forall (fun e => forall es' eqs' st0 st0',
                    unnest_exp G1 true e st0 = (es', eqs', st0') ->
                    st_follows st st0 ->
                    st_follows st0' st' ->
                    Forall (wt_exp G2 (vars++st_senv st0')) es' /\
                    Forall (wt_equation G2 (vars++st_senv st0')) eqs') es ->
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        Forall (fun e => typeof e = [OpAux.bool_velus_type]) es ->
        mmap2 (unnest_reset (unnest_exp G1 true)) es st = (es', eqs', st') ->
        Forall (fun e => typeof e = [OpAux.bool_velus_type]) es' /\
        Forall (wt_exp G2 (vars++st_senv st')) es' /\
        Forall (wt_equation G2 (vars++st_senv st')) (concat eqs').
    Proof.

    Local Hint Resolve iface_incl_wt_type : norm.

    Fact unnest_controls_fst : forall (es : list (enumtag * list exp)) es' eqs' st st',
        mmap2 (fun '(i, es) => bind2 (mmap2 (unnest_exp G1 true) es)
                                  (fun es' eqs' => ret (i, concat es', concat eqs'))) es st = (es', eqs', st') ->
        map fst es' = map fst es.
    Proof.

    Lemma unnest_exp_wt : forall vars e is_control es' eqs' st st',
        wt_exp G1 (vars++st_senv st) e ->
        unnest_exp G1 is_control e st = (es', eqs', st') ->
        Forall (wt_exp G2 (vars++st_senv st')) es' /\
        Forall (wt_equation G2 (vars++st_senv st')) eqs'.
    Proof with

    Corollary mmap2_unnest_exp_wt : forall vars is_control es es' eqs' st st' ,
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        mmap2 (unnest_exp G1 is_control) es st = (es', eqs', st') ->
        Forall (wt_exp G2 (vars++st_senv st')) (concat es') /\
        Forall (wt_equation G2 (vars++st_senv st')) (concat eqs').
    Proof.

    Corollary unnest_exps_wt : forall vars es es' eqs' st st' ,
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        unnest_exps G1 es st = (es', eqs', st') ->
        Forall (wt_exp G2 (vars++st_senv st')) es' /\
        Forall (wt_equation G2 (vars++st_senv st')) eqs'.
    Proof.

    Fact unnest_rhs_wt : forall vars e es' eqs' st st' ,
        wt_exp G1 (vars++st_senv st) e ->
        unnest_rhs G1 e st = (es', eqs', st') ->
        Forall (wt_exp G2 (vars++st_senv st')) es' /\
        Forall (wt_equation G2 (vars++st_senv st')) eqs'.
    Proof with

    Corollary unnest_rhss_wt : forall vars es es' eqs' st st' ,
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        unnest_rhss G1 es st = (es', eqs', st') ->
        Forall (wt_exp G2 (vars++st_senv st')) es' /\
        Forall (wt_equation G2 (vars++st_senv st')) eqs'.
    Proof with

    Fact unnest_equation_wt_eq : forall vars eq eqs' st st' ,
        wt_equation G1 (vars++st_senv st) eq ->
        unnest_equation G1 eq st = (eqs', st') ->
        Forall (wt_equation G2 (vars++st_senv st')) eqs'.
    Proof with

    Lemma unnest_block_wt : forall vars d blocks' st st',
        wt_block G1 (vars++st_senv st) d ->
        unnest_block G1 d st = (blocks', st') ->
        Forall (wt_block G2 (vars++st_senv st')) blocks'.
    Proof.

    Corollary unnest_blocks_wt_block : forall vars blocks blocks' st st' ,
        Forall (wt_block G1 (vars++st_senv st)) blocks ->
        unnest_blocks G1 blocks st = (blocks', st') ->
        Forall (wt_block G2 (vars++st_senv st')) blocks'.
    Proof with

Preservation of wt_clock


    Definition st_clocks {pref} (st : fresh_st pref (Op.type * clock)) : list clock :=
      map (fun '(_, (_, cl)) => cl) (st_anns st).

    Fact fresh_ident_wt_clock : forall types pref hint vars ty cl id (st st' : fresh_st pref _),
        Forall (wt_clock types vars) (st_clocks st) ->
        wt_clock types vars cl ->
        fresh_ident hint (ty, cl) st = (id, st') ->
        Forall (wt_clock types vars) (st_clocks st').
    Proof.

    Corollary idents_for_anns_wt_clock : forall types vars anns ids st st',
        Forall (wt_clock types vars) (st_clocks st) ->
        Forall (wt_clock types vars) (map snd anns) ->
        idents_for_anns anns st = (ids, st') ->
        Forall (wt_clock types vars) (st_clocks st').
    Proof.

    Fact mmap2_wt_clock {A A1 A2 : Type} :
      forall types vars (k : A -> FreshAnn (A1 * A2)) a a1s a2s st st',
        Forall (wt_clock types (vars++st_senv st)) (st_clocks st) ->
        mmap2 k a st = (a1s, a2s, st') ->
        (forall st st' a es a2s, k a st = (es, a2s, st') -> st_follows st st') ->
        Forall (fun a => forall a1s a2s st0 st0',
                    Forall (wt_clock types (vars++st_senv st0)) (st_clocks st0) ->
                    k a st0 = (a1s, a2s, st0') ->
                    st_follows st st0 ->
                    st_follows st0' st' ->
                    Forall (wt_clock types (vars++st_senv st0')) (st_clocks st0')) a ->
        Forall (wt_clock types (vars++st_senv st')) (st_clocks st').
    Proof with

    Fact unnest_reset_wt_clock : forall vars e e' eqs' st st' ,
        (forall es' eqs' st0',
            st_follows st0' st' ->
            unnest_exp G1 true e st = (es', eqs', st0') ->
            Forall (wt_clock G2.(types) (vars++st_senv st0')) (st_clocks st0')) ->
        wt_exp G1 (vars++st_senv st) e ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_reset (unnest_exp G1 true) e st = (e', eqs', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof with

    Fact unnest_resets_wt_clock : forall vars es es' eqs' st st' ,
        Forall (fun e => forall es' eqs' st0 st0',
                    Forall (wt_clock G2.(types) (vars++st_senv st0)) (st_clocks st0) ->
                    unnest_exp G1 true e st0 = (es', eqs', st0') ->
                    st_follows st st0 ->
                    st_follows st0' st' ->
                    Forall (wt_clock G2.(types) (vars++st_senv st0')) (st_clocks st0')) es ->
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        mmap2 (unnest_reset (unnest_exp G1 true)) es st = (es', eqs', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof.

    Lemma unnest_noops_exps_wt_clock : forall vars cks es es' eqs' st st' ,
        length es = length cks ->
        Forall normalized_lexp es ->
        Forall (fun e => numstreams e = 1) es ->
        Forall (wt_exp G2 (vars++st_senv st)) es ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_noops_exps cks es st = (es', eqs', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof.

    Fact unnest_exp_wt_clock : forall vars e is_control es' eqs' st st' ,
        wt_exp G1 (vars++st_senv st) e ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_exp G1 is_control e st = (es', eqs', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof with

    Corollary unnest_exps_wt_clock : forall vars es es' eqs' st st' ,
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_exps G1 es st = (es', eqs', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof.

    Fact unnest_rhs_wt_clock : forall vars e es' eqs' st st' ,
        wt_exp G1 (vars++st_senv st) e ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_rhs G1 e st = (es', eqs', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof with

    Corollary unnest_rhss_wt_clock : forall vars es es' eqs' st st' ,
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_rhss G1 es st = (es', eqs', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof.

    Fact unnest_equation_wt_clock : forall vars eq eqs' st st' ,
        wt_equation G1 (vars++st_senv st) eq ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_equation G1 eq st = (eqs', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof.

    Fact unnest_block_wt_clock : forall vars d blocks' st st' ,
        wt_block G1 (vars++st_senv st) d ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_block G1 d st = (blocks', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof.

    Corollary unnest_blocks_wt_clock : forall vars blocks blocks' st st' ,
        Forall (wt_block G1 (vars++st_senv st)) blocks ->
        Forall (wt_clock G2.(types) (vars++st_senv st)) (st_clocks st) ->
        unnest_blocks G1 blocks st = (blocks', st') ->
        Forall (wt_clock G2.(types) (vars++st_senv st')) (st_clocks st').
    Proof.

The enum types used in the new variables are also correct

    Lemma fresh_ident_wt_type : forall vars pref hint ty ck id (st st': fresh_st pref _),
        wt_type G2.(types) ty ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        fresh_ident hint (ty, ck) st = (id, st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Corollary idents_for_anns_wt_type : forall vars anns ids st st',
        Forall (wt_type G2.(types)) (map fst anns) ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        idents_for_anns anns st = (ids, st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Fact mmap2_wt_type {A A1 A2 : Type} :
      forall vars (k : A -> FreshAnn (A1 * A2)) a a1s a2s st st',
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        mmap2 k a st = (a1s, a2s, st') ->
        (forall st st' a es a2s, k a st = (es, a2s, st') -> st_follows st st') ->
        Forall (fun a => forall a1s a2s st0 st0',
                    Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st0)) ->
                    k a st0 = (a1s, a2s, st0') ->
                    st_follows st st0 ->
                    st_follows st0' st' ->
                    Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st0'))) a ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof with

    Hypothesis WtG1 : wt_global G1.
    Hypothesis WtG2 : wt_global G2.

    Fact unnest_reset_wt_type : forall vars e e' eqs' st st' ,
        (forall es' eqs' st0',
            st_follows st0' st' ->
            unnest_exp G1 true e st = (es', eqs', st0') ->
            Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st0'))) ->
        wt_exp G1 (vars++st_senv st) e ->
        typeof e = [OpAux.bool_velus_type] ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_reset (unnest_exp G1 true) e st = (e', eqs', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof with

    Corollary unnest_resets_wt_type : forall vars es es' eqs' st st' ,
        Forall (fun e => forall es' eqs' st0 st0',
                    Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st0)) ->
                    unnest_exp G1 true e st0 = (es', eqs', st0') ->
                    st_follows st st0 ->
                    st_follows st0' st' ->
                    Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st0'))) es ->
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        Forall (fun e => typeof e = [OpAux.bool_velus_type]) es ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        mmap2 (unnest_reset (unnest_exp G1 true)) es st = (es', eqs', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Fact unnest_noops_exps_wt_type : forall vars cks es es' eqs' st st' ,
        length es = length cks ->
        Forall (fun e => numstreams e = 1) es ->
        Forall (wt_exp G2 (vars++st_senv st)) es ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_noops_exps cks es st = (es', eqs', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Lemma unnest_exp_wt_type : forall vars e is_control es' eqs' st st' ,
        wt_exp G1 (vars++st_senv st) e ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_exp G1 is_control e st = (es', eqs', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Corollary unnest_exps_wt_type : forall vars es es' eqs' st st' ,
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_exps G1 es st = (es', eqs', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Fact unnest_rhs_wt_type : forall vars e es' eqs' st st' ,
        wt_exp G1 (vars++st_senv st) e ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_rhs G1 e st = (es', eqs', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof with

    Corollary unnest_rhss_wt_type : forall vars es es' eqs' st st' ,
        Forall (wt_exp G1 (vars++st_senv st)) es ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_rhss G1 es st = (es', eqs', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Fact unnest_equation_wt_type : forall vars eq eqs' st st' ,
        wt_equation G1 (vars++st_senv st) eq ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_equation G1 eq st = (eqs', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Fact unnest_block_wt_type : forall vars d blocks' st st' ,
        wt_block G1 (vars++st_senv st) d ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_block G1 d st = (blocks', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

    Corollary unnest_blocks_wt_type : forall vars blocks blocks' st st' ,
        Forall (wt_block G1 (vars++st_senv st)) blocks ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st)) ->
        unnest_blocks G1 blocks st = (blocks', st') ->
        Forall (wt_type G2.(types)) (map (fun '(_, ann) => ann.(typ)) (vars++st_senv st')).
    Proof.

Finally, we can prove that the node is properly normalized

    Lemma unnest_node_wt : forall n,
        wt_node G1 n ->
        wt_node G2 (unnest_node G1 n).
    Proof.

  End unnest_node_wt.

  Lemma unnest_global_wt : forall G,
      wt_global G ->
      wt_global (unnest_global G).
  Proof.

End UTYPING.

Module UTypingFun
       (Ids : IDS)
       (Op : OPERATORS)
       (OpAux : OPERATORS_AUX Ids Op)
       (Cks : CLOCKS Ids Op OpAux)
       (Senv : STATICENV Ids Op OpAux Cks)
       (Syn : LSYNTAX Ids Op OpAux Cks Senv)
       (Typ : LTYPING Ids Op OpAux Cks Senv Syn)
       (Un : UNNESTING Ids Op OpAux Cks Senv Syn)
       <: UTYPING Ids Op OpAux Cks Senv Syn Typ Un.
  Include UTYPING Ids Op OpAux Cks Senv Syn Typ Un.
End UTypingFun.