fn derive_fold()

in amzn-smt-ir-derive/src/lib.rs [352:429]


fn derive_fold(mut s: synstructure::Structure) -> TokenStream {
    let smt_ir = smt_ir_crate_path();
    let name = &s.ast().ident; // E.g. `Foo`

    #[allow(non_snake_case)]
    let (Logic, Fold, SuperFold, Folder) = (
        quote!(#smt_ir::Logic),
        quote!(#smt_ir::fold::Fold),
        quote!(#smt_ir::fold::SuperFold),
        quote!(#smt_ir::fold::Folder),
    );

    s.add_bounds(synstructure::AddBounds::None)
        .bind_with(|_| synstructure::BindStyle::Move);

    let impl_fold = s.gen_impl(quote! {
        extern crate std;
        gen impl<L: #Logic<Op = Self>, Out> #Fold<L, Out> for @Self {
            type Output = Out;

            fn fold_with<F, M>(
                self,
                folder: &mut F,
            ) -> std::result::Result<Self::Output, F::Error>
            where
                F: #Folder<L, M, Output = Out>,
            {
                folder.fold_theory_op(self.into())
            }
        }
    });

    let impl_super_fold = {
        // Bound each generic parameter to implement `Fold`
        let mut where_clause = None;
        s.add_trait_bounds(
            &parse_quote!(#Fold<L, Out>),
            &mut where_clause,
            synstructure::AddBounds::Generics,
        );

        // For each variant, construct a new version by folding each of the fields
        let body = s.each_variant(|vi| {
            vi.construct(|_, idx| {
                let field = &vi.bindings()[idx];
                quote!(#Fold::fold_with(#field, folder)?)
            })
        });

        // If input type is `Foo<A, B>`, output `Foo<<A as Fold>::Output, <B as Fold>::Output>`
        let out_params = s
            .referenced_ty_params()
            .into_iter()
            .map(|ty| quote!(<#ty as #Fold<L, Out>>::Output));

        s.gen_impl(quote! {
            extern crate std;
            gen impl<L: #Logic, Out> #SuperFold<L, Out> for @Self #where_clause {
                type Output = #name<#(#out_params),*>;

                fn super_fold_with<F, M>(
                    self,
                    folder: &mut F,
                ) -> std::result::Result<Self::Output, F::Error>
                where
                    F: #Folder<L, M, Output = Out>,
                {
                    Ok(match self { #body })
                }
            }
        })
    };

    quote! {
        #impl_fold
        #impl_super_fold
    }
}