Squashed 'third_party/autocxx/' content from commit 629e8fa53

git-subtree-dir: third_party/autocxx
git-subtree-split: 629e8fa531a633164c0b52e2a3cab536d4cd0849
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I62a03b0049f49adf029e0204639cdb5468dde1a1
diff --git a/engine/src/conversion/codegen_rs/fun_codegen.rs b/engine/src/conversion/codegen_rs/fun_codegen.rs
new file mode 100644
index 0000000..7c32b1b
--- /dev/null
+++ b/engine/src/conversion/codegen_rs/fun_codegen.rs
@@ -0,0 +1,424 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use indexmap::set::IndexSet as HashSet;
+use std::borrow::Cow;
+
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::{
+    parse::Parser,
+    parse_quote,
+    punctuated::Punctuated,
+    token::{Comma, Unsafe},
+    Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType,
+};
+
+use super::{
+    function_wrapper_rs::RustParamConversion,
+    maybe_unsafes_to_tokens,
+    unqualify::{unqualify_params, unqualify_ret_type},
+    ImplBlockDetails, MaybeUnsafeStmt, RsCodegenResult, TraitImplBlockDetails, Use,
+};
+use crate::{
+    conversion::{
+        analysis::fun::{
+            ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy,
+            TraitMethodDetails,
+        },
+        api::UnsafetyNeeded,
+    },
+    types::{Namespace, QualifiedName},
+};
+use crate::{
+    conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary},
+    types::make_ident,
+};
+
+impl UnsafetyNeeded {
+    pub(crate) fn bridge_token(&self) -> Option<Unsafe> {
+        match self {
+            UnsafetyNeeded::None => None,
+            _ => Some(parse_quote! { unsafe }),
+        }
+    }
+
+    pub(crate) fn wrapper_token(&self) -> Option<Unsafe> {
+        match self {
+            UnsafetyNeeded::Always => Some(parse_quote! { unsafe }),
+            _ => None,
+        }
+    }
+
+    pub(crate) fn from_param_details(params: &[ArgumentAnalysis], ignore_placements: bool) -> Self {
+        params.iter().fold(UnsafetyNeeded::None, |accumulator, pd| {
+            if matches!(accumulator, UnsafetyNeeded::Always) {
+                UnsafetyNeeded::Always
+            } else if (pd.self_type.is_some() || pd.is_placement_return_destination)
+                && ignore_placements
+            {
+                if matches!(
+                    pd.requires_unsafe,
+                    UnsafetyNeeded::Always | UnsafetyNeeded::JustBridge
+                ) {
+                    UnsafetyNeeded::JustBridge
+                } else {
+                    accumulator
+                }
+            } else if matches!(pd.requires_unsafe, UnsafetyNeeded::Always) {
+                UnsafetyNeeded::Always
+            } else if matches!(accumulator, UnsafetyNeeded::JustBridge)
+                || matches!(pd.requires_unsafe, UnsafetyNeeded::JustBridge)
+            {
+                UnsafetyNeeded::JustBridge
+            } else {
+                UnsafetyNeeded::None
+            }
+        })
+    }
+}
+
+pub(super) fn gen_function(
+    ns: &Namespace,
+    fun: FuncToConvert,
+    analysis: FnAnalysis,
+    cpp_call_name: String,
+    non_pod_types: &HashSet<QualifiedName>,
+) -> RsCodegenResult {
+    if analysis.ignore_reason.is_err() || !analysis.externally_callable {
+        return RsCodegenResult::default();
+    }
+    let cxxbridge_name = analysis.cxxbridge_name;
+    let rust_name = &analysis.rust_name;
+    let ret_type = analysis.ret_type;
+    let param_details = analysis.param_details;
+    let wrapper_function_needed = analysis.cpp_wrapper.is_some();
+    let params = analysis.params;
+    let vis = analysis.vis;
+    let kind = analysis.kind;
+    let doc_attrs = fun.doc_attrs;
+
+    let mut cpp_name_attr = Vec::new();
+    let mut impl_entry = None;
+    let mut trait_impl_entry = None;
+    let mut bindgen_mod_items = Vec::new();
+    let always_unsafe_due_to_trait_definition = match kind {
+        FnKind::TraitMethod { ref details, .. } => details.trait_call_is_unsafe,
+        _ => false,
+    };
+    let fn_generator = FnGenerator {
+        param_details: &param_details,
+        cxxbridge_name: &cxxbridge_name,
+        rust_name,
+        unsafety: &analysis.requires_unsafe,
+        always_unsafe_due_to_trait_definition,
+        doc_attrs: &doc_attrs,
+        non_pod_types,
+    };
+    // In rare occasions, we might need to give an explicit lifetime.
+    let (lifetime_tokens, params, ret_type) = add_explicit_lifetime_if_necessary(
+        &param_details,
+        params,
+        Cow::Borrowed(&ret_type),
+        non_pod_types,
+        true,
+    );
+
+    if analysis.rust_wrapper_needed {
+        match kind {
+            FnKind::Method {
+                ref impl_for,
+                method_kind: MethodKind::Constructor { .. },
+                ..
+            } => {
+                // Constructor.
+                impl_entry = Some(fn_generator.generate_constructor_impl(impl_for));
+            }
+            FnKind::Method {
+                ref impl_for,
+                ref method_kind,
+                ..
+            } => {
+                // Method, or static method.
+                impl_entry = Some(fn_generator.generate_method_impl(
+                    matches!(method_kind, MethodKind::Constructor { .. }),
+                    impl_for,
+                    &ret_type,
+                ));
+            }
+            FnKind::TraitMethod { ref details, .. } => {
+                trait_impl_entry = Some(fn_generator.generate_trait_impl(details, &ret_type));
+            }
+            _ => {
+                // Generate plain old function
+                bindgen_mod_items.push(fn_generator.generate_function_impl(&ret_type));
+            }
+        }
+    }
+
+    let materialization = match kind {
+        FnKind::Method { .. } | FnKind::TraitMethod { .. } => None,
+        FnKind::Function => match analysis.rust_rename_strategy {
+            _ if analysis.rust_wrapper_needed => {
+                Some(Use::SpecificNameFromBindgen(make_ident(rust_name)))
+            }
+            RustRenameStrategy::RenameInOutputMod(ref alias) => {
+                Some(Use::UsedFromCxxBridgeWithAlias(alias.clone()))
+            }
+            _ => Some(Use::UsedFromCxxBridge),
+        },
+    };
+    if cxxbridge_name != cpp_call_name && !wrapper_function_needed {
+        cpp_name_attr = Attribute::parse_outer
+            .parse2(quote!(
+                #[cxx_name = #cpp_call_name]
+            ))
+            .unwrap();
+    }
+
+    // Finally - namespace support. All the Types in everything
+    // above this point are fully qualified. We need to unqualify them.
+    // We need to do that _after_ the above wrapper_function_needed
+    // work, because it relies upon spotting fully qualified names like
+    // std::unique_ptr. However, after it's done its job, all such
+    // well-known types should be unqualified already (e.g. just UniquePtr)
+    // and the following code will act to unqualify only those types
+    // which the user has declared.
+    let params = unqualify_params(params);
+    let ret_type = unqualify_ret_type(ret_type.into_owned());
+    // And we need to make an attribute for the namespace that the function
+    // itself is in.
+    let namespace_attr = if ns.is_empty() || wrapper_function_needed {
+        Vec::new()
+    } else {
+        let namespace_string = ns.to_string();
+        Attribute::parse_outer
+            .parse2(quote!(
+                #[namespace = #namespace_string]
+            ))
+            .unwrap()
+    };
+    // At last, actually generate the cxx::bridge entry.
+    let bridge_unsafety = analysis.requires_unsafe.bridge_token();
+    let extern_c_mod_item = ForeignItem::Fn(parse_quote!(
+        #(#namespace_attr)*
+        #(#cpp_name_attr)*
+        #(#doc_attrs)*
+        #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type;
+    ));
+    RsCodegenResult {
+        extern_c_mod_items: vec![extern_c_mod_item],
+        bindgen_mod_items,
+        impl_entry,
+        trait_impl_entry,
+        materializations: materialization.into_iter().collect(),
+        ..Default::default()
+    }
+}
+
+/// Knows how to generate a given function.
+#[derive(Clone)]
+struct FnGenerator<'a> {
+    param_details: &'a [ArgumentAnalysis],
+    cxxbridge_name: &'a Ident,
+    rust_name: &'a str,
+    unsafety: &'a UnsafetyNeeded,
+    always_unsafe_due_to_trait_definition: bool,
+    doc_attrs: &'a Vec<Attribute>,
+    non_pod_types: &'a HashSet<QualifiedName>,
+}
+
+impl<'a> FnGenerator<'a> {
+    fn common_parts<'b>(
+        &self,
+        avoid_self: bool,
+        parameter_reordering: &Option<Vec<usize>>,
+        ret_type: &'b ReturnType,
+    ) -> (
+        Option<TokenStream>,
+        Punctuated<FnArg, Comma>,
+        std::borrow::Cow<'b, ReturnType>,
+        TokenStream,
+    ) {
+        let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new();
+        let mut local_variables = Vec::new();
+        let mut arg_list = Vec::new();
+        let mut ptr_arg_name = None;
+        let mut ret_type = Cow::Borrowed(ret_type);
+        let mut any_conversion_requires_unsafe = false;
+        for pd in self.param_details {
+            let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
+                parse_quote!(self)
+            } else {
+                pd.name.clone()
+            };
+            let rust_for_param = pd.conversion.rust_conversion(wrapper_arg_name.clone());
+            match rust_for_param {
+                RustParamConversion::Param {
+                    ty,
+                    conversion,
+                    local_variables: mut these_local_variables,
+                    conversion_requires_unsafe,
+                } => {
+                    arg_list.push(conversion.clone());
+                    local_variables.append(&mut these_local_variables);
+                    if pd.is_placement_return_destination {
+                        ptr_arg_name = Some(conversion);
+                    } else {
+                        let param_mutability = pd.conversion.rust_conversion.requires_mutability();
+                        wrapper_params.push(parse_quote!(
+                            #param_mutability #wrapper_arg_name: #ty
+                        ));
+                    }
+                    any_conversion_requires_unsafe =
+                        conversion_requires_unsafe || any_conversion_requires_unsafe;
+                }
+                RustParamConversion::ReturnValue { ty } => {
+                    ptr_arg_name = Some(pd.name.to_token_stream());
+                    ret_type = Cow::Owned(parse_quote! {
+                        -> impl autocxx::moveit::new::New<Output = #ty>
+                    });
+                    arg_list.push(pd.name.to_token_stream());
+                }
+            }
+        }
+        if let Some(parameter_reordering) = &parameter_reordering {
+            wrapper_params = Self::reorder_parameters(wrapper_params, parameter_reordering);
+        }
+        let (lifetime_tokens, wrapper_params, ret_type) = add_explicit_lifetime_if_necessary(
+            self.param_details,
+            wrapper_params,
+            ret_type,
+            self.non_pod_types,
+            false,
+        );
+
+        let cxxbridge_name = self.cxxbridge_name;
+        let call_body = MaybeUnsafeStmt::maybe_unsafe(
+            quote! {
+                cxxbridge::#cxxbridge_name ( #(#arg_list),* )
+            },
+            any_conversion_requires_unsafe || matches!(self.unsafety, UnsafetyNeeded::JustBridge),
+        );
+        let call_stmts = if let Some(ptr_arg_name) = ptr_arg_name {
+            let mut closure_stmts = local_variables;
+            closure_stmts.push(MaybeUnsafeStmt::binary(
+                quote! { let #ptr_arg_name = unsafe { #ptr_arg_name.get_unchecked_mut().as_mut_ptr() };},
+                quote! { let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr();},
+            ));
+            closure_stmts.push(call_body);
+            let closure_stmts = maybe_unsafes_to_tokens(closure_stmts, true);
+            vec![MaybeUnsafeStmt::needs_unsafe(parse_quote! {
+                autocxx::moveit::new::by_raw(move |#ptr_arg_name| {
+                    #closure_stmts
+                })
+            })]
+        } else {
+            let mut call_stmts = local_variables;
+            call_stmts.push(call_body);
+            call_stmts
+        };
+        let context_is_unsafe = matches!(self.unsafety, UnsafetyNeeded::Always)
+            || self.always_unsafe_due_to_trait_definition;
+        let call_body = maybe_unsafes_to_tokens(call_stmts, context_is_unsafe);
+        (lifetime_tokens, wrapper_params, ret_type, call_body)
+    }
+
+    /// Generate an 'impl Type { methods-go-here }' item
+    fn generate_method_impl(
+        &self,
+        avoid_self: bool,
+        impl_block_type_name: &QualifiedName,
+        ret_type: &ReturnType,
+    ) -> Box<ImplBlockDetails> {
+        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
+            self.common_parts(avoid_self, &None, ret_type);
+        let rust_name = make_ident(self.rust_name);
+        let unsafety = self.unsafety.wrapper_token();
+        let doc_attrs = self.doc_attrs;
+        Box::new(ImplBlockDetails {
+            item: ImplItem::Method(parse_quote! {
+                #(#doc_attrs)*
+                pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
+                    #call_body
+                }
+            }),
+            ty: impl_block_type_name.get_final_ident(),
+        })
+    }
+
+    /// Generate an 'impl Trait for Type { methods-go-here }' in its entrety.
+    fn generate_trait_impl(
+        &self,
+        details: &TraitMethodDetails,
+        ret_type: &ReturnType,
+    ) -> Box<TraitImplBlockDetails> {
+        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
+            self.common_parts(details.avoid_self, &details.parameter_reordering, ret_type);
+        let doc_attrs = self.doc_attrs;
+        let unsafety = self.unsafety.wrapper_token();
+        let key = details.trt.clone();
+        let method_name = &details.method_name;
+        let item = parse_quote! {
+            #(#doc_attrs)*
+            #unsafety fn #method_name #lifetime_tokens ( #wrapper_params ) #ret_type {
+                #call_body
+            }
+        };
+        Box::new(TraitImplBlockDetails { item, key })
+    }
+
+    /// Generate a 'impl Type { methods-go-here }' item which is a constructor
+    /// for use with moveit traits.
+    fn generate_constructor_impl(
+        &self,
+        impl_block_type_name: &QualifiedName,
+    ) -> Box<ImplBlockDetails> {
+        let ret_type: ReturnType = parse_quote! { -> impl autocxx::moveit::new::New<Output=Self> };
+        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
+            self.common_parts(true, &None, &ret_type);
+        let rust_name = make_ident(&self.rust_name);
+        let doc_attrs = self.doc_attrs;
+        let unsafety = self.unsafety.wrapper_token();
+        Box::new(ImplBlockDetails {
+            item: ImplItem::Method(parse_quote! {
+                #(#doc_attrs)*
+                pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
+                    #call_body
+                }
+            }),
+            ty: impl_block_type_name.get_final_ident(),
+        })
+    }
+
+    /// Generate a function call wrapper
+    fn generate_function_impl(&self, ret_type: &ReturnType) -> Item {
+        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
+            self.common_parts(false, &None, ret_type);
+        let rust_name = make_ident(self.rust_name);
+        let doc_attrs = self.doc_attrs;
+        let unsafety = self.unsafety.wrapper_token();
+        Item::Fn(parse_quote! {
+            #(#doc_attrs)*
+            pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
+                #call_body
+            }
+        })
+    }
+
+    fn reorder_parameters(
+        params: Punctuated<FnArg, Comma>,
+        parameter_ordering: &[usize],
+    ) -> Punctuated<FnArg, Comma> {
+        let old_params = params.into_iter().collect::<Vec<_>>();
+        parameter_ordering
+            .iter()
+            .map(|n| old_params.get(*n).unwrap().clone())
+            .collect()
+    }
+}
diff --git a/engine/src/conversion/codegen_rs/function_wrapper_rs.rs b/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
new file mode 100644
index 0000000..a3fc71f
--- /dev/null
+++ b/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
@@ -0,0 +1,172 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use proc_macro2::TokenStream;
+use syn::{Pat, Type, TypePtr};
+
+use crate::{
+    conversion::analysis::fun::function_wrapper::{RustConversionType, TypeConversionPolicy},
+    types::make_ident,
+};
+use quote::quote;
+use syn::parse_quote;
+
+use super::MaybeUnsafeStmt;
+
+/// Output Rust snippets for how to deal with a given parameter.
+pub(super) enum RustParamConversion {
+    Param {
+        ty: Type,
+        local_variables: Vec<MaybeUnsafeStmt>,
+        conversion: TokenStream,
+        conversion_requires_unsafe: bool,
+    },
+    ReturnValue {
+        ty: Type,
+    },
+}
+
+impl TypeConversionPolicy {
+    /// If returns `None` then this parameter should be omitted entirely.
+    pub(super) fn rust_conversion(&self, var: Pat) -> RustParamConversion {
+        match self.rust_conversion {
+            RustConversionType::None => RustParamConversion::Param {
+                ty: self.converted_rust_type(),
+                local_variables: Vec::new(),
+                conversion: quote! { #var },
+                conversion_requires_unsafe: false,
+            },
+            RustConversionType::FromStr => RustParamConversion::Param {
+                ty: parse_quote! { impl ToCppString },
+                local_variables: Vec::new(),
+                conversion: quote! ( #var .into_cpp() ),
+                conversion_requires_unsafe: false,
+            },
+            RustConversionType::ToBoxedUpHolder(ref sub) => {
+                let holder_type = sub.holder();
+                let id = sub.id();
+                let ty = parse_quote! { autocxx::subclass::CppSubclassRustPeerHolder<
+                    super::super::super:: #id>
+                };
+                RustParamConversion::Param {
+                    ty,
+                    local_variables: Vec::new(),
+                    conversion: quote! {
+                        Box::new(#holder_type(#var))
+                    },
+                    conversion_requires_unsafe: false,
+                }
+            }
+            RustConversionType::FromPinMaybeUninitToPtr => {
+                let ty = match &self.unwrapped_type {
+                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    _ => panic!("Not a ptr"),
+                };
+                let ty = parse_quote! {
+                    ::std::pin::Pin<&mut ::std::mem::MaybeUninit< #ty >>
+                };
+                RustParamConversion::Param {
+                    ty,
+                    local_variables: Vec::new(),
+                    conversion: quote! {
+                        #var.get_unchecked_mut().as_mut_ptr()
+                    },
+                    conversion_requires_unsafe: true,
+                }
+            }
+            RustConversionType::FromPinMoveRefToPtr => {
+                let ty = match &self.unwrapped_type {
+                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    _ => panic!("Not a ptr"),
+                };
+                let ty = parse_quote! {
+                    ::std::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >>
+                };
+                RustParamConversion::Param {
+                    ty,
+                    local_variables: Vec::new(),
+                    conversion: quote! {
+                        { let r: &mut _ = ::std::pin::Pin::into_inner_unchecked(#var.as_mut());
+                            r
+                        }
+                    },
+                    conversion_requires_unsafe: true,
+                }
+            }
+            RustConversionType::FromTypeToPtr => {
+                let ty = match &self.unwrapped_type {
+                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    _ => panic!("Not a ptr"),
+                };
+                let ty = parse_quote! { &mut #ty };
+                RustParamConversion::Param {
+                    ty,
+                    local_variables: Vec::new(),
+                    conversion: quote! {
+                        #var
+                    },
+                    conversion_requires_unsafe: false,
+                }
+            }
+            RustConversionType::FromValueParamToPtr | RustConversionType::FromRValueParamToPtr => {
+                let (handler_type, param_trait) = match self.rust_conversion {
+                    RustConversionType::FromValueParamToPtr => ("ValueParamHandler", "ValueParam"),
+                    RustConversionType::FromRValueParamToPtr => {
+                        ("RValueParamHandler", "RValueParam")
+                    }
+                    _ => unreachable!(),
+                };
+                let handler_type = make_ident(handler_type);
+                let param_trait = make_ident(param_trait);
+                let var_name = if let Pat::Ident(pti) = &var {
+                    &pti.ident
+                } else {
+                    panic!("Unexpected non-ident parameter name");
+                };
+                let space_var_name = make_ident(format!("{}_space", var_name));
+                let ty = &self.unwrapped_type;
+                let ty = parse_quote! { impl autocxx::#param_trait<#ty> };
+                // This is the usual trick to put something on the stack, then
+                // immediately shadow the variable name so it can't be accessed or moved.
+                RustParamConversion::Param {
+                    ty,
+                    local_variables: vec![
+                        MaybeUnsafeStmt::new(
+                            quote! { let mut #space_var_name = autocxx::#handler_type::default(); },
+                        ),
+                        MaybeUnsafeStmt::binary(
+                            quote! { let mut #space_var_name =
+                                unsafe { ::std::pin::Pin::new_unchecked(&mut #space_var_name) };
+                            },
+                            quote! { let mut #space_var_name =
+                                ::std::pin::Pin::new_unchecked(&mut #space_var_name);
+                            },
+                        ),
+                        MaybeUnsafeStmt::needs_unsafe(
+                            quote! { #space_var_name.as_mut().populate(#var_name); },
+                        ),
+                    ],
+                    conversion: quote! {
+                        #space_var_name.get_ptr()
+                    },
+                    conversion_requires_unsafe: false,
+                }
+            }
+            // This type of conversion means that this function parameter appears in the cxx::bridge
+            // but not in the arguments for the wrapper function, because instead we return an
+            // impl New which uses the cxx::bridge function's pointer parameter.
+            RustConversionType::FromPlacementParamToNewReturn => {
+                let ty = match &self.unwrapped_type {
+                    Type::Ptr(TypePtr { elem, .. }) => *(*elem).clone(),
+                    _ => panic!("Not a ptr"),
+                };
+                RustParamConversion::ReturnValue { ty }
+            }
+        }
+    }
+}
diff --git a/engine/src/conversion/codegen_rs/impl_item_creator.rs b/engine/src/conversion/codegen_rs/impl_item_creator.rs
new file mode 100644
index 0000000..89ef896
--- /dev/null
+++ b/engine/src/conversion/codegen_rs/impl_item_creator.rs
@@ -0,0 +1,41 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx_parser::IncludeCppConfig;
+use syn::{parse_quote, Ident, Item};
+
+pub(crate) fn create_impl_items(
+    id: &Ident,
+    movable: bool,
+    destroyable: bool,
+    config: &IncludeCppConfig,
+) -> Vec<Item> {
+    if config.exclude_impls {
+        return vec![];
+    }
+    let mut results = Vec::new();
+    if destroyable {
+        results.extend([
+            Item::Impl(parse_quote! {
+                impl UniquePtr<#id> {}
+            }),
+            Item::Impl(parse_quote! {
+                impl SharedPtr<#id> {}
+            }),
+            Item::Impl(parse_quote! {
+                impl WeakPtr<#id> {}
+            }),
+        ]);
+    }
+    if movable {
+        results.push(Item::Impl(parse_quote! {
+            impl CxxVector<#id> {}
+        }))
+    }
+    results
+}
diff --git a/engine/src/conversion/codegen_rs/lifetime.rs b/engine/src/conversion/codegen_rs/lifetime.rs
new file mode 100644
index 0000000..ea2c782
--- /dev/null
+++ b/engine/src/conversion/codegen_rs/lifetime.rs
@@ -0,0 +1,205 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+use crate::{
+    conversion::analysis::fun::{
+        function_wrapper::RustConversionType, ArgumentAnalysis, ReceiverMutability,
+    },
+    types::QualifiedName,
+};
+use indexmap::set::IndexSet as HashSet;
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use std::borrow::Cow;
+use syn::{
+    parse_quote, punctuated::Punctuated, token::Comma, FnArg, GenericArgument, PatType, Path,
+    PathSegment, ReturnType, Type, TypePath, TypeReference,
+};
+
+/// Function which can add explicit lifetime parameters to function signatures
+/// where necessary, based on analysis of parameters and return types.
+/// This is necessary in three cases:
+/// 1) where the parameter is a Pin<&mut T>
+///    and the return type is some kind of reference - because lifetime elision
+///    is not smart enough to see inside a Pin.
+/// 2) as a workaround for https://github.com/dtolnay/cxx/issues/1024, where the
+///    input parameter is a non-POD type but the output reference is a POD or
+///    built-in type
+/// 3) Any parameter is any form of reference, and we're returning an `impl New`
+///    3a) an 'impl ValueParam' counts as a reference.
+pub(crate) fn add_explicit_lifetime_if_necessary<'r>(
+    param_details: &[ArgumentAnalysis],
+    mut params: Punctuated<FnArg, Comma>,
+    ret_type: Cow<'r, ReturnType>,
+    non_pod_types: &HashSet<QualifiedName>,
+    assert_all_parameters_are_references: bool,
+) -> (
+    Option<TokenStream>,
+    Punctuated<FnArg, Comma>,
+    Cow<'r, ReturnType>,
+) {
+    let has_mutable_receiver = param_details.iter().any(|pd| {
+        matches!(pd.self_type, Some((_, ReceiverMutability::Mutable)))
+            && !pd.is_placement_return_destination
+    });
+
+    let any_param_is_reference = param_details.iter().any(|pd| {
+        pd.has_lifetime
+            || matches!(
+                pd.conversion.rust_conversion,
+                RustConversionType::FromValueParamToPtr
+            )
+    });
+    let return_type_is_impl = return_type_is_impl(&ret_type);
+    let non_pod_ref_param = reference_parameter_is_non_pod_reference(&params, non_pod_types);
+    let ret_type_pod = return_type_is_pod_or_known_type_reference(&ret_type, non_pod_types);
+    let returning_impl_with_a_reference_param = return_type_is_impl && any_param_is_reference;
+    let hits_1024_bug = non_pod_ref_param && ret_type_pod;
+    if !(has_mutable_receiver || hits_1024_bug || returning_impl_with_a_reference_param) {
+        return (None, params, ret_type);
+    }
+    let new_return_type = match ret_type.as_ref() {
+        ReturnType::Type(rarrow, boxed_type) => match boxed_type.as_ref() {
+            Type::Reference(rtr) => {
+                let mut new_rtr = rtr.clone();
+                new_rtr.lifetime = Some(parse_quote! { 'a });
+                Some(ReturnType::Type(
+                    *rarrow,
+                    Box::new(Type::Reference(new_rtr)),
+                ))
+            }
+            Type::Path(typ) => {
+                let mut new_path = typ.clone();
+                add_lifetime_to_pinned_reference(&mut new_path.path.segments)
+                    .ok()
+                    .map(|_| ReturnType::Type(*rarrow, Box::new(Type::Path(new_path))))
+            }
+            Type::ImplTrait(tyit) => {
+                let old_tyit = tyit.to_token_stream();
+                Some(parse_quote! {
+                    #rarrow #old_tyit + 'a
+                })
+            }
+            _ => None,
+        },
+        _ => None,
+    };
+
+    match new_return_type {
+        None => (None, params, ret_type),
+        Some(new_return_type) => {
+            for mut param in params.iter_mut() {
+                match &mut param {
+                    FnArg::Typed(PatType { ty, .. }) => match ty.as_mut() {
+                        Type::Path(TypePath {
+                            path: Path { segments, .. },
+                            ..
+                        }) => add_lifetime_to_pinned_reference(segments).unwrap_or_else(|e| {
+                            if assert_all_parameters_are_references {
+                                panic!("Expected a pinned reference: {:?}", e)
+                            }
+                        }),
+                        Type::Reference(tyr) => add_lifetime_to_reference(tyr),
+                        Type::ImplTrait(tyit) => add_lifetime_to_impl_trait(tyit),
+                        _ if assert_all_parameters_are_references => {
+                            panic!("Expected Pin<&mut T> or &T")
+                        }
+                        _ => {}
+                    },
+                    _ if assert_all_parameters_are_references => panic!("Unexpected fnarg"),
+                    _ => {}
+                }
+            }
+
+            (Some(quote! { <'a> }), params, Cow::Owned(new_return_type))
+        }
+    }
+}
+
+fn reference_parameter_is_non_pod_reference(
+    params: &Punctuated<FnArg, Comma>,
+    non_pod_types: &HashSet<QualifiedName>,
+) -> bool {
+    params.iter().any(|param| match param {
+        FnArg::Typed(PatType { ty, .. }) => match ty.as_ref() {
+            Type::Reference(TypeReference { elem, .. }) => match elem.as_ref() {
+                Type::Path(typ) => {
+                    let qn = QualifiedName::from_type_path(typ);
+                    non_pod_types.contains(&qn)
+                }
+                _ => false,
+            },
+            _ => false,
+        },
+        _ => false,
+    })
+}
+
+fn return_type_is_pod_or_known_type_reference(
+    ret_type: &ReturnType,
+    non_pod_types: &HashSet<QualifiedName>,
+) -> bool {
+    match ret_type {
+        ReturnType::Type(_, boxed_type) => match boxed_type.as_ref() {
+            Type::Reference(rtr) => match rtr.elem.as_ref() {
+                Type::Path(typ) => {
+                    let qn = QualifiedName::from_type_path(typ);
+                    !non_pod_types.contains(&qn)
+                }
+                _ => false,
+            },
+            _ => false,
+        },
+        _ => false,
+    }
+}
+
+fn return_type_is_impl(ret_type: &ReturnType) -> bool {
+    matches!(ret_type, ReturnType::Type(_, boxed_type) if matches!(boxed_type.as_ref(), Type::ImplTrait(..)))
+}
+
+#[derive(Debug)]
+enum AddLifetimeError {
+    WasNotPin,
+}
+
+fn add_lifetime_to_pinned_reference(
+    segments: &mut Punctuated<PathSegment, syn::token::Colon2>,
+) -> Result<(), AddLifetimeError> {
+    static EXPECTED_SEGMENTS: &[(&str, bool)] = &[
+        ("std", false),
+        ("pin", false),
+        ("Pin", true), // true = act on the arguments of this segment
+    ];
+
+    for (seg, (expected_name, act)) in segments.iter_mut().zip(EXPECTED_SEGMENTS.iter()) {
+        if seg.ident != expected_name {
+            return Err(AddLifetimeError::WasNotPin);
+        }
+        if *act {
+            match &mut seg.arguments {
+                syn::PathArguments::AngleBracketed(aba) => match aba.args.iter_mut().next() {
+                    Some(GenericArgument::Type(Type::Reference(tyr))) => {
+                        add_lifetime_to_reference(tyr);
+                    }
+                    _ => panic!("Expected generic args with a reference"),
+                },
+                _ => panic!("Expected angle bracketed args"),
+            }
+        }
+    }
+    Ok(())
+}
+
+fn add_lifetime_to_reference(tyr: &mut syn::TypeReference) {
+    tyr.lifetime = Some(parse_quote! { 'a })
+}
+
+fn add_lifetime_to_impl_trait(tyit: &mut syn::TypeImplTrait) {
+    tyit.bounds
+        .push(syn::TypeParamBound::Lifetime(parse_quote! { 'a }))
+}
diff --git a/engine/src/conversion/codegen_rs/mod.rs b/engine/src/conversion/codegen_rs/mod.rs
new file mode 100644
index 0000000..d488d52
--- /dev/null
+++ b/engine/src/conversion/codegen_rs/mod.rs
@@ -0,0 +1,1379 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod fun_codegen;
+mod function_wrapper_rs;
+mod impl_item_creator;
+mod lifetime;
+mod namespace_organizer;
+mod non_pod_struct;
+pub(crate) mod unqualify;
+
+use indexmap::map::IndexMap as HashMap;
+use indexmap::set::IndexSet as HashSet;
+
+use autocxx_parser::{ExternCppType, IncludeCppConfig, RustFun};
+
+use itertools::Itertools;
+use proc_macro2::{Span, TokenStream};
+use syn::{
+    parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem,
+    ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, TraitItem, TypePath,
+};
+
+use crate::{
+    conversion::{
+        codegen_rs::{
+            non_pod_struct::{make_non_pod, new_non_pod_struct},
+            unqualify::{unqualify_params, unqualify_ret_type},
+        },
+        doc_attr::get_doc_attrs,
+    },
+    types::{make_ident, Namespace, QualifiedName},
+};
+use impl_item_creator::create_impl_items;
+
+use self::{
+    fun_codegen::gen_function,
+    namespace_organizer::{HasNs, NamespaceEntries},
+};
+
+use super::{
+    analysis::{
+        fun::{FnPhase, PodAndDepAnalysis, ReceiverMutability},
+        pod::PodAnalysis,
+    },
+    api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind},
+    convert_error::ErrorContextType,
+};
+use super::{
+    api::{Layout, Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature},
+    apivec::ApiVec,
+    codegen_cpp::type_to_cpp::{
+        namespaced_name_using_original_name_map, original_name_map_from_apis, CppNameMap,
+    },
+};
+use super::{convert_error::ErrorContext, ConvertError};
+use quote::quote;
+
+/// An entry which needs to go into an `impl` block for a given type.
+struct ImplBlockDetails {
+    item: ImplItem,
+    ty: Ident,
+}
+
+struct TraitImplBlockDetails {
+    item: TraitItem,
+    key: TraitImplSignature,
+}
+
+/// Whether and how this item should be exposed in the mods constructed
+/// for actual end-user use.
+#[derive(Clone)]
+enum Use {
+    /// Uses from cxx::bridge
+    UsedFromCxxBridge,
+    /// 'use' points to cxx::bridge with a different name
+    UsedFromCxxBridgeWithAlias(Ident),
+    /// 'use' directive points to bindgen
+    UsedFromBindgen,
+    /// 'use' a specific name from bindgen.
+    SpecificNameFromBindgen(Ident),
+    /// Some kind of custom item
+    Custom(Box<Item>),
+}
+
+fn get_string_items() -> Vec<Item> {
+    [
+        Item::Trait(parse_quote! {
+            pub trait ToCppString {
+                fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString>;
+            }
+        }),
+        // We can't just impl<T: AsRef<str>> ToCppString for T
+        // because the compiler says that this trait could be implemented
+        // in future for cxx::UniquePtr<cxx::CxxString>. Fair enough.
+        Item::Impl(parse_quote! {
+            impl ToCppString for &str {
+                fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
+                    make_string(self)
+                }
+            }
+        }),
+        Item::Impl(parse_quote! {
+            impl ToCppString for String {
+                fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
+                    make_string(&self)
+                }
+            }
+        }),
+        Item::Impl(parse_quote! {
+            impl ToCppString for &String {
+                fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
+                    make_string(self)
+                }
+            }
+        }),
+        Item::Impl(parse_quote! {
+            impl ToCppString for cxx::UniquePtr<cxx::CxxString> {
+                fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
+                    self
+                }
+            }
+        }),
+    ]
+    .to_vec()
+}
+
+/// Type which handles generation of Rust code.
+/// In practice, much of the "generation" involves connecting together
+/// existing lumps of code within the Api structures.
+pub(crate) struct RsCodeGenerator<'a> {
+    include_list: &'a [String],
+    bindgen_mod: ItemMod,
+    original_name_map: CppNameMap,
+    config: &'a IncludeCppConfig,
+    header_name: Option<String>,
+}
+
+impl<'a> RsCodeGenerator<'a> {
+    /// Generate code for a set of APIs that was discovered during parsing.
+    pub(crate) fn generate_rs_code(
+        all_apis: ApiVec<FnPhase>,
+        include_list: &'a [String],
+        bindgen_mod: ItemMod,
+        config: &'a IncludeCppConfig,
+        header_name: Option<String>,
+    ) -> Vec<Item> {
+        let c = Self {
+            include_list,
+            bindgen_mod,
+            original_name_map: original_name_map_from_apis(&all_apis),
+            config,
+            header_name,
+        };
+        c.rs_codegen(all_apis)
+    }
+
+    fn rs_codegen(mut self, all_apis: ApiVec<FnPhase>) -> Vec<Item> {
+        // ... and now let's start to generate the output code.
+        // First off, when we generate structs we may need to add some methods
+        // if they're superclasses.
+        let methods_by_superclass = self.accumulate_superclass_methods(&all_apis);
+        let subclasses_with_a_single_trivial_constructor =
+            find_trivially_constructed_subclasses(&all_apis);
+        let non_pod_types = find_non_pod_types(&all_apis);
+        // Now let's generate the Rust code.
+        let (rs_codegen_results_and_namespaces, additional_cpp_needs): (Vec<_>, Vec<_>) = all_apis
+            .into_iter()
+            .map(|api| {
+                let more_cpp_needed = api.needs_cpp_codegen();
+                let name = api.name().clone();
+                let gen = self.generate_rs_for_api(
+                    api,
+                    &methods_by_superclass,
+                    &subclasses_with_a_single_trivial_constructor,
+                    &non_pod_types,
+                );
+                ((name, gen), more_cpp_needed)
+            })
+            .unzip();
+        // First, the hierarchy of mods containing lots of 'use' statements
+        // which is the final API exposed as 'ffi'.
+        let mut use_statements =
+            Self::generate_final_use_statements(&rs_codegen_results_and_namespaces);
+        // And work out what we need for the bindgen mod.
+        let bindgen_root_items =
+            self.generate_final_bindgen_mods(&rs_codegen_results_and_namespaces);
+        // Both of the above ('use' hierarchy and bindgen mod) are organized into
+        // sub-mods by namespace. From here on, things are flat.
+        let (_, rs_codegen_results): (Vec<_>, Vec<_>) =
+            rs_codegen_results_and_namespaces.into_iter().unzip();
+        let (extern_c_mod_items, extern_rust_mod_items, all_items, bridge_items): (
+            Vec<_>,
+            Vec<_>,
+            Vec<_>,
+            Vec<_>,
+        ) = rs_codegen_results
+            .into_iter()
+            .map(|api| {
+                (
+                    api.extern_c_mod_items,
+                    api.extern_rust_mod_items,
+                    api.global_items,
+                    api.bridge_items,
+                )
+            })
+            .multiunzip();
+        // Items for the [cxx::bridge] mod...
+        let mut bridge_items: Vec<Item> = bridge_items.into_iter().flatten().collect();
+        // Things to include in the "extern "C"" mod passed within the cxx::bridge
+        let mut extern_c_mod_items: Vec<ForeignItem> =
+            extern_c_mod_items.into_iter().flatten().collect();
+        // The same for extern "Rust"
+        let mut extern_rust_mod_items = extern_rust_mod_items.into_iter().flatten().collect();
+        // And a list of global items to include at the top level.
+        let mut all_items: Vec<Item> = all_items.into_iter().flatten().collect();
+        // And finally any C++ we need to generate. And by "we" I mean autocxx not cxx.
+        let has_additional_cpp_needs = additional_cpp_needs.into_iter().any(std::convert::identity);
+        extern_c_mod_items.extend(self.build_include_foreign_items(has_additional_cpp_needs));
+        // We will always create an extern "C" mod even if bindgen
+        // didn't generate one, e.g. because it only generated types.
+        // We still want cxx to know about those types.
+        let mut extern_c_mod: ItemForeignMod = parse_quote!(
+            extern "C++" {}
+        );
+        extern_c_mod.items.append(&mut extern_c_mod_items);
+        bridge_items.push(Self::make_foreign_mod_unsafe(extern_c_mod));
+        let mut extern_rust_mod: ItemForeignMod = parse_quote!(
+            extern "Rust" {}
+        );
+        extern_rust_mod.items.append(&mut extern_rust_mod_items);
+        bridge_items.push(Item::ForeignMod(extern_rust_mod));
+        // The extensive use of parse_quote here could end up
+        // being a performance bottleneck. If so, we might want
+        // to set the 'contents' field of the ItemMod
+        // structures directly.
+        if !bindgen_root_items.is_empty() {
+            self.bindgen_mod.vis = parse_quote! {};
+            self.bindgen_mod.content.as_mut().unwrap().1 = vec![Item::Mod(parse_quote! {
+                pub(super) mod root {
+                    #(#bindgen_root_items)*
+                }
+            })];
+            all_items.push(Item::Mod(self.bindgen_mod));
+        }
+        all_items.push(Item::Mod(parse_quote! {
+            #[cxx::bridge]
+            mod cxxbridge {
+                #(#bridge_items)*
+            }
+        }));
+
+        all_items.push(Item::Use(parse_quote! {
+            #[allow(unused_imports)]
+            use bindgen::root;
+        }));
+        all_items.append(&mut use_statements);
+        all_items
+    }
+
+    fn accumulate_superclass_methods(
+        &self,
+        apis: &ApiVec<FnPhase>,
+    ) -> HashMap<QualifiedName, Vec<SuperclassMethod>> {
+        let mut results = HashMap::new();
+        results.extend(
+            self.config
+                .superclasses()
+                .map(|sc| (QualifiedName::new_from_cpp_name(sc), Vec::new())),
+        );
+        for api in apis.iter() {
+            if let Api::SubclassTraitItem { details, .. } = api {
+                let list = results.get_mut(&details.receiver);
+                if let Some(list) = list {
+                    list.push(details.clone());
+                }
+            }
+        }
+        results
+    }
+
+    fn make_foreign_mod_unsafe(ifm: ItemForeignMod) -> Item {
+        // At the moment syn does not support outputting 'unsafe extern "C"' except in verbatim
+        // items. See https://github.com/dtolnay/syn/pull/938
+        Item::Verbatim(quote! {
+            unsafe #ifm
+        })
+    }
+
+    fn build_include_foreign_items(&self, has_additional_cpp_needs: bool) -> Vec<ForeignItem> {
+        let extra_inclusion = if has_additional_cpp_needs {
+            Some(self.header_name.clone().unwrap())
+        } else {
+            None
+        };
+        let chained = self.include_list.iter().chain(extra_inclusion.iter());
+        chained
+            .map(|inc| {
+                ForeignItem::Macro(parse_quote! {
+                    include!(#inc);
+                })
+            })
+            .collect()
+    }
+
+    /// Generate lots of 'use' statements to pull cxxbridge items into the output
+    /// mod hierarchy according to C++ namespaces.
+    fn generate_final_use_statements(
+        input_items: &[(QualifiedName, RsCodegenResult)],
+    ) -> Vec<Item> {
+        let mut output_items = Vec::new();
+        let ns_entries = NamespaceEntries::new(input_items);
+        Self::append_child_use_namespace(&ns_entries, &mut output_items);
+        output_items
+    }
+
+    fn append_child_use_namespace(
+        ns_entries: &NamespaceEntries<(QualifiedName, RsCodegenResult)>,
+        output_items: &mut Vec<Item>,
+    ) {
+        for (name, codegen) in ns_entries.entries() {
+            output_items.extend(codegen.materializations.iter().map(|materialization| {
+                match materialization {
+                    Use::UsedFromCxxBridgeWithAlias(ref alias) => {
+                        Self::generate_cxx_use_stmt(name, Some(alias))
+                    }
+                    Use::UsedFromCxxBridge => Self::generate_cxx_use_stmt(name, None),
+                    Use::UsedFromBindgen => Self::generate_bindgen_use_stmt(name),
+                    Use::SpecificNameFromBindgen(id) => {
+                        let name = QualifiedName::new(name.get_namespace(), id.clone());
+                        Self::generate_bindgen_use_stmt(&name)
+                    }
+                    Use::Custom(item) => *item.clone(),
+                }
+            }));
+        }
+        for (child_name, child_ns_entries) in ns_entries.children() {
+            if child_ns_entries.is_empty() {
+                continue;
+            }
+            let child_id = make_ident(child_name);
+            let mut new_mod: ItemMod = parse_quote!(
+                pub mod #child_id {
+                }
+            );
+            Self::append_child_use_namespace(
+                child_ns_entries,
+                &mut new_mod.content.as_mut().unwrap().1,
+            );
+            output_items.push(Item::Mod(new_mod));
+        }
+    }
+
+    fn append_uses_for_ns(&mut self, items: &mut Vec<Item>, ns: &Namespace) {
+        let super_duper = std::iter::repeat(make_ident("super")); // I'll get my coat
+        let supers = super_duper.clone().take(ns.depth() + 2);
+        items.push(Item::Use(parse_quote! {
+            #[allow(unused_imports)]
+            use self::
+                #(#supers)::*
+            ::cxxbridge;
+        }));
+        if !self.config.exclude_utilities() {
+            let supers = super_duper.clone().take(ns.depth() + 2);
+            items.push(Item::Use(parse_quote! {
+                #[allow(unused_imports)]
+                use self::
+                    #(#supers)::*
+                ::ToCppString;
+            }));
+        }
+        let supers = super_duper.take(ns.depth() + 1);
+        items.push(Item::Use(parse_quote! {
+            #[allow(unused_imports)]
+            use self::
+                #(#supers)::*
+            ::root;
+        }));
+    }
+
+    fn append_child_bindgen_namespace(
+        &mut self,
+        ns_entries: &NamespaceEntries<(QualifiedName, RsCodegenResult)>,
+        output_items: &mut Vec<Item>,
+        ns: &Namespace,
+    ) {
+        let mut impl_entries_by_type: HashMap<_, Vec<_>> = HashMap::new();
+        let mut trait_impl_entries_by_trait_and_ty: HashMap<_, Vec<_>> = HashMap::new();
+        for item in ns_entries.entries() {
+            output_items.extend(item.1.bindgen_mod_items.iter().cloned());
+            if let Some(impl_entry) = &item.1.impl_entry {
+                impl_entries_by_type
+                    .entry(impl_entry.ty.clone())
+                    .or_default()
+                    .push(&impl_entry.item);
+            }
+            if let Some(trait_impl_entry) = &item.1.trait_impl_entry {
+                trait_impl_entries_by_trait_and_ty
+                    .entry(trait_impl_entry.key.clone())
+                    .or_default()
+                    .push(&trait_impl_entry.item);
+            }
+        }
+        for (ty, entries) in impl_entries_by_type.into_iter() {
+            output_items.push(Item::Impl(parse_quote! {
+                impl #ty {
+                    #(#entries)*
+                }
+            }))
+        }
+        for (key, entries) in trait_impl_entries_by_trait_and_ty.into_iter() {
+            let unsafety = key.unsafety;
+            let ty = key.ty;
+            let trt = key.trait_signature;
+            output_items.push(Item::Impl(parse_quote! {
+                #unsafety impl #trt for #ty {
+                    #(#entries)*
+                }
+            }))
+        }
+        for (child_name, child_ns_entries) in ns_entries.children() {
+            let new_ns = ns.push((*child_name).clone());
+            let child_id = make_ident(child_name);
+
+            let mut inner_output_items = Vec::new();
+            self.append_child_bindgen_namespace(child_ns_entries, &mut inner_output_items, &new_ns);
+            if !inner_output_items.is_empty() {
+                let mut new_mod: ItemMod = parse_quote!(
+                    pub mod #child_id {
+                    }
+                );
+                self.append_uses_for_ns(&mut inner_output_items, &new_ns);
+                new_mod.content.as_mut().unwrap().1 = inner_output_items;
+                output_items.push(Item::Mod(new_mod));
+            }
+        }
+    }
+
+    fn id_to_expr(id: &Ident) -> Expr {
+        parse_quote! { #id }
+    }
+
+    fn generate_final_bindgen_mods(
+        &mut self,
+        input_items: &[(QualifiedName, RsCodegenResult)],
+    ) -> Vec<Item> {
+        let mut output_items = Vec::new();
+        let ns = Namespace::new();
+        let ns_entries = NamespaceEntries::new(input_items);
+        self.append_child_bindgen_namespace(&ns_entries, &mut output_items, &ns);
+        self.append_uses_for_ns(&mut output_items, &ns);
+        output_items
+    }
+
+    fn generate_rs_for_api(
+        &self,
+        api: Api<FnPhase>,
+        associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>,
+        subclasses_with_a_single_trivial_constructor: &HashSet<QualifiedName>,
+        non_pod_types: &HashSet<QualifiedName>,
+    ) -> RsCodegenResult {
+        let name = api.name().clone();
+        let id = name.get_final_ident();
+        let cpp_call_name = api.effective_cpp_name().to_string();
+        match api {
+            Api::StringConstructor { .. } => {
+                let make_string_name = make_ident(self.config.get_makestring_name());
+                RsCodegenResult {
+                    extern_c_mod_items: vec![ForeignItem::Fn(parse_quote!(
+                        fn #make_string_name(str_: &str) -> UniquePtr<CxxString>;
+                    ))],
+                    global_items: get_string_items(),
+                    materializations: vec![Use::UsedFromCxxBridgeWithAlias(make_ident(
+                        "make_string",
+                    ))],
+                    ..Default::default()
+                }
+            }
+            Api::Function { fun, analysis, .. } => gen_function(
+                name.get_namespace(),
+                *fun,
+                analysis,
+                cpp_call_name,
+                non_pod_types,
+            ),
+            Api::Const { const_item, .. } => RsCodegenResult {
+                bindgen_mod_items: vec![Item::Const(const_item)],
+                materializations: vec![Use::UsedFromBindgen],
+                ..Default::default()
+            },
+            Api::Typedef { analysis, .. } => RsCodegenResult {
+                bindgen_mod_items: vec![match analysis.kind {
+                    TypedefKind::Type(type_item) => Item::Type(type_item),
+                    TypedefKind::Use(use_item, _) => Item::Use(use_item),
+                }],
+                materializations: vec![Use::UsedFromBindgen],
+                ..Default::default()
+            },
+            Api::Struct {
+                details,
+                analysis:
+                    PodAndDepAnalysis {
+                        pod:
+                            PodAnalysis {
+                                is_generic, kind, ..
+                            },
+                        constructors,
+                        ..
+                    },
+                ..
+            } => {
+                let doc_attrs = get_doc_attrs(&details.item.attrs);
+                let layout = details.layout.clone();
+                self.generate_type(
+                    &name,
+                    id,
+                    kind,
+                    constructors.move_constructor,
+                    constructors.destructor,
+                    || Some((Item::Struct(details.item), doc_attrs)),
+                    associated_methods,
+                    layout,
+                    is_generic,
+                )
+            }
+            Api::Enum { item, .. } => {
+                let doc_attrs = get_doc_attrs(&item.attrs);
+                self.generate_type(
+                    &name,
+                    id,
+                    TypeKind::Pod,
+                    true,
+                    true,
+                    || Some((Item::Enum(item), doc_attrs)),
+                    associated_methods,
+                    None,
+                    false,
+                )
+            }
+            Api::ForwardDeclaration { .. }
+            | Api::ConcreteType { .. }
+            | Api::OpaqueTypedef { .. } => self.generate_type(
+                &name,
+                id,
+                TypeKind::Abstract,
+                false, // assume for now that these types can't be kept in a Vector
+                true,  // assume for now that these types can be put in a smart pointer
+                || None,
+                associated_methods,
+                None,
+                false,
+            ),
+            Api::CType { .. } => RsCodegenResult {
+                extern_c_mod_items: vec![ForeignItem::Verbatim(quote! {
+                    type #id = autocxx::#id;
+                })],
+                ..Default::default()
+            },
+            Api::RustType { path, .. } => RsCodegenResult {
+                global_items: vec![parse_quote! {
+                    use super::#path;
+                }],
+                extern_rust_mod_items: vec![parse_quote! {
+                    type #id;
+                }],
+                ..Default::default()
+            },
+            Api::RustFn {
+                details:
+                    RustFun {
+                        path,
+                        sig,
+                        receiver: None,
+                        ..
+                    },
+                ..
+            } => RsCodegenResult {
+                global_items: vec![parse_quote! {
+                    use super::#path;
+                }],
+                extern_rust_mod_items: vec![parse_quote! {
+                    #sig;
+                }],
+                ..Default::default()
+            },
+            Api::RustFn {
+                details:
+                    RustFun {
+                        sig,
+                        receiver: Some(_),
+                        ..
+                    },
+                ..
+            } => RsCodegenResult {
+                extern_rust_mod_items: vec![parse_quote! {
+                    #sig;
+                }],
+                ..Default::default()
+            },
+            Api::RustSubclassFn {
+                details, subclass, ..
+            } => Self::generate_subclass_fn(id, *details, subclass),
+            Api::Subclass {
+                name, superclass, ..
+            } => {
+                let methods = associated_methods.get(&superclass);
+                let generate_peer_constructor =
+                    subclasses_with_a_single_trivial_constructor.contains(&name.0.name);
+                self.generate_subclass(name, &superclass, methods, generate_peer_constructor)
+            }
+            Api::ExternCppType {
+                details: ExternCppType { rust_path, .. },
+                ..
+            } => self.generate_extern_cpp_type(&name, rust_path, name.ns_segment_iter().count()),
+            Api::IgnoredItem {
+                err,
+                ctx: Some(ctx),
+                ..
+            } => Self::generate_error_entry(err, ctx),
+            Api::IgnoredItem { .. } | Api::SubclassTraitItem { .. } => RsCodegenResult::default(),
+        }
+    }
+
+    fn generate_subclass(
+        &self,
+        sub: SubclassName,
+        superclass: &QualifiedName,
+        methods: Option<&Vec<SuperclassMethod>>,
+        generate_peer_constructor: bool,
+    ) -> RsCodegenResult {
+        let super_name = superclass.get_final_item();
+        let super_path = superclass.to_type_path();
+        let super_cxxxbridge_id = superclass.get_final_ident();
+        let id = sub.id();
+        let holder = sub.holder();
+        let full_cpp = sub.cpp();
+        let cpp_path = full_cpp.to_type_path();
+        let cpp_id = full_cpp.get_final_ident();
+        let mut global_items = Vec::new();
+        global_items.push(parse_quote! {
+            pub use bindgen::root::#holder;
+        });
+        let relinquish_ownership_call = sub.cpp_remove_ownership();
+        let mut bindgen_mod_items = vec![
+            parse_quote! {
+                pub use cxxbridge::#cpp_id;
+            },
+            parse_quote! {
+                pub struct #holder(pub autocxx::subclass::CppSubclassRustPeerHolder<super::super::super::#id>);
+            },
+            parse_quote! {
+                impl autocxx::subclass::CppSubclassCppPeer for #cpp_id {
+                    fn relinquish_ownership(&self) {
+                        self.#relinquish_ownership_call();
+                    }
+                }
+            },
+        ];
+        let mut extern_c_mod_items = vec![
+            self.generate_cxxbridge_type(&full_cpp, false, Vec::new()),
+            parse_quote! {
+                fn #relinquish_ownership_call(self: &#cpp_id);
+            },
+        ];
+        if let Some(methods) = methods {
+            let supers = SubclassName::get_supers_trait_name(superclass).to_type_path();
+            let methods_impls: Vec<ImplItem> = methods
+                .iter()
+                .filter(|m| !m.is_pure_virtual)
+                .map(|m| {
+                    let cpp_super_method_name =
+                        SubclassName::get_super_fn_name(&Namespace::new(), &m.name.to_string())
+                            .get_final_ident();
+                    let mut params = m.params.clone();
+                    let ret = &m.ret_type.clone();
+                    let (peer_fn, first_param) = match m.receiver_mutability {
+                        ReceiverMutability::Const => ("peer", parse_quote!(&self)),
+                        ReceiverMutability::Mutable => ("peer_mut", parse_quote!(&mut self)),
+                    };
+                    let peer_fn = make_ident(peer_fn);
+                    *(params.iter_mut().next().unwrap()) = first_param;
+                    let param_names = m.param_names.iter().skip(1);
+                    let unsafe_token = m.requires_unsafe.wrapper_token();
+                    parse_quote! {
+                        #unsafe_token fn #cpp_super_method_name(#params) #ret {
+                            use autocxx::subclass::CppSubclass;
+                            self.#peer_fn().#cpp_super_method_name(#(#param_names),*)
+                        }
+                    }
+                })
+                .collect();
+            if !methods_impls.is_empty() {
+                bindgen_mod_items.push(parse_quote! {
+                    #[allow(non_snake_case)]
+                    impl #supers for super::super::super::#id {
+                        #(#methods_impls)*
+                    }
+                });
+            }
+        }
+        if generate_peer_constructor {
+            bindgen_mod_items.push(parse_quote! {
+                impl autocxx::subclass::CppPeerConstructor<#cpp_id> for super::super::super::#id {
+                    fn make_peer(&mut self, peer_holder: autocxx::subclass::CppSubclassRustPeerHolder<Self>) -> cxx::UniquePtr<#cpp_path> {
+                        use autocxx::moveit::EmplaceUnpinned;
+                        cxx::UniquePtr::emplace(#cpp_id :: new(peer_holder))
+                    }
+                }
+            })
+        };
+
+        // Once for each superclass, in future...
+        let as_id = make_ident(format!("As_{}", super_name));
+        extern_c_mod_items.push(parse_quote! {
+            fn #as_id(self: &#cpp_id) -> &#super_cxxxbridge_id;
+        });
+        let as_mut_id = make_ident(format!("As_{}_mut", super_name));
+        extern_c_mod_items.push(parse_quote! {
+            fn #as_mut_id(self: Pin<&mut #cpp_id>) -> Pin<&mut #super_cxxxbridge_id>;
+        });
+        bindgen_mod_items.push(parse_quote! {
+            impl AsRef<#super_path> for super::super::super::#id {
+                fn as_ref(&self) -> &cxxbridge::#super_cxxxbridge_id {
+                    use autocxx::subclass::CppSubclass;
+                    self.peer().#as_id()
+                }
+            }
+        });
+        // TODO it would be nice to impl AsMut here but pin prevents us
+        bindgen_mod_items.push(parse_quote! {
+            impl super::super::super::#id {
+                pub fn pin_mut(&mut self) -> ::std::pin::Pin<&mut cxxbridge::#super_cxxxbridge_id> {
+                    use autocxx::subclass::CppSubclass;
+                    self.peer_mut().#as_mut_id()
+                }
+            }
+        });
+        let remove_ownership = sub.remove_ownership();
+        global_items.push(parse_quote! {
+            #[allow(non_snake_case)]
+            pub fn #remove_ownership(me: Box<#holder>) -> Box<#holder> {
+                Box::new(#holder(me.0.relinquish_ownership()))
+            }
+        });
+        RsCodegenResult {
+            extern_c_mod_items,
+            // For now we just assume we can't keep subclasses in vectors, but we can put them in
+            // smart pointers.
+            // That's the reason for the 'false' and 'true'
+            bridge_items: create_impl_items(&cpp_id, false, true, self.config),
+            bindgen_mod_items,
+            materializations: vec![Use::Custom(Box::new(parse_quote! {
+                pub use cxxbridge::#cpp_id;
+            }))],
+            global_items,
+            extern_rust_mod_items: vec![
+                parse_quote! {
+                    pub type #holder;
+                },
+                parse_quote! {
+                    fn #remove_ownership(me: Box<#holder>) -> Box<#holder>;
+                },
+            ],
+            ..Default::default()
+        }
+    }
+
+    fn generate_subclass_fn(
+        api_name: Ident,
+        details: RustSubclassFnDetails,
+        subclass: SubclassName,
+    ) -> RsCodegenResult {
+        let params = details.params;
+        let ret = details.ret;
+        let unsafe_token = details.requires_unsafe.wrapper_token();
+        let global_def = quote! { #unsafe_token fn #api_name(#params) #ret };
+        let params = unqualify_params(params);
+        let ret = unqualify_ret_type(ret);
+        let method_name = details.method_name;
+        let cxxbridge_decl: ForeignItemFn =
+            parse_quote! { #unsafe_token fn #api_name(#params) #ret; };
+        let args: Punctuated<Expr, Comma> =
+            Self::args_from_sig(&cxxbridge_decl.sig.inputs).collect();
+        let superclass_id = details.superclass.get_final_ident();
+        let methods_trait = SubclassName::get_methods_trait_name(&details.superclass);
+        let methods_trait = methods_trait.to_type_path();
+        let (deref_ty, deref_call, borrow, mut_token) = match details.receiver_mutability {
+            ReceiverMutability::Const => ("Deref", "deref", "try_borrow", None),
+            ReceiverMutability::Mutable => (
+                "DerefMut",
+                "deref_mut",
+                "try_borrow_mut",
+                Some(syn::token::Mut(Span::call_site())),
+            ),
+        };
+        let deref_ty = make_ident(deref_ty);
+        let deref_call = make_ident(deref_call);
+        let borrow = make_ident(borrow);
+        let destroy_panic_msg = format!("Rust subclass API (method {} of subclass {} of superclass {}) called after subclass destroyed", method_name, subclass.0.name, superclass_id);
+        let reentrancy_panic_msg = format!("Rust subclass API (method {} of subclass {} of superclass {}) called whilst subclass already borrowed - likely a re-entrant call",  method_name, subclass.0.name, superclass_id);
+        RsCodegenResult {
+            global_items: vec![parse_quote! {
+                #global_def {
+                    let rc = me.0
+                        .get()
+                        .expect(#destroy_panic_msg);
+                    let #mut_token b = rc
+                        .as_ref()
+                        .#borrow()
+                        .expect(#reentrancy_panic_msg);
+                    let r = std::ops::#deref_ty::#deref_call(& #mut_token b);
+                    #methods_trait :: #method_name
+                        (r,
+                        #args)
+                }
+            }],
+            extern_rust_mod_items: vec![ForeignItem::Fn(cxxbridge_decl)],
+            ..Default::default()
+        }
+    }
+
+    fn args_from_sig(params: &Punctuated<FnArg, Comma>) -> impl Iterator<Item = Expr> + '_ {
+        params.iter().skip(1).filter_map(|fnarg| match fnarg {
+            syn::FnArg::Receiver(_) => None,
+            syn::FnArg::Typed(fnarg) => match &*fnarg.pat {
+                syn::Pat::Ident(id) => Some(Self::id_to_expr(&id.ident)),
+                _ => None,
+            },
+        })
+    }
+
+    #[allow(clippy::too_many_arguments)] // currently the least unclear way
+    fn generate_type<F>(
+        &self,
+        name: &QualifiedName,
+        id: Ident,
+        type_kind: TypeKind,
+        movable: bool,
+        destroyable: bool,
+        item_creator: F,
+        associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>,
+        layout: Option<Layout>,
+        is_generic: bool,
+    ) -> RsCodegenResult
+    where
+        F: FnOnce() -> Option<(Item, Vec<Attribute>)>,
+    {
+        let mut bindgen_mod_items = Vec::new();
+        let mut materializations = vec![Use::UsedFromBindgen];
+        Self::add_superclass_stuff_to_type(
+            name,
+            &mut bindgen_mod_items,
+            &mut materializations,
+            associated_methods.get(name),
+        );
+        let orig_item = item_creator();
+        let doc_attrs = orig_item
+            .as_ref()
+            .map(|maybe_item| maybe_item.1.clone())
+            .unwrap_or_default();
+        // We have a choice here to either:
+        // a) tell cxx to generate an opaque type using 'type A;'
+        // b) generate a concrete type definition, e.g. by using bindgen's
+        //    or doing our own, and then telling cxx 'type A = bindgen::A';'
+        match type_kind {
+            TypeKind::Pod | TypeKind::NonPod => {
+                // Feed cxx "type T = root::bindgen::T"
+                // For non-POD types, there might be the option of simply giving
+                // cxx a "type T;" as we do for abstract types below. There's
+                // two reasons we don't:
+                // a) we want to specify size and alignment for the sake of
+                //    moveit;
+                // b) for nested types such as 'A::B', there is no combination
+                //    of cxx-acceptable attributes which will inform cxx that
+                //    A is a class rather than a namespace.
+                let mut item = orig_item
+                    .expect("Instantiable types must provide instance")
+                    .0;
+                if matches!(type_kind, TypeKind::NonPod) {
+                    if let Item::Struct(ref mut s) = item {
+                        // Retain generics and doc attrs.
+                        make_non_pod(s, layout);
+                    } else {
+                        // enum
+                        item = Item::Struct(new_non_pod_struct(id.clone()));
+                    }
+                }
+                bindgen_mod_items.push(item);
+
+                if is_generic {
+                    // Still generate the type as emitted by bindgen,
+                    // but don't attempt to tell cxx about it
+                    RsCodegenResult {
+                        bindgen_mod_items,
+                        materializations,
+                        ..Default::default()
+                    }
+                } else {
+                    RsCodegenResult {
+                        global_items: self.generate_extern_type_impl(type_kind, name),
+                        bridge_items: create_impl_items(&id, movable, destroyable, self.config),
+                        extern_c_mod_items: vec![
+                            self.generate_cxxbridge_type(name, true, doc_attrs)
+                        ],
+                        bindgen_mod_items,
+                        materializations,
+                        ..Default::default()
+                    }
+                }
+            }
+            TypeKind::Abstract => {
+                if is_generic {
+                    RsCodegenResult::default()
+                } else {
+                    // Feed cxx "type T;"
+                    // We MUST do this because otherwise cxx assumes this can be
+                    // instantiated using UniquePtr etc.
+                    bindgen_mod_items.push(Item::Use(parse_quote! { pub use cxxbridge::#id; }));
+                    RsCodegenResult {
+                        extern_c_mod_items: vec![
+                            self.generate_cxxbridge_type(name, false, doc_attrs)
+                        ],
+                        bindgen_mod_items,
+                        materializations,
+                        ..Default::default()
+                    }
+                }
+            }
+        }
+    }
+
+    fn add_superclass_stuff_to_type(
+        name: &QualifiedName,
+        bindgen_mod_items: &mut Vec<Item>,
+        materializations: &mut Vec<Use>,
+        methods: Option<&Vec<SuperclassMethod>>,
+    ) {
+        if let Some(methods) = methods {
+            let (supers, mains): (Vec<_>, Vec<_>) = methods
+                .iter()
+                .map(|method| {
+                    let id = &method.name;
+                    let super_id =
+                        SubclassName::get_super_fn_name(&Namespace::new(), &id.to_string())
+                            .get_final_ident();
+                    let param_names: Punctuated<Expr, Comma> =
+                        Self::args_from_sig(&method.params).collect();
+                    let mut params = method.params.clone();
+                    *(params.iter_mut().next().unwrap()) = match method.receiver_mutability {
+                        ReceiverMutability::Const => parse_quote!(&self),
+                        ReceiverMutability::Mutable => parse_quote!(&mut self),
+                    };
+                    let ret_type = &method.ret_type;
+                    let unsafe_token = method.requires_unsafe.wrapper_token();
+                    if method.is_pure_virtual {
+                        (
+                            None,
+                            parse_quote!(
+                                #unsafe_token fn #id(#params) #ret_type;
+                            ),
+                        )
+                    } else {
+                        let a: Option<TraitItem> = Some(parse_quote!(
+                            #unsafe_token fn #super_id(#params) #ret_type;
+                        ));
+                        let b: TraitItem = parse_quote!(
+                            #unsafe_token fn #id(#params) #ret_type {
+                                self.#super_id(#param_names)
+                            }
+                        );
+                        (a, b)
+                    }
+                })
+                .unzip();
+            let supers: Vec<_> = supers.into_iter().flatten().collect();
+            let supers_name = SubclassName::get_supers_trait_name(name).get_final_ident();
+            let methods_name = SubclassName::get_methods_trait_name(name).get_final_ident();
+            if !supers.is_empty() {
+                bindgen_mod_items.push(parse_quote! {
+                    #[allow(non_snake_case)]
+                    pub trait #supers_name {
+                        #(#supers)*
+                    }
+                });
+                bindgen_mod_items.push(parse_quote! {
+                    #[allow(non_snake_case)]
+                    pub trait #methods_name : #supers_name {
+                        #(#mains)*
+                    }
+                });
+                materializations.push(Use::SpecificNameFromBindgen(supers_name));
+            } else {
+                bindgen_mod_items.push(parse_quote! {
+                    #[allow(non_snake_case)]
+                    pub trait #methods_name {
+                        #(#mains)*
+                    }
+                });
+            }
+            materializations.push(Use::SpecificNameFromBindgen(methods_name));
+        }
+    }
+
+    fn generate_extern_cpp_type(
+        &self,
+        name: &QualifiedName,
+        rust_path: TypePath,
+        ns_depth: usize,
+    ) -> RsCodegenResult {
+        let id = name.get_final_ident();
+        let super_duper = std::iter::repeat(make_ident("super"));
+        let supers = super_duper.take(ns_depth + 2);
+        let use_statement = parse_quote! {
+            pub use #(#supers)::* :: #id;
+        };
+        RsCodegenResult {
+            bindgen_mod_items: vec![use_statement],
+            extern_c_mod_items: vec![self.generate_cxxbridge_type(name, true, Vec::new())],
+            materializations: vec![Use::Custom(Box::new(parse_quote! { pub use #rust_path; }))],
+            ..Default::default()
+        }
+    }
+
+    /// Generates something in the output mod that will carry a docstring
+    /// explaining why a given type or function couldn't have bindings
+    /// generated.
+    fn generate_error_entry(err: ConvertError, ctx: ErrorContext) -> RsCodegenResult {
+        let err = format!("autocxx bindings couldn't be generated: {}", err);
+        let (impl_entry, bindgen_mod_item, materialization) = match ctx.into_type() {
+            ErrorContextType::Item(id) => (
+                // Populate within bindgen mod because impl blocks may attach.
+                None,
+                Some(parse_quote! {
+                    #[doc = #err]
+                    pub struct #id;
+                }),
+                Some(Use::SpecificNameFromBindgen(id)),
+            ),
+            ErrorContextType::SanitizedItem(id) => (
+                // Guaranteed to be no impl blocks - populate directly in output mod.
+                None,
+                None,
+                Some(Use::Custom(Box::new(parse_quote! {
+                    #[doc = #err]
+                    pub struct #id;
+                }))),
+            ),
+            ErrorContextType::Method { self_ty, method } => (
+                Some(Box::new(ImplBlockDetails {
+                    item: parse_quote! {
+                        #[doc = #err]
+                        fn #method(_uhoh: autocxx::BindingGenerationFailure) {
+                        }
+                    },
+                    ty: self_ty,
+                })),
+                None,
+                None,
+            ),
+        };
+        RsCodegenResult {
+            impl_entry,
+            bindgen_mod_items: bindgen_mod_item.into_iter().collect(),
+            materializations: materialization.into_iter().collect(),
+            ..Default::default()
+        }
+    }
+
+    fn generate_cxx_use_stmt(name: &QualifiedName, alias: Option<&Ident>) -> Item {
+        let segs = Self::find_output_mod_root(name.get_namespace())
+            .chain(std::iter::once(make_ident("cxxbridge")))
+            .chain(std::iter::once(name.get_final_ident()));
+        Item::Use(match alias {
+            None => parse_quote! {
+                pub use #(#segs)::*;
+            },
+            Some(alias) => parse_quote! {
+                pub use #(#segs)::* as #alias;
+            },
+        })
+    }
+
+    fn generate_bindgen_use_stmt(name: &QualifiedName) -> Item {
+        let segs =
+            Self::find_output_mod_root(name.get_namespace()).chain(name.get_bindgen_path_idents());
+        Item::Use(parse_quote! {
+            pub use #(#segs)::*;
+        })
+    }
+
+    fn generate_extern_type_impl(&self, type_kind: TypeKind, tyname: &QualifiedName) -> Vec<Item> {
+        let tynamestring = namespaced_name_using_original_name_map(tyname, &self.original_name_map);
+        let fulltypath = tyname.get_bindgen_path_idents();
+        let kind_item = match type_kind {
+            TypeKind::Pod => "Trivial",
+            _ => "Opaque",
+        };
+        let kind_item = make_ident(kind_item);
+        vec![Item::Impl(parse_quote! {
+            unsafe impl cxx::ExternType for #(#fulltypath)::* {
+                type Id = cxx::type_id!(#tynamestring);
+                type Kind = cxx::kind::#kind_item;
+            }
+        })]
+    }
+
+    fn generate_cxxbridge_type(
+        &self,
+        name: &QualifiedName,
+        references_bindgen: bool,
+        doc_attrs: Vec<Attribute>,
+    ) -> ForeignItem {
+        let ns = name.get_namespace();
+        let id = name.get_final_ident();
+        // The following lines actually Tell A Lie.
+        // If we have a nested class, B::C, within namespace A,
+        // we actually have to tell cxx that we have nested class C
+        // within namespace A.
+        let mut ns_components: Vec<_> = ns.iter().cloned().collect();
+        let mut cxx_name = None;
+        if let Some(cpp_name) = self.original_name_map.get(name) {
+            let cpp_name = QualifiedName::new_from_cpp_name(cpp_name);
+            cxx_name = Some(cpp_name.get_final_item().to_string());
+            ns_components.extend(cpp_name.ns_segment_iter().cloned());
+        };
+
+        let mut for_extern_c_ts = if !ns_components.is_empty() {
+            let ns_string = ns_components.join("::");
+            quote! {
+                #[namespace = #ns_string]
+            }
+        } else {
+            TokenStream::new()
+        };
+
+        if let Some(n) = cxx_name {
+            for_extern_c_ts.extend(quote! {
+                #[cxx_name = #n]
+            });
+        }
+
+        for_extern_c_ts.extend(quote! {
+            #(#doc_attrs)*
+        });
+
+        if references_bindgen {
+            for_extern_c_ts.extend(quote! {
+                type #id = super::bindgen::root::
+            });
+            for_extern_c_ts.extend(ns.iter().map(make_ident).map(|id| {
+                quote! {
+                    #id::
+                }
+            }));
+            for_extern_c_ts.extend(quote! {
+                #id;
+            });
+        } else {
+            for_extern_c_ts.extend(quote! {
+                type #id;
+            });
+        }
+        ForeignItem::Verbatim(for_extern_c_ts)
+    }
+
+    fn find_output_mod_root(ns: &Namespace) -> impl Iterator<Item = Ident> {
+        std::iter::repeat(make_ident("super")).take(ns.depth())
+    }
+}
+
+fn find_trivially_constructed_subclasses(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> {
+    let (simple_constructors, complex_constructors): (Vec<_>, Vec<_>) = apis
+        .iter()
+        .filter_map(|api| match api {
+            Api::Function { fun, .. } => match &fun.provenance {
+                Provenance::SynthesizedSubclassConstructor(details) => {
+                    Some((&details.subclass.0.name, details.is_trivial))
+                }
+                _ => None,
+            },
+            _ => None,
+        })
+        .partition(|(_, trivial)| *trivial);
+    let simple_constructors: HashSet<_> =
+        simple_constructors.into_iter().map(|(qn, _)| qn).collect();
+    let complex_constructors: HashSet<_> =
+        complex_constructors.into_iter().map(|(qn, _)| qn).collect();
+    (&simple_constructors - &complex_constructors)
+        .into_iter()
+        .cloned()
+        .collect()
+}
+
+fn find_non_pod_types(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> {
+    apis.iter()
+        .filter_map(|api| match api {
+            Api::Struct {
+                name,
+                analysis:
+                    PodAndDepAnalysis {
+                        pod:
+                            PodAnalysis {
+                                kind: TypeKind::NonPod,
+                                ..
+                            },
+                        ..
+                    },
+                ..
+            } => Some(name.name.clone()),
+            _ => None,
+        })
+        .collect()
+}
+
+impl HasNs for (QualifiedName, RsCodegenResult) {
+    fn get_namespace(&self) -> &Namespace {
+        self.0.get_namespace()
+    }
+}
+
+impl<T: AnalysisPhase> HasNs for Api<T> {
+    fn get_namespace(&self) -> &Namespace {
+        self.name().get_namespace()
+    }
+}
+
+/// Snippets of code generated from a particular API.
+/// These are then concatenated together into the final generated code.
+#[derive(Default)]
+struct RsCodegenResult {
+    extern_c_mod_items: Vec<ForeignItem>,
+    extern_rust_mod_items: Vec<ForeignItem>,
+    bridge_items: Vec<Item>,
+    global_items: Vec<Item>,
+    bindgen_mod_items: Vec<Item>,
+    impl_entry: Option<Box<ImplBlockDetails>>,
+    trait_impl_entry: Option<Box<TraitImplBlockDetails>>,
+    materializations: Vec<Use>,
+}
+
+/// An [`Item`] that always needs to be in an unsafe block.
+#[derive(Clone)]
+enum MaybeUnsafeStmt {
+    // This could almost be a syn::Stmt, but that doesn't quite work
+    // because the last stmt in a function is actually an expression
+    // thus lacking a semicolon.
+    Normal(TokenStream),
+    NeedsUnsafe(TokenStream),
+    Binary {
+        in_safe_context: TokenStream,
+        in_unsafe_context: TokenStream,
+    },
+}
+
+impl MaybeUnsafeStmt {
+    fn new(stmt: TokenStream) -> Self {
+        Self::Normal(stmt)
+    }
+
+    fn needs_unsafe(stmt: TokenStream) -> Self {
+        Self::NeedsUnsafe(stmt)
+    }
+
+    fn maybe_unsafe(stmt: TokenStream, needs_unsafe: bool) -> Self {
+        if needs_unsafe {
+            Self::NeedsUnsafe(stmt)
+        } else {
+            Self::Normal(stmt)
+        }
+    }
+
+    fn binary(in_safe_context: TokenStream, in_unsafe_context: TokenStream) -> Self {
+        Self::Binary {
+            in_safe_context,
+            in_unsafe_context,
+        }
+    }
+}
+
+fn maybe_unsafes_to_tokens(
+    items: Vec<MaybeUnsafeStmt>,
+    context_is_already_unsafe: bool,
+) -> TokenStream {
+    if context_is_already_unsafe {
+        let items = items.into_iter().map(|item| match item {
+            MaybeUnsafeStmt::Normal(stmt)
+            | MaybeUnsafeStmt::NeedsUnsafe(stmt)
+            | MaybeUnsafeStmt::Binary {
+                in_unsafe_context: stmt,
+                ..
+            } => stmt,
+        });
+        quote! {
+            #(#items)*
+        }
+    } else {
+        let mut currently_unsafe_list = None;
+        let mut output = Vec::new();
+        for item in items {
+            match item {
+                MaybeUnsafeStmt::NeedsUnsafe(stmt) => {
+                    if currently_unsafe_list.is_none() {
+                        currently_unsafe_list = Some(Vec::new());
+                    }
+                    currently_unsafe_list.as_mut().unwrap().push(stmt);
+                }
+                MaybeUnsafeStmt::Normal(stmt)
+                | MaybeUnsafeStmt::Binary {
+                    in_safe_context: stmt,
+                    ..
+                } => {
+                    if let Some(currently_unsafe_list) = currently_unsafe_list.take() {
+                        output.push(quote! {
+                            unsafe {
+                                #(#currently_unsafe_list)*
+                            }
+                        })
+                    }
+                    output.push(stmt);
+                }
+            }
+        }
+        if let Some(currently_unsafe_list) = currently_unsafe_list.take() {
+            output.push(quote! {
+                unsafe {
+                    #(#currently_unsafe_list)*
+                }
+            })
+        }
+        quote! {
+            #(#output)*
+        }
+    }
+}
+
+#[test]
+fn test_maybe_unsafes_to_tokens() {
+    let items = vec![
+        MaybeUnsafeStmt::new(quote! { use A; }),
+        MaybeUnsafeStmt::new(quote! { use B; }),
+        MaybeUnsafeStmt::needs_unsafe(quote! { use C; }),
+        MaybeUnsafeStmt::needs_unsafe(quote! { use D; }),
+        MaybeUnsafeStmt::new(quote! { use E; }),
+        MaybeUnsafeStmt::needs_unsafe(quote! { use F; }),
+    ];
+    assert_eq!(
+        maybe_unsafes_to_tokens(items.clone(), false).to_string(),
+        quote! {
+            use A;
+            use B;
+            unsafe {
+                use C;
+                use D;
+            }
+            use E;
+            unsafe {
+                use F;
+            }
+        }
+        .to_string()
+    );
+    assert_eq!(
+        maybe_unsafes_to_tokens(items, true).to_string(),
+        quote! {
+            use A;
+            use B;
+            use C;
+            use D;
+            use E;
+            use F;
+        }
+        .to_string()
+    );
+}
diff --git a/engine/src/conversion/codegen_rs/namespace_organizer.rs b/engine/src/conversion/codegen_rs/namespace_organizer.rs
new file mode 100644
index 0000000..886cb1c
--- /dev/null
+++ b/engine/src/conversion/codegen_rs/namespace_organizer.rs
@@ -0,0 +1,136 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::types::Namespace;
+use std::collections::BTreeMap;
+
+pub trait HasNs {
+    fn get_namespace(&self) -> &Namespace;
+}
+
+pub struct NamespaceEntries<'a, T: HasNs> {
+    entries: Vec<&'a T>,
+    children: BTreeMap<&'a String, NamespaceEntries<'a, T>>,
+}
+
+impl<'a, T: HasNs> NamespaceEntries<'a, T> {
+    pub(crate) fn new(apis: &'a [T]) -> Self {
+        let api_refs = apis.iter().collect::<Vec<_>>();
+        Self::sort_by_inner_namespace(api_refs, 0)
+    }
+
+    pub(crate) fn is_empty(&self) -> bool {
+        self.entries.is_empty() && self.children.iter().all(|(_, child)| child.is_empty())
+    }
+
+    pub(crate) fn entries(&self) -> &[&'a T] {
+        &self.entries
+    }
+
+    pub(crate) fn children(&self) -> impl Iterator<Item = (&&String, &NamespaceEntries<T>)> {
+        self.children.iter()
+    }
+
+    fn sort_by_inner_namespace(apis: Vec<&'a T>, depth: usize) -> Self {
+        let mut root = NamespaceEntries {
+            entries: Vec::new(),
+            children: BTreeMap::new(),
+        };
+
+        let mut kids_by_child_ns = BTreeMap::new();
+        for api in apis {
+            let first_ns_elem = api.get_namespace().iter().nth(depth);
+            if let Some(first_ns_elem) = first_ns_elem {
+                let list = kids_by_child_ns
+                    .entry(first_ns_elem)
+                    .or_insert_with(Vec::new);
+                list.push(api);
+                continue;
+            }
+            root.entries.push(api);
+        }
+
+        for (k, v) in kids_by_child_ns.into_iter() {
+            root.children
+                .insert(k, Self::sort_by_inner_namespace(v, depth + 1));
+        }
+
+        root
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HasNs, NamespaceEntries};
+    use crate::types::Namespace;
+
+    struct TestApi(&'static str, Namespace);
+    impl HasNs for TestApi {
+        fn get_namespace(&self) -> &Namespace {
+            &self.1
+        }
+    }
+
+    #[test]
+    fn test_ns_entries_sort() {
+        let entries = vec![
+            make_api(None, "C"),
+            make_api(None, "A"),
+            make_api(Some("G"), "E"),
+            make_api(Some("D"), "F"),
+            make_api(Some("G"), "H"),
+            make_api(Some("D::K"), "L"),
+            make_api(Some("D::K"), "M"),
+            make_api(None, "B"),
+            make_api(Some("D"), "I"),
+            make_api(Some("D"), "J"),
+        ];
+        let ns = NamespaceEntries::new(&entries);
+        let root_entries = ns.entries();
+        assert_eq!(root_entries.len(), 3);
+        assert_ident(root_entries[0], "C");
+        assert_ident(root_entries[1], "A");
+        assert_ident(root_entries[2], "B");
+        let mut kids = ns.children();
+        let (d_id, d_nse) = kids.next().unwrap();
+        assert_eq!(d_id.to_string(), "D");
+        let (g_id, g_nse) = kids.next().unwrap();
+        assert_eq!(g_id.to_string(), "G");
+        assert!(kids.next().is_none());
+        let d_nse_entries = d_nse.entries();
+        assert_eq!(d_nse_entries.len(), 3);
+        assert_ident(d_nse_entries[0], "F");
+        assert_ident(d_nse_entries[1], "I");
+        assert_ident(d_nse_entries[2], "J");
+        let g_nse_entries = g_nse.entries();
+        assert_eq!(g_nse_entries.len(), 2);
+        assert_ident(g_nse_entries[0], "E");
+        assert_ident(g_nse_entries[1], "H");
+        let mut g_kids = g_nse.children();
+        assert!(g_kids.next().is_none());
+        let mut d_kids = d_nse.children();
+        let (k_id, k_nse) = d_kids.next().unwrap();
+        assert_eq!(k_id.to_string(), "K");
+        let k_nse_entries = k_nse.entries();
+        assert_eq!(k_nse_entries.len(), 2);
+        assert_ident(k_nse_entries[0], "L");
+        assert_ident(k_nse_entries[1], "M");
+    }
+
+    fn assert_ident(api: &TestApi, expected: &str) {
+        assert_eq!(api.0, expected);
+    }
+
+    fn make_api(ns: Option<&str>, id: &'static str) -> TestApi {
+        let ns = match ns {
+            Some(st) => Namespace::from_user_input(st),
+            None => Namespace::new(),
+        };
+        TestApi(id, ns)
+    }
+}
diff --git a/engine/src/conversion/codegen_rs/non_pod_struct.rs b/engine/src/conversion/codegen_rs/non_pod_struct.rs
new file mode 100644
index 0000000..e4bec2f
--- /dev/null
+++ b/engine/src/conversion/codegen_rs/non_pod_struct.rs
@@ -0,0 +1,132 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::conversion::api::Layout;
+use crate::types::make_ident;
+use proc_macro2::{Ident, Span};
+use quote::quote;
+use syn::parse::Parser;
+use syn::punctuated::Punctuated;
+use syn::{parse_quote, Field, Fields, GenericParam, ItemStruct, LitInt};
+
+pub(crate) fn new_non_pod_struct(id: Ident) -> ItemStruct {
+    let mut s = parse_quote! {
+        pub struct #id {
+        }
+    };
+    make_non_pod(&mut s, None);
+    s
+}
+
+pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
+    // Make an opaque struct. If we have layout information, we pass
+    // that through to Rust. We keep only doc attrs, plus add a #[repr(C)]
+    // if necessary.
+    // Constraints here (thanks to dtolnay@ for this explanation of why the
+    // following is needed:)
+    // (1) If the real alignment of the C++ type is smaller and a reference
+    // is returned from C++ to Rust, mere existence of an insufficiently
+    // aligned reference in Rust causes UB even if never dereferenced
+    // by Rust code
+    // (see https://doc.rust-lang.org/1.47.0/reference/behavior-considered-undefined.html).
+    // Rustc can use least-significant bits of the reference for other storage.
+    // (if we have layout information from bindgen we use that instead)
+    // (2) We want to ensure the type is !Unpin
+    // (3) We want to ensure it's not Send or Sync
+    //
+    // For opaque types, the Rusty opaque structure could in fact be generated
+    // by three different things:
+    // a) bindgen, using its --opaque-type command line argument or the library
+    //    equivalent;
+    // b) us (autocxx), which is what this code does
+    // c) cxx, using "type B;" in an "extern "C++"" section
+    // We never use (a) because bindgen requires an allowlist of opaque types.
+    // Furthermore, it sometimes then discards struct definitions entirely
+    // and says "type A = [u8;2];" or something else which makes our life
+    // much more difficult.
+    // We use (c) for abstract types. For everything else, we do it ourselves
+    // for maximal control. See codegen_rs/mod.rs generate_type for more notes.
+    // First work out attributes.
+    let doc_attr = s
+        .attrs
+        .iter()
+        .filter(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
+        .cloned();
+    let repr_attr = if let Some(layout) = &layout {
+        let align = make_lit_int(layout.align);
+        if layout.packed {
+            parse_quote! {
+                #[repr(C,align(#align),packed)]
+            }
+        } else {
+            parse_quote! {
+                #[repr(C,align(#align))]
+            }
+        }
+    } else {
+        parse_quote! {
+            #[repr(C, packed)]
+        }
+    };
+    let attrs = doc_attr.chain(std::iter::once(repr_attr));
+    s.attrs = attrs.collect();
+    // Now fill in fields. Usually, we just want a single field
+    // but if this is a generic type we need to faff a bit.
+    let generic_type_fields = s
+        .generics
+        .params
+        .iter()
+        .enumerate()
+        .filter_map(|(counter, gp)| match gp {
+            GenericParam::Type(gpt) => {
+                let id = &gpt.ident;
+                let field_name = make_ident(&format!("_phantom_{}", counter));
+                let toks = quote! {
+                    #field_name: ::std::marker::PhantomData<::std::cell::UnsafeCell< #id >>
+                };
+                Some(Field::parse_named.parse2(toks).unwrap())
+            }
+            _ => None,
+        });
+    let data_field = if let Some(layout) = layout {
+        let size = make_lit_int(layout.size);
+        Some(
+            syn::Field::parse_named
+                .parse2(quote! {
+                    _data: [u8; #size]
+                })
+                .unwrap(),
+        )
+    } else {
+        None
+    }
+    .into_iter();
+    let pin_field = syn::Field::parse_named
+        .parse2(quote! {
+            _pinned: core::marker::PhantomData<core::marker::PhantomPinned>
+        })
+        .unwrap();
+
+    let non_send_sync_field = syn::Field::parse_named
+        .parse2(quote! {
+            _non_send_sync: core::marker::PhantomData<[*const u8;0]>
+        })
+        .unwrap();
+    let all_fields: Punctuated<_, syn::token::Comma> = std::iter::once(pin_field)
+        .chain(std::iter::once(non_send_sync_field))
+        .chain(generic_type_fields)
+        .chain(data_field)
+        .collect();
+    s.fields = Fields::Named(parse_quote! { {
+        #all_fields
+    } })
+}
+
+fn make_lit_int(val: usize) -> LitInt {
+    LitInt::new(&val.to_string(), Span::call_site())
+}
diff --git a/engine/src/conversion/codegen_rs/unqualify.rs b/engine/src/conversion/codegen_rs/unqualify.rs
new file mode 100644
index 0000000..6f245c2
--- /dev/null
+++ b/engine/src/conversion/codegen_rs/unqualify.rs
@@ -0,0 +1,94 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use syn::{
+    parse_quote, punctuated::Punctuated, FnArg, GenericArgument, PathArguments, PathSegment,
+    ReturnType, Token, Type, TypePath,
+};
+
+/// Mod to handle stripping paths off the front of types.
+
+fn unqualify_type_path(typ: TypePath) -> TypePath {
+    // If we've still got more than one
+    // path segment then this is referring to a type within
+    // C++ namespaces. Strip them off for now, until cxx supports
+    // nested mods within a cxx::bridge.
+    // This is 'safe' because earlier code will already have
+    // failed with 'DuplicateType' if we had several types called
+    // the same thing.
+    let last_seg = typ.path.segments.into_iter().last().unwrap();
+    let ident = &last_seg.ident;
+    let args = match last_seg.arguments {
+        PathArguments::AngleBracketed(mut ab) => {
+            ab.args = unqualify_punctuated(ab.args);
+            PathArguments::AngleBracketed(ab)
+        }
+        _ => last_seg.arguments.clone(),
+    };
+    let last_seg: PathSegment = parse_quote!( #ident #args );
+    parse_quote!(
+        #last_seg
+    )
+}
+
+fn unqualify_punctuated<P>(pun: Punctuated<GenericArgument, P>) -> Punctuated<GenericArgument, P>
+where
+    P: Default,
+{
+    let mut new_pun = Punctuated::new();
+    for arg in pun.into_iter() {
+        new_pun.push(match arg {
+            GenericArgument::Type(t) => GenericArgument::Type(unqualify_type(t)),
+            _ => arg,
+        });
+    }
+    new_pun
+}
+
+fn unqualify_type(typ: Type) -> Type {
+    match typ {
+        Type::Path(typ) => Type::Path(unqualify_type_path(typ)),
+        Type::Reference(mut typeref) => {
+            typeref.elem = unqualify_boxed_type(typeref.elem);
+            Type::Reference(typeref)
+        }
+        Type::Ptr(mut typeptr) => {
+            typeptr.elem = unqualify_boxed_type(typeptr.elem);
+            Type::Ptr(typeptr)
+        }
+        _ => typ,
+    }
+}
+
+fn unqualify_boxed_type(typ: Box<Type>) -> Box<Type> {
+    Box::new(unqualify_type(*typ))
+}
+
+pub(crate) fn unqualify_ret_type(ret_type: ReturnType) -> ReturnType {
+    match ret_type {
+        ReturnType::Type(tok, boxed_type) => {
+            ReturnType::Type(tok, unqualify_boxed_type(boxed_type))
+        }
+        _ => ret_type,
+    }
+}
+
+pub(crate) fn unqualify_params(
+    params: Punctuated<FnArg, Token![,]>,
+) -> Punctuated<FnArg, Token![,]> {
+    params
+        .into_iter()
+        .map(|p| match p {
+            FnArg::Typed(mut pt) => {
+                pt.ty = unqualify_boxed_type(pt.ty);
+                FnArg::Typed(pt)
+            }
+            _ => p,
+        })
+        .collect()
+}