Squashed 'third_party/autocxx/' changes from c35090b75..fdfb26e26

fdfb26e26 Merge pull request #1315 from google/update-for-proc-macro-prob
ed6ab7560 cargo update
bfca1e046 Merge pull request #1310 from google/remove-errant-line
b06647b89 Remove commented Cargo line
dfb63e2fa Merge pull request #1309 from google/syn-newtype-wrapper-3
c3cdbcbe3 Newtype wrapper for more concise syn debug output.
2743df8f7 Merge pull request #1306 from google/more-cpp-version-notes-tests
1b68eb0b9 Further documentation on C++ versions.
907b2eff3 Merge pull request #1305 from google/cpp-version-notes
90a17d9c2 Documentation about C++ versions.
60230d257 Merge pull request #1299 from google/rev-0.26
10346e838 Revise to 0.26.
5492cb25f Merge pull request #1297 from google/dyn-trait-test
ad8fd562f Merge pull request #1298 from google/fix-moveit-unsoundness
3ff66d1c2 Use new trait in tests.
2f8fb49f9 Fix subclass Emplace trait name.
e420eb7b4 Merge commit '93ed457' into fix-moveit-unsoundness
5e863df88 Roll to moveit 0.6.
0386ad9c8 Testing and documenting dynamic dispatch for CppRef.
ad9377beb Merge pull request #1274 from google/test-for-1265
84281f824 Merge pull request #1296 from google/fix-mutable-refs-part-two
21d72ad05 Fix mutable ref returns, part two.
cfbe62b08 Merge pull request #1295 from google/fix-1294
a67fbe6a6 Fix reference wrappers + mutable refs problem.
366d1a32b Merge pull request #1293 from google/roll-bindgen-2
8cacf99c2 Ignore new test so it can be merged in.
b9a56c697 Absorb published autocxx-bindgen 0.65.1.
1bfbf9dcc Merge pull request #1292 from google/reference-wrapper-changes
7000c91e6 Upgrade to bindgen 0.65.1.
d35bdbd59 Fix doc problem.
43d94cf1f Remove unused features.
ebf858266 Improve reference wrapper example.
e0dfbf958 Allow unsized types in C++ references.
004ef5ab9 Add comments to C++ reference
7c8b8c0f6 Add comment on potential lurking bug.
902077adc Switch reference wrappers' Deref
6ee12660a Merge pull request #1286 from maurer/syn-2
2633a9aaf Update to syn-2
6cb6d5879 Merge pull request #1291 from badicsalex/main
718c50d30 Crude workaround for nested anonymous enums bug
b1ee9dae5 Add test for nested anonymous enums
cf123189e Merge pull request #1290 from google/adetaylor-patch-2
0c27dcb18 Update bug_report.md
73cf4798b Update issue templates
cce6f6c5d Merge pull request #1288 from google/clippy-1.70
b43a16e59 Clippy fix.
93ed45714 Simplify generics
7e6fc4982 Remove `moveit::EmplaceUnpinned`
2113daf76 Make `as_mov` take `ptr: impl AsMove<P>` and relax bound to `P: Deref`
2e442110d Patch moveit dependency
ad69a8417 Merge pull request #1284 from google/cargo-update-2
776bc8162 Merge pull request #1283 from google/subclass-diagnostics
70f9f0f7e cargo update to remove vulnerable version of time.
25c0ded71 Further book fixes.
f8749c71c Book fixes.
fee4442cf Fix diagnostics for in-line subclass macro.
ae4a4eeeb Merge pull request #1277 from gen-xu/main
02ba1a54e fix: feature flag for runtime
e397e2e71 Merge branch 'main' into test-for-1265
bec2301b5 Merge pull request #1264 from google/reject-unions
b0275ff0f Merge pull request #1275 from google/typo3
370e81d87 Fix typo.
f050f5ae7 Add test for unsoundness.
a44f39806 Format
deba33de8 Further tweaks
994a0b164 Merge pull request #1273 from google/rvalueparamhandler-comment
7b8b14507 More using nuance.
e9a62b429 Merge pull request #1272 from google/1270
98fcc88a8 Add comment explaining RValueParamHandler.
d93a1fd1c Attempts at fixing 1270.
6594410d4 Use a more sophisticated 'using' statement.
abb59541f Merge pull request #1263 from google/va-list
1f9a1d258 Attempt to fix namespaced anon struct case.
d4ad6fecc Add test for funsigned-char
f27bf7856 Add va_list test.
7f38ae7c9 Shorten excessively long name.
f4e57eaed Fix namespaced destructors.
ada12160e Merge pull request #1259 from google/fix-remove-dir-all
edabf36f1 Merge pull request #1258 from google/rev-0.25
8c7452b68 Roll tempfile.
22515641f Revise to 0.25.0.
9f5c550c5 Merge pull request #1257 from htynkn/patch-1
7c724ba32 Update examples.md
97c103aaa Update examples.md
a9edb13c5 fix 404 in examples document
c3771d02d Merge pull request #1253 from nak3/add-cxx.h
b429077f8 Merge pull request #1249 from google/reduction-tweaks
143b2944f Merge pull request #1254 from google/issue-1238
94f0cd51b Reject fns returning mut refs but no mut ref param
e7640d2dd Add failing test case for issue 1238.
0f1a28a28 Tweaks to reduction
252608fb4 Add an option to autocxx_gen to create cxx.h
79c72939e Merge pull request #1250 from google/tests-forcing-wrappers
2121c1a30 Disable the two tests which fail when wrappers are forced.
52028c9a2 Add to book about this.
4359edcf3 Clippy fix.
c34ae5e6d Add github workflow.
dca93fa74 Add to test suite
e20192af4 Add option to force wrapper generation.
5540b8618 Merge pull request #1206 from russelltg/construct_concrete_class
1783da369 Merge pull request #1248 from nak3/llvm-example
a1468090a Merge pull request #1247 from nak3/fix-log
9154d3187 Merge pull request #1245 from nak3/use-subclass
d24515a90 Enable llvm example for CI
36f7a3570 Print the number of files generated in the error of `--generate-exact`
faf3d8202 Do not use deprecated annotation is_subclass in examples
a303a9a72 fmt
e47ff2eff Merge pull request #1241 from google/reproduce-1237
8db3a4241 Fix problem with nested extern types.
20345ebbb Merge pull request #1242 from google/remove-no-mod-found-error
83cec3c50 Clippy fix.
d0e1b23e3 Merge pull request #1240 from google/fix-1229
88e702523 Support extern_rust_type without include_cpp!
6a429bd62 Add test for autodiscover without include_cpp
3bd7dba80 Fix use of extern_rust_type in Box<T> in wrappers.
68c236afb Rename a test to avoid ambiguity.
93e4c9629 Remove warning from test case.
68d2323c5 Fix for Box being passed without std::move.
ba81127ca Add reproduction test for #1237
ca436170e Experimental fix for #1229
59eaac153 Merge pull request #1224 from kitlith/no_std
05c0071a0 Move duplicate Pin definition into "non_cannonical_name"
14eeb91db Merge pull request #1232 from nak3/fix-comment
047943e22 Merge pull request #1234 from nak3/fix-help
31ccc891e Merge branch 'google:main' into test
cb16073c3 Add test for issue #1229
c7b6af021 Make help message of gen-rs-include accurate
9c359b4cc Fix comment about AUTOCXX_RS_JSON_ARCHIVE
8a5e11574 Merge remote-tracking branch 'origin/main' into construct_concrete_class
0b46f92ce go through APIs one final time to go through methods before classes
dcb4de8ba Merge pull request #1225 from EmbarkStudios/main
74a78bd16 FIXUP! cargo fmt
b579e7cde Merge pull request #1227 from google/linking-docs
29c267f4f Add some generic advice about linking.
6e1200d3a Merge pull request #1226 from google/remove-publish-delay
70b2d2b43 Cargo publish now waits automatically.
21a007020 Merge pull request #1223 from google/test-for-1214
a6ffcc103 Merge branch 'main' of github.com:google/autocxx into test-for-1214
788816f30 Format.
8935e64bd Revise to fixed autocxx-bindgen version.
e0da33972 Furthger enhance test.
88641ff57 Slightly bolstering an existing test.
2e4318803 Automatically create outdir if it doesn't exist
ad89df311 when adding a lifetime to pinned reference, allow from core as well.
6a09a1d1c Replace std with core (where possible) in integration tests.
abed8eabe s/::std/::core/, where possible
a3ab76cd7 Merge pull request #1222 from google/rev-0.24
21995a555 Add test for #1214.
1100f09f4 Format.
6c252fad2 More clippy changes.
6c3d25c09 Revise to 0.24.0
9e69fae2d Merge pull request #1193 from google/test-1192
4400acf2a Merge pull request #1221 from google/document-expectations
4d6f80303 Add comments towards #1192.
bde880b67 Merge pull request #1220 from google/repro-1197
aef43a1b8 Merge pull request #1217 from google/add_doc_cfg
c1102a73b Merge pull request #1218 from google/improve-diagnostic
0e8dd146b Merge pull request #1219 from google/no-rewrite
8cd277b4b Make cxxgen.h names predictable in autocxx-build.
e5898442c Fix doc build issue.
7433ac6a5 Improve diagnostics for wrong filename. Fixes #1200.
ae9181f43 Avoid unnecessary rebuilds.
b3748f736 Merge pull request #1216 from google/clippy-new
6a9d9227b Format.
c90cd896e address some comments
ef6dfd9ac Latest clippy fixes.
e28826e00 Clippy
103ce3f9e Merge pull request #1203 from aaronrsiphone/patch-1
2db6e7341 Merge pull request #1210 from nak3/remove-memory
eb486273a Merge pull request #1208 from nak3/fix-cpp_calling_rust
4d831b6a5 Merge pull request #1209 from nak3/fix-typo
316e63643 Merge pull request #1213 from nak3/bump-llvm
495e90916 Bump autocxx version in examples/llvm
49ba02805 Remove unnecessary memory include
fd728b20f Fix typo
acc14b9b6 Add test in github action
5def9b050 Bump miette to 5.x
8d4d785f4 clippy
e1d172f4a fmt
38f26efcc add missing trait
12bf667d1 listen to clippy
f6829c226 fix up overload resolution
d2aa27715 some more work
910e7b5f6 get started
b7a23c5d0 Merge pull request #1202 from google/reference-tweak
eb4b28d5b Update building.md
a476df3e5 Add a note about unsafety of reference wrappers.
34a143f12 Merge pull request #1199 from google/clippy-fixes
4b6020f7c Fix new cargo doc error.
c132a0180 Clippy fixes.
bd0efed70 Fix issue 1192.
81a3d9686 Log some more details of some APIs.
a7c4d35dd Fix the test
16c8a7059 Add test for issue 1192.
f6427a110 Merge pull request #1188 from google/rev-0.23.1
f5ea8a674 Revise to 0.23.1
1c11f4b78 Merge pull request #1187 from google/recursive-struct-fields
f68e08d21 Denote only dependencies of real field types.
a009d0f86 Add tests for circular dependencies.
034e10432 Merge pull request #1185 from google/rev-0.23
05a5c4243 Revise to 0.23.0.
0885061f4 Merge pull request #1180 from google/fix-815
1ee6da4c2 Merge branch 'main' of github.com:google/autocxx into fix-815
b00c5cd17 Merge pull request #1184 from google/nightly-clippies
74dadfb26 Further clippy fixes.
f81c4db3c Further clippy fixes.
d0c5616a2 Shrink error types to keep clippy happy.
5cae78b3d Merge branch 'main' of github.com:google/autocxx into fix-815
68d280b3c Merge pull request #1183 from google/merge-bindgen-upstream-0.62
8aed5e309 Update to released autocxx-bindgen.
59da591a0 Merge pull request #1179 from google/failing-1170-test
65dd0e218 Format.
775317a85 Cargo lock
f052bbdd7 Test against upstream bindgen 0.62.
a19307aa8 Depend on custom bindgen branch
162ec7df8 Remove bogus bits of test
29296ee9c Consider that enums are always movable/destroyable.
4eab29a29 Properly propagate deps for non-POD structs
04eab3251 Simpler test for #1170.
66ca18c78 Record defaulted attributes - no intentional changes yet
3ad39ce5c Failing test for derived class.
f3a803040 Test for explicitly defaulted constructors.
782d1365f Adding failing test for #1170.
6cca9b29c Merge pull request #1172 from google/reduce-with-rustc
9d021f66f Typo.
ee2bdf87f Merge pull request #1176 from google/rustversion-swap
3b28ff920 Fix nightly check error.
1cdbcc964 Clippy complaint.
bc5f4ff05 Compile generated rust code during reduction.
5ef057dce Merge pull request #1173 from google/up-of-vecs
9577311aa Update integration-tests/tests/cpprefs_test.rs
1cb8338a1 Eliminate further use of rustc_version.
e78b73b1c Remove dependency on rustc_version.
1b949ad9c Fix test.
20c8d6ba7 Merge pull request #1175 from google/1152-upmerge
8f2dbaef0 Fix compatibility with clap 3.2.
dda51d028 Merge branch 'main' of github.com:google/autocxx into 1152-upmerge
3086a9023 Merge pull request #1168 from google/extern_rust_fun_type_checking
14f224fde Merge pull request #1171 from google/reduce-quicker
fbeb0f3d6 Adding a test.
ea57ea7d7 Slight reduction speedup.
26f9cf4a2 extern_rust_function types: diagnostics
1196c39b1 Rename: ConvertError -> ConvertErrorFromCpp
759970ae6 Merge pull request #1164 from google/cppref-changes
cb9c680b2 Adding diagnostics for use on non-nightly.
93657b848 Add failing test for extern_rust_function
075bd8e79 Switch from receiver feature to arbitrary_self_types
8c576081d Fix tests for nightly.
51e4c271c Doc fixes.
1204dc7fa Rewrite reference_wrapper.rs to be structs.
c2846add0 Merge pull request #1163 from google/rev-cxx-1.0.72
4d939f0d5 Merge pull request #1162 from google/fix-1161
3d2767958 Revise cxx to 1.0.78.
cf96537ca Clippy fix.
458384470 Add Box test.
23cff78ca Fix GC problem with extern_rust_type.
b24563627 Merge pull request #1158 from google/issue-1097-partial-fix
1ce2ee335 Merge pull request #1160 from google/clippy-1.64-fix
3d376da55 Rust 1.64 clippy lint.
d01bcece0 Merge pull request #1157 from google/adetaylor-patch-1
bce713bfa Partial fix for issue 1097.
7460bfacb Remove extra logging
2d86cdb4a Merge pull request #1156 from google/fix-1143
e7ca09610 Clippy fix.
4fdb3ca22 Enable test.
40d70634e Fix #1143.
f785a168d Merge pull request #1150 from bsilver8192/extern_cpp_type-different-name
79b6b91df Update deps, format TOML
8d2e1dffb Merge pull request #1154 from google/rev-0.22.4
6555d8212 Revise to 0.22.4.
4b44f7f20 Merge pull request #1153 from google/autocxx-bindgen-0.60.3
05dcbf555 Update autocxx-bindgen and bump version to 0.22.4
2388f2d50 Support `extern_cpp_type!` with a different name
7cfa5ea58 Merge pull request #1149 from google/rev-autocxx-bindgen-0.60.1
dadb77ac5 Use official release version.
25cc290a9 Use specific branch and merge fixes
9d2487fab Revise to autocxx-bindgen 0.60.1.
8a9f5fd6d Merge pull request #1145 from boydjohnson/test-case
9d7d2cbdd Merge pull request #1147 from bsilver8192/multiple-json-archive-paths
948e49c4f Merge pull request #1148 from bsilver8192/1.63.0-warning-fixes
c3c020459 Support multiple AUTOCXX_RS_JSON_ARCHIVE entries
37353fabd Fix new clippy and doc warnings in 1.63.0
1e1d16d73 Add failing test case
d0bc76eb5 Merge pull request #1137 from bsilver8192/impl-abstract-type
ec7a4541a Generate `impl UniquePtr` for abstract types too
b830b9da6 Merge pull request #1139 from bsilver8192/bump-mdbook
da8dbd829 Bump mdbook to fix build on nightly
cdebaee64 Merge pull request #1136 from google/disable-windows-gnu-2
5fcc700c7 Disable windows-gnu tests.
0f8cf6309 Fix a comment.
b60c4d032 Merge pull request #1127 from dtolnay-contrib/prettyplease
8b766bd46 Pretty print using prettyplease instead of rustfmt

Change-Id: I0529fc0c35c16160944f5b3a5326c2d53fbe904b
git-subtree-dir: third_party/autocxx
git-subtree-split: fdfb26e26602e24466dfc506782567deb72c9a29
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/engine/src/conversion/analysis/abstract_types.rs b/engine/src/conversion/analysis/abstract_types.rs
index dac9684..884cbd0 100644
--- a/engine/src/conversion/analysis/abstract_types.rs
+++ b/engine/src/conversion/analysis/abstract_types.rs
@@ -6,6 +6,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use indexmap::map::IndexMap as HashMap;
+use syn::{punctuated::Punctuated, token::Comma, FnArg};
+
 use super::{
     fun::{
         FnAnalysis, FnKind, FnPhase, FnPrePhase2, MethodKind, PodAndConstructorAnalysis,
@@ -13,106 +16,164 @@
     },
     pod::PodAnalysis,
 };
-use crate::conversion::{api::Api, apivec::ApiVec};
 use crate::conversion::{
-    api::TypeKind,
+    analysis::{depth_first::fields_and_bases_first, fun::ReceiverMutability},
+    api::{ApiName, TypeKind},
     error_reporter::{convert_apis, convert_item_apis},
-    ConvertError,
+    ConvertErrorFromCpp,
+};
+use crate::{
+    conversion::{api::Api, apivec::ApiVec},
+    types::QualifiedName,
 };
 use indexmap::set::IndexSet as HashSet;
 
+#[derive(Hash, PartialEq, Eq, Clone, Debug)]
+struct Signature {
+    name: String,
+    args: Vec<syn::Type>,
+    constness: ReceiverMutability,
+}
+
+impl Signature {
+    fn new(
+        name: &ApiName,
+        params: &Punctuated<FnArg, Comma>,
+        constness: ReceiverMutability,
+    ) -> Self {
+        Signature {
+            name: name.cpp_name(),
+            args: params
+                .iter()
+                .skip(1) // skip `this` implicit argument
+                .filter_map(|p| {
+                    if let FnArg::Typed(t) = p {
+                        Some((*t.ty).clone())
+                    } else {
+                        None
+                    }
+                })
+                .collect(),
+            constness,
+        }
+    }
+}
+
 /// Spot types with pure virtual functions and mark them abstract.
-pub(crate) fn mark_types_abstract(mut apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPrePhase2> {
-    let mut abstract_types: HashSet<_> = apis
-        .iter()
-        .filter_map(|api| match &api {
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind:
-                            FnKind::Method {
-                                impl_for: self_ty_name,
-                                method_kind: MethodKind::PureVirtual(_),
-                                ..
-                            },
-                        ..
-                    },
-                ..
-            } => Some(self_ty_name.clone()),
-            _ => None,
+pub(crate) fn mark_types_abstract(apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPrePhase2> {
+    #[derive(Default, Debug, Clone)]
+    struct ClassAbstractState {
+        undefined: HashSet<Signature>,
+        defined: HashSet<Signature>,
+    }
+    let mut class_states: HashMap<QualifiedName, ClassAbstractState> = HashMap::new();
+    let mut abstract_classes = HashSet::new();
+
+    for api in apis.iter() {
+        if let Api::Function {
+            name,
+            analysis:
+                FnAnalysis {
+                    kind:
+                        FnKind::Method {
+                            impl_for: self_ty_name,
+                            method_kind,
+                            ..
+                        },
+                    params,
+                    ..
+                },
+            ..
+        } = api
+        {
+            match method_kind {
+                MethodKind::PureVirtual(constness) => {
+                    class_states
+                        .entry(self_ty_name.clone())
+                        .or_default()
+                        .undefined
+                        .insert(Signature::new(name, params, *constness));
+                }
+                MethodKind::Virtual(constness) => {
+                    class_states
+                        .entry(self_ty_name.clone())
+                        .or_default()
+                        .defined
+                        .insert(Signature::new(name, params, *constness));
+                }
+                _ => {}
+            }
+        }
+    }
+
+    for api in fields_and_bases_first(apis.iter()) {
+        if let Api::Struct {
+            analysis:
+                PodAndConstructorAnalysis {
+                    pod:
+                        PodAnalysis {
+                            bases,
+                            kind: TypeKind::Pod | TypeKind::NonPod,
+                            ..
+                        },
+                    ..
+                },
+            name,
+            ..
+        } = api
+        {
+            // resolve virtuals for a class: start with new pure virtuals in this class
+            let mut self_cs = class_states.get(&name.name).cloned().unwrap_or_default();
+
+            // then add pure virtuals of bases
+            for base in bases.iter() {
+                if let Some(base_cs) = class_states.get(base) {
+                    self_cs.undefined.extend(base_cs.undefined.iter().cloned());
+                }
+            }
+
+            // then remove virtuals defined in this class
+            self_cs
+                .undefined
+                .retain(|und| !self_cs.defined.contains(und));
+
+            // if there are undefined functions, mark as virtual
+            if !self_cs.undefined.is_empty() {
+                abstract_classes.insert(name.name.clone());
+            }
+
+            // store it back so child classes can read it properly
+            *class_states.entry(name.name.clone()).or_default() = self_cs;
+        }
+    }
+
+    // mark abstract types as abstract
+    let mut apis: ApiVec<_> = apis
+        .into_iter()
+        .map(|mut api| {
+            if let Api::Struct { name, analysis, .. } = &mut api {
+                if abstract_classes.contains(&name.name) {
+                    analysis.pod.kind = TypeKind::Abstract;
+                }
+            }
+            api
         })
         .collect();
 
-    // Spot any derived classes (recursively). Also, any types which have a base
-    // class that's not on the allowlist are presumed to be abstract, because we
-    // have no way of knowing (as they're not on the allowlist, there will be
-    // no methods associated so we won't be able to spot pure virtual methods).
-    let mut iterate = true;
-    while iterate {
-        iterate = false;
-        apis = apis
-            .into_iter()
-            .map(|api| {
-                match api {
-                    Api::Struct {
-                        analysis:
-                            PodAndConstructorAnalysis {
-                                pod:
-                                    PodAnalysis {
-                                        bases,
-                                        kind: TypeKind::Pod | TypeKind::NonPod,
-                                        castable_bases,
-                                        field_deps,
-                                        field_info,
-                                        is_generic,
-                                        in_anonymous_namespace,
-                                    },
-                                constructors,
-                            },
-                        name,
-                        details,
-                    } if abstract_types.contains(&name.name)
-                        || !abstract_types.is_disjoint(&bases) =>
-                    {
-                        abstract_types.insert(name.name.clone());
-                        // Recurse in case there are further dependent types
-                        iterate = true;
-                        Api::Struct {
-                            analysis: PodAndConstructorAnalysis {
-                                pod: PodAnalysis {
-                                    bases,
-                                    kind: TypeKind::Abstract,
-                                    castable_bases,
-                                    field_deps,
-                                    field_info,
-                                    is_generic,
-                                    in_anonymous_namespace,
-                                },
-                                constructors,
-                            },
-                            name,
-                            details,
-                        }
-                    }
-                    _ => api,
-                }
-            })
-            .collect()
-    }
-
     // We also need to remove any constructors belonging to these
     // abstract types.
     apis.retain(|api| {
         !matches!(&api,
-        Api::Function {
-            analysis:
-                FnAnalysis {
-                    kind: FnKind::Method{impl_for: self_ty, method_kind: MethodKind::Constructor{..}, ..}
-                        | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..},
+            Api::Function {
+                analysis:
+                    FnAnalysis {
+                        kind: FnKind::Method{impl_for: self_ty, method_kind: MethodKind::Constructor{..}, ..}
+                            | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..},
+                        ..
+                    },
                     ..
-                },
-                ..
-        } if abstract_types.contains(self_ty))
+            } if abstract_classes.contains(self_ty)
+        )
     });
 
     // Finally, if there are any types which are nested inside other types,
@@ -143,10 +204,11 @@
             .map(|n| n.contains("::"))
             .unwrap_or_default() =>
         {
-            Err(ConvertError::AbstractNestedType)
+            Err(ConvertErrorFromCpp::AbstractNestedType)
         }
         _ => Ok(Box::new(std::iter::once(api))),
     });
+
     results
 }
 
diff --git a/engine/src/conversion/analysis/allocators.rs b/engine/src/conversion/analysis/allocators.rs
index 3695de0..da25a77 100644
--- a/engine/src/conversion/analysis/allocators.rs
+++ b/engine/src/conversion/analysis/allocators.rs
@@ -12,9 +12,13 @@
 
 use crate::{
     conversion::{
-        api::{Api, ApiName, CppVisibility, FuncToConvert, Provenance, References, TraitSynthesis},
+        api::{
+            Api, ApiName, CppVisibility, DeletedOrDefaulted, FuncToConvert, Provenance, References,
+            TraitSynthesis,
+        },
         apivec::ApiVec,
     },
+    minisyn::minisynize_punctuated,
     types::{make_ident, QualifiedName},
 };
 
@@ -73,8 +77,8 @@
                 fun: Box::new(FuncToConvert {
                     ident,
                     doc_attrs: Vec::new(),
-                    inputs,
-                    output,
+                    inputs: minisynize_punctuated(inputs),
+                    output: output.into(),
                     vis: parse_quote! { pub },
                     virtualness: crate::conversion::api::Virtualness::None,
                     cpp_vis: CppVisibility::Public,
@@ -86,7 +90,7 @@
                     synthesized_this_type: None,
                     synthetic_cpp: Some((cpp_function_body, CppFunctionKind::Function)),
                     add_to_trait: Some(synthesis),
-                    is_deleted: false,
+                    is_deleted: DeletedOrDefaulted::Neither,
                     provenance: Provenance::SynthesizedOther,
                     variadic: false,
                 }),
diff --git a/engine/src/conversion/analysis/casts.rs b/engine/src/conversion/analysis/casts.rs
index 5f493f9..2426a50 100644
--- a/engine/src/conversion/analysis/casts.rs
+++ b/engine/src/conversion/analysis/casts.rs
@@ -6,13 +6,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use crate::minisyn::FnArg;
 use itertools::Itertools;
 use quote::quote;
-use syn::{parse_quote, FnArg};
+use syn::parse_quote;
 
 use crate::{
     conversion::{
-        api::{Api, ApiName, CastMutability, Provenance, References, TraitSynthesis},
+        api::{
+            Api, ApiName, CastMutability, DeletedOrDefaulted, Provenance, References,
+            TraitSynthesis,
+        },
         apivec::ApiVec,
     },
     types::{make_ident, QualifiedName},
@@ -112,7 +116,7 @@
                 mutable,
             }),
             synthetic_cpp: Some((CppFunctionBody::Cast, CppFunctionKind::Function)),
-            is_deleted: false,
+            is_deleted: DeletedOrDefaulted::Neither,
             provenance: Provenance::SynthesizedOther,
             variadic: false,
         }),
diff --git a/engine/src/conversion/analysis/ctypes.rs b/engine/src/conversion/analysis/ctypes.rs
index b2ce840..bc6f096 100644
--- a/engine/src/conversion/analysis/ctypes.rs
+++ b/engine/src/conversion/analysis/ctypes.rs
@@ -8,7 +8,7 @@
 
 use indexmap::map::IndexMap as HashMap;
 
-use syn::Ident;
+use crate::minisyn::Ident;
 
 use crate::conversion::api::ApiName;
 use crate::conversion::apivec::ApiVec;
diff --git a/engine/src/conversion/analysis/deps.rs b/engine/src/conversion/analysis/deps.rs
index 7aca96c..a301090 100644
--- a/engine/src/conversion/analysis/deps.rs
+++ b/engine/src/conversion/analysis/deps.rs
@@ -6,8 +6,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use itertools::Itertools;
-
 use crate::{
     conversion::api::{Api, TypeKind},
     types::QualifiedName,
@@ -22,10 +20,6 @@
 pub(crate) trait HasDependencies {
     fn name(&self) -> &QualifiedName;
     fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>;
-
-    fn format_deps(&self) -> String {
-        self.deps().join(",")
-    }
 }
 
 impl HasDependencies for Api<FnPrePhase1> {
@@ -37,13 +31,9 @@
                 ..
             } => Box::new(old_tyname.iter().chain(deps.iter())),
             Api::Struct {
-                analysis:
-                    PodAnalysis {
-                        kind: TypeKind::Pod,
-                        bases,
-                        field_deps,
-                        ..
-                    },
+                analysis: PodAnalysis {
+                    bases, field_deps, ..
+                },
                 ..
             } => Box::new(field_deps.iter().chain(bases.iter())),
             Api::Function { analysis, .. } => Box::new(analysis.deps.iter()),
@@ -52,7 +42,7 @@
                 superclass,
             } => Box::new(std::iter::once(superclass)),
             Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()),
-            Api::RustFn { receiver, .. } => Box::new(receiver.iter()),
+            Api::RustFn { deps, .. } => Box::new(deps.iter()),
             _ => Box::new(std::iter::empty()),
         }
     }
@@ -105,7 +95,7 @@
                 superclass,
             } => Box::new(std::iter::once(superclass)),
             Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()),
-            Api::RustFn { receiver, .. } => Box::new(receiver.iter()),
+            Api::RustFn { deps, .. } => Box::new(deps.iter()),
             _ => Box::new(std::iter::empty()),
         }
     }
diff --git a/engine/src/conversion/analysis/depth_first.rs b/engine/src/conversion/analysis/depth_first.rs
index 02459e1..baadbd7 100644
--- a/engine/src/conversion/analysis/depth_first.rs
+++ b/engine/src/conversion/analysis/depth_first.rs
@@ -14,10 +14,20 @@
 
 use crate::types::QualifiedName;
 
-use super::deps::HasDependencies;
+/// A little like `HasDependencies` but accounts for only direct fiele
+/// and bases.
+pub(crate) trait HasFieldsAndBases {
+    fn name(&self) -> &QualifiedName;
+    /// Return field and base class dependencies of this item.
+    /// This should only include those items where a definition is required,
+    /// not merely a declaration. So if the field type is
+    /// `std::unique_ptr<A>`, this should only return `std::unique_ptr`.
+    fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>;
+}
 
-/// Return APIs in a depth-first order, i.e. those with no dependencies first.
-pub(super) fn depth_first<'a, T: HasDependencies + Debug + 'a>(
+/// Iterate through APIs in an order such that later APIs have no fields or bases
+/// other than those whose types have already been processed.
+pub(super) fn fields_and_bases_first<'a, T: HasFieldsAndBases + Debug + 'a>(
     inputs: impl Iterator<Item = &'a T> + 'a,
 ) -> impl Iterator<Item = &'a T> {
     let queue: VecDeque<_> = inputs.collect();
@@ -25,18 +35,21 @@
     DepthFirstIter { queue, yet_to_do }
 }
 
-struct DepthFirstIter<'a, T: HasDependencies + Debug> {
+struct DepthFirstIter<'a, T: HasFieldsAndBases + Debug> {
     queue: VecDeque<&'a T>,
     yet_to_do: HashSet<&'a QualifiedName>,
 }
 
-impl<'a, T: HasDependencies + Debug> Iterator for DepthFirstIter<'a, T> {
+impl<'a, T: HasFieldsAndBases + Debug> Iterator for DepthFirstIter<'a, T> {
     type Item = &'a T;
 
     fn next(&mut self) -> Option<Self::Item> {
         let first_candidate = self.queue.get(0).map(|api| api.name());
         while let Some(candidate) = self.queue.pop_front() {
-            if !candidate.deps().any(|d| self.yet_to_do.contains(&d)) {
+            if !candidate
+                .field_and_base_deps()
+                .any(|d| self.yet_to_do.contains(&d))
+            {
                 self.yet_to_do.remove(candidate.name());
                 return Some(candidate);
             }
@@ -46,7 +59,11 @@
                     "Failed to find a candidate; there must be a circular dependency. Queue is {}",
                     self.queue
                         .iter()
-                        .map(|item| format!("{}: {}", item.name(), item.deps().join(",")))
+                        .map(|item| format!(
+                            "{}: {}",
+                            item.name(),
+                            item.field_and_base_deps().join(",")
+                        ))
                         .join("\n")
                 );
             }
@@ -59,17 +76,17 @@
 mod test {
     use crate::types::QualifiedName;
 
-    use super::{depth_first, HasDependencies};
+    use super::{fields_and_bases_first, HasFieldsAndBases};
 
     #[derive(Debug)]
     struct Thing(QualifiedName, Vec<QualifiedName>);
 
-    impl HasDependencies for Thing {
+    impl HasFieldsAndBases for Thing {
         fn name(&self) -> &QualifiedName {
             &self.0
         }
 
-        fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+        fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
             Box::new(self.1.iter())
         }
     }
@@ -89,7 +106,7 @@
             vec![QualifiedName::new_from_cpp_name("a")],
         );
         let api_list = vec![a, b, c];
-        let mut it = depth_first(api_list.iter());
+        let mut it = fields_and_bases_first(api_list.iter());
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("a"));
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("c"));
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("b"));
diff --git a/engine/src/conversion/analysis/fun/bridge_name_tracker.rs b/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
index 7e81c59..c98beaf 100644
--- a/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
+++ b/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
@@ -104,7 +104,7 @@
             *count += 1;
             prefix
         } else {
-            let r = format!("{}_autocxx{}", prefix, count);
+            let r = format!("{prefix}_autocxx{count}");
             *count += 1;
             r
         }
diff --git a/engine/src/conversion/analysis/fun/function_wrapper.rs b/engine/src/conversion/analysis/fun/function_wrapper.rs
index ab3b7d9..0495533 100644
--- a/engine/src/conversion/analysis/fun/function_wrapper.rs
+++ b/engine/src/conversion/analysis/fun/function_wrapper.rs
@@ -6,12 +6,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use crate::minisyn::Ident;
 use crate::{
-    conversion::api::SubclassName,
+    conversion::{api::SubclassName, type_helpers::extract_pinned_mutable_reference_type},
     types::{Namespace, QualifiedName},
 };
 use quote::ToTokens;
-use syn::{parse_quote, Ident, Type, TypeReference};
+use syn::{parse_quote, Type, TypeReference};
 
 #[derive(Clone, Debug)]
 pub(crate) enum CppConversionType {
@@ -85,9 +86,9 @@
 /// variant params. That would remove the possibility of various runtime
 /// panics by enforcing (for example) that conversion from a pointer always
 /// has a Type::Ptr.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct TypeConversionPolicy {
-    unwrapped_type: Type,
+    unwrapped_type: crate::minisyn::Type,
     pub(crate) cpp_conversion: CppConversionType,
     pub(crate) rust_conversion: RustConversionType,
 }
@@ -103,7 +104,7 @@
         rust_conversion: RustConversionType,
     ) -> Self {
         Self {
-            unwrapped_type: ty,
+            unwrapped_type: ty.into(),
             cpp_conversion,
             rust_conversion,
         }
@@ -118,7 +119,14 @@
             Type::Reference(TypeReference {
                 elem, mutability, ..
             }) => (*elem, mutability.is_some()),
-            _ => panic!("Not a ptr: {}", ty.to_token_stream()),
+            Type::Path(ref tp) => {
+                if let Some(unwrapped_type) = extract_pinned_mutable_reference_type(tp) {
+                    (unwrapped_type.clone(), true)
+                } else {
+                    panic!("Path was not a mutable reference: {}", ty.to_token_stream())
+                }
+            }
+            _ => panic!("Not a reference: {}", ty.to_token_stream()),
         };
         TypeConversionPolicy {
             unwrapped_type: if is_mut {
@@ -133,7 +141,7 @@
 
     pub(crate) fn new_to_unique_ptr(ty: Type) -> Self {
         TypeConversionPolicy {
-            unwrapped_type: ty,
+            unwrapped_type: ty.into(),
             cpp_conversion: CppConversionType::FromValueToUniquePtr,
             rust_conversion: RustConversionType::None,
         }
@@ -141,7 +149,7 @@
 
     pub(crate) fn new_for_placement_return(ty: Type) -> Self {
         TypeConversionPolicy {
-            unwrapped_type: ty,
+            unwrapped_type: ty.into(),
             cpp_conversion: CppConversionType::FromReturnValueToPlacementPtr,
             // Rust conversion is marked as none here, since this policy
             // will be applied to the return value, and the Rust-side
@@ -157,7 +165,7 @@
     pub(crate) fn unconverted_rust_type(&self) -> Type {
         match self.cpp_conversion {
             CppConversionType::FromValueToUniquePtr => self.make_unique_ptr_type(),
-            _ => self.unwrapped_type.clone(),
+            _ => self.unwrapped_type.clone().into(),
         }
     }
 
@@ -170,7 +178,7 @@
                     *mut #innerty
                 }
             }
-            _ => self.unwrapped_type.clone(),
+            _ => self.unwrapped_type.clone().into(),
         }
     }
 
@@ -222,7 +230,7 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum CppFunctionBody {
     FunctionCall(Namespace, Ident),
     StaticMethodCall(Namespace, Ident, Ident),
@@ -234,7 +242,7 @@
     FreeUninitialized(QualifiedName),
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum CppFunctionKind {
     Function,
     Method,
@@ -243,10 +251,10 @@
     SynthesizedConstructor,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct CppFunction {
     pub(crate) payload: CppFunctionBody,
-    pub(crate) wrapper_function_name: Ident,
+    pub(crate) wrapper_function_name: crate::minisyn::Ident,
     pub(crate) original_cpp_name: String,
     pub(crate) return_conversion: Option<TypeConversionPolicy>,
     pub(crate) argument_conversion: Vec<TypeConversionPolicy>,
diff --git a/engine/src/conversion/analysis/fun/implicit_constructors.rs b/engine/src/conversion/analysis/fun/implicit_constructors.rs
index 469ed89..a1ebf27 100644
--- a/engine/src/conversion/analysis/fun/implicit_constructors.rs
+++ b/engine/src/conversion/analysis/fun/implicit_constructors.rs
@@ -11,13 +11,16 @@
 
 use syn::{Type, TypeArray};
 
+use crate::conversion::api::DeletedOrDefaulted;
 use crate::{
     conversion::{
-        analysis::{depth_first::depth_first, pod::PodAnalysis, type_converter::TypeKind},
+        analysis::{
+            depth_first::fields_and_bases_first, pod::PodAnalysis, type_converter::TypeKind,
+        },
         api::{Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind},
         apivec::ApiVec,
         convert_error::ConvertErrorWithContext,
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     known_types::{known_types, KnownTypeConstructorDetails},
     types::QualifiedName,
@@ -177,6 +180,13 @@
     apis: &ApiVec<FnPrePhase1>,
 ) -> HashMap<QualifiedName, ItemsFound> {
     let (explicits, unknown_types) = find_explicit_items(apis);
+    let enums: HashSet<QualifiedName> = apis
+        .iter()
+        .filter_map(|api| match api {
+            Api::Enum { name, .. } => Some(name.name.clone()),
+            _ => None,
+        })
+        .collect();
 
     // These contain all the classes we've seen so far with the relevant properties on their
     // constructors of each kind. We iterate via [`depth_first`], so analyzing later classes
@@ -189,7 +199,7 @@
     // These analyses include all bases and members of each class.
     let mut all_items_found: HashMap<QualifiedName, ItemsFound> = HashMap::new();
 
-    for api in depth_first(apis.iter()) {
+    for api in fields_and_bases_first(apis.iter()) {
         if let Api::Struct {
             name,
             analysis:
@@ -211,7 +221,17 @@
                 })
             };
             let get_items_found = |qn: &QualifiedName| -> Option<ItemsFound> {
-                if let Some(constructor_details) = known_types().get_constructor_details(qn) {
+                if enums.contains(qn) {
+                    Some(ItemsFound {
+                        default_constructor: SpecialMemberFound::NotPresent,
+                        destructor: SpecialMemberFound::Implicit,
+                        const_copy_constructor: SpecialMemberFound::Implicit,
+                        non_const_copy_constructor: SpecialMemberFound::NotPresent,
+                        move_constructor: SpecialMemberFound::Implicit,
+                        name: Some(name.clone()),
+                    })
+                } else if let Some(constructor_details) = known_types().get_constructor_details(qn)
+                {
                     Some(known_type_items_found(constructor_details))
                 } else {
                     all_items_found.get(qn).cloned()
@@ -539,8 +559,7 @@
                 all_items_found
                     .insert(name.name.clone(), items_found)
                     .is_none(),
-                "Duplicate struct: {:?}",
-                name
+                "Duplicate struct: {name:?}"
             );
         }
     }
@@ -556,7 +575,7 @@
         .entry(ExplicitType { ty, kind })
     {
         Entry::Vacant(entry) => {
-            entry.insert(if fun.is_deleted {
+            entry.insert(if matches!(fun.is_deleted, DeletedOrDefaulted::Deleted) {
                 ExplicitFound::Deleted
             } else {
                 ExplicitFound::UserDefined(fun.cpp_vis)
@@ -575,7 +594,8 @@
                         kind: FnKind::Method { impl_for, .. },
                         param_details,
                         ignore_reason:
-                            Ok(()) | Err(ConvertErrorWithContext(ConvertError::AssignmentOperator, _)),
+                            Ok(())
+                            | Err(ConvertErrorWithContext(ConvertErrorFromCpp::AssignmentOperator, _)),
                         ..
                     },
                 fun,
diff --git a/engine/src/conversion/analysis/fun/mod.rs b/engine/src/conversion/analysis/fun/mod.rs
index 7194746..415e40a 100644
--- a/engine/src/conversion/analysis/fun/mod.rs
+++ b/engine/src/conversion/analysis/fun/mod.rs
@@ -19,9 +19,9 @@
             type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
         },
         api::{
-            ApiName, CastMutability, CppVisibility, FuncToConvert, NullPhase, Provenance,
-            References, SpecialMemberKind, SubclassName, TraitImplSignature, TraitSynthesis,
-            UnsafetyNeeded, Virtualness,
+            ApiName, CastMutability, CppVisibility, DeletedOrDefaulted, FuncToConvert, NullPhase,
+            Provenance, References, SpecialMemberKind, SubclassName, TraitImplSignature,
+            TraitSynthesis, UnsafetyNeeded, Virtualness,
         },
         apivec::ApiVec,
         convert_error::ErrorContext,
@@ -29,6 +29,7 @@
         error_reporter::{convert_apis, report_any_error},
     },
     known_types::known_types,
+    minisyn::minisynize_punctuated,
     types::validate_ident_ok_for_rust,
 };
 use indexmap::map::IndexMap as HashMap;
@@ -40,14 +41,14 @@
 use proc_macro2::Span;
 use quote::quote;
 use syn::{
-    parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, ReturnType, Type,
-    TypePath, TypePtr, TypeReference, Visibility,
+    parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, PatType, ReturnType,
+    Type, TypePath, TypePtr, TypeReference, Visibility,
 };
 
 use crate::{
     conversion::{
         api::{AnalysisPhase, Api, TypeKind},
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::{make_ident, validate_ident_ok_for_cxx, Namespace, QualifiedName},
 };
@@ -64,13 +65,14 @@
 };
 
 use super::{
+    depth_first::HasFieldsAndBases,
     doc_label::make_doc_attrs,
     pod::{PodAnalysis, PodPhase},
     tdef::TypedefAnalysis,
     type_converter::{Annotated, PointerTreatment},
 };
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 pub(crate) enum ReceiverMutability {
     Const,
     Mutable,
@@ -85,7 +87,7 @@
     PureVirtual(ReceiverMutability),
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum TraitMethodKind {
     CopyConstructor,
     MoveConstructor,
@@ -95,11 +97,11 @@
     Dealloc,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct TraitMethodDetails {
     pub(crate) trt: TraitImplSignature,
     pub(crate) avoid_self: bool,
-    pub(crate) method_name: Ident,
+    pub(crate) method_name: crate::minisyn::Ident,
     /// For traits, where we're trying to implement a specific existing
     /// interface, we may need to reorder the parameters to fit that
     /// interface.
@@ -109,7 +111,7 @@
     pub(crate) trait_call_is_unsafe: bool,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum FnKind {
     Function,
     Method {
@@ -129,31 +131,31 @@
 
 /// Strategy for ensuring that the final, callable, Rust name
 /// is what the user originally expected.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 
 pub(crate) enum RustRenameStrategy {
     /// cxx::bridge name matches user expectations
     None,
     /// Even the #[rust_name] attribute would cause conflicts, and we need
     /// to use a 'use XYZ as ABC'
-    RenameInOutputMod(Ident),
+    RenameInOutputMod(crate::minisyn::Ident),
     /// This function requires us to generate a Rust function to do
     /// parameter conversion.
     RenameUsingWrapperFunction,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct FnAnalysis {
     /// Each entry in the cxx::bridge needs to have a unique name, even if
     /// (from the perspective of Rust and C++) things are in different
     /// namespaces/mods.
-    pub(crate) cxxbridge_name: Ident,
+    pub(crate) cxxbridge_name: crate::minisyn::Ident,
     /// ... so record also the name under which we wish to expose it in Rust.
     pub(crate) rust_name: String,
     pub(crate) rust_rename_strategy: RustRenameStrategy,
     pub(crate) params: Punctuated<FnArg, Comma>,
     pub(crate) kind: FnKind,
-    pub(crate) ret_type: ReturnType,
+    pub(crate) ret_type: crate::minisyn::ReturnType,
     pub(crate) param_details: Vec<ArgumentAnalysis>,
     pub(crate) ret_conversion: Option<TypeConversionPolicy>,
     pub(crate) requires_unsafe: UnsafetyNeeded,
@@ -172,12 +174,13 @@
     pub(crate) rust_wrapper_needed: bool,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct ArgumentAnalysis {
     pub(crate) conversion: TypeConversionPolicy,
-    pub(crate) name: Pat,
+    pub(crate) name: crate::minisyn::Pat,
     pub(crate) self_type: Option<(QualifiedName, ReceiverMutability)>,
     pub(crate) has_lifetime: bool,
+    pub(crate) is_mutable_reference: bool,
     pub(crate) deps: HashSet<QualifiedName>,
     pub(crate) requires_unsafe: UnsafetyNeeded,
     pub(crate) is_placement_return_destination: bool,
@@ -187,6 +190,7 @@
     rt: ReturnType,
     conversion: Option<TypeConversionPolicy>,
     was_reference: bool,
+    was_mutable_reference: bool,
     deps: HashSet<QualifiedName>,
     placement_param_needed: Option<(FnArg, ArgumentAnalysis)>,
 }
@@ -197,12 +201,14 @@
             rt: parse_quote! {},
             conversion: None,
             was_reference: false,
+            was_mutable_reference: false,
             deps: Default::default(),
             placement_param_needed: None,
         }
     }
 }
 
+#[derive(std::fmt::Debug)]
 pub(crate) struct PodAndConstructorAnalysis {
     pub(crate) pod: PodAnalysis,
     pub(crate) constructors: PublicConstructors,
@@ -210,6 +216,7 @@
 
 /// An analysis phase where we've analyzed each function, but
 /// haven't yet determined which constructors/etc. belong to each type.
+#[derive(std::fmt::Debug)]
 pub(crate) struct FnPrePhase1;
 
 impl AnalysisPhase for FnPrePhase1 {
@@ -220,6 +227,7 @@
 
 /// An analysis phase where we've analyzed each function, and identified
 /// what implicit constructors/destructors are present in each type.
+#[derive(std::fmt::Debug)]
 pub(crate) struct FnPrePhase2;
 
 impl AnalysisPhase for FnPrePhase2 {
@@ -228,6 +236,7 @@
     type FunAnalysis = FnAnalysis;
 }
 
+#[derive(Debug)]
 pub(crate) struct PodAndDepAnalysis {
     pub(crate) pod: PodAnalysis,
     pub(crate) constructor_and_allocator_deps: Vec<QualifiedName>,
@@ -236,6 +245,7 @@
 
 /// Analysis phase after we've finished analyzing functions and determined
 /// which constructors etc. belong to them.
+#[derive(std::fmt::Debug)]
 pub(crate) struct FnPhase;
 
 /// Indicates which kinds of public constructors are known to exist for a type.
@@ -283,6 +293,7 @@
     generic_types: HashSet<QualifiedName>,
     types_in_anonymous_namespace: HashSet<QualifiedName>,
     existing_superclass_trait_api_names: HashSet<QualifiedName>,
+    force_wrapper_generation: bool,
 }
 
 impl<'a> FnAnalyzer<'a> {
@@ -290,6 +301,7 @@
         apis: ApiVec<PodPhase>,
         unsafe_policy: &'a UnsafePolicy,
         config: &'a IncludeCppConfig,
+        force_wrapper_generation: bool,
     ) -> ApiVec<FnPrePhase2> {
         let mut me = Self {
             unsafe_policy,
@@ -305,6 +317,7 @@
             generic_types: Self::build_generic_type_set(&apis),
             existing_superclass_trait_api_names: HashSet::new(),
             types_in_anonymous_namespace: Self::build_types_in_anonymous_namespace(&apis),
+            force_wrapper_generation,
         };
         let mut results = ApiVec::new();
         convert_apis(
@@ -427,7 +440,7 @@
         ty: Box<Type>,
         ns: &Namespace,
         pointer_treatment: PointerTreatment,
-    ) -> Result<Annotated<Box<Type>>, ConvertError> {
+    ) -> Result<Annotated<Box<Type>>, ConvertErrorFromCpp> {
         let ctx = TypeConversionContext::OuterType { pointer_treatment };
         let mut annotated = self.type_converter.convert_boxed_type(ty, ns, &ctx)?;
         self.extra_apis.append(&mut annotated.extra_apis);
@@ -513,7 +526,7 @@
                     **fun,
                     FuncToConvert {
                         special_member: Some(SpecialMemberKind::Destructor),
-                        is_deleted: false,
+                        is_deleted: DeletedOrDefaulted::Neither | DeletedOrDefaulted::Defaulted,
                         cpp_vis: CppVisibility::Public,
                         ..
                     }
@@ -741,6 +754,10 @@
                     sophistication,
                     false,
                 )
+                .map_err(|err| ConvertErrorFromCpp::Argument {
+                    arg: describe_arg(i),
+                    err: Box::new(err),
+                })
             })
             .partition(Result::is_ok);
         let (mut params, mut param_details): (Punctuated<_, Comma>, Vec<_>) =
@@ -775,7 +792,7 @@
                 if initial_rust_name.ends_with('_') {
                     initial_rust_name // case 2
                 } else if validate_ident_ok_for_rust(cpp_name).is_err() {
-                    format!("{}_", cpp_name) // case 5
+                    format!("{cpp_name}_") // case 5
                 } else {
                     cpp_name.to_string() // cases 3, 4, 6
                 }
@@ -829,7 +846,7 @@
                 let is_move =
                     matches!(fun.special_member, Some(SpecialMemberKind::MoveConstructor));
                 if let Some(constructor_suffix) = rust_name.strip_prefix(nested_type_ident) {
-                    rust_name = format!("new{}", constructor_suffix);
+                    rust_name = format!("new{constructor_suffix}");
                 }
                 rust_name = predetermined_rust_name
                     .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
@@ -868,7 +885,7 @@
                             impl_for: self_ty,
                             details: Box::new(TraitMethodDetails {
                                 trt: TraitImplSignature {
-                                    ty,
+                                    ty: ty.into(),
                                     trait_signature: parse_quote! {
                                         autocxx::moveit::new:: #trait_id
                                     },
@@ -904,7 +921,7 @@
                         impl_for: self_ty,
                         details: Box::new(TraitMethodDetails {
                             trt: TraitImplSignature {
-                                ty,
+                                ty: ty.into(),
                                 trait_signature: parse_quote! {
                                     Drop
                                 },
@@ -934,7 +951,7 @@
                     // fn make_unique(...args) -> UniquePtr<Type>
                     // If there are multiple constructors, bindgen generates
                     // new, new1, new2 etc. and we'll keep those suffixes.
-                    rust_name = format!("new{}", constructor_suffix);
+                    rust_name = format!("new{constructor_suffix}");
                     MethodKind::Constructor {
                         is_default: matches!(
                             fun.special_member,
@@ -1033,10 +1050,10 @@
                 ..
             } => {
                 if param_details.len() < 2 {
-                    set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+                    set_ignore_reason(ConvertErrorFromCpp::ConstructorWithOnlyOneParam);
                 }
                 if param_details.len() > 2 {
-                    set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
+                    set_ignore_reason(ConvertErrorFromCpp::ConstructorWithMultipleParams);
                 }
                 self.reanalyze_parameter(
                     0,
@@ -1058,10 +1075,10 @@
                 ..
             } => {
                 if param_details.len() < 2 {
-                    set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+                    set_ignore_reason(ConvertErrorFromCpp::ConstructorWithOnlyOneParam);
                 }
                 if param_details.len() > 2 {
-                    set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
+                    set_ignore_reason(ConvertErrorFromCpp::ConstructorWithMultipleParams);
                 }
                 self.reanalyze_parameter(
                     0,
@@ -1099,14 +1116,14 @@
         // or note whether the type is abstract.
         let externally_callable = match fun.cpp_vis {
             CppVisibility::Private => {
-                set_ignore_reason(ConvertError::PrivateMethod);
+                set_ignore_reason(ConvertErrorFromCpp::PrivateMethod);
                 false
             }
             CppVisibility::Protected => false,
             CppVisibility::Public => true,
         };
         if fun.variadic {
-            set_ignore_reason(ConvertError::Variadic);
+            set_ignore_reason(ConvertErrorFromCpp::Variadic);
         }
         if let Some(problem) = bads.into_iter().next() {
             match problem {
@@ -1116,7 +1133,7 @@
         } else if fun.unused_template_param {
             // This indicates that bindgen essentially flaked out because templates
             // were too complex.
-            set_ignore_reason(ConvertError::UnusedTemplateParam)
+            set_ignore_reason(ConvertErrorFromCpp::UnusedTemplateParam)
         } else if matches!(
             fun.special_member,
             Some(SpecialMemberKind::AssignmentOperator)
@@ -1124,11 +1141,11 @@
             // Be careful with the order of this if-else tree. Anything above here means we won't
             // treat it as an assignment operator, but anything below we still consider when
             // deciding which other C++ special member functions are implicitly defined.
-            set_ignore_reason(ConvertError::AssignmentOperator)
+            set_ignore_reason(ConvertErrorFromCpp::AssignmentOperator)
         } else if fun.references.rvalue_ref_return {
-            set_ignore_reason(ConvertError::RValueReturn)
-        } else if fun.is_deleted {
-            set_ignore_reason(ConvertError::Deleted)
+            set_ignore_reason(ConvertErrorFromCpp::RValueReturn)
+        } else if matches!(fun.is_deleted, DeletedOrDefaulted::Deleted) {
+            set_ignore_reason(ConvertErrorFromCpp::Deleted)
         } else {
             match kind {
                 FnKind::Method {
@@ -1140,21 +1157,21 @@
                         | MethodKind::Virtual(..),
                     ..
                 } if !known_types().is_cxx_acceptable_receiver(impl_for) => {
-                    set_ignore_reason(ConvertError::UnsupportedReceiver);
+                    set_ignore_reason(ConvertErrorFromCpp::UnsupportedReceiver);
                 }
                 FnKind::Method { ref impl_for, .. } if !self.is_on_allowlist(impl_for) => {
                     // Bindgen will output methods for types which have been encountered
                     // virally as arguments on other allowlisted types. But we don't want
                     // to generate methods unless the user has specifically asked us to.
                     // It may, for instance, be a private type.
-                    set_ignore_reason(ConvertError::MethodOfNonAllowlistedType);
+                    set_ignore_reason(ConvertErrorFromCpp::MethodOfNonAllowlistedType);
                 }
                 FnKind::Method { ref impl_for, .. } | FnKind::TraitMethod { ref impl_for, .. } => {
                     if self.is_generic_type(impl_for) {
-                        set_ignore_reason(ConvertError::MethodOfGenericType);
+                        set_ignore_reason(ConvertErrorFromCpp::MethodOfGenericType);
                     }
                     if self.types_in_anonymous_namespace.contains(impl_for) {
-                        set_ignore_reason(ConvertError::MethodInAnonymousNamespace);
+                        set_ignore_reason(ConvertErrorFromCpp::MethodInAnonymousNamespace);
                     }
                 }
                 _ => {}
@@ -1199,12 +1216,46 @@
 
         let requires_unsafe = self.should_be_unsafe(&param_details, &kind);
 
-        let num_input_references = param_details.iter().filter(|pd| pd.has_lifetime).count();
-        if num_input_references != 1 && return_analysis.was_reference {
+        // The following sections reject some types of function because of the arrangement
+        // of Rust references. We could lift these restrictions when/if we switch to using
+        // CppRef to represent C++ references.
+        if return_analysis.was_reference {
             // cxx only allows functions to return a reference if they take exactly
-            // one reference as a parameter. Let's see...
-            set_ignore_reason(ConvertError::NotOneInputReference(rust_name.clone()));
+            // one reference as a parameter. Let's see.
+            let num_input_references = param_details.iter().filter(|pd| pd.has_lifetime).count();
+            if num_input_references == 0 {
+                set_ignore_reason(ConvertErrorFromCpp::NoInputReference(rust_name.clone()));
+            }
+            if num_input_references > 1 {
+                set_ignore_reason(ConvertErrorFromCpp::MultipleInputReferences(
+                    rust_name.clone(),
+                ));
+            }
         }
+        if return_analysis.was_mutable_reference {
+            // This one's a bit more subtle. We can't have:
+            //    fn foo(thing: &Thing) -> &mut OtherThing
+            // because Rust doesn't allow it.
+            // We could probably allow:
+            //    fn foo(thing: &mut Thing, thing2: &mut OtherThing) -> &mut OtherThing
+            // but probably cxx doesn't allow that. (I haven't checked). Even if it did,
+            // there's ambiguity here so won't allow it.
+            let num_input_mutable_references = param_details
+                .iter()
+                .filter(|pd| pd.has_lifetime && pd.is_mutable_reference)
+                .count();
+            if num_input_mutable_references == 0 {
+                set_ignore_reason(ConvertErrorFromCpp::NoMutableInputReference(
+                    rust_name.clone(),
+                ));
+            }
+            if num_input_mutable_references > 1 {
+                set_ignore_reason(ConvertErrorFromCpp::MultipleMutableInputReferences(
+                    rust_name.clone(),
+                ));
+            }
+        }
+
         let mut ret_type = return_analysis.rt;
         let ret_type_conversion = return_analysis.conversion;
 
@@ -1248,19 +1299,23 @@
             _ if ret_type_conversion_needed => true,
             _ if cpp_name_incompatible_with_cxx => true,
             _ if fun.synthetic_cpp.is_some() => true,
+            _ if self.force_wrapper_generation => true,
             _ => false,
         };
 
         let cpp_wrapper = if wrapper_function_needed {
             // Generate a new layer of C++ code to wrap/unwrap parameters
             // and return values into/out of std::unique_ptrs.
-            let cpp_construction_ident = make_ident(&effective_cpp_name);
+            let cpp_construction_ident = make_ident(effective_cpp_name);
             let joiner = if cxxbridge_name.to_string().ends_with('_') {
                 ""
             } else {
                 "_"
             };
-            cxxbridge_name = make_ident(&format!("{}{}autocxx_wrapper", cxxbridge_name, joiner));
+            cxxbridge_name = make_ident(
+                self.config
+                    .uniquify_name_per_mod(&format!("{cxxbridge_name}{joiner}autocxx_wrapper")),
+            );
             let (payload, cpp_function_kind) = match fun.synthetic_cpp.as_ref().cloned() {
                 Some((payload, cpp_function_kind)) => (payload, cpp_function_kind),
                 None => match kind {
@@ -1321,10 +1376,10 @@
             params.clear();
             for pd in &param_details {
                 let type_name = pd.conversion.converted_rust_type();
-                let arg_name = if pd.self_type.is_some() {
+                let arg_name: syn::Pat = if pd.self_type.is_some() {
                     parse_quote!(autocxx_gen_this)
                 } else {
-                    pd.name.clone()
+                    pd.name.clone().into()
                 };
                 params.push(parse_quote!(
                     #arg_name: #type_name
@@ -1358,12 +1413,15 @@
             _ if any_param_needs_rust_conversion || return_needs_rust_conversion => true,
             FnKind::TraitMethod { .. } => true,
             FnKind::Method { .. } => cxxbridge_name != rust_name,
+            _ if self.force_wrapper_generation => true,
             _ => false,
         };
 
         // Naming, part two.
         // Work out our final naming strategy.
-        validate_ident_ok_for_cxx(&cxxbridge_name.to_string()).unwrap_or_else(set_ignore_reason);
+        validate_ident_ok_for_cxx(&cxxbridge_name.to_string())
+            .map_err(ConvertErrorFromCpp::InvalidIdent)
+            .unwrap_or_else(set_ignore_reason);
         let rust_name_ident = make_ident(&rust_name);
         let rust_rename_strategy = match kind {
             _ if rust_wrapper_needed => RustRenameStrategy::RenameUsingWrapperFunction,
@@ -1380,10 +1438,10 @@
             params,
             ret_conversion: ret_type_conversion,
             kind,
-            ret_type,
+            ret_type: ret_type.into(),
             param_details,
             requires_unsafe,
-            vis,
+            vis: vis.into(),
             cpp_wrapper,
             deps,
             ignore_reason,
@@ -1422,7 +1480,7 @@
         sophistication: TypeConversionSophistication,
         construct_into_self: bool,
         is_move_constructor: bool,
-    ) -> Result<(), ConvertError> {
+    ) -> Result<(), ConvertErrorFromCpp> {
         self.convert_fn_arg(
             fun.inputs.iter().nth(param_idx).unwrap(),
             ns,
@@ -1484,7 +1542,7 @@
                             AsRef < #to_type >
                         },
                         parse_quote! {
-                            &'a mut ::std::pin::Pin < &'a mut #from_type_path >
+                            &'a mut ::core::pin::Pin < &'a mut #from_type_path >
                         },
                         "as_ref",
                     ),
@@ -1493,7 +1551,7 @@
                             autocxx::PinMut < #to_type >
                         },
                         parse_quote! {
-                            ::std::pin::Pin < &'a mut #from_type_path >
+                            ::core::pin::Pin < &'a mut #from_type_path >
                         },
                         "pin_mut",
                     ),
@@ -1505,7 +1563,7 @@
                         impl_for: from_type.clone(),
                         details: Box::new(TraitMethodDetails {
                             trt: TraitImplSignature {
-                                ty,
+                                ty: ty.into(),
                                 trait_signature,
                                 unsafety: None,
                             },
@@ -1549,7 +1607,7 @@
                 impl_for: ty.clone(),
                 details: Box::new(TraitMethodDetails {
                     trt: TraitImplSignature {
-                        ty: Type::Path(typ),
+                        ty: Type::Path(typ).into(),
                         trait_signature: parse_quote! { autocxx::moveit::MakeCppStorage },
                         unsafety: Some(parse_quote! { unsafe }),
                     },
@@ -1590,7 +1648,7 @@
         force_rust_conversion: Option<RustConversionType>,
         sophistication: TypeConversionSophistication,
         construct_into_self: bool,
-    ) -> Result<(FnArg, ArgumentAnalysis), ConvertError> {
+    ) -> Result<(FnArg, ArgumentAnalysis), ConvertErrorFromCpp> {
         Ok(match arg {
             FnArg::Typed(pt) => {
                 let mut pt = pt.clone();
@@ -1627,12 +1685,11 @@
                                     };
                                     Ok((this_type, receiver_mutability))
                                 }
-                                _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
-                                    ns,
-                                    make_ident(fn_name),
-                                ))),
+                                _ => Err(ConvertErrorFromCpp::UnexpectedThisType(
+                                    QualifiedName::new(ns, make_ident(fn_name)),
+                                )),
                             },
-                            _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
+                            _ => Err(ConvertErrorFromCpp::UnexpectedThisType(QualifiedName::new(
                                 ns,
                                 make_ident(fn_name),
                             ))),
@@ -1646,8 +1703,9 @@
                         syn::Pat::Ident(pp)
                     }
                     syn::Pat::Ident(pp) => {
-                        validate_ident_ok_for_cxx(&pp.ident.to_string())?;
-                        pointer_treatment = references.param_treatment(&pp.ident);
+                        validate_ident_ok_for_cxx(&pp.ident.to_string())
+                            .map_err(ConvertErrorFromCpp::InvalidIdent)?;
+                        pointer_treatment = references.param_treatment(&pp.ident.clone().into());
                         syn::Pat::Ident(pp)
                     }
                     _ => old_pat,
@@ -1684,13 +1742,17 @@
                     FnArg::Typed(pt),
                     ArgumentAnalysis {
                         self_type,
-                        name: new_pat,
+                        name: new_pat.into(),
                         conversion,
                         has_lifetime: matches!(
                             annotated_type.kind,
                             type_converter::TypeKind::Reference
                                 | type_converter::TypeKind::MutableReference
                         ),
+                        is_mutable_reference: matches!(
+                            annotated_type.kind,
+                            type_converter::TypeKind::MutableReference
+                        ),
                         deps: annotated_type.types_encountered,
                         requires_unsafe,
                         is_placement_return_destination,
@@ -1859,7 +1921,7 @@
         ns: &Namespace,
         references: &References,
         sophistication: TypeConversionSophistication,
-    ) -> Result<ReturnTypeAnalysis, ConvertError> {
+    ) -> Result<ReturnTypeAnalysis, ConvertErrorFromCpp> {
         Ok(match rt {
             ReturnType::Default => ReturnTypeAnalysis::default(),
             ReturnType::Type(rarrow, boxed_type) => {
@@ -1902,9 +1964,9 @@
                                 conversion: Some(TypeConversionPolicy::new_for_placement_return(
                                     ty.clone(),
                                 )),
-                                was_reference: false,
                                 deps: annotated_type.types_encountered,
                                 placement_param_needed: Some((fnarg, analysis)),
+                                ..Default::default()
                             }
                         } else {
                             // There are some types which we can't currently represent within a moveit::new::New.
@@ -1917,14 +1979,18 @@
                             ReturnTypeAnalysis {
                                 rt: ReturnType::Type(*rarrow, boxed_type),
                                 conversion,
-                                was_reference: false,
                                 deps: annotated_type.types_encountered,
-                                placement_param_needed: None,
+                                ..Default::default()
                             }
                         }
                     }
                     _ => {
-                        let was_reference = references.ref_return;
+                        let was_mutable_reference = matches!(
+                            annotated_type.kind,
+                            type_converter::TypeKind::MutableReference
+                        );
+                        let was_reference = was_mutable_reference
+                            || matches!(annotated_type.kind, type_converter::TypeKind::Reference);
                         let conversion = Some(
                             if was_reference
                                 && matches!(
@@ -1941,6 +2007,7 @@
                             rt: ReturnType::Type(*rarrow, boxed_type),
                             conversion,
                             was_reference,
+                            was_mutable_reference,
                             deps: annotated_type.types_encountered,
                             placement_param_needed: None,
                         }
@@ -2091,9 +2158,12 @@
                     Box::new(FuncToConvert {
                         self_ty: Some(self_ty.clone()),
                         ident,
-                        doc_attrs: make_doc_attrs(format!("Synthesized {}.", special_member)),
-                        inputs,
-                        output: ReturnType::Default,
+                        doc_attrs: make_doc_attrs(format!("Synthesized {special_member}."))
+                            .into_iter()
+                            .map(Into::into)
+                            .collect(),
+                        inputs: minisynize_punctuated(inputs),
+                        output: ReturnType::Default.into(),
                         vis: parse_quote! { pub },
                         virtualness: Virtualness::None,
                         cpp_vis: CppVisibility::Public,
@@ -2102,7 +2172,7 @@
                         references,
                         original_name: None,
                         synthesized_this_type: None,
-                        is_deleted: false,
+                        is_deleted: DeletedOrDefaulted::Neither,
                         add_to_trait: None,
                         synthetic_cpp: None,
                         provenance: Provenance::SynthesizedOther,
@@ -2192,7 +2262,7 @@
         )
     }
 
-    pub(crate) fn cxxbridge_name(&self) -> Option<Ident> {
+    pub(crate) fn cxxbridge_name(&self) -> Option<crate::minisyn::Ident> {
         match self {
             Api::Function { ref analysis, .. } => Some(analysis.cxxbridge_name.clone()),
             Api::StringConstructor { .. }
@@ -2224,3 +2294,60 @@
         _ => panic!("did not find angle bracketed args"),
     }
 }
+
+impl HasFieldsAndBases for Api<FnPrePhase1> {
+    fn name(&self) -> &QualifiedName {
+        self.name()
+    }
+
+    fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+        match self {
+            Api::Struct {
+                analysis:
+                    PodAnalysis {
+                        field_definition_deps,
+                        bases,
+                        ..
+                    },
+                ..
+            } => Box::new(field_definition_deps.iter().chain(bases.iter())),
+            _ => Box::new(std::iter::empty()),
+        }
+    }
+}
+
+impl HasFieldsAndBases for Api<FnPrePhase2> {
+    fn name(&self) -> &QualifiedName {
+        self.name()
+    }
+
+    fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+        match self {
+            Api::Struct {
+                analysis:
+                    PodAndConstructorAnalysis {
+                        pod:
+                            PodAnalysis {
+                                field_definition_deps,
+                                bases,
+                                ..
+                            },
+                        ..
+                    },
+                ..
+            } => Box::new(field_definition_deps.iter().chain(bases.iter())),
+            _ => Box::new(std::iter::empty()),
+        }
+    }
+}
+
+/// Stringify a function argument for diagnostics
+fn describe_arg(arg: &FnArg) -> String {
+    match arg {
+        FnArg::Receiver(_) => "the function receiver (this/self paramter)".into(),
+        FnArg::Typed(PatType { pat, .. }) => match pat.as_ref() {
+            Pat::Ident(pti) => pti.ident.to_string(),
+            _ => "another argument we don't know how to describe".into(),
+        },
+    }
+}
diff --git a/engine/src/conversion/analysis/fun/overload_tracker.rs b/engine/src/conversion/analysis/fun/overload_tracker.rs
index 6fc532c..20165f5 100644
--- a/engine/src/conversion/analysis/fun/overload_tracker.rs
+++ b/engine/src/conversion/analysis/fun/overload_tracker.rs
@@ -49,7 +49,7 @@
         if this_offset == 0 {
             cpp_method_name
         } else {
-            format!("{}{}", cpp_method_name, this_offset)
+            format!("{cpp_method_name}{this_offset}")
         }
     }
 }
diff --git a/engine/src/conversion/analysis/fun/subclass.rs b/engine/src/conversion/analysis/fun/subclass.rs
index 6383d2c..8698539 100644
--- a/engine/src/conversion/analysis/fun/subclass.rs
+++ b/engine/src/conversion/analysis/fun/subclass.rs
@@ -6,6 +6,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::ops::DerefMut;
+
 use indexmap::map::IndexMap as HashMap;
 
 use syn::{parse_quote, FnArg, PatType, Type, TypePtr};
@@ -17,6 +19,7 @@
     SubclassName, SuperclassMethod, UnsafetyNeeded, Virtualness,
 };
 use crate::conversion::apivec::ApiVec;
+use crate::minisyn::minisynize_punctuated;
 use crate::{
     conversion::{
         analysis::fun::function_wrapper::{
@@ -85,6 +88,7 @@
         .param_details
         .iter()
         .map(|pd| pd.name.clone())
+        .map(Into::into)
         .collect();
     let requires_unsafe = if matches!(unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) {
         UnsafetyNeeded::Always
@@ -95,10 +99,10 @@
         name,
         details: SuperclassMethod {
             name: make_ident(&analysis.rust_name),
-            params: analysis.params.clone(),
+            params: minisynize_punctuated(analysis.params.clone()),
             ret_type: analysis.ret_type.clone(),
             param_names,
-            receiver_mutability: receiver_mutability.clone(),
+            receiver_mutability: *receiver_mutability,
             requires_unsafe,
             is_pure_virtual,
             receiver,
@@ -122,10 +126,10 @@
         sub.0.name.get_final_item(),
         name.name.get_final_item()
     ));
-    let params = std::iter::once(parse_quote! {
+    let params = std::iter::once(crate::minisyn::FnArg(parse_quote! {
         me: & #holder_name
-    })
-    .chain(analysis.params.iter().skip(1).cloned())
+    }))
+    .chain(analysis.params.iter().skip(1).cloned().map(Into::into))
     .collect();
     let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) {
         CppFunctionKind::Method
@@ -161,7 +165,7 @@
                 qualification: Some(cpp),
             },
             superclass: superclass.clone(),
-            receiver_mutability: receiver_mutability.clone(),
+            receiver_mutability: *receiver_mutability,
             dependencies,
             requires_unsafe,
             is_pure_virtual: matches!(
@@ -213,7 +217,9 @@
     let subclass_constructor_name =
         make_ident(format!("{}_{}", cpp.get_final_item(), cpp.get_final_item()));
     let mut existing_params = fun.inputs.clone();
-    if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() {
+    if let Some(FnArg::Typed(PatType { ty, .. })) =
+        existing_params.first_mut().map(DerefMut::deref_mut)
+    {
         if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty {
             *elem = Box::new(Type::Path(sub.cpp().to_type_path()));
         } else {
@@ -229,7 +235,7 @@
     };
     let inputs = self_param
         .into_iter()
-        .chain(std::iter::once(boxed_holder_param))
+        .chain(std::iter::once(boxed_holder_param.into()))
         .chain(existing_params)
         .collect();
     let maybe_wrap = Box::new(FuncToConvert {
diff --git a/engine/src/conversion/analysis/name_check.rs b/engine/src/conversion/analysis/name_check.rs
index 7547c7c..38b8db4 100644
--- a/engine/src/conversion/analysis/name_check.rs
+++ b/engine/src/conversion/analysis/name_check.rs
@@ -8,14 +8,14 @@
 
 use indexmap::map::IndexMap as HashMap;
 
-use syn::Ident;
+use crate::minisyn::Ident;
 
 use crate::{
     conversion::{
         api::{Api, SubclassName},
         apivec::ApiVec,
         error_reporter::convert_item_apis,
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::{validate_ident_ok_for_cxx, QualifiedName},
 };
@@ -92,7 +92,7 @@
         if let Some(name) = my_name {
             let symbols_for_this_name = names_found.entry(name).or_default();
             if symbols_for_this_name.len() > 1usize {
-                Err(ConvertError::DuplicateCxxBridgeName(
+                Err(ConvertErrorFromCpp::DuplicateCxxBridgeName(
                     symbols_for_this_name.clone(),
                 ))
             } else {
@@ -107,9 +107,9 @@
 
 fn validate_all_segments_ok_for_cxx(
     items: impl Iterator<Item = String>,
-) -> Result<(), ConvertError> {
+) -> Result<(), ConvertErrorFromCpp> {
     for seg in items {
-        validate_ident_ok_for_cxx(&seg)?;
+        validate_ident_ok_for_cxx(&seg).map_err(ConvertErrorFromCpp::InvalidIdent)?;
     }
     Ok(())
 }
diff --git a/engine/src/conversion/analysis/pod/byvalue_checker.rs b/engine/src/conversion/analysis/pod/byvalue_checker.rs
index de72eec..6e2e9b9 100644
--- a/engine/src/conversion/analysis/pod/byvalue_checker.rs
+++ b/engine/src/conversion/analysis/pod/byvalue_checker.rs
@@ -7,7 +7,7 @@
 // except according to those terms.
 
 use crate::conversion::apivec::ApiVec;
-use crate::{conversion::ConvertError, known_types::known_types};
+use crate::{conversion::ConvertErrorFromCpp, known_types::known_types};
 use crate::{
     conversion::{
         analysis::tdef::TypedefPhase,
@@ -48,6 +48,9 @@
 /// std::string contains a self-referential pointer.
 /// It is possible that this is duplicative of the information stored
 /// elsewhere in the `Api` list and could possibly be removed or simplified.
+/// In general this is one of the oldest parts of autocxx and
+/// the code here could quite possibly be simplified by reusing code
+/// elsewhere.
 pub struct ByValueChecker {
     // Mapping from type name to whether it is safe to be POD
     results: HashMap<QualifiedName, StructDetails>,
@@ -60,7 +63,7 @@
             let safety = if by_value_safe {
                 PodState::IsPod
             } else {
-                PodState::UnsafeToBePod(format!("type {} is not safe for POD", tn))
+                PodState::UnsafeToBePod(format!("type {tn} is not safe for POD"))
             };
             results.insert(tn.clone(), StructDetails::new(safety));
         }
@@ -72,7 +75,7 @@
     pub(crate) fn new_from_apis(
         apis: &ApiVec<TypedefPhase>,
         config: &IncludeCppConfig,
-    ) -> Result<ByValueChecker, ConvertError> {
+    ) -> Result<ByValueChecker, ConvertErrorFromCpp> {
         let mut byvalue_checker = ByValueChecker::new();
         for blocklisted in config.get_blocklist() {
             let tn = QualifiedName::new_from_cpp_name(blocklisted);
@@ -81,6 +84,11 @@
                 .results
                 .insert(tn, StructDetails::new(safety));
         }
+        // As we do this analysis, we need to be aware that structs
+        // may depend on other types. Ideally we'd use the depth first iterator
+        // but that's awkward given that our ApiPhase does not yet have a fixed
+        // list of field/base types. Instead, we'll iterate first over non-struct
+        // types and then over structs.
         for api in apis.iter() {
             match api {
                 Api::Typedef { analysis, .. } => {
@@ -94,7 +102,7 @@
                             _ => None,
                         },
                         TypedefKind::Use(_, ref ty) => match **ty {
-                            Type::Path(ref typ) => {
+                            crate::minisyn::Type(Type::Path(ref typ)) => {
                                 let target_tn = QualifiedName::from_type_path(typ);
                                 known_types().consider_substitution(&target_tn)
                             }
@@ -113,15 +121,7 @@
                         None => byvalue_checker.ingest_nonpod_type(name.clone()),
                     }
                 }
-                Api::Struct { details, .. } => {
-                    byvalue_checker.ingest_struct(&details.item, api.name().get_namespace())
-                }
-                Api::Enum { .. } => {
-                    byvalue_checker
-                        .results
-                        .insert(api.name().clone(), StructDetails::new(PodState::IsPod));
-                }
-                Api::ExternCppType { pod: true, .. } => {
+                Api::Enum { .. } | Api::ExternCppType { pod: true, .. } => {
                     byvalue_checker
                         .results
                         .insert(api.name().clone(), StructDetails::new(PodState::IsPod));
@@ -129,6 +129,11 @@
                 _ => {}
             }
         }
+        for api in apis.iter() {
+            if let Api::Struct { details, .. } = api {
+                byvalue_checker.ingest_struct(&details.item, api.name().get_namespace())
+            }
+        }
         let pod_requests = config
             .get_pod_requests()
             .iter()
@@ -136,27 +141,32 @@
             .collect();
         byvalue_checker
             .satisfy_requests(pod_requests)
-            .map_err(ConvertError::UnsafePodType)?;
+            .map_err(ConvertErrorFromCpp::UnsafePodType)?;
         Ok(byvalue_checker)
     }
 
     fn ingest_struct(&mut self, def: &ItemStruct, ns: &Namespace) {
         // For this struct, work out whether it _could_ be safe as a POD.
-        let tyname = QualifiedName::new(ns, def.ident.clone());
+        let tyname = QualifiedName::new(ns, def.ident.clone().into());
         let mut field_safety_problem = PodState::SafeToBePod;
         let fieldlist = Self::get_field_types(def);
         for ty_id in &fieldlist {
             match self.results.get(ty_id) {
+                None if ty_id.get_final_item() == "__BindgenUnionField" => {
+                    field_safety_problem = PodState::UnsafeToBePod(format!(
+                        "Type {tyname} could not be POD because it is a union"
+                    ));
+                    break;
+                }
                 None => {
                     field_safety_problem = PodState::UnsafeToBePod(format!(
-                        "Type {} could not be POD because its dependent type {} isn't known",
-                        tyname, ty_id
+                        "Type {tyname} could not be POD because its dependent type {ty_id} isn't known"
                     ));
                     break;
                 }
                 Some(deets) => {
                     if let PodState::UnsafeToBePod(reason) = &deets.state {
-                        let new_reason = format!("Type {} could not be POD because its dependent type {} isn't safe to be POD. Because: {}", tyname, ty_id, reason);
+                        let new_reason = format!("Type {tyname} could not be POD because its dependent type {ty_id} isn't safe to be POD. Because: {reason}");
                         field_safety_problem = PodState::UnsafeToBePod(new_reason);
                         break;
                     }
@@ -164,10 +174,8 @@
             }
         }
         if Self::has_vtable(def) {
-            let reason = format!(
-                "Type {} could not be POD because it has virtual functions.",
-                tyname
-            );
+            let reason =
+                format!("Type {tyname} could not be POD because it has virtual functions.");
             field_safety_problem = PodState::UnsafeToBePod(reason);
         }
         let mut my_details = StructDetails::new(field_safety_problem);
@@ -176,7 +184,7 @@
     }
 
     fn ingest_nonpod_type(&mut self, tyname: QualifiedName) {
-        let new_reason = format!("Type {} is a typedef to a complex type", tyname);
+        let new_reason = format!("Type {tyname} is a typedef to a complex type");
         self.results.insert(
             tyname,
             StructDetails::new(PodState::UnsafeToBePod(new_reason)),
@@ -191,8 +199,7 @@
             match deets {
                 None => {
                     return Err(format!(
-                        "Unable to make {} POD because we never saw a struct definition",
-                        ty_id
+                        "Unable to make {ty_id} POD because we never saw a struct definition"
                     ))
                 }
                 Some(deets) => match &deets.state {
@@ -236,6 +243,10 @@
         )
     }
 
+    /// This is a miniature version of the analysis in `super::get_struct_field_types`.
+    /// It would be nice to unify them. However, this version only cares about spotting
+    /// fields which may be non-POD, so can largely concern itself with just `Type::Path`
+    /// fields.
     fn get_field_types(def: &ItemStruct) -> Vec<QualifiedName> {
         let mut results = Vec::new();
         for f in &def.fields {
@@ -261,10 +272,11 @@
 #[cfg(test)]
 mod tests {
     use super::ByValueChecker;
+    use crate::minisyn::ItemStruct;
     use crate::types::{Namespace, QualifiedName};
-    use syn::{parse_quote, Ident, ItemStruct};
+    use syn::parse_quote;
 
-    fn ty_from_ident(id: &Ident) -> QualifiedName {
+    fn ty_from_ident(id: &syn::Ident) -> QualifiedName {
         QualifiedName::new_from_cpp_name(&id.to_string())
     }
 
diff --git a/engine/src/conversion/analysis/pod/mod.rs b/engine/src/conversion/analysis/pod/mod.rs
index eeb5051..2fafc95 100644
--- a/engine/src/conversion/analysis/pod/mod.rs
+++ b/engine/src/conversion/analysis/pod/mod.rs
@@ -13,7 +13,7 @@
 
 use autocxx_parser::IncludeCppConfig;
 use byvalue_checker::ByValueChecker;
-use syn::{ItemEnum, ItemStruct, Type, Visibility};
+use syn::{ItemStruct, Type, Visibility};
 
 use crate::{
     conversion::{
@@ -23,18 +23,21 @@
         convert_error::{ConvertErrorWithContext, ErrorContext},
         error_reporter::convert_apis,
         parse::BindgenSemanticAttributes,
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::{Namespace, QualifiedName},
 };
 
 use super::tdef::{TypedefAnalysis, TypedefPhase};
 
+#[derive(std::fmt::Debug)]
+
 pub(crate) struct FieldInfo {
     pub(crate) ty: Type,
     pub(crate) type_kind: type_converter::TypeKind,
 }
 
+#[derive(std::fmt::Debug)]
 pub(crate) struct PodAnalysis {
     pub(crate) kind: TypeKind,
     pub(crate) bases: HashSet<QualifiedName>,
@@ -43,12 +46,18 @@
     /// because otherwise we don't know whether they're
     /// abstract or not.
     pub(crate) castable_bases: HashSet<QualifiedName>,
+    /// All field types. e.g. for std::unique_ptr<A>, this would include
+    /// both std::unique_ptr and A
     pub(crate) field_deps: HashSet<QualifiedName>,
+    /// Types within fields where we need a definition, e.g. for
+    /// std::unique_ptr<A> it would just be std::unique_ptr.
+    pub(crate) field_definition_deps: HashSet<QualifiedName>,
     pub(crate) field_info: Vec<FieldInfo>,
     pub(crate) is_generic: bool,
     pub(crate) in_anonymous_namespace: bool,
 }
 
+#[derive(std::fmt::Debug)]
 pub(crate) struct PodPhase;
 
 impl AnalysisPhase for PodPhase {
@@ -65,7 +74,7 @@
 pub(crate) fn analyze_pod_apis(
     apis: ApiVec<TypedefPhase>,
     config: &IncludeCppConfig,
-) -> Result<ApiVec<PodPhase>, ConvertError> {
+) -> Result<ApiVec<PodPhase>, ConvertErrorFromCpp> {
     // This next line will return an error if any of the 'generate_pod'
     // directives from the user can't be met because, for instance,
     // a type contains a std::string or some other type which can't be
@@ -118,7 +127,7 @@
 
 fn analyze_enum(
     name: ApiName,
-    mut item: ItemEnum,
+    mut item: crate::minisyn::ItemEnum,
 ) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
     let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
     metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
@@ -138,12 +147,14 @@
     metadata.check_for_fatal_attrs(&id)?;
     let bases = get_bases(&details.item);
     let mut field_deps = HashSet::new();
+    let mut field_definition_deps = HashSet::new();
     let mut field_info = Vec::new();
     let field_conversion_errors = get_struct_field_types(
         type_converter,
         name.name.get_namespace(),
         &details.item,
         &mut field_deps,
+        &mut field_definition_deps,
         &mut field_info,
         extra_apis,
     );
@@ -152,7 +163,7 @@
         // Let's not allow anything to be POD if it's got rvalue reference fields.
         if details.has_rvalue_reference_fields {
             return Err(ConvertErrorWithContext(
-                ConvertError::RValueReferenceField,
+                ConvertErrorFromCpp::RValueReferenceField,
                 Some(ErrorContext::new_for_item(id)),
             ));
         }
@@ -186,6 +197,7 @@
             bases: bases.into_keys().collect(),
             castable_bases,
             field_deps,
+            field_definition_deps,
             field_info,
             is_generic,
             in_anonymous_namespace,
@@ -198,9 +210,10 @@
     ns: &Namespace,
     s: &ItemStruct,
     field_deps: &mut HashSet<QualifiedName>,
+    field_definition_deps: &mut HashSet<QualifiedName>,
     field_info: &mut Vec<FieldInfo>,
     extra_apis: &mut ApiVec<NullPhase>,
-) -> Vec<ConvertError> {
+) -> Vec<ConvertErrorFromCpp> {
     let mut convert_errors = Vec::new();
     let struct_type_params = s
         .generics
@@ -225,6 +238,14 @@
                     .unwrap_or(false)
                 {
                     field_deps.extend(r.types_encountered);
+                    if let Type::Path(typ) = &r.ty {
+                        // Later analyses need to know about the field
+                        // types where we need full definitions, as opposed
+                        // to just declarations. That means just the outermost
+                        // type path.
+                        // TODO: consider arrays.
+                        field_definition_deps.insert(QualifiedName::from_type_path(typ));
+                    }
                     field_info.push(FieldInfo {
                         ty: r.ty,
                         type_kind: r.kind,
diff --git a/engine/src/conversion/analysis/remove_ignored.rs b/engine/src/conversion/analysis/remove_ignored.rs
index bd11b13..4ed4b9f 100644
--- a/engine/src/conversion/analysis/remove_ignored.rs
+++ b/engine/src/conversion/analysis/remove_ignored.rs
@@ -11,7 +11,7 @@
 use super::deps::HasDependencies;
 use super::fun::{FnAnalysis, FnKind, FnPhase};
 use crate::conversion::apivec::ApiVec;
-use crate::conversion::{convert_error::ErrorContext, ConvertError};
+use crate::conversion::{convert_error::ErrorContext, ConvertErrorFromCpp};
 use crate::{conversion::api::Api, known_types};
 
 /// Remove any APIs which depend on other items which have been ignored.
@@ -44,7 +44,10 @@
                 if !ignored_dependents.is_empty() {
                     iterate_again = true;
                     ignored_items.insert(api.name().clone());
-                    create_ignore_item(api, ConvertError::IgnoredDependent(ignored_dependents))
+                    create_ignore_item(
+                        api,
+                        ConvertErrorFromCpp::IgnoredDependent(ignored_dependents),
+                    )
                 } else {
                     let mut missing_deps = api.deps().filter(|dep| {
                         !valid_types.contains(*dep) && !known_types().is_known_type(dep)
@@ -52,7 +55,10 @@
                     let first = missing_deps.next();
                     std::mem::drop(missing_deps);
                     if let Some(missing_dep) = first.cloned() {
-                        create_ignore_item(api, ConvertError::UnknownDependentType(missing_dep))
+                        create_ignore_item(
+                            api,
+                            ConvertErrorFromCpp::UnknownDependentType(missing_dep),
+                        )
                     } else {
                         api
                     }
@@ -63,7 +69,7 @@
     apis
 }
 
-fn create_ignore_item(api: Api<FnPhase>, err: ConvertError) -> Api<FnPhase> {
+fn create_ignore_item(api: Api<FnPhase>, err: ConvertErrorFromCpp) -> Api<FnPhase> {
     let id = api.name().get_final_ident();
     log::info!("Marking as ignored: {} because {}", id.to_string(), err);
     Api::IgnoredItem {
diff --git a/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs b/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
index 8d5d033..918fb8f 100644
--- a/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
+++ b/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
@@ -15,7 +15,7 @@
         api::Api,
         apivec::ApiVec,
         convert_error::{ConvertErrorWithContext, ErrorContext},
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::QualifiedName,
 };
@@ -71,7 +71,7 @@
                 {
                     Api::IgnoredItem {
                         name: api.name_info().clone(),
-                        err: ConvertError::NestedOpaqueTypedef,
+                        err: ConvertErrorFromCpp::NestedOpaqueTypedef,
                         ctx: Some(ErrorContext::new_for_item(name_id)),
                     }
                 } else {
diff --git a/engine/src/conversion/analysis/tdef.rs b/engine/src/conversion/analysis/tdef.rs
index 5a635f8..1c414e8 100644
--- a/engine/src/conversion/analysis/tdef.rs
+++ b/engine/src/conversion/analysis/tdef.rs
@@ -19,11 +19,12 @@
         convert_error::{ConvertErrorWithContext, ErrorContext},
         error_reporter::convert_apis,
         parse::BindgenSemanticAttributes,
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::QualifiedName,
 };
 
+#[derive(std::fmt::Debug)]
 pub(crate) struct TypedefAnalysis {
     pub(crate) kind: TypedefKind,
     pub(crate) deps: HashSet<QualifiedName>,
@@ -31,6 +32,7 @@
 
 /// Analysis phase where typedef analysis has been performed but no other
 /// analyses just yet.
+#[derive(std::fmt::Debug)]
 pub(crate) struct TypedefPhase;
 
 impl AnalysisPhase for TypedefPhase {
@@ -57,7 +59,7 @@
             Ok(Box::new(std::iter::once(match item {
                 TypedefKind::Type(ity) => get_replacement_typedef(
                     name,
-                    ity,
+                    ity.into(),
                     old_tyname,
                     &mut type_converter,
                     &mut extra_apis,
@@ -87,7 +89,7 @@
 ) -> Result<Api<TypedefPhase>, ConvertErrorWithContext> {
     if !ity.generics.params.is_empty() {
         return Err(ConvertErrorWithContext(
-            ConvertError::TypedefTakesGenericParameters,
+            ConvertErrorFromCpp::TypedefTakesGenericParameters,
             Some(ErrorContext::new_for_item(name.name.get_final_ident())),
         ));
     }
@@ -108,7 +110,7 @@
             ty: syn::Type::Path(ref typ),
             ..
         }) if QualifiedName::from_type_path(typ) == name.name => Err(ConvertErrorWithContext(
-            ConvertError::InfinitelyRecursiveTypedef(name.name.clone()),
+            ConvertErrorFromCpp::InfinitelyRecursiveTypedef(name.name.clone()),
             Some(ErrorContext::new_for_item(name.name.get_final_ident())),
         )),
         Ok(mut final_type) => {
@@ -116,10 +118,10 @@
             extra_apis.append(&mut final_type.extra_apis);
             Ok(Api::Typedef {
                 name,
-                item: TypedefKind::Type(ity),
+                item: TypedefKind::Type(ity.into()),
                 old_tyname,
                 analysis: TypedefAnalysis {
-                    kind: TypedefKind::Type(converted_type),
+                    kind: TypedefKind::Type(converted_type.into()),
                     deps: final_type.types_encountered,
                 },
             })
diff --git a/engine/src/conversion/analysis/type_converter.rs b/engine/src/conversion/analysis/type_converter.rs
index 7e2d2bd..63f7ae9 100644
--- a/engine/src/conversion/analysis/type_converter.rs
+++ b/engine/src/conversion/analysis/type_converter.rs
@@ -10,8 +10,8 @@
     conversion::{
         api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind, UnanalyzedApi},
         apivec::ApiVec,
-        codegen_cpp::type_to_cpp::type_to_cpp,
-        ConvertError,
+        codegen_cpp::type_to_cpp::CppNameMap,
+        ConvertErrorFromCpp,
     },
     known_types::{known_types, CxxGenericType},
     types::{make_ident, Namespace, QualifiedName},
@@ -34,7 +34,7 @@
 pub(crate) enum TypeKind {
     Regular,
     Pointer,
-    SubclassHolder(Ident),
+    SubclassHolder(crate::minisyn::Ident),
     Reference,
     RValueReference,
     MutableReference,
@@ -109,14 +109,9 @@
         matches!(self, Self::WithinReference)
     }
     fn allowed_generic_type(&self, ident: &Ident) -> bool {
-        match self {
+        !matches!(self,
             Self::WithinStructField { struct_type_params }
-                if struct_type_params.contains(ident) =>
-            {
-                false
-            }
-            _ => true,
-        }
+                if struct_type_params.contains(ident))
     }
 }
 
@@ -135,6 +130,7 @@
     forward_declarations: HashSet<QualifiedName>,
     ignored_types: HashSet<QualifiedName>,
     config: &'a IncludeCppConfig,
+    original_name_map: CppNameMap,
 }
 
 impl<'a> TypeConverter<'a> {
@@ -149,6 +145,7 @@
             forward_declarations: Self::find_incomplete_types(apis),
             ignored_types: Self::find_ignored_types(apis),
             config,
+            original_name_map: CppNameMap::new_from_apis(apis),
         }
     }
 
@@ -157,7 +154,7 @@
         ty: Box<Type>,
         ns: &Namespace,
         ctx: &TypeConversionContext,
-    ) -> Result<Annotated<Box<Type>>, ConvertError> {
+    ) -> Result<Annotated<Box<Type>>, ConvertErrorFromCpp> {
         Ok(self.convert_type(*ty, ns, ctx)?.map(Box::new))
     }
 
@@ -166,7 +163,7 @@
         ty: Type,
         ns: &Namespace,
         ctx: &TypeConversionContext,
-    ) -> Result<Annotated<Type>, ConvertError> {
+    ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
         let result = match ty {
             Type::Path(p) => {
                 let newp = self.convert_type_path(p, ns, ctx)?;
@@ -175,7 +172,7 @@
                     if !ctx.allow_instantiation_of_forward_declaration()
                         && self.forward_declarations.contains(&qn)
                     {
-                        return Err(ConvertError::TypeContainingForwardDeclaration(qn));
+                        return Err(ConvertErrorFromCpp::TypeContainingForwardDeclaration(qn));
                     }
                     // Special handling because rust_Str (as emitted by bindgen)
                     // doesn't simply get renamed to a different type _identifier_.
@@ -220,7 +217,11 @@
                 )
             }
             Type::Ptr(ptr) => self.convert_ptr(ptr, ns, ctx.pointer_treatment())?,
-            _ => return Err(ConvertError::UnknownType(ty.to_token_stream().to_string())),
+            _ => {
+                return Err(ConvertErrorFromCpp::UnknownType(
+                    ty.to_token_stream().to_string(),
+                ))
+            }
         };
         Ok(result)
     }
@@ -230,7 +231,7 @@
         mut typ: TypePath,
         ns: &Namespace,
         ctx: &TypeConversionContext,
-    ) -> Result<Annotated<Type>, ConvertError> {
+    ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
         // First, qualify any unqualified paths.
         if typ.path.segments.iter().next().unwrap().ident != "root" {
             let ty = QualifiedName::from_type_path(&typ);
@@ -241,7 +242,7 @@
             if !known_types().is_known_type(&ty) {
                 let num_segments = typ.path.segments.len();
                 if num_segments > 1 {
-                    return Err(ConvertError::UnsupportedBuiltInType(ty));
+                    return Err(ConvertErrorFromCpp::UnsupportedBuiltInType(ty));
                 }
                 if !self.types_found.contains(&ty) {
                     typ.path.segments = std::iter::once(&"root".to_string())
@@ -257,9 +258,11 @@
         }
 
         let original_tn = QualifiedName::from_type_path(&typ);
-        original_tn.validate_ok_for_cxx()?;
+        original_tn
+            .validate_ok_for_cxx()
+            .map_err(ConvertErrorFromCpp::InvalidIdent)?;
         if self.config.is_on_blocklist(&original_tn.to_cpp_name()) {
-            return Err(ConvertError::Blocked(original_tn));
+            return Err(ConvertErrorFromCpp::Blocked(original_tn));
         }
         let mut deps = HashSet::new();
 
@@ -331,7 +334,9 @@
                     )?;
                     deps.extend(innerty.types_encountered.drain(..));
                 } else {
-                    return Err(ConvertError::TemplatedTypeContainingNonPathArg(tn.clone()));
+                    return Err(ConvertErrorFromCpp::TemplatedTypeContainingNonPathArg(
+                        tn.clone(),
+                    ));
                 }
             } else {
                 // Oh poop. It's a generic type which cxx won't be able to handle.
@@ -347,7 +352,9 @@
                                     if typ.path.segments.len() == 1
                                         && !ctx.allowed_generic_type(&seg.ident)
                                     {
-                                        return Err(ConvertError::ReferringToGenericTypeParam);
+                                        return Err(
+                                            ConvertErrorFromCpp::ReferringToGenericTypeParam,
+                                        );
                                     }
                                 }
                             }
@@ -361,7 +368,7 @@
                 // this a bit.
                 let qn = QualifiedName::from_type_path(&typ); // ignores generic params
                 if self.ignored_types.contains(&qn) {
-                    return Err(ConvertError::ConcreteVersionOfIgnoredTemplate);
+                    return Err(ConvertErrorFromCpp::ConcreteVersionOfIgnoredTemplate);
                 }
                 let (new_tn, api) = self.get_templated_typename(&Type::Path(typ))?;
                 extra_apis.extend(api.into_iter());
@@ -385,7 +392,7 @@
         pun: Punctuated<GenericArgument, P>,
         ns: &Namespace,
         ctx: &TypeConversionContext,
-    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
+    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertErrorFromCpp>
     where
         P: Default,
     {
@@ -411,7 +418,10 @@
         ))
     }
 
-    fn resolve_typedef<'b>(&'b self, tn: &QualifiedName) -> Result<Option<&'b Type>, ConvertError> {
+    fn resolve_typedef<'b>(
+        &'b self,
+        tn: &QualifiedName,
+    ) -> Result<Option<&'b Type>, ConvertErrorFromCpp> {
         let mut encountered = HashSet::new();
         let mut tn = tn.clone();
         let mut previous_typ = None;
@@ -422,7 +432,7 @@
                     previous_typ = r;
                     let new_tn = QualifiedName::from_type_path(typ);
                     if encountered.contains(&new_tn) {
-                        return Err(ConvertError::InfinitelyRecursiveTypedef(tn.clone()));
+                        return Err(ConvertErrorFromCpp::InfinitelyRecursiveTypedef(tn.clone()));
                     }
                     if typ
                         .path
@@ -430,7 +440,7 @@
                         .iter()
                         .any(|seg| seg.ident.to_string().starts_with("_bindgen_mod"))
                     {
-                        return Err(ConvertError::TypedefToTypeInAnonymousNamespace);
+                        return Err(ConvertErrorFromCpp::TypedefToTypeInAnonymousNamespace);
                     }
                     encountered.insert(new_tn.clone());
                     tn = new_tn;
@@ -446,10 +456,10 @@
         mut ptr: TypePtr,
         ns: &Namespace,
         pointer_treatment: PointerTreatment,
-    ) -> Result<Annotated<Type>, ConvertError> {
+    ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
         match pointer_treatment {
             PointerTreatment::Pointer => {
-                crate::known_types::ensure_pointee_is_valid(&ptr)?;
+                Self::ensure_pointee_is_valid(&ptr)?;
                 let innerty =
                     self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?;
                 ptr.elem = innerty.ty;
@@ -470,7 +480,7 @@
                 // be a plain value. We should detect and abort.
                 let mut outer = elem.map(|elem| match mutability {
                     Some(_) => Type::Path(parse_quote! {
-                        ::std::pin::Pin < & #mutability #elem >
+                        ::core::pin::Pin < & #mutability #elem >
                     }),
                     None => Type::Reference(parse_quote! {
                         & #elem
@@ -484,7 +494,7 @@
                 Ok(outer)
             }
             PointerTreatment::RValueReference => {
-                crate::known_types::ensure_pointee_is_valid(&ptr)?;
+                Self::ensure_pointee_is_valid(&ptr)?;
                 let innerty =
                     self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?;
                 ptr.elem = innerty.ty;
@@ -498,15 +508,26 @@
         }
     }
 
+    fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertErrorFromCpp> {
+        match *ptr.elem {
+            Type::Path(..) => Ok(()),
+            Type::Array(..) => Err(ConvertErrorFromCpp::InvalidArrayPointee),
+            Type::Ptr(..) => Err(ConvertErrorFromCpp::InvalidPointerPointee),
+            _ => Err(ConvertErrorFromCpp::InvalidPointee(
+                ptr.elem.to_token_stream().to_string(),
+            )),
+        }
+    }
+
     fn get_templated_typename(
         &mut self,
         rs_definition: &Type,
-    ) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertError> {
+    ) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertErrorFromCpp> {
         let count = self.concrete_templates.len();
         // We just use this as a hash key, essentially.
         // TODO: Once we've completed the TypeConverter refactoring (see #220),
         // pass in an actual original_name_map here.
-        let cpp_definition = type_to_cpp(rs_definition, &HashMap::new())?;
+        let cpp_definition = self.original_name_map.type_to_cpp(rs_definition)?;
         let e = self.concrete_templates.get(&cpp_definition);
         match e {
             Some(tn) => Ok((tn.clone(), None)),
@@ -530,12 +551,12 @@
                     .find(|s| s == &synthetic_ident)
                 {
                     None => synthetic_ident,
-                    Some(_) => format!("AutocxxConcrete{}", count),
+                    Some(_) => format!("AutocxxConcrete{count}"),
                 };
                 let api = UnanalyzedApi::ConcreteType {
-                    name: ApiName::new_in_root_namespace(make_ident(&synthetic_ident)),
+                    name: ApiName::new_in_root_namespace(make_ident(synthetic_ident)),
                     cpp_definition: cpp_definition.clone(),
-                    rs_definition: Some(Box::new(rs_definition.clone())),
+                    rs_definition: Some(Box::new(rs_definition.clone().into())),
                 };
                 self.concrete_templates
                     .insert(cpp_definition, api.name().clone());
@@ -550,21 +571,25 @@
         desc: &QualifiedName,
         generic_behavior: CxxGenericType,
         forward_declarations_ok: bool,
-    ) -> Result<TypeKind, ConvertError> {
+    ) -> Result<TypeKind, ConvertErrorFromCpp> {
         for inner in path_args {
             match inner {
                 GenericArgument::Type(Type::Path(typ)) => {
                     let inner_qn = QualifiedName::from_type_path(typ);
                     if !forward_declarations_ok && self.forward_declarations.contains(&inner_qn) {
-                        return Err(ConvertError::TypeContainingForwardDeclaration(inner_qn));
+                        return Err(ConvertErrorFromCpp::TypeContainingForwardDeclaration(
+                            inner_qn,
+                        ));
                     }
                     match generic_behavior {
                         CxxGenericType::Rust => {
                             if !inner_qn.get_namespace().is_empty() {
-                                return Err(ConvertError::RustTypeWithAPath(inner_qn));
+                                return Err(ConvertErrorFromCpp::RustTypeWithAPath(inner_qn));
                             }
                             if !self.config.is_rust_type(&inner_qn.get_final_ident()) {
-                                return Err(ConvertError::BoxContainingNonRustType(inner_qn));
+                                return Err(ConvertErrorFromCpp::BoxContainingNonRustType(
+                                    inner_qn,
+                                ));
                             }
                             if self
                                 .config
@@ -577,12 +602,12 @@
                         }
                         CxxGenericType::CppPtr => {
                             if !known_types().permissible_within_unique_ptr(&inner_qn) {
-                                return Err(ConvertError::InvalidTypeForCppPtr(inner_qn));
+                                return Err(ConvertErrorFromCpp::InvalidTypeForCppPtr(inner_qn));
                             }
                         }
                         CxxGenericType::CppVector => {
                             if !known_types().permissible_within_vector(&inner_qn) {
-                                return Err(ConvertError::InvalidTypeForCppVector(inner_qn));
+                                return Err(ConvertErrorFromCpp::InvalidTypeForCppVector(inner_qn));
                             }
                             if matches!(
                                 typ.path.segments.last().map(|ps| &ps.arguments),
@@ -591,14 +616,14 @@
                                         | PathArguments::AngleBracketed(_)
                                 )
                             ) {
-                                return Err(ConvertError::GenericsWithinVector);
+                                return Err(ConvertErrorFromCpp::GenericsWithinVector);
                             }
                         }
                         _ => {}
                     }
                 }
                 _ => {
-                    return Err(ConvertError::TemplatedTypeContainingNonPathArg(
+                    return Err(ConvertErrorFromCpp::TemplatedTypeContainingNonPathArg(
                         desc.clone(),
                     ))
                 }