bedrock.lang.cpp.syntax.mtraverse

(*
 * Copyright (c) 2023-2024 BlueRock Security, Inc.
 * This software is distributed under the terms of the BedRock Open-Source License.
 * See the LICENSE-BedRock file in the repository root for details.
 *)


Require Import bedrock.lang.cpp.syntax.prelude.
Require Import bedrock.lang.cpp.syntax.core.
Require Export bedrock.lang.cpp.syntax.handler.
Require Import bedrock.lang.cpp.syntax.decl.
Require Import bedrock.lang.cpp.syntax.translation_unit.

Import UPoly.

#[local] Set Printing Implicit.

#[local] Set Universe Polymorphism.
#[local] Set Polymorphic Inductive Cumulativity.
#[local] Unset Auto Template Polymorphism.
#[local] Unset Universe Minimization ToSet.

(* We factor a traversal of the abstract syntax tree in order
   to reduce duplication between a variety of traversals that
   occur on the syntax.
 *)


(* TODO (upstream): lift these definitions *)
Module prod.
  Definition fmap {A B A' B' : Set} (f : A -> B) (g : A' -> B') (p : A * A') : B * B' :=
    (f p.1, g p.2).
  #[global] Arguments fmap _ _ _ _ _ _ & _ : assert.
  #[global] Hint Opaque fmap : typeclass_instances.

  #[universes(polymorphic)]
  Definition traverse@{u | } {F : Set -> Type@{u}} `{!FMap F, !MRet F, AP : !Ap F}
      {A B A' B' : Set} (f : A -> F B) (g : A' -> F B') (p : prod A A') : F (prod B B') :=
    pair <$> f p.1 <*> g p.2.
  #[global] Arguments traverse _ _ _ _ _ _ _ _ _ & _ _ : assert.
  #[global] Hint Opaque traverse : typeclass_instances.
End prod.

Section option.
  Universes u1 u2 uA uB.
  Constraint u1 <= u2 , uA <= u1 , uB <= u1.
  Constraint uA <= option.u0 , uB <= option.u0.

  Context {F : Type@{u1} -> Type@{u2}}.
  Context `{!FMap@{u1 u2 uB} F}.
  Context `{!MRet F, !MFail F}.
  Context `{AP : !Ap@{u1 u2 uA uB} F}.

  Definition option_traverse2@{uC | uC <= uA , uC <= uB}
      {A : Type@{uA}} {B : Type@{uB}} {C : Type@{uC}}
      (f : A -> B -> F C) :=
    fix option_traverse2 (xs : option A) (ys : option B) : F (option C) :=
      match xs , ys with
      | None , None => mret None
      | Some x , Some y => Some <$> f x y
      | _ , _ => mfail
      end.

  Definition option_iter2@{} {A : Type@{uA}} {B : Type@{uB}} (f : A -> B -> F unit) :=
    fix option_iter2 (xs : option A) (ys : option B) : F unit :=
      match xs , ys with
      | None , None => mret ()
      | Some x , Some y => const1 () <$> f x y
      | _ , _ => mfail
      end.
End option.

Section list.
  Universes u1 u2 uA uB.
  Constraint u1 <= u2 , uA <= u1 , uB <= u1.
  Constraint uA <= list.u0 , uB <= list.u0.

  Context {F : Type@{u1} -> Type@{u2}}.
  Context `{!FMap@{u1 u2 uB} F}.
  Context `{!MRet F, !MFail F}.
  Context `{AP : !Ap@{u1 u2 uA uB} F}.

  Definition list_traverse2@{uC | uC <= uA , uC <= uB}
      {A : Type@{uA}} {B : Type@{uB}} {C : Type@{uC}}
      (f : A -> B -> F C) :=
    fix list_traverse2 (xs : list A) (ys : list B) : F (list C) :=
      match xs , ys with
      | nil , nil => mret nil
      | x :: xs , y :: ys => cons <$> f x y <*> list_traverse2 xs ys
      | _ , _ => mfail
      end.

  Definition list_iter2@{} {A : Type@{uA}} {B : Type@{uB}} (f : A -> B -> F unit) :=
    fix list_iter2 (xs : list A) (ys : list B) : F unit :=
      match xs , ys with
      | nil , nil => mret ()
      | x :: xs , y :: ys => const2 () <$> f x y <*> list_iter2 xs ys
      | _ , _ => mfail
      end.

  Definition list_iter3@{uC | uC <= list.u0 }
      {A : Type@{uA}} {B : Type@{uB}} {C : Type@{uC}}
      (f : A -> B -> C -> F unit) :=
    fix list_iter3 (xs : list A) (ys : list B) (zs : list C) : F unit :=
      match xs , ys , zs with
      | nil , nil , nil => mret ()
      | x :: xs , y :: ys , z :: zs => const2 () <$> f x y z <*> list_iter3 xs ys zs
      | _ , _ , _ => mfail
      end.
End list.

Definition alterF {K A M : Type} `{!Lookup K A M, !PartialAlter K A M}
    {F : Type -> Type} `{!FMap F}
    (f : option A -> F (option A)) (k : K) (m : M) : F M :=
  match m !! k with
  | None =>
    (
      fun v =>
      match v with
      | None => m
      | Some v => partial_alter (fun _ => Some v) k m
      end
    ) <$> f None
  | Some cur =>
    (fun x => partial_alter (fun _ => x) k m) <$> f (Some cur)
  end.

Traversals on template language syntax


Module MTraverse.

  Section traverse.
    Universe u.
    Context {F : Set -> Type@{u}}.
    Context `{!UPoly.FMap F, !UPoly.MRet F, !UPoly.MBind F, AP : !UPoly.Ap F}.
    Context {lang1 lang2 : lang.t}.
    Context (hT : type_handler lang1 lang2 F).
    Context (hE : expr_handler lang1 lang2 F).

    #[local] Notation ET := (hE.(handle_expr_type)) (only parsing).

    Fixpoint traverseN (n : name' lang1) : F (name' lang2) :=
      match n with
      | Ninst n xs => Ninst <$> traverseN n <*> traverse (T:=eta list) (temp_arg.traverse traverseN traverseT traverseE) xs
      | Nglobal c => Nglobal <$> atomic_name.traverse traverseT c
      | Ndependent t => Ndependent <$> traverseT t
      | Nscoped n c => Nscoped <$> traverseN n <*> atomic_name.traverse traverseT c
      | Nunsupported msg => mret $ Nunsupported msg
      end

    with traverseT (t : type' lang1) : F (type' lang2) :=
      match t with
      | Tparam T => hT.(handle_Tparam) T
      | Tresult_param X => hT.(handle_Tresult_param) X
      | Tresult_global on => hT.(handle_Tresult_global) on (fun _ => traverseN on)
      | Tresult_unop o t => hT.(handle_Tresult_unop) o t (fun _ => traverseT t)
      | Tresult_binop o l r => hT.(handle_Tresult_binop) o l r (fun _ => traverseT l) (fun _ => traverseT r)
      | Tresult_call on ts => hT.(handle_Tresult_call) on ts (fun _ => traverseN on) (fun _ => traverseT <$> ts)
      | Tresult_member_call on o ts => hT.(handle_Tresult_member_call) on o ts (fun _ => traverseN on) (fun _ => traverseT o) (fun _ => traverseT <$> ts)
      | Tresult_parenlist t ts => hT.(handle_Tresult_parenlist) t ts (fun _ => traverseT t) (fun _ => traverseT <$> ts)
      | Tresult_member o f => hT.(handle_Tresult_member) o f (fun _ => traverseT o) (fun _ => traverseN f)
      | Tnamed gn => hT.(handle_Tnamed) gn (fun _ => traverseN gn)
      | Tref t => hT.(handle_Tref) t (fun _ => traverseT t)
      | Trv_ref t => hT.(handle_Trv_ref) t (fun _ => traverseT t)
      | Tqualified cv t => hT.(handle_Tqualified) cv t (fun _ => traverseT t)

      
Everything else is structural

      | Tptr t => Tptr <$> traverseT t
      | Tnum sz sgn => mret $ Tnum sz sgn
      | Tchar_ t => mret $ Tchar_ t
      | Tvoid => mret Tvoid
      | Tarray t n => Tarray <$> traverseT t <*> mret n
      | Tincomplete_array t => Tincomplete_array <$> traverseT t
      | Tvariable_array t e => Tvariable_array <$> traverseT t <*> traverseE e
      | Tenum gn => Tenum <$> traverseN gn
      | Tfunction ft => Tfunction <$> function_type.traverse traverseT ft
      | Tbool => mret Tbool
      | Tmember_pointer gn t => Tmember_pointer <$> traverseT gn <*> traverseT t
      | Tfloat_ t => mret $ Tfloat_ t
      | Tnullptr => mret Tnullptr
      | Tarch sz t => mret $ Tarch sz t
      | Tdecltype e => Tdecltype <$> traverseE e
      | Texprtype e => Texprtype <$> traverseE e
      | Tunsupported msg => mret $ Tunsupported msg
      end

    with traverseE (e : Expr' lang1) : F (Expr' lang2) :=
      match e with
      | Eparam X => hE.(handle_Eparam) X
      | Eunresolved_global on => hE.(handle_Eunresolved_global) on (fun _ => traverseN on)
      | Eunresolved_unop o e => hE.(handle_Eunresolved_unop) o e (fun _ => traverseE e)
      | Eunresolved_binop o l r => hE.(handle_Eunresolved_binop) o l r (fun _ => traverseE l) (fun _ => traverseE r)
      | Eunresolved_call on es => hE.(handle_Eunresolved_call) on es (fun _ => traverseN on) (fun _ => traverseE <$> es)
      | Eunresolved_member_call on e es => hE.(handle_Eunresolved_member_call) on e es (fun _ => traverseN on) (fun _ => traverseE e) (fun _ => traverseE <$> es)
      | Eunresolved_parenlist t es => hE.(handle_Eunresolved_parenlist) t es (fun _ => traverseT <$> t) (fun _ => traverseE <$> es)
      | Eunresolved_member e f => hE.(handle_Eunresolved_member) e f (fun _ => traverseE e) (fun _ => traverseN f)

      
Everything else is structural

      | Evar v t => Evar v <$> traverseT t
      | Eenum_const gn c => Eenum_const <$> traverseN gn <*> mret c
      | Eglobal on t => Eglobal <$> traverseN on <*> traverseT t
      | Eglobal_member on t => Eglobal_member <$> traverseN on <*> traverseT t

      | Echar c t => Echar c <$> ET (traverseT t)
      | Estring s t => Estring s <$> ET (traverseT t)
      | Eint z t => Eint z <$> ET (traverseT t)
      | Ebool b => mret $ Ebool b
      | Eunop o e t => Eunop o <$> traverseE e <*> ET (traverseT t)
      | Ebinop o e1 e2 t => Ebinop o <$> traverseE e1 <*> traverseE e2 <*> ET (traverseT t)
      | Ederef e t => Ederef <$> traverseE e <*> ET (traverseT t)
      | Eaddrof e => Eaddrof <$> traverseE e
      | Eassign e1 e2 t => Eassign <$> traverseE e1 <*> traverseE e2 <*> ET (traverseT t)
      | Eassign_op o e1 e2 t => Eassign_op o <$> traverseE e1 <*> traverseE e2 <*> ET (traverseT t)
      | Epreinc e t => Epreinc <$> traverseE e <*> ET (traverseT t)
      | Epostinc e t => Epostinc <$> traverseE e <*> ET (traverseT t)
      | Epredec e t => Epredec <$> traverseE e <*> ET (traverseT t)
      | Epostdec e t => Epostdec <$> traverseE e <*> ET (traverseT t)
      | Eseqand e1 e2 => Eseqand <$> traverseE e1 <*> traverseE e2
      | Eseqor e1 e2 => Eseqor <$> traverseE e1 <*> traverseE e2
      | Ecomma e1 e2 => Ecomma <$> traverseE e1 <*> traverseE e2
      | Ecall e es => Ecall <$> traverseE e <*> traverse (T:=eta list) traverseE es
      | Eexplicit_cast s t e => Eexplicit_cast s <$> traverseT t <*> traverseE e

      | Ecast c e => Ecast <$> traverseC c <*> traverseE e
      | Emember arrow e f mut t => Emember arrow <$> traverseE e <*> atomic_name.traverse traverseT f <*> mret mut <*> traverseT t
      | Emember_ignore arrow eobj e => Emember_ignore arrow <$> traverseE eobj <*> traverseE e
      | Emember_call arrow m o es => Emember_call arrow <$> MethodRef.traverse traverseN traverseT traverseE m <*> traverseE o <*> traverse (T:=eta list) traverseE es
      | Eoperator_call oo oi es => Eoperator_call oo <$> operator_impl.traverse traverseN traverseT oi <*> traverse (T:=eta list) traverseE es
      | Esubscript e1 e2 t => Esubscript <$> traverseE e1 <*> traverseE e2 <*> ET (traverseT t)
      | Esizeof et t => Esizeof <$> sum_traverse traverseT traverseE et <*> ET (traverseT t)
      | Ealignof et t => Ealignof <$> sum_traverse traverseT traverseE et <*> ET (traverseT t)
      | Eoffsetof gn f t => Eoffsetof <$> traverseT gn <*> mret f <*> ET (traverseT t)
      | Econstructor n es t => Econstructor <$> traverseN n <*> traverse (T:=eta list) traverseE es <*> ET (traverseT t)
      | Elambda n es => Elambda <$> traverseN n <*> traverse (T:=eta list) traverseE es

      | Eimplicit e => Eimplicit <$> traverseE e
      | Eimplicit_init t => Eimplicit_init <$> ET (traverseT t)
      | Eif a b c t => Eif <$> traverseE a <*> traverseE b <*> traverseE c <*> traverseT t
      | Eif2 n a b c d t => Eif2 n <$> traverseE a <*> traverseE b <*> traverseE c <*> traverseE d <*> traverseT t
      | Ethis t => Ethis <$> ET (traverseT t)
      | Enull => mret Enull
      | Einitlist es oe t =>
          Einitlist <$> traverse (T:=eta list) traverseE es <*> traverse (T:=eta option) traverseE oe <*> ET (traverseT t)
      | Einitlist_union fld oe t =>
          Einitlist_union <$> atomic_name.traverse traverseT fld <*> traverse (T:=eta option) traverseE oe <*> ET (traverseT t)

      | Enew fn es pass_align t sz init => Enew <$> prod.traverse traverseN traverseT fn <*> traverse (T:=eta list) traverseE es <*> mret pass_align <*> traverseT t <*> traverse (T:=eta option) traverseE sz <*> traverse (T:=eta option) traverseE init
      | Edelete a fn e t => Edelete a <$> prod.traverse traverseN traverseT fn <*> traverseE e <*> traverseT t
      | Eandclean e => Eandclean <$> traverseE e
      | Ematerialize_temp e vc => Ematerialize_temp <$> traverseE e <*> mret vc
      | Eatomic ao es t => Eatomic ao <$> traverse (T:=eta list) traverseE es <*> ET (traverseT t)
      | Estmt s t => Estmt <$> traverseS s <*> traverseT t
      | Eva_arg e t =>
          
TODO: Eva_arg takes a declaration type while expr.Eva_arg takes an expression type.
        Eva_arg <$> traverseE e <*> ET (traverseT t)
      | Epseudo_destructor arrow dt e => Epseudo_destructor arrow <$> traverseT dt <*> traverseE e
      | Earrayloop_init n e1 from to e2 t => Earrayloop_init n <$> traverseE e1 <*> mret from <*> mret to <*> traverseE e2 <*> ET (traverseT t)
      | Earrayloop_index n t => Earrayloop_index n <$> ET (traverseT t)
      | Eopaque_ref n t => Eopaque_ref n <$> traverseT t
      | Eunsupported msg t => Eunsupported msg <$> traverseT t
      end

    with traverseD (d : VarDecl' lang1) : F (VarDecl' lang2) :=
      match d with
      | Dvar n t oe =>
          let k := hE.(handle_unresolved_init) t (fun _ => traverseT t) $ (fun e => (e, fun _ => traverseE e)) <$> oe in
          (fun '(t,oe) => Dvar n t oe) <$> k

      | Dinit ts on t oe =>
          let k := hE.(handle_unresolved_init) t (fun _ => traverseT t) $ (fun e => (e, fun _ => traverseE e)) <$> oe in
          (fun n '(t,oe) => Dinit ts n t oe) <$> traverseN on <*> k

      | Ddecompose e id ds =>
          Ddecompose <$> traverseE e <*> mret id
            <*> traverse (T:=eta list) traverseB ds
      end

    with traverseB (d : BindingDecl' lang1) : F (BindingDecl' lang2) :=
      match d with
      | Bvar n t e =>
          Bvar n <$> traverseT t <*> traverseE e
      | Bbind n t e =>
          Bbind n <$> traverseT t <*> traverseE e
      end

    with traverseS (s : Stmt' lang1) : F (Stmt' lang2) :=
      match s with
      | Sseq ss => Sseq <$> traverse (T:=eta list) traverseS ss
      | Sdecl ds => Sdecl <$> traverse (T:=eta list) traverseD ds
      | Sif d e sif selse => Sif <$> traverse (T:=eta option) traverseD d <*> traverseE e <*> traverseS sif <*> traverseS selse
      | Sif_consteval sif selse => Sif_consteval <$> traverseS sif <*> traverseS selse
      | Swhile d e s => Swhile <$> traverse (T:=eta option) traverseD d <*> traverseE e <*> traverseS s
      | Sfor i g c s => Sfor <$> traverse (T:=eta option) traverseS i <*> traverse (T:=eta option) traverseE g <*> traverse (T:=eta option) traverseE c <*> traverseS s
      | Sdo s e => Sdo <$> traverseS s <*> traverseE e
      | Sswitch d e s => Sswitch <$> traverse (T:=eta option) traverseD d <*> traverseE e <*> traverseS s
      | Scase br => mret $ Scase br
      | Sdefault => mret Sdefault
      | Sbreak => mret Sbreak
      | Scontinue => mret Scontinue
      | Sreturn e => Sreturn <$> traverse (T:=eta option) traverseE e
      | Sexpr e => Sexpr <$> traverseE e
      | Sattr a s => Sattr a <$> traverseS s
      | Sasm code v i o c => Sasm code v <$> traverse (T:=eta list) (fun '(a,b) => pair a <$> traverseE b) i
                                        <*> traverse (T:=eta list) (fun '(a,b) => pair a <$> traverseE b) o <*> mret c
      | Slabeled l s => Slabeled l <$> traverseS s
      | Sgoto id => mret $ Sgoto id
      | Sunsupported msg => mret $ Sunsupported msg
      end

    with traverseC (c : Cast' lang1) : F (Cast' lang2) :=
      match c with
      
TODO (FM-4319): Does Cdependent have something to do with clang's notion of dependent types? If so, we must take more care.
      | Cdependent t => Cdependent <$> traverseT t
      | Cbitcast t => Cbitcast <$> traverseT t
      | Clvaluebitcast t => Clvaluebitcast <$> traverseT t
      | Cl2r => mret Cl2r
      | Cl2r_bitcast t => Cl2r_bitcast <$> traverseT t

      | Cnoop t => Cnoop <$> traverseT t
      | Carray2ptr => mret Carray2ptr
      | Cfun2ptr => mret Cfun2ptr
      | Cint2ptr t => Cint2ptr <$> traverseT t
      | Cptr2int t => Cptr2int <$> traverseT t
      | Cptr2bool => mret Cptr2bool
      | Cderived2base path t => Cderived2base <$> traverse (T:=eta list) traverseT path <*> traverseT t
      | Cbase2derived path t => Cbase2derived <$> traverse (T:=eta list) traverseT path <*> traverseT t
      | Cintegral t => Cintegral <$> traverseT t
      | Cint2bool => mret Cint2bool
      | Cfloat2int t => Cfloat2int <$> traverseT t
      | Cint2float t => Cint2float <$> traverseT t
      | Cfloat t => Cfloat <$> traverseT t
      | Cnull2ptr t => Cnull2ptr <$> traverseT t
      | Cnull2memberptr t => Cnull2memberptr <$> traverseT t
      | Cbuiltin2fun t => Cbuiltin2fun <$> traverseT t
      | Cctor t => Cctor <$> traverseT t
      | C2void => mret C2void
      | Cuser => mret Cuser
      | Cdynamic gn => Cdynamic <$> traverseT gn
      | Cunsupported msg t => Cunsupported msg <$> traverseT t
      end.

    Definition traverseCN : classname' lang1 -> F (classname' lang2) :=
      match lang1 as lang1 , lang2 as lang2
            return (name' lang1 -> F (name' lang2)) ->
                   (type' lang1 -> F (type' lang2)) ->
                   classname' lang1 -> F (classname' lang2)
      with
      | lang.cpp , lang.cpp => fun tN _ => tN
      | lang.temp , lang.temp => fun _ tT => tT
      | lang.cpp , lang.temp => fun tN _ nm => Tnamed <$> tN nm
      | lang.temp , lang.cpp => fun _ tT ty =>
          (fun ty => match ty with
                      | Tnamed nm => nm
                      | _ => Nunsupported "??" (* TODO: this is not implementable, but I don't think we actually use this *)
                      end) <$> tT ty
      end traverseN traverseT.

    Definition mk_core_traversal : core_traversal lang1 lang2 F :=
    {| handle_traverseN := traverseN
     ; handle_traverseT := traverseT
     ; handle_traverseE := traverseE
     ; handle_traverseS := traverseS
     ; handle_classname := traverseCN
     ; handle_traverseE_init ty oe :=
         let oe' :=
           match oe with
           | None => None
           | Some e => Some (e, fun _ => traverseE e)
           end
         in
         hE.(handle_unresolved_init) ty (fun _ => traverseT ty) oe'
     |}.

  End traverse.

  Section traverse.
    Universe u.
    Context {F : Set -> Type@{u}}.
    Context `{!UPoly.FMap F, !UPoly.MRet F, AP : !Ap F}.
    Context {lang1 lang2 : lang.t}.
    Context (hD : core_traversal lang1 lang2 F).

    #[local] Notation traverseN := (hD.(handle_traverseN)).
    #[local] Notation traverseT := (hD.(handle_traverseT)).
    #[local] Notation traverseE := (hD.(handle_traverseE)).
    #[local] Notation traverseS := (hD.(handle_traverseS)).
    #[local] Notation traverseCN := (hD.(handle_classname)).
    #[local] Notation traverseE_init := (hD.(handle_traverseE_init)).

    Definition traverseF_bodyS (f : FunctionBody' lang1) : F (FunctionBody' lang2) :=
      match f with
      | Impl s => Impl <$> traverseS s
      | Builtin f => mret $ Builtin f
      end.

    Definition traverseF (f : Func' lang1) : F (Func' lang2) :=
      Build_Func
        <$> traverseT f.(f_return)
        <*> traverse (T:=eta list) (prod.traverse mret traverseT) f.(f_params)
        <*> mret f.(f_cc) <*> mret f.(f_arity)
        <*> traverse (T:=eta option) traverseF_bodyS f.(f_body).

    Definition traverseM (f : Method' lang1) : F (Method' lang2) :=
      Build_Method <$> traverseT f.(m_return)
                   <*> traverseCN f.(m_class)
                   <*> mret f.(m_this_qual)
                   <*> traverse (T:=eta list) (prod.traverse mret traverseT) f.(m_params)
                   <*> mret f.(m_cc)
                   <*> mret f.(m_arity)
                   <*> traverse (T:=eta option) (traverse (T:=eta OrDefault) traverseS) f.(m_body).

    Definition traverseP (i : InitPath' lang1) : F (InitPath' lang2) :=
      match i with
      | InitBase b => InitBase <$> traverseCN b
      | InitField f =>
          InitField (lang:=lang2) <$> atomic_name.traverse (F:=F) traverseT f
      | InitIndirect path f =>
          let elemF '(a,b) :=
            pair <$> atomic_name.traverse (F:=F) traverseT a <*> traverseCN b
          in
          InitIndirect <$> traverse (F:=F) (T:=eta list) elemF path
            <*> (atomic_name.traverse (F:=F) traverseT f)
      | InitThis => mret (M:=F) $ InitThis (lang:=lang2)
      end.

    Definition traverseI (i : Initializer' lang1) : F (Initializer' lang2) :=
      Build_Initializer <$> traverseP i.(init_path)
                        <*> traverseE i.(init_init).

    Definition traverseCtor (c : Ctor' lang1) : F (Ctor' lang2) :=
      Build_Ctor
        <$> traverseCN c.(c_class)
        <*> traverse (T:=eta list) (traverse (T:=eta (prod _)) traverseT) c.(c_params)
        <*> mret c.(c_cc)
        <*> mret c.(c_arity)
        <*> traverse (T:=eta option) (traverse (T:=eta OrDefault) (prod.traverse (traverse (T:=eta list) traverseI) traverseS)) c.(c_body).

    Definition traverseDtor (d : Dtor' lang1) : F (Dtor' lang2) :=
      Build_Dtor
        <$> traverseCN d.(d_class)
        <*> mret d.(d_cc)
        <*> traverse (T:=eta option) (traverse (T:=eta OrDefault) traverseS) d.(d_body).

    Definition traverseMember (m : Member' lang1) : F (Member' lang2) :=
      mkMember
        <$> atomic_name.traverse traverseT m.(mem_name)
        <*> traverseT m.(mem_type)
        <*> mret m.(mem_mutable)
        <*> traverse (T:=eta option) traverseE m.(mem_init)
        <*> mret m.(mem_layout).

    Definition traverseUnion (u : Union' lang1) : F (Union' lang2) :=
      Build_Union
        <$> traverse (T:=eta list) traverseMember u.(u_fields)
        <*> traverseN u.(u_dtor)
        <*> mret u.(u_trivially_destructible)
        <*> traverse (T:=eta option) traverseN u.(u_delete)
        <*> mret u.(u_size)
        <*> mret u.(u_alignment).

    Definition traverseStruct (s : Struct' lang1) : F (Struct' lang2) :=
      Build_Struct
        <$> traverse (T:=eta list) (prod.traverse traverseCN mret) s.(s_bases)
        <*> traverse (T:=eta list) traverseMember s.(s_fields)
        <*> traverse (T:=eta list) (prod.traverse traverseN (traverse (T:=eta option) traverseN))
               s.(s_virtuals)
        <*> traverse (T:=eta list) (prod.traverse traverseN traverseN) s.(s_overrides)
        <*> traverseN s.(s_dtor)
        <*> mret s.(s_trivially_destructible)
        <*> traverse (T:=eta option) traverseN s.(s_delete)
        <*> mret s.(s_layout)
        <*> mret s.(s_size)
        <*> mret s.(s_alignment).

    Definition traverseGD (gd : GlobDecl' lang1) : F (GlobDecl' lang2) :=
      match gd with
      | Gtype => mret Gtype
      | Gunion u => Gunion <$> traverseUnion u
      | Gstruct s => Gstruct <$> traverseStruct s
      | Genum t ids => Genum <$> traverseT t <*> mret ids
      | Gconstant t oe => Gconstant <$> traverseT t <*> traverse (T:=eta option) traverseE oe
      | Gtypedef t => Gtypedef <$> traverseT t
      | Gunsupported m => mret $ Gunsupported m
      end.

  End traverse.

End MTraverse.