Squashed 'third_party/autocxx/' changes from 629e8fa53..c35090b75

c35090b75 Merge pull request #1131 from google/rev-0.22.3
94f20d716 Revise to 0.22.3.
b4776fdd5 Merge pull request #1070 from google/reference-wrapper
25f08f567 Better encapsulate TypeConversionPolicy. No functional changes.
b389afdeb Add reference wrapper safety policy.
cd169853b Merge pull request #1126 from google/issue-1125
92f48fa30 Merge pull request #1123 from google/segfault-detection
ca60bacca Add comment
724a4971d Add test for issue 1125.
d8a9a8ca7 Detect segfaults in reduction
e147efc7c Merge pull request #1122 from google/rev-0.22.2
dfa9b99a4 Revise to 0.22.2.
4cb1da848 Merge pull request #1120 from chbaker0/main
79afb97d9 Replace lingering tempdir usages with tempfile
f945331a3 Merge pull request #1118 from google/fix-test-fixed-num
5a8b28751 Merge pull request #1117 from google/docs-tweaks
b5486faa1 Merge pull request #1109 from bsilver8192/bindgen-skip-rustfmt
f62c17273 Fix faulty test_fixed_num test.
ad954fa72 Minor doc updates.
eaa1f8737 Leave bindgen enabled when logging is
bdff5db56 Merge pull request #1110 from bsilver8192/subclass-std
922f98be4 Merge pull request #1111 from bsilver8192/subclass-unsafe
bfbcc6b94 Merge pull request #1114 from google/rev-0.22.1
4f68a2e59 Merge pull request #1112 from bsilver8192/subclass-upcast-uniqueptr
993c5705b Revise to 0.22.1.
7bf667bbf Add a function to upcast UniquePtr
733d751a2 Fix clippy and docs
8023cee43 Fix and test subclass without `safety!(unsafe)`
4ae4d47e4 Fix and test subclasses with C++ std in scope
c50b1ee7e Tell bindgen to skip rustfmt
f9b24b90e Merge pull request #1107 from google/reject-anon-namespace-typedefs
070c9755d Merge pull request #1093 from google/always-output-rs
8eb71c5e7 Merge pull request #1095 from google/issue-1094
c86f1ce7e Reject forward declared nested types.
c118dba64 Merge branch 'main' into reject-anon-namespace-typedefs
243079997 Merge pull request #1108 from google/reject-type-param-typedefs
f803c3ba5 Reject type params - fixes #1098
f3381ba52 Reject typedefs to anon namespaces.
669d932a7 Merge pull request #1106 from google/lotsa-failing-tests
f0e8487fe Marking tests as ignored.
524c2bbfc Add tests for multiple issues.
67e16ac2a Merge branch 'main' of github.com:google/autocxx into always-output-rs
5f62daf3f Merge pull request #1104 from google/roll-cxx
43ee55ca2 Further upgrade from 1.0.67 to 1.0.68
e29e3c899 Merge pull request #1100 from bsilver8192/extern_cpp_type-namespace
d2c8edef4 Merge pull request #1101 from google/fix-1081
094dbd957 Roll cxx minimal version.
94c39f35b Merge pull request #1102 from google/fix-book-build
8764f1218 Alter mdbook-mermaid installation.
85543656f Test for issue 1081
b170df056 Fix and test extern_cpp_type with type in a C++ namespace
e4b56dd49 Fix gen tests.
5457e615d Fix #1092.

git-subtree-dir: third_party/autocxx
git-subtree-split: c35090b754619531b4eebdf4d8b583db72349943
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: Ia34285bc1c30f7e3c71fa9e7b677a58902648843
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3eb54d1..367b729 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -139,6 +139,9 @@
       - name: Build non-trivial-type-on-stack example
         working-directory: ./examples/non-trivial-type-on-stack
         run: cargo build
+      - name: Build reference-wrappers example
+        working-directory: ./examples/reference-wrappers
+        run: cargo build
         # We do not build the LLVM example because even 'apt-get install llvm-13-dev'
         # does not work to install the LLVM 13 headers.
 
diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml
index 0dd4237..e1951c8 100644
--- a/.github/workflows/site.yml
+++ b/.github/workflows/site.yml
@@ -27,9 +27,7 @@
         run: cargo install mdbook-linkcheck
     
       - name: Install mdbook-mermaid
-        run: |
-          curl -LSfs https://japaric.github.io/trust/install.sh | \
-            sh -s -- --git badboy/mdbook-mermaid
+        run: cargo install mdbook-mermaid
     
       - run: mdbook build
         working-directory: book
diff --git a/Cargo.lock b/Cargo.lock
index a5bfc0a..4f58182 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -87,7 +87,7 @@
 
 [[package]]
 name = "autocxx"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "aquamarine",
  "autocxx-macro",
@@ -97,9 +97,9 @@
 
 [[package]]
 name = "autocxx-bindgen"
-version = "0.59.16"
+version = "0.59.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "435723e14bf88f198322f8555a4fdb108363021d97a47bb6492891ca86055e79"
+checksum = "f9a9a26dd38d385d23b1bf61bd231b77f690c4368aef4c77cee1b7a6da2e2042"
 dependencies = [
  "bitflags",
  "cexpr",
@@ -121,7 +121,7 @@
 
 [[package]]
 name = "autocxx-build"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "autocxx-engine",
  "env_logger 0.9.0",
@@ -131,7 +131,7 @@
 
 [[package]]
 name = "autocxx-demo"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "autocxx",
  "autocxx-build",
@@ -141,7 +141,7 @@
 
 [[package]]
 name = "autocxx-engine"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "aquamarine",
  "autocxx-bindgen",
@@ -167,7 +167,7 @@
 
 [[package]]
 name = "autocxx-gen"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "assert_cmd",
  "autocxx",
@@ -181,12 +181,12 @@
  "miette",
  "pathdiff",
  "proc-macro2",
- "tempdir",
+ "tempfile",
 ]
 
 [[package]]
 name = "autocxx-integration-tests"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "autocxx",
  "autocxx-engine",
@@ -209,7 +209,7 @@
 
 [[package]]
 name = "autocxx-macro"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "autocxx-parser",
  "proc-macro-error",
@@ -220,7 +220,7 @@
 
 [[package]]
 name = "autocxx-mdbook-preprocessor"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "anyhow",
  "autocxx-integration-tests",
@@ -238,7 +238,7 @@
 
 [[package]]
 name = "autocxx-parser"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "indexmap",
  "itertools 0.10.3",
@@ -254,7 +254,7 @@
 
 [[package]]
 name = "autocxx-reduce"
-version = "0.22.0"
+version = "0.22.3"
 dependencies = [
  "assert_cmd",
  "autocxx-engine",
@@ -271,7 +271,6 @@
  "serde_derive",
  "serde_json",
  "syn",
- "tempdir",
  "tempfile",
 ]
 
@@ -491,9 +490,9 @@
 
 [[package]]
 name = "cxx"
-version = "1.0.66"
+version = "1.0.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce2295fe8865279f404147e9b2328e5af0ad11a2c016e58c13acfd48a07d8a55"
+checksum = "7e599641dff337570f6aa9c304ecca92341d30bf72e1c50287869ed6a36615a6"
 dependencies = [
  "cc",
  "cxxbridge-flags",
@@ -503,9 +502,9 @@
 
 [[package]]
 name = "cxx-gen"
-version = "0.7.66"
+version = "0.7.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bba249c8ea90cff9c647cd76928efc82f1e8684da0a6cedb4416cc79478050d"
+checksum = "1e2c726d93799c3129c65224ab09eae1a31276bc593d4f7344be1c592c16a1ec"
 dependencies = [
  "codespan-reporting",
  "proc-macro2",
@@ -515,15 +514,15 @@
 
 [[package]]
 name = "cxxbridge-flags"
-version = "1.0.66"
+version = "1.0.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a670224c6686471df12560a0b97a08145082e70bd38e2b0b5383b79e46c3da7"
+checksum = "3894ad0c6d517cb5a4ce8ec20b37cd0ea31b480fe582a104c5db67ae21270853"
 
 [[package]]
 name = "cxxbridge-macro"
-version = "1.0.66"
+version = "1.0.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b700096ca0dece28d9535fdb17ab784a8ae155d7f29d39c273643e6292c9620"
+checksum = "34fa7e395dc1c001083c7eed28c8f0f0b5a225610f3b6284675f444af6fab86b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -610,12 +609,6 @@
 ]
 
 [[package]]
-name = "fuchsia-cprng"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
-
-[[package]]
 name = "gag"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1135,34 +1128,6 @@
 ]
 
 [[package]]
-name = "rand"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
-dependencies = [
- "fuchsia-cprng",
- "libc",
- "rand_core 0.3.1",
- "rdrand",
- "winapi",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
-dependencies = [
- "rand_core 0.4.2",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
-
-[[package]]
 name = "rayon"
 version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1187,15 +1152,6 @@
 ]
 
 [[package]]
-name = "rdrand"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
-dependencies = [
- "rand_core 0.3.1",
-]
-
-[[package]]
 name = "redox_syscall"
 version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1392,16 +1348,6 @@
 ]
 
 [[package]]
-name = "tempdir"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
-dependencies = [
- "rand",
- "remove_dir_all",
-]
-
-[[package]]
 name = "tempfile"
 version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 5c24af8..4a4a64a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
@@ -25,14 +25,14 @@
 resolver = "2"
 
 [dependencies]
-autocxx-macro = { path="macro", version="0.22.0" }
-cxx = "1.0.54" # ... also needed because expansion of type_id refers to ::cxx
+autocxx-macro = { path="macro", version="0.22.3" }
+cxx = "1.0.68" # ... also needed because expansion of type_id refers to ::cxx
 aquamarine = "0.1" # docs
 moveit = { version = "0.5", features = [ "cxx" ] }
 
 [workspace]
 members = ["parser", "engine", "gen/cmd", "gen/build", "macro", "demo", "tools/reduce", "tools/mdbook-preprocessor", "integration-tests"]
-exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack", "examples/llvm", "tools/stress-test"]
+exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack", "examples/llvm", "examples/reference-wrappers", "tools/stress-test"]
 
 #[patch.crates-io]
 #cxx = { path="../cxx" }
diff --git a/book/src/contributing.md b/book/src/contributing.md
index 1f00bc9..e95ca5a 100644
--- a/book/src/contributing.md
+++ b/book/src/contributing.md
@@ -80,7 +80,7 @@
 order of preference here's how we would like to hear about your problem:
 
 * Raise a pull request adding a new failing integration test to
-  `engine/src/integration_tests.rs`.
+  [`integration_test.rs`](https://github.com/google/autocxx/blob/main/integration-tests/tests/integration_test.rs)
 * Minimize the test using `tools/reduce`, something like this:
   `target/debug/autocxx-reduce file -d "safety!(unsafe_ffi)" -d
   'generate_pod!("A")' -I ~/my-include-dir -h my-header.h -p
diff --git a/book/src/references_etc.md b/book/src/references_etc.md
index a4b6d09..95456dc 100644
--- a/book/src/references_etc.md
+++ b/book/src/references_etc.md
@@ -39,12 +39,13 @@
 Exactly the same issues apply to C++ references _in theory_, but in practice,
 they usually don't. Therefore [`cxx`](https://cxx.rs) has taken the view that we can "trust"
 a C++ reference to a higher degree than a pointer, and autocxx follows that
-lead. In practice, of course, references are rarely return values from C++
+lead (in fact we 'trust' references even slightly more than cxx).
+In practice, of course, references are rarely return values from C++
 APIs so we rarely have to navel-gaze about the trustworthiness of a
 reference.
 
 (See also the discussion of [`safety`](safety.md) - if you haven't specified
-an unsafety policy, _all_ C++ APIs require `unsafe` so the discussion is moot.)
+an unsafety policy, _all_ C++ APIs require `unsafe` so the discussion is moot.
 
 If you're given a C++ object by pointer, and you want to interact with it,
 you'll need to figure out the guarantees attached to the C++ object - most
diff --git a/book/src/safety.md b/book/src/safety.md
index 34724d2..e4c858a 100644
--- a/book/src/safety.md
+++ b/book/src/safety.md
@@ -59,7 +59,6 @@
 )
 ```
 
-
 ## Pragmatism in a complex C++ codebase
 
 This crate mostly intends to follow the lead of the `cxx` crate in where and when `unsafe` is required. But, this crate is opinionated. It believes some unsafety requires more careful review than other bits, along the following spectrum:
@@ -89,3 +88,12 @@
 * It doesn't delete it.
 * or any of the other things that you're not permitted to do in unsafe Rust.
 
+## Soundness
+
+This crate shares the general approach to safety and soundness pioneered by cxx, but has two important differences:
+
+* cxx requires you to specify your interface in detail, and thus think through all aspects of the language boundary. autocxx doesn't, and may autogenerate footguns.
+* cxx may allow multiple conflicting Rust references to exist to 'trivial' data types ("plain old data" or POD in autocxx parlance), but they're rare. autocxx may allow conflicting Rust references to exist even to 'opaque' (non-POD) data, and they're more common. This difference exists because opaque data is zero-sized in cxx, and zero-sized references cannot conflict. (In autocxx, we tell Rust about the size in order that we can allocate such types on the stack.)
+
+There are preliminary explorations to avoid this problem by using a C++ reference wrapper type. See `examples/reference-wrappers`.
+
diff --git a/book/src/tutorial.md b/book/src/tutorial.md
index 3bedb26..3e8e5b2 100644
--- a/book/src/tutorial.md
+++ b/book/src/tutorial.md
@@ -15,15 +15,17 @@
 
 The rest of this 'getting started' section assumes Cargo - if you're using something else, see the [building](building.md) section.
 
-First, add `autocxx` *and `cxx`* to your `dependencies` and `autocxx-build` to your `build-dependencies` in your `Cargo.toml`.
+First, add `autocxx` *and `cxx`* to your `dependencies` and `autocxx-build` to your `build-dependencies` in your `Cargo.toml`. **You must specify both.**
+
+
 
 ```toml
 [dependencies]
-autocxx = "0.22.0"
+autocxx = "0.22.3"
 cxx = "1.0"
 
 [build-dependencies]
-autocxx-build = "0.22.0"
+autocxx-build = "0.22.3"
 miette = { version="4.3", features=["fancy"] } # optional but gives nicer error messages!
 ```
 
diff --git a/demo/Cargo.toml b/demo/Cargo.toml
index c6a7227..e81cb80 100644
--- a/demo/Cargo.toml
+++ b/demo/Cargo.toml
@@ -8,14 +8,14 @@
 
 [package]
 name = "autocxx-demo"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.54"
-autocxx = { path = "..", version="0.22.0" }
+cxx = "1.0.68"
+autocxx = { path = "..", version="0.22.3" }
 
 [build-dependencies]
-autocxx-build = { path = "../gen/build", version="0.22.0" }
+autocxx-build = { path = "../gen/build", version="0.22.3" }
 miette = { version="4.3", features=["fancy"]}
diff --git a/engine/Cargo.toml b/engine/Cargo.toml
index 964108e..1400847 100644
--- a/engine/Cargo.toml
+++ b/engine/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-engine"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
@@ -30,15 +30,15 @@
 proc-macro2 = "1.0.11"
 quote = "1.0"
 indoc = "1.0"
-autocxx-bindgen = "=0.59.16"
+autocxx-bindgen = "=0.59.17"
 #autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "pollute-fewer-typedefs" }
 itertools = "0.10.3"
 cc = { version = "1.0", optional = true }
 # Note: Keep the patch-level version of cxx-gen and cxx in sync.
 # There can be interdependencies between the code generated by cxx-gen and
 # what cxx expects to be there.
-cxx-gen = "0.7.54"
-autocxx-parser = { version = "=0.22.0", path="../parser" }
+cxx-gen = "0.7.68"
+autocxx-parser = { version = "=0.22.3", path="../parser" }
 version_check = "0.9"
 aquamarine = "0.1" # docs
 tempfile = "3.1"
diff --git a/engine/src/conversion/analysis/fun/function_wrapper.rs b/engine/src/conversion/analysis/fun/function_wrapper.rs
index f2f6aaa..ab3b7d9 100644
--- a/engine/src/conversion/analysis/fun/function_wrapper.rs
+++ b/engine/src/conversion/analysis/fun/function_wrapper.rs
@@ -10,7 +10,8 @@
     conversion::api::SubclassName,
     types::{Namespace, QualifiedName},
 };
-use syn::{parse_quote, Ident, Type};
+use quote::ToTokens;
+use syn::{parse_quote, Ident, Type, TypeReference};
 
 #[derive(Clone, Debug)]
 pub(crate) enum CppConversionType {
@@ -23,6 +24,8 @@
     /// Ignored in the sense that it isn't passed into the C++ function.
     IgnoredPlacementPtrParameter,
     FromReturnValueToPlacementPtr,
+    FromPointerToReference, // unwrapped_type is always Type::Ptr
+    FromReferenceToPointer, // unwrapped_type is always Type::Ptr
 }
 
 impl CppConversionType {
@@ -36,6 +39,8 @@
                 CppConversionType::FromValueToUniquePtr
             }
             CppConversionType::FromValueToUniquePtr => CppConversionType::FromUniquePtrToValue,
+            CppConversionType::FromPointerToReference => CppConversionType::FromReferenceToPointer,
+            CppConversionType::FromReferenceToPointer => CppConversionType::FromPointerToReference,
             _ => panic!("Did not expect to have to invert this conversion"),
         }
     }
@@ -52,6 +57,8 @@
     FromValueParamToPtr,
     FromPlacementParamToNewReturn,
     FromRValueParamToPtr,
+    FromReferenceWrapperToPointer, // unwrapped_type is always Type::Ptr
+    FromPointerToReferenceWrapper, // unwrapped_type is always Type::Ptr
 }
 
 impl RustConversionType {
@@ -74,19 +81,53 @@
 /// * Finally, the actual C++ API receives a `std::string` by value.
 /// The implementation here is distributed across this file, and
 /// `function_wrapper_rs` and `function_wrapper_cpp`.
+/// TODO: we should make this into a single enum, with the Type as enum
+/// 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)]
 pub(crate) struct TypeConversionPolicy {
-    pub(crate) unwrapped_type: Type,
+    unwrapped_type: Type,
     pub(crate) cpp_conversion: CppConversionType,
     pub(crate) rust_conversion: RustConversionType,
 }
 
 impl TypeConversionPolicy {
     pub(crate) fn new_unconverted(ty: Type) -> Self {
-        TypeConversionPolicy {
+        Self::new(ty, CppConversionType::None, RustConversionType::None)
+    }
+
+    pub(crate) fn new(
+        ty: Type,
+        cpp_conversion: CppConversionType,
+        rust_conversion: RustConversionType,
+    ) -> Self {
+        Self {
             unwrapped_type: ty,
-            cpp_conversion: CppConversionType::None,
-            rust_conversion: RustConversionType::None,
+            cpp_conversion,
+            rust_conversion,
+        }
+    }
+
+    pub(crate) fn cxxbridge_type(&self) -> &Type {
+        &self.unwrapped_type
+    }
+
+    pub(crate) fn return_reference_into_wrapper(ty: Type) -> Self {
+        let (unwrapped_type, is_mut) = match ty {
+            Type::Reference(TypeReference {
+                elem, mutability, ..
+            }) => (*elem, mutability.is_some()),
+            _ => panic!("Not a ptr: {}", ty.to_token_stream()),
+        };
+        TypeConversionPolicy {
+            unwrapped_type: if is_mut {
+                parse_quote! { *mut #unwrapped_type }
+            } else {
+                parse_quote! { *const #unwrapped_type }
+            },
+            cpp_conversion: CppConversionType::FromReferenceToPointer,
+            rust_conversion: RustConversionType::FromPointerToReferenceWrapper,
         }
     }
 
@@ -161,6 +202,8 @@
             RustConversionType::FromValueParamToPtr
                 | RustConversionType::FromRValueParamToPtr
                 | RustConversionType::FromPlacementParamToNewReturn
+                | RustConversionType::FromPointerToReferenceWrapper { .. }
+                | RustConversionType::FromReferenceWrapperToPointer { .. }
         )
     }
 
diff --git a/engine/src/conversion/analysis/fun/mod.rs b/engine/src/conversion/analysis/fun/mod.rs
index 19340f9..7194746 100644
--- a/engine/src/conversion/analysis/fun/mod.rs
+++ b/engine/src/conversion/analysis/fun/mod.rs
@@ -41,7 +41,7 @@
 use quote::quote;
 use syn::{
     parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, ReturnType, Type,
-    TypePtr, Visibility,
+    TypePath, TypePtr, TypeReference, Visibility,
 };
 
 use crate::{
@@ -183,7 +183,7 @@
     pub(crate) is_placement_return_destination: bool,
 }
 
-struct ReturnTypeAnalysis {
+pub(crate) struct ReturnTypeAnalysis {
     rt: ReturnType,
     conversion: Option<TypeConversionPolicy>,
     was_reference: bool,
@@ -270,7 +270,7 @@
 }
 
 pub(crate) struct FnAnalyzer<'a> {
-    unsafe_policy: UnsafePolicy,
+    unsafe_policy: &'a UnsafePolicy,
     extra_apis: ApiVec<NullPhase>,
     type_converter: TypeConverter<'a>,
     bridge_name_tracker: BridgeNameTracker,
@@ -288,7 +288,7 @@
 impl<'a> FnAnalyzer<'a> {
     pub(crate) fn analyze_functions(
         apis: ApiVec<PodPhase>,
-        unsafe_policy: UnsafePolicy,
+        unsafe_policy: &'a UnsafePolicy,
         config: &'a IncludeCppConfig,
     ) -> ApiVec<FnPrePhase2> {
         let mut me = Self {
@@ -476,7 +476,9 @@
                 UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
                 _ => unsafest_param,
             },
-            _ if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe => UnsafetyNeeded::Always,
+            _ if matches!(self.unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) => {
+                UnsafetyNeeded::Always
+            }
             _ => match unsafest_non_placement_param {
                 UnsafetyNeeded::Always => UnsafetyNeeded::Always,
                 UnsafetyNeeded::JustBridge => match unsafest_param {
@@ -638,6 +640,7 @@
                     receiver_mutability,
                     sup,
                     subclass_fn_deps,
+                    self.unsafe_policy,
                 ));
 
                 // Create the trait item for the <superclass>_methods and <superclass>_supers
@@ -655,6 +658,7 @@
                         receiver_mutability,
                         sup.clone(),
                         is_pure_virtual,
+                        self.unsafe_policy,
                     ));
                 }
             }
@@ -836,7 +840,7 @@
                 let arg_is_reference = matches!(
                     param_details
                         .get(1)
-                        .map(|param| &param.conversion.unwrapped_type),
+                        .map(|param| param.conversion.cxxbridge_type()),
                     Some(Type::Reference(_))
                 );
                 // Some exotic forms of copy constructor have const and/or volatile qualifiers.
@@ -1209,6 +1213,11 @@
         let ret_type_conversion_needed = ret_type_conversion
             .as_ref()
             .map_or(false, |x| x.cpp_work_needed());
+        let return_needs_rust_conversion = ret_type_conversion
+            .as_ref()
+            .map(|ra| ra.rust_work_needed())
+            .unwrap_or_default();
+
         // See https://github.com/dtolnay/cxx/issues/878 for the reason for this next line.
         let effective_cpp_name = cpp_name.as_ref().unwrap_or(&rust_name);
         let cpp_name_incompatible_with_cxx =
@@ -1346,9 +1355,10 @@
             .any(|pd| pd.conversion.rust_work_needed());
 
         let rust_wrapper_needed = match kind {
+            _ if any_param_needs_rust_conversion || return_needs_rust_conversion => true,
             FnKind::TraitMethod { .. } => true,
-            FnKind::Method { .. } => any_param_needs_rust_conversion || cxxbridge_name != rust_name,
-            _ => any_param_needs_rust_conversion,
+            FnKind::Method { .. } => cxxbridge_name != rust_name,
+            _ => false,
         };
 
         // Naming, part two.
@@ -1642,6 +1652,7 @@
                     }
                     _ => old_pat,
                 };
+
                 let is_placement_return_destination = is_placement_return_destination
                     || matches!(
                         force_rust_conversion,
@@ -1653,6 +1664,8 @@
                     is_move_constructor,
                     force_rust_conversion,
                     sophistication,
+                    self_type.is_some(),
+                    is_placement_return_destination,
                 );
                 let new_ty = annotated_type.ty;
                 pt.pat = Box::new(new_pat.clone());
@@ -1694,6 +1707,8 @@
         is_move_constructor: bool,
         force_rust_conversion: Option<RustConversionType>,
         sophistication: TypeConversionSophistication,
+        is_self: bool,
+        is_placement_return_destination: bool,
     ) -> TypeConversionPolicy {
         let is_subclass_holder = match &annotated_type.kind {
             type_converter::TypeKind::SubclassHolder(holder) => Some(holder),
@@ -1703,6 +1718,9 @@
             annotated_type.kind,
             type_converter::TypeKind::RValueReference
         );
+        let is_reference =
+            matches!(annotated_type.kind, type_converter::TypeKind::Reference) || is_self;
+        let rust_conversion_forced = force_rust_conversion.is_some();
         let ty = &*annotated_type.ty;
         if let Some(holder_id) = is_subclass_holder {
             let subclass = SubclassName::from_holder_name(holder_id);
@@ -1710,91 +1728,127 @@
                 let ty = parse_quote! {
                     rust::Box<#holder_id>
                 };
-                TypeConversionPolicy {
-                    unwrapped_type: ty,
-                    cpp_conversion: CppConversionType::Move,
-                    rust_conversion: RustConversionType::ToBoxedUpHolder(subclass),
-                }
+                TypeConversionPolicy::new(
+                    ty,
+                    CppConversionType::Move,
+                    RustConversionType::ToBoxedUpHolder(subclass),
+                )
             };
         } else if matches!(
             force_rust_conversion,
             Some(RustConversionType::FromPlacementParamToNewReturn)
         ) && matches!(sophistication, TypeConversionSophistication::Regular)
         {
-            return TypeConversionPolicy {
-                unwrapped_type: ty.clone(),
-                cpp_conversion: CppConversionType::IgnoredPlacementPtrParameter,
-                rust_conversion: RustConversionType::FromPlacementParamToNewReturn,
-            };
+            return TypeConversionPolicy::new(
+                ty.clone(),
+                CppConversionType::IgnoredPlacementPtrParameter,
+                RustConversionType::FromPlacementParamToNewReturn,
+            );
         }
         match ty {
             Type::Path(p) => {
                 let ty = ty.clone();
                 let tn = QualifiedName::from_type_path(p);
-                if self.pod_safe_types.contains(&tn) {
+                if matches!(
+                    self.config.unsafe_policy,
+                    UnsafePolicy::ReferencesWrappedAllFunctionsSafe
+                ) && is_reference
+                    && !rust_conversion_forced
+                // must be std::pin::Pin<&mut T>
+                {
+                    let unwrapped_type = extract_type_from_pinned_mut_ref(p);
+                    TypeConversionPolicy::new(
+                        parse_quote! { *mut #unwrapped_type },
+                        CppConversionType::FromPointerToReference,
+                        RustConversionType::FromReferenceWrapperToPointer,
+                    )
+                } else if self.pod_safe_types.contains(&tn) {
                     if known_types().lacks_copy_constructor(&tn) {
-                        TypeConversionPolicy {
-                            unwrapped_type: ty,
-                            cpp_conversion: CppConversionType::Move,
-                            rust_conversion: RustConversionType::None,
-                        }
+                        TypeConversionPolicy::new(
+                            ty,
+                            CppConversionType::Move,
+                            RustConversionType::None,
+                        )
                     } else {
                         TypeConversionPolicy::new_unconverted(ty)
                     }
                 } else if known_types().convertible_from_strs(&tn)
                     && !self.config.exclude_utilities()
                 {
-                    TypeConversionPolicy {
-                        unwrapped_type: ty,
-                        cpp_conversion: CppConversionType::FromUniquePtrToValue,
-                        rust_conversion: RustConversionType::FromStr,
-                    }
+                    TypeConversionPolicy::new(
+                        ty,
+                        CppConversionType::FromUniquePtrToValue,
+                        RustConversionType::FromStr,
+                    )
                 } else if matches!(
                     sophistication,
                     TypeConversionSophistication::SimpleForSubclasses
                 ) {
-                    TypeConversionPolicy {
-                        unwrapped_type: ty,
-                        cpp_conversion: CppConversionType::FromUniquePtrToValue,
-                        rust_conversion: RustConversionType::None,
-                    }
+                    TypeConversionPolicy::new(
+                        ty,
+                        CppConversionType::FromUniquePtrToValue,
+                        RustConversionType::None,
+                    )
                 } else {
-                    TypeConversionPolicy {
-                        unwrapped_type: ty,
-                        cpp_conversion: CppConversionType::FromPtrToValue,
-                        rust_conversion: RustConversionType::FromValueParamToPtr,
-                    }
+                    TypeConversionPolicy::new(
+                        ty,
+                        CppConversionType::FromPtrToValue,
+                        RustConversionType::FromValueParamToPtr,
+                    )
                 }
             }
             Type::Ptr(tp) => {
                 let rust_conversion = force_rust_conversion.unwrap_or(RustConversionType::None);
                 if is_move_constructor {
-                    TypeConversionPolicy {
-                        unwrapped_type: ty.clone(),
-                        cpp_conversion: CppConversionType::FromPtrToMove,
+                    TypeConversionPolicy::new(
+                        ty.clone(),
+                        CppConversionType::FromPtrToMove,
                         rust_conversion,
-                    }
+                    )
                 } else if is_rvalue_ref {
-                    TypeConversionPolicy {
-                        unwrapped_type: *tp.elem.clone(),
-                        cpp_conversion: CppConversionType::FromPtrToValue,
-                        rust_conversion: RustConversionType::FromRValueParamToPtr,
-                    }
+                    TypeConversionPolicy::new(
+                        *tp.elem.clone(),
+                        CppConversionType::FromPtrToValue,
+                        RustConversionType::FromRValueParamToPtr,
+                    )
+                } else if matches!(
+                    self.config.unsafe_policy,
+                    UnsafePolicy::ReferencesWrappedAllFunctionsSafe
+                ) && is_reference
+                    && !rust_conversion_forced
+                    && !is_placement_return_destination
+                {
+                    TypeConversionPolicy::new(
+                        ty.clone(),
+                        CppConversionType::FromPointerToReference,
+                        RustConversionType::FromReferenceWrapperToPointer,
+                    )
                 } else {
-                    TypeConversionPolicy {
-                        unwrapped_type: ty.clone(),
-                        cpp_conversion: CppConversionType::None,
-                        rust_conversion,
-                    }
+                    TypeConversionPolicy::new(ty.clone(), CppConversionType::None, rust_conversion)
                 }
             }
+            Type::Reference(TypeReference {
+                elem, mutability, ..
+            }) if matches!(
+                self.config.unsafe_policy,
+                UnsafePolicy::ReferencesWrappedAllFunctionsSafe
+            ) && !rust_conversion_forced
+                && !is_placement_return_destination =>
+            {
+                let is_mut = mutability.is_some();
+                TypeConversionPolicy::new(
+                    if is_mut {
+                        panic!("Never expected to find &mut T at this point, we should be Pin<&mut T> by now")
+                    } else {
+                        parse_quote! { *const #elem }
+                    },
+                    CppConversionType::FromPointerToReference,
+                    RustConversionType::FromReferenceWrapperToPointer,
+                )
+            }
             _ => {
                 let rust_conversion = force_rust_conversion.unwrap_or(RustConversionType::None);
-                TypeConversionPolicy {
-                    unwrapped_type: ty.clone(),
-                    cpp_conversion: CppConversionType::None,
-                    rust_conversion,
-                }
+                TypeConversionPolicy::new(ty.clone(), CppConversionType::None, rust_conversion)
             }
         }
     }
@@ -1870,8 +1924,19 @@
                         }
                     }
                     _ => {
-                        let was_reference = matches!(boxed_type.as_ref(), Type::Reference(_));
-                        let conversion = Some(TypeConversionPolicy::new_unconverted(ty.clone()));
+                        let was_reference = references.ref_return;
+                        let conversion = Some(
+                            if was_reference
+                                && matches!(
+                                    self.config.unsafe_policy,
+                                    UnsafePolicy::ReferencesWrappedAllFunctionsSafe
+                                )
+                            {
+                                TypeConversionPolicy::return_reference_into_wrapper(ty.clone())
+                            } else {
+                                TypeConversionPolicy::new_unconverted(ty.clone())
+                            },
+                        );
                         ReturnTypeAnalysis {
                             rt: ReturnType::Type(*rarrow, boxed_type),
                             conversion,
@@ -2138,3 +2203,24 @@
         }
     }
 }
+
+fn extract_type_from_pinned_mut_ref(ty: &TypePath) -> Type {
+    match ty
+        .path
+        .segments
+        .last()
+        .expect("was not std::pin::Pin")
+        .arguments
+    {
+        syn::PathArguments::AngleBracketed(ref ab) => {
+            match ab.args.first().expect("did not have angle bracketed args") {
+                syn::GenericArgument::Type(ref ty) => match ty {
+                    Type::Reference(ref tyr) => tyr.elem.as_ref().clone(),
+                    _ => panic!("pin did not contain a reference"),
+                },
+                _ => panic!("argument was not a type"),
+            }
+        }
+        _ => panic!("did not find angle bracketed args"),
+    }
+}
diff --git a/engine/src/conversion/analysis/fun/subclass.rs b/engine/src/conversion/analysis/fun/subclass.rs
index c017249..6383d2c 100644
--- a/engine/src/conversion/analysis/fun/subclass.rs
+++ b/engine/src/conversion/analysis/fun/subclass.rs
@@ -10,7 +10,7 @@
 
 use syn::{parse_quote, FnArg, PatType, Type, TypePtr};
 
-use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability};
+use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability, UnsafePolicy};
 use crate::conversion::analysis::pod::PodPhase;
 use crate::conversion::api::{
     CppVisibility, FuncToConvert, Provenance, RustSubclassFnDetails, SubclassConstructorDetails,
@@ -79,12 +79,18 @@
     receiver_mutability: &ReceiverMutability,
     receiver: QualifiedName,
     is_pure_virtual: bool,
+    unsafe_policy: &UnsafePolicy,
 ) -> Api<FnPrePhase1> {
     let param_names = analysis
         .param_details
         .iter()
         .map(|pd| pd.name.clone())
         .collect();
+    let requires_unsafe = if matches!(unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) {
+        UnsafetyNeeded::Always
+    } else {
+        UnsafetyNeeded::from_param_details(&analysis.param_details, false)
+    };
     Api::SubclassTraitItem {
         name,
         details: SuperclassMethod {
@@ -93,7 +99,7 @@
             ret_type: analysis.ret_type.clone(),
             param_names,
             receiver_mutability: receiver_mutability.clone(),
-            requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false),
+            requires_unsafe,
             is_pure_virtual,
             receiver,
         },
@@ -107,6 +113,7 @@
     receiver_mutability: &ReceiverMutability,
     superclass: &QualifiedName,
     dependencies: Vec<QualifiedName>,
+    unsafe_policy: &UnsafePolicy,
 ) -> Api<FnPrePhase1> {
     let cpp = sub.cpp();
     let holder_name = sub.holder();
@@ -131,6 +138,11 @@
         .skip(1)
         .map(|p| p.conversion.clone())
         .collect();
+    let requires_unsafe = if matches!(unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) {
+        UnsafetyNeeded::Always
+    } else {
+        UnsafetyNeeded::from_param_details(&analysis.param_details, false)
+    };
     Api::RustSubclassFn {
         name: ApiName::new_in_root_namespace(rust_call_name.clone()),
         subclass: sub.clone(),
@@ -151,7 +163,7 @@
             superclass: superclass.clone(),
             receiver_mutability: receiver_mutability.clone(),
             dependencies,
-            requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false),
+            requires_unsafe,
             is_pure_virtual: matches!(
                 analysis.kind,
                 FnKind::Method {
diff --git a/engine/src/conversion/analysis/pod/mod.rs b/engine/src/conversion/analysis/pod/mod.rs
index 6722c23..eeb5051 100644
--- a/engine/src/conversion/analysis/pod/mod.rs
+++ b/engine/src/conversion/analysis/pod/mod.rs
@@ -18,7 +18,7 @@
 use crate::{
     conversion::{
         analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
-        api::{AnalysisPhase, Api, ApiName, CppVisibility, NullPhase, StructDetails, TypeKind},
+        api::{AnalysisPhase, Api, ApiName, NullPhase, StructDetails, TypeKind},
         apivec::ApiVec,
         convert_error::{ConvertErrorWithContext, ErrorContext},
         error_reporter::convert_apis,
@@ -134,12 +134,6 @@
     config: &IncludeCppConfig,
 ) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
     let id = name.name.get_final_ident();
-    if details.vis != CppVisibility::Public {
-        return Err(ConvertErrorWithContext(
-            ConvertError::NonPublicNestedType,
-            Some(ErrorContext::new_for_item(id)),
-        ));
-    }
     let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
     metadata.check_for_fatal_attrs(&id)?;
     let bases = get_bases(&details.item);
@@ -208,9 +202,14 @@
     extra_apis: &mut ApiVec<NullPhase>,
 ) -> Vec<ConvertError> {
     let mut convert_errors = Vec::new();
+    let struct_type_params = s
+        .generics
+        .type_params()
+        .map(|tp| tp.ident.clone())
+        .collect();
+    let type_conversion_context = TypeConversionContext::WithinStructField { struct_type_params };
     for f in &s.fields {
-        let annotated =
-            type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::WithinReference);
+        let annotated = type_converter.convert_type(f.ty.clone(), ns, &type_conversion_context);
         match annotated {
             Ok(mut r) => {
                 extra_apis.append(&mut r.extra_apis);
diff --git a/engine/src/conversion/analysis/type_converter.rs b/engine/src/conversion/analysis/type_converter.rs
index afdda8a..7e2d2bd 100644
--- a/engine/src/conversion/analysis/type_converter.rs
+++ b/engine/src/conversion/analysis/type_converter.rs
@@ -91,6 +91,7 @@
 /// from [TypeConverter] _might_ be used in the [cxx::bridge].
 pub(crate) enum TypeConversionContext {
     WithinReference,
+    WithinStructField { struct_type_params: HashSet<Ident> },
     WithinContainer,
     OuterType { pointer_treatment: PointerTreatment },
 }
@@ -98,13 +99,25 @@
 impl TypeConversionContext {
     fn pointer_treatment(&self) -> PointerTreatment {
         match self {
-            Self::WithinReference | Self::WithinContainer => PointerTreatment::Pointer,
+            Self::WithinReference | Self::WithinContainer | Self::WithinStructField { .. } => {
+                PointerTreatment::Pointer
+            }
             Self::OuterType { pointer_treatment } => *pointer_treatment,
         }
     }
     fn allow_instantiation_of_forward_declaration(&self) -> bool {
         matches!(self, Self::WithinReference)
     }
+    fn allowed_generic_type(&self, ident: &Ident) -> bool {
+        match self {
+            Self::WithinStructField { struct_type_params }
+                if struct_type_params.contains(ident) =>
+            {
+                false
+            }
+            _ => true,
+        }
+    }
 }
 
 /// A type which can convert from a type encountered in `bindgen`
@@ -156,7 +169,7 @@
     ) -> Result<Annotated<Type>, ConvertError> {
         let result = match ty {
             Type::Path(p) => {
-                let newp = self.convert_type_path(p, ns)?;
+                let newp = self.convert_type_path(p, ns, ctx)?;
                 if let Type::Path(newpp) = &newp.ty {
                     let qn = QualifiedName::from_type_path(newpp);
                     if !ctx.allow_instantiation_of_forward_declaration()
@@ -216,6 +229,7 @@
         &mut self,
         mut typ: TypePath,
         ns: &Namespace,
+        ctx: &TypeConversionContext,
     ) -> Result<Annotated<Type>, ConvertError> {
         // First, qualify any unqualified paths.
         if typ.path.segments.iter().next().unwrap().ident != "root" {
@@ -323,7 +337,24 @@
                 // Oh poop. It's a generic type which cxx won't be able to handle.
                 // We'll have to come up with a concrete type in both the cxx::bridge (in Rust)
                 // and a corresponding typedef in C++.
-                // Let's first see if this is a concrete version of a templated type
+                // First let's see if this actually depends on a generic type
+                // param of the surrounding struct.
+                for seg in &typ.path.segments {
+                    if let PathArguments::AngleBracketed(args) = &seg.arguments {
+                        for arg in args.args.iter() {
+                            if let GenericArgument::Type(Type::Path(typ)) = arg {
+                                if let Some(seg) = typ.path.segments.last() {
+                                    if typ.path.segments.len() == 1
+                                        && !ctx.allowed_generic_type(&seg.ident)
+                                    {
+                                        return Err(ConvertError::ReferringToGenericTypeParam);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                // Let's second see if this is a concrete version of a templated type
                 // which we already rejected. Some, but possibly not all, of the reasons
                 // for its rejection would also apply to any concrete types we
                 // make. Err on the side of caution. In future we may be able to relax
@@ -393,6 +424,14 @@
                     if encountered.contains(&new_tn) {
                         return Err(ConvertError::InfinitelyRecursiveTypedef(tn.clone()));
                     }
+                    if typ
+                        .path
+                        .segments
+                        .iter()
+                        .any(|seg| seg.ident.to_string().starts_with("_bindgen_mod"))
+                    {
+                        return Err(ConvertError::TypedefToTypeInAnonymousNamespace);
+                    }
                     encountered.insert(new_tn.clone());
                     tn = new_tn;
                 }
diff --git a/engine/src/conversion/api.rs b/engine/src/conversion/api.rs
index 3d04674..c5a1b60 100644
--- a/engine/src/conversion/api.rs
+++ b/engine/src/conversion/api.rs
@@ -54,7 +54,6 @@
 
 /// Details about a C++ struct.
 pub(crate) struct StructDetails {
-    pub(crate) vis: CppVisibility,
     pub(crate) item: ItemStruct,
     pub(crate) layout: Option<Layout>,
     pub(crate) has_rvalue_reference_fields: bool,
@@ -713,3 +712,10 @@
         Ok(Box::new(std::iter::once(Api::Enum { name, item })))
     }
 }
+
+/// Whether a type is a pointer of some kind.
+pub(crate) enum Pointerness {
+    Not,
+    ConstPtr,
+    MutPtr,
+}
diff --git a/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs b/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
index b2e1ea3..5367626 100644
--- a/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
+++ b/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
@@ -6,8 +6,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use syn::{Type, TypePtr};
+
 use crate::conversion::{
     analysis::fun::function_wrapper::{CppConversionType, TypeConversionPolicy},
+    api::Pointerness,
     ConvertError,
 };
 
@@ -30,12 +33,39 @@
     pub(super) fn converted_type(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
         match self.cpp_conversion {
             CppConversionType::FromValueToUniquePtr => self.unique_ptr_wrapped_type(cpp_name_map),
+            CppConversionType::FromReferenceToPointer => {
+                let (const_string, ty) = match self.cxxbridge_type() {
+                    Type::Ptr(TypePtr {
+                        mutability: Some(_),
+                        elem,
+                        ..
+                    }) => ("", elem.as_ref()),
+                    Type::Ptr(TypePtr { elem, .. }) => ("const ", elem.as_ref()),
+                    _ => panic!("Not a pointer"),
+                };
+                Ok(format!(
+                    "{}{}*",
+                    const_string,
+                    type_to_cpp(ty, cpp_name_map)?
+                ))
+            }
             _ => self.unwrapped_type_as_string(cpp_name_map),
         }
     }
 
     fn unwrapped_type_as_string(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
-        type_to_cpp(&self.unwrapped_type, cpp_name_map)
+        type_to_cpp(self.cxxbridge_type(), cpp_name_map)
+    }
+
+    pub(crate) fn is_a_pointer(&self) -> Pointerness {
+        match self.cxxbridge_type() {
+            Type::Ptr(TypePtr {
+                mutability: Some(_),
+                ..
+            }) => Pointerness::MutPtr,
+            Type::Ptr(_) => Pointerness::ConstPtr,
+            _ => Pointerness::Not,
+        }
     }
 
     fn unique_ptr_wrapped_type(
@@ -60,6 +90,7 @@
             CppConversionType::None | CppConversionType::FromReturnValueToPlacementPtr => {
                 Some(var_name.to_string())
             }
+            CppConversionType::FromPointerToReference { .. } => Some(format!("(*{})", var_name)),
             CppConversionType::Move => Some(format!("std::move({})", var_name)),
             CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToMove => {
                 Some(format!("std::move(*{})", var_name))
@@ -78,6 +109,7 @@
                 })
             }
             CppConversionType::IgnoredPlacementPtrParameter => None,
+            CppConversionType::FromReferenceToPointer { .. } => Some(format!("&{}", var_name)),
         })
     }
 }
diff --git a/engine/src/conversion/codegen_cpp/mod.rs b/engine/src/conversion/codegen_cpp/mod.rs
index 02e92b2..0e6dcce 100644
--- a/engine/src/conversion/codegen_cpp/mod.rs
+++ b/engine/src/conversion/codegen_cpp/mod.rs
@@ -559,7 +559,7 @@
 
             underlying_function_call = match placement_param {
                 Some(placement_param) => {
-                    let tyname = type_to_cpp(&ret.unwrapped_type, &self.original_name_map)?;
+                    let tyname = type_to_cpp(ret.cxxbridge_type(), &self.original_name_map)?;
                     format!("new({}) {}({})", placement_param, tyname, call_itself)
                 }
                 None => format!("return {}", call_itself),
@@ -684,6 +684,13 @@
             "{}& As_{}_mut() {{ return *this; }}",
             super_name, super_name
         ));
+        self.additional_functions.push(ExtraCpp {
+            declaration: Some(format!(
+                "inline std::unique_ptr<{}> {}_As_{}_UniquePtr(std::unique_ptr<{}> u) {{ return std::unique_ptr<{}>(u.release()); }}",
+                superclass.to_cpp_name(), subclass.cpp(), super_name, subclass.cpp(), superclass.to_cpp_name(),
+                )),
+                ..Default::default()
+        });
         // And now constructors
         let mut constructor_decls: Vec<String> = Vec::new();
         for constructor in constructors {
@@ -700,7 +707,7 @@
         }
         self.additional_functions.push(ExtraCpp {
             type_definition: Some(format!(
-                "class {} : {}\n{{\npublic:\n{}\n{}\nvoid {}() const;\nprivate:rust::Box<{}> obs;\nvoid really_remove_ownership();\n\n}};",
+                "class {} : public {}\n{{\npublic:\n{}\n{}\nvoid {}() const;\nprivate:rust::Box<{}> obs;\nvoid really_remove_ownership();\n\n}};",
                 subclass.cpp(),
                 superclass.to_cpp_name(),
                 constructor_decls.join("\n"),
diff --git a/engine/src/conversion/codegen_rs/fun_codegen.rs b/engine/src/conversion/codegen_rs/fun_codegen.rs
index 7c32b1b..db222a6 100644
--- a/engine/src/conversion/codegen_rs/fun_codegen.rs
+++ b/engine/src/conversion/codegen_rs/fun_codegen.rs
@@ -6,6 +6,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use autocxx_parser::IncludeCppConfig;
 use indexmap::set::IndexSet as HashSet;
 use std::borrow::Cow;
 
@@ -23,15 +24,15 @@
     function_wrapper_rs::RustParamConversion,
     maybe_unsafes_to_tokens,
     unqualify::{unqualify_params, unqualify_ret_type},
-    ImplBlockDetails, MaybeUnsafeStmt, RsCodegenResult, TraitImplBlockDetails, Use,
+    ImplBlockDetails, ImplBlockKey, MaybeUnsafeStmt, RsCodegenResult, TraitImplBlockDetails, Use,
 };
 use crate::{
     conversion::{
         analysis::fun::{
-            ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy,
-            TraitMethodDetails,
+            function_wrapper::TypeConversionPolicy, ArgumentAnalysis, FnAnalysis, FnKind,
+            MethodKind, RustRenameStrategy, TraitMethodDetails,
         },
-        api::UnsafetyNeeded,
+        api::{Pointerness, UnsafetyNeeded},
     },
     types::{Namespace, QualifiedName},
 };
@@ -89,6 +90,7 @@
     analysis: FnAnalysis,
     cpp_call_name: String,
     non_pod_types: &HashSet<QualifiedName>,
+    config: &IncludeCppConfig,
 ) -> RsCodegenResult {
     if analysis.ignore_reason.is_err() || !analysis.externally_callable {
         return RsCodegenResult::default();
@@ -96,6 +98,7 @@
     let cxxbridge_name = analysis.cxxbridge_name;
     let rust_name = &analysis.rust_name;
     let ret_type = analysis.ret_type;
+    let ret_conversion = analysis.ret_conversion;
     let param_details = analysis.param_details;
     let wrapper_function_needed = analysis.cpp_wrapper.is_some();
     let params = analysis.params;
@@ -119,6 +122,9 @@
         always_unsafe_due_to_trait_definition,
         doc_attrs: &doc_attrs,
         non_pod_types,
+        ret_type: &ret_type,
+        ret_conversion: &ret_conversion,
+        reference_wrappers: config.unsafe_policy.requires_cpprefs(),
     };
     // In rare occasions, we might need to give an explicit lifetime.
     let (lifetime_tokens, params, ret_type) = add_explicit_lifetime_if_necessary(
@@ -148,15 +154,14 @@
                 impl_entry = Some(fn_generator.generate_method_impl(
                     matches!(method_kind, MethodKind::Constructor { .. }),
                     impl_for,
-                    &ret_type,
                 ));
             }
             FnKind::TraitMethod { ref details, .. } => {
-                trait_impl_entry = Some(fn_generator.generate_trait_impl(details, &ret_type));
+                trait_impl_entry = Some(fn_generator.generate_trait_impl(details));
             }
             _ => {
                 // Generate plain old function
-                bindgen_mod_items.push(fn_generator.generate_function_impl(&ret_type));
+                bindgen_mod_items.push(fn_generator.generate_function_impl());
             }
         }
     }
@@ -225,20 +230,23 @@
 #[derive(Clone)]
 struct FnGenerator<'a> {
     param_details: &'a [ArgumentAnalysis],
+    ret_conversion: &'a Option<TypeConversionPolicy>,
+    ret_type: &'a ReturnType,
     cxxbridge_name: &'a Ident,
     rust_name: &'a str,
     unsafety: &'a UnsafetyNeeded,
     always_unsafe_due_to_trait_definition: bool,
     doc_attrs: &'a Vec<Attribute>,
     non_pod_types: &'a HashSet<QualifiedName>,
+    reference_wrappers: bool,
 }
 
 impl<'a> FnGenerator<'a> {
     fn common_parts<'b>(
-        &self,
+        &'b self,
         avoid_self: bool,
         parameter_reordering: &Option<Vec<usize>>,
-        ret_type: &'b ReturnType,
+        ret_type: Option<ReturnType>,
     ) -> (
         Option<TokenStream>,
         Punctuated<FnArg, Comma>,
@@ -249,15 +257,20 @@
         let mut local_variables = Vec::new();
         let mut arg_list = Vec::new();
         let mut ptr_arg_name = None;
-        let mut ret_type = Cow::Borrowed(ret_type);
+        let mut ret_type: Cow<'a, _> = ret_type
+            .map(Cow::Owned)
+            .unwrap_or(Cow::Borrowed(self.ret_type));
         let mut any_conversion_requires_unsafe = false;
+        let mut variable_counter = 0usize;
         for pd in self.param_details {
             let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
                 parse_quote!(self)
             } else {
                 pd.name.clone()
             };
-            let rust_for_param = pd.conversion.rust_conversion(wrapper_arg_name.clone());
+            let rust_for_param = pd
+                .conversion
+                .rust_conversion(parse_quote! { #wrapper_arg_name }, &mut variable_counter);
             match rust_for_param {
                 RustParamConversion::Param {
                     ty,
@@ -305,6 +318,39 @@
             },
             any_conversion_requires_unsafe || matches!(self.unsafety, UnsafetyNeeded::JustBridge),
         );
+        let context_is_unsafe = matches!(self.unsafety, UnsafetyNeeded::Always)
+            || self.always_unsafe_due_to_trait_definition;
+        let (call_body, ret_type) = match self.ret_conversion {
+            Some(ret_conversion) if ret_conversion.rust_work_needed() => {
+                let expr = maybe_unsafes_to_tokens(vec![call_body], context_is_unsafe);
+                let conv =
+                    ret_conversion.rust_conversion(parse_quote! { #expr }, &mut variable_counter);
+                let (conversion, requires_unsafe, ty) = match conv {
+                    RustParamConversion::Param {
+                        local_variables, ..
+                    } if !local_variables.is_empty() => panic!("return type required variables"),
+                    RustParamConversion::Param {
+                        conversion,
+                        conversion_requires_unsafe,
+                        ty,
+                        ..
+                    } => (conversion, conversion_requires_unsafe, ty),
+                    _ => panic!(
+                        "Unexpected - return type is supposed to be converted to a return type"
+                    ),
+                };
+                (
+                    if requires_unsafe {
+                        MaybeUnsafeStmt::NeedsUnsafe(conversion)
+                    } else {
+                        MaybeUnsafeStmt::Normal(conversion)
+                    },
+                    Cow::Owned(parse_quote! { -> #ty }),
+                )
+            }
+            _ => (call_body, ret_type),
+        };
+
         let call_stmts = if let Some(ptr_arg_name) = ptr_arg_name {
             let mut closure_stmts = local_variables;
             closure_stmts.push(MaybeUnsafeStmt::binary(
@@ -323,8 +369,6 @@
             call_stmts.push(call_body);
             call_stmts
         };
-        let context_is_unsafe = matches!(self.unsafety, UnsafetyNeeded::Always)
-            || self.always_unsafe_due_to_trait_definition;
         let call_body = maybe_unsafes_to_tokens(call_stmts, context_is_unsafe);
         (lifetime_tokens, wrapper_params, ret_type, call_body)
     }
@@ -334,13 +378,44 @@
         &self,
         avoid_self: bool,
         impl_block_type_name: &QualifiedName,
-        ret_type: &ReturnType,
     ) -> Box<ImplBlockDetails> {
         let (lifetime_tokens, wrapper_params, ret_type, call_body) =
-            self.common_parts(avoid_self, &None, ret_type);
+            self.common_parts(avoid_self, &None, None);
         let rust_name = make_ident(self.rust_name);
         let unsafety = self.unsafety.wrapper_token();
         let doc_attrs = self.doc_attrs;
+        let receiver_pointerness = self
+            .param_details
+            .iter()
+            .next()
+            .map(|pd| pd.conversion.is_a_pointer())
+            .unwrap_or(Pointerness::Not);
+        let ty = impl_block_type_name.get_final_ident();
+        let ty = if self.reference_wrappers {
+            match receiver_pointerness {
+                Pointerness::MutPtr => ImplBlockKey {
+                    ty: parse_quote! {
+                        CppMutRef< 'a, #ty>
+                    },
+                    lifetime: Some(parse_quote! { 'a }),
+                },
+                Pointerness::ConstPtr => ImplBlockKey {
+                    ty: parse_quote! {
+                        CppRef< 'a, #ty>
+                    },
+                    lifetime: Some(parse_quote! { 'a }),
+                },
+                Pointerness::Not => ImplBlockKey {
+                    ty: parse_quote! { # ty },
+                    lifetime: None,
+                },
+            }
+        } else {
+            ImplBlockKey {
+                ty: parse_quote! { # ty },
+                lifetime: None,
+            }
+        };
         Box::new(ImplBlockDetails {
             item: ImplItem::Method(parse_quote! {
                 #(#doc_attrs)*
@@ -348,18 +423,14 @@
                     #call_body
                 }
             }),
-            ty: impl_block_type_name.get_final_ident(),
+            ty,
         })
     }
 
     /// Generate an 'impl Trait for Type { methods-go-here }' in its entrety.
-    fn generate_trait_impl(
-        &self,
-        details: &TraitMethodDetails,
-        ret_type: &ReturnType,
-    ) -> Box<TraitImplBlockDetails> {
+    fn generate_trait_impl(&self, details: &TraitMethodDetails) -> Box<TraitImplBlockDetails> {
         let (lifetime_tokens, wrapper_params, ret_type, call_body) =
-            self.common_parts(details.avoid_self, &details.parameter_reordering, ret_type);
+            self.common_parts(details.avoid_self, &details.parameter_reordering, None);
         let doc_attrs = self.doc_attrs;
         let unsafety = self.unsafety.wrapper_token();
         let key = details.trt.clone();
@@ -381,25 +452,28 @@
     ) -> Box<ImplBlockDetails> {
         let ret_type: ReturnType = parse_quote! { -> impl autocxx::moveit::new::New<Output=Self> };
         let (lifetime_tokens, wrapper_params, ret_type, call_body) =
-            self.common_parts(true, &None, &ret_type);
+            self.common_parts(true, &None, Some(ret_type));
         let rust_name = make_ident(&self.rust_name);
         let doc_attrs = self.doc_attrs;
         let unsafety = self.unsafety.wrapper_token();
-        Box::new(ImplBlockDetails {
-            item: ImplItem::Method(parse_quote! {
+        let ty = impl_block_type_name.get_final_ident();
+        let ty = parse_quote! { #ty };
+        let stuff = quote! {
                 #(#doc_attrs)*
                 pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                     #call_body
                 }
-            }),
-            ty: impl_block_type_name.get_final_ident(),
+        };
+        Box::new(ImplBlockDetails {
+            item: ImplItem::Method(parse_quote! { #stuff }),
+            ty: ImplBlockKey { ty, lifetime: None },
         })
     }
 
     /// Generate a function call wrapper
-    fn generate_function_impl(&self, ret_type: &ReturnType) -> Item {
+    fn generate_function_impl(&self) -> Item {
         let (lifetime_tokens, wrapper_params, ret_type, call_body) =
-            self.common_parts(false, &None, ret_type);
+            self.common_parts(false, &None, None);
         let rust_name = make_ident(self.rust_name);
         let doc_attrs = self.doc_attrs;
         let unsafety = self.unsafety.wrapper_token();
diff --git a/engine/src/conversion/codegen_rs/function_wrapper_rs.rs b/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
index a3fc71f..708d41c 100644
--- a/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
+++ b/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
@@ -7,7 +7,7 @@
 // except according to those terms.
 
 use proc_macro2::TokenStream;
-use syn::{Pat, Type, TypePtr};
+use syn::{Expr, Type, TypePtr};
 
 use crate::{
     conversion::analysis::fun::function_wrapper::{RustConversionType, TypeConversionPolicy},
@@ -32,8 +32,7 @@
 }
 
 impl TypeConversionPolicy {
-    /// If returns `None` then this parameter should be omitted entirely.
-    pub(super) fn rust_conversion(&self, var: Pat) -> RustParamConversion {
+    pub(super) fn rust_conversion(&self, var: Expr, counter: &mut usize) -> RustParamConversion {
         match self.rust_conversion {
             RustConversionType::None => RustParamConversion::Param {
                 ty: self.converted_rust_type(),
@@ -63,7 +62,7 @@
                 }
             }
             RustConversionType::FromPinMaybeUninitToPtr => {
-                let ty = match &self.unwrapped_type {
+                let ty = match self.cxxbridge_type() {
                     Type::Ptr(TypePtr { elem, .. }) => &*elem,
                     _ => panic!("Not a ptr"),
                 };
@@ -80,7 +79,7 @@
                 }
             }
             RustConversionType::FromPinMoveRefToPtr => {
-                let ty = match &self.unwrapped_type {
+                let ty = match self.cxxbridge_type() {
                     Type::Ptr(TypePtr { elem, .. }) => &*elem,
                     _ => panic!("Not a ptr"),
                 };
@@ -99,7 +98,7 @@
                 }
             }
             RustConversionType::FromTypeToPtr => {
-                let ty = match &self.unwrapped_type {
+                let ty = match self.cxxbridge_type() {
                     Type::Ptr(TypePtr { elem, .. }) => &*elem,
                     _ => panic!("Not a ptr"),
                 };
@@ -123,13 +122,11 @@
                 };
                 let handler_type = make_ident(handler_type);
                 let param_trait = make_ident(param_trait);
-                let var_name = if let Pat::Ident(pti) = &var {
-                    &pti.ident
-                } else {
-                    panic!("Unexpected non-ident parameter name");
-                };
-                let space_var_name = make_ident(format!("{}_space", var_name));
-                let ty = &self.unwrapped_type;
+                let var_counter = *counter;
+                *counter += 1;
+                let space_var_name = format!("space{}", var_counter);
+                let space_var_name = make_ident(space_var_name);
+                let ty = self.cxxbridge_type();
                 let ty = parse_quote! { impl autocxx::#param_trait<#ty> };
                 // This is the usual trick to put something on the stack, then
                 // immediately shadow the variable name so it can't be accessed or moved.
@@ -148,7 +145,7 @@
                             },
                         ),
                         MaybeUnsafeStmt::needs_unsafe(
-                            quote! { #space_var_name.as_mut().populate(#var_name); },
+                            quote! { #space_var_name.as_mut().populate(#var); },
                         ),
                     ],
                     conversion: quote! {
@@ -161,12 +158,55 @@
             // but not in the arguments for the wrapper function, because instead we return an
             // impl New which uses the cxx::bridge function's pointer parameter.
             RustConversionType::FromPlacementParamToNewReturn => {
-                let ty = match &self.unwrapped_type {
+                let ty = match self.cxxbridge_type() {
                     Type::Ptr(TypePtr { elem, .. }) => *(*elem).clone(),
                     _ => panic!("Not a ptr"),
                 };
                 RustParamConversion::ReturnValue { ty }
             }
+            RustConversionType::FromPointerToReferenceWrapper => {
+                let (is_mut, ty) = match self.cxxbridge_type() {
+                    Type::Ptr(TypePtr {
+                        mutability, elem, ..
+                    }) => (mutability.is_some(), elem.as_ref()),
+                    _ => panic!("Not a pointer"),
+                };
+                let (ty, wrapper_name) = if is_mut {
+                    (parse_quote! { CppMutRef<'a, #ty> }, "CppMutRef")
+                } else {
+                    (parse_quote! { CppRef<'a, #ty> }, "CppRef")
+                };
+                let wrapper_name = make_ident(wrapper_name);
+                RustParamConversion::Param {
+                    ty,
+                    local_variables: Vec::new(),
+                    conversion: quote! {
+                        #wrapper_name (#var, std::marker::PhantomData)
+                    },
+                    conversion_requires_unsafe: false,
+                }
+            }
+            RustConversionType::FromReferenceWrapperToPointer => {
+                let (is_mut, ty) = match self.cxxbridge_type() {
+                    Type::Ptr(TypePtr {
+                        mutability, elem, ..
+                    }) => (mutability.is_some(), elem.as_ref()),
+                    _ => panic!("Not a pointer"),
+                };
+                let ty = if is_mut {
+                    parse_quote! { &mut CppMutRef<'a, #ty> }
+                } else {
+                    parse_quote! { &CppRef<'a, #ty> }
+                };
+                RustParamConversion::Param {
+                    ty,
+                    local_variables: Vec::new(),
+                    conversion: quote! {
+                        #var .0
+                    },
+                    conversion_requires_unsafe: false,
+                }
+            }
         }
     }
 }
diff --git a/engine/src/conversion/codegen_rs/mod.rs b/engine/src/conversion/codegen_rs/mod.rs
index d488d52..5dd4cbb 100644
--- a/engine/src/conversion/codegen_rs/mod.rs
+++ b/engine/src/conversion/codegen_rs/mod.rs
@@ -17,13 +17,14 @@
 use indexmap::map::IndexMap as HashMap;
 use indexmap::set::IndexSet as HashSet;
 
-use autocxx_parser::{ExternCppType, IncludeCppConfig, RustFun};
+use autocxx_parser::{ExternCppType, IncludeCppConfig, RustFun, UnsafePolicy};
 
 use itertools::Itertools;
 use proc_macro2::{Span, TokenStream};
 use syn::{
     parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem,
-    ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, TraitItem, TypePath,
+    ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, Lifetime, TraitItem, Type,
+    TypePath,
 };
 
 use crate::{
@@ -61,10 +62,16 @@
 use super::{convert_error::ErrorContext, ConvertError};
 use quote::quote;
 
+#[derive(Clone, Hash, PartialEq, Eq)]
+struct ImplBlockKey {
+    ty: Type,
+    lifetime: Option<Lifetime>,
+}
+
 /// An entry which needs to go into an `impl` block for a given type.
 struct ImplBlockDetails {
     item: ImplItem,
-    ty: Ident,
+    ty: ImplBlockKey,
 }
 
 struct TraitImplBlockDetails {
@@ -130,10 +137,96 @@
     .to_vec()
 }
 
+fn get_cppref_items() -> Vec<Item> {
+    [
+        Item::Struct(parse_quote! {
+            #[repr(transparent)]
+            pub struct CppRef<'a, T>(pub *const T, pub ::std::marker::PhantomData<&'a T>);
+        }),
+        Item::Impl(parse_quote! {
+            impl<'a, T> autocxx::CppRef<'a, T> for CppRef<'a, T> {
+                fn as_ptr(&self) -> *const T {
+                    self.0
+                }
+            }
+        }),
+        Item::Struct(parse_quote! {
+            #[repr(transparent)]
+            pub struct CppMutRef<'a, T>(pub *mut T, pub ::std::marker::PhantomData<&'a T>);
+        }),
+        Item::Impl(parse_quote! {
+            impl<'a, T> autocxx::CppRef<'a, T> for CppMutRef<'a, T> {
+                fn as_ptr(&self) -> *const T {
+                    self.0
+                }
+            }
+        }),
+        Item::Impl(parse_quote! {
+            impl<'a, T> autocxx::CppMutRef<'a, T> for CppMutRef<'a, T> {
+                fn as_mut_ptr(&self) -> *mut T {
+                    self.0
+                }
+            }
+        }),
+        Item::Impl(parse_quote! {
+            impl<'a, T: ::cxx::private::UniquePtrTarget> CppMutRef<'a, T> {
+                /// Create a const C++ reference from this mutable C++ reference.
+                pub fn as_cpp_ref(&self) -> CppRef<'a, T> {
+                    use autocxx::CppRef;
+                    CppRef(self.as_ptr(), ::std::marker::PhantomData)
+                }
+            }
+        }),
+        Item::Struct(parse_quote! {
+            /// "Pins" a `UniquePtr` to an object, so that C++-compatible references can be created.
+            /// See [`::autocxx::CppPin`]
+            #[repr(transparent)]
+            pub struct CppUniquePtrPin<T: ::cxx::private::UniquePtrTarget>(::cxx::UniquePtr<T>);
+        }),
+        Item::Impl(parse_quote! {
+            impl<'a, T: 'a + ::cxx::private::UniquePtrTarget> autocxx::CppPin<'a, T> for CppUniquePtrPin<T>
+            {
+                type CppRef = CppRef<'a, T>;
+                type CppMutRef = CppMutRef<'a, T>;
+                fn as_ptr(&self) -> *const T {
+                    // TODO add as_ptr to cxx to avoid the ephemeral reference
+                    self.0.as_ref().unwrap() as *const T
+                }
+                fn as_mut_ptr(&mut self) -> *mut T {
+                    unsafe { ::std::pin::Pin::into_inner_unchecked(self.0.as_mut().unwrap()) as *mut T  }
+                }
+                fn as_cpp_ref(&self) -> Self::CppRef {
+                    CppRef(self.as_ptr(), ::std::marker::PhantomData)
+                }
+                fn as_cpp_mut_ref(&mut self) -> Self::CppMutRef {
+                    CppMutRef(self.as_mut_ptr(), ::std::marker::PhantomData)
+                }
+            }
+        }),
+        Item::Impl(parse_quote! {
+            impl<T: ::cxx::private::UniquePtrTarget> CppUniquePtrPin<T> {
+                pub fn new(item: ::cxx::UniquePtr<T>) -> Self {
+                    Self(item)
+                }
+            }
+        }),
+        Item::Fn(parse_quote! {
+            /// Pin this item so that we can create C++ references to it.
+            /// This makes it impossible to hold Rust references because Rust
+            /// references are fundamentally incompatible with C++ references.
+            pub fn cpp_pin_uniqueptr<T: ::cxx::private::UniquePtrTarget> (item: ::cxx::UniquePtr<T>) -> CppUniquePtrPin<T> {
+                CppUniquePtrPin::new(item)
+            }
+        })
+    ]
+    .to_vec()
+}
+
 /// Type which handles generation of Rust code.
 /// In practice, much of the "generation" involves connecting together
 /// existing lumps of code within the Api structures.
 pub(crate) struct RsCodeGenerator<'a> {
+    unsafe_policy: &'a UnsafePolicy,
     include_list: &'a [String],
     bindgen_mod: ItemMod,
     original_name_map: CppNameMap,
@@ -145,12 +238,14 @@
     /// Generate code for a set of APIs that was discovered during parsing.
     pub(crate) fn generate_rs_code(
         all_apis: ApiVec<FnPhase>,
+        unsafe_policy: &'a UnsafePolicy,
         include_list: &'a [String],
         bindgen_mod: ItemMod,
         config: &'a IncludeCppConfig,
         header_name: Option<String>,
     ) -> Vec<Item> {
         let c = Self {
+            unsafe_policy,
             include_list,
             bindgen_mod,
             original_name_map: original_name_map_from_apis(&all_apis),
@@ -219,6 +314,9 @@
         let mut extern_rust_mod_items = extern_rust_mod_items.into_iter().flatten().collect();
         // And a list of global items to include at the top level.
         let mut all_items: Vec<Item> = all_items.into_iter().flatten().collect();
+        if self.config.unsafe_policy.requires_cpprefs() {
+            all_items.append(&mut get_cppref_items())
+        }
         // And finally any C++ we need to generate. And by "we" I mean autocxx not cxx.
         let has_additional_cpp_needs = additional_cpp_needs.into_iter().any(std::convert::identity);
         extern_c_mod_items.extend(self.build_include_foreign_items(has_additional_cpp_needs));
@@ -357,23 +455,24 @@
     }
 
     fn append_uses_for_ns(&mut self, items: &mut Vec<Item>, ns: &Namespace) {
+        let mut imports_from_super = vec!["cxxbridge"];
+        if !self.config.exclude_utilities() {
+            imports_from_super.push("ToCppString");
+        }
+        if self.config.unsafe_policy.requires_cpprefs() {
+            imports_from_super.extend(["CppRef", "CppMutRef"]);
+        }
+        let imports_from_super = imports_from_super.into_iter().map(make_ident);
         let super_duper = std::iter::repeat(make_ident("super")); // I'll get my coat
         let supers = super_duper.clone().take(ns.depth() + 2);
         items.push(Item::Use(parse_quote! {
             #[allow(unused_imports)]
             use self::
                 #(#supers)::*
-            ::cxxbridge;
+            ::{
+                #(#imports_from_super),*
+            };
         }));
-        if !self.config.exclude_utilities() {
-            let supers = super_duper.clone().take(ns.depth() + 2);
-            items.push(Item::Use(parse_quote! {
-                #[allow(unused_imports)]
-                use self::
-                    #(#supers)::*
-                ::ToCppString;
-            }));
-        }
         let supers = super_duper.take(ns.depth() + 1);
         items.push(Item::Use(parse_quote! {
             #[allow(unused_imports)]
@@ -407,8 +506,10 @@
             }
         }
         for (ty, entries) in impl_entries_by_type.into_iter() {
+            let lt = ty.lifetime.map(|lt| quote! { < #lt > });
+            let ty = ty.ty;
             output_items.push(Item::Impl(parse_quote! {
-                impl #ty {
+                impl #lt #ty {
                     #(#entries)*
                 }
             }))
@@ -487,6 +588,7 @@
                 analysis,
                 cpp_call_name,
                 non_pod_types,
+                self.config,
             ),
             Api::Const { const_item, .. } => RsCodegenResult {
                 bindgen_mod_items: vec![Item::Const(const_item)],
@@ -609,8 +711,11 @@
                 name, superclass, ..
             } => {
                 let methods = associated_methods.get(&superclass);
-                let generate_peer_constructor =
-                    subclasses_with_a_single_trivial_constructor.contains(&name.0.name);
+                let generate_peer_constructor = subclasses_with_a_single_trivial_constructor.contains(&name.0.name) &&
+                    // TODO: Create an UnsafeCppPeerConstructor trait for calling an unsafe
+                    // constructor instead? Need to create unsafe versions of everything that uses
+                    // it too.
+                    matches!(self.unsafe_policy, UnsafePolicy::AllFunctionsSafe);
                 self.generate_subclass(name, &superclass, methods, generate_peer_constructor)
             }
             Api::ExternCppType {
@@ -723,6 +828,10 @@
         extern_c_mod_items.push(parse_quote! {
             fn #as_mut_id(self: Pin<&mut #cpp_id>) -> Pin<&mut #super_cxxxbridge_id>;
         });
+        let as_unique_ptr_id = make_ident(format!("{}_As_{}_UniquePtr", cpp_id, super_name));
+        extern_c_mod_items.push(parse_quote! {
+            fn #as_unique_ptr_id(u: UniquePtr<#cpp_id>) -> UniquePtr<#super_cxxxbridge_id>;
+        });
         bindgen_mod_items.push(parse_quote! {
             impl AsRef<#super_path> for super::super::super::#id {
                 fn as_ref(&self) -> &cxxbridge::#super_cxxxbridge_id {
@@ -740,6 +849,14 @@
                 }
             }
         });
+        let rs_as_unique_ptr_id = make_ident(format!("as_{}_unique_ptr", super_name));
+        bindgen_mod_items.push(parse_quote! {
+            impl super::super::super::#id {
+                pub fn #rs_as_unique_ptr_id(u: cxx::UniquePtr<#cpp_id>) -> cxx::UniquePtr<cxxbridge::#super_cxxxbridge_id> {
+                    cxxbridge::#as_unique_ptr_id(u)
+                }
+            }
+        });
         let remove_ownership = sub.remove_ownership();
         global_items.push(parse_quote! {
             #[allow(non_snake_case)]
@@ -813,7 +930,7 @@
                         .as_ref()
                         .#borrow()
                         .expect(#reentrancy_panic_msg);
-                    let r = std::ops::#deref_ty::#deref_call(& #mut_token b);
+                    let r = ::std::ops::#deref_ty::#deref_call(& #mut_token b);
                     #methods_trait :: #method_name
                         (r,
                         #args)
@@ -1012,7 +1129,7 @@
         rust_path: TypePath,
         ns_depth: usize,
     ) -> RsCodegenResult {
-        let id = name.get_final_ident();
+        let id = name.type_path_from_root();
         let super_duper = std::iter::repeat(make_ident("super"));
         let supers = super_duper.take(ns_depth + 2);
         let use_statement = parse_quote! {
@@ -1057,7 +1174,10 @@
                         fn #method(_uhoh: autocxx::BindingGenerationFailure) {
                         }
                     },
-                    ty: self_ty,
+                    ty: ImplBlockKey {
+                        ty: parse_quote! { #self_ty },
+                        lifetime: None,
+                    },
                 })),
                 None,
                 None,
diff --git a/engine/src/conversion/convert_error.rs b/engine/src/conversion/convert_error.rs
index 0de4f19..ba8344d 100644
--- a/engine/src/conversion/convert_error.rs
+++ b/engine/src/conversion/convert_error.rs
@@ -125,6 +125,12 @@
     ConcreteVersionOfIgnoredTemplate,
     #[error("bindgen decided to call this type _bindgen_ty_N because it couldn't deduce the correct name for it. That means we can't generate C++ bindings to it.")]
     BindgenTy,
+    #[error("This is a typedef to a type in an anonymous namespace, not currently supported.")]
+    TypedefToTypeInAnonymousNamespace,
+    #[error("This type refers to a generic type parameter of an outer type, which is not yet supported.")]
+    ReferringToGenericTypeParam,
+    #[error("This forward declaration was nested within another struct/class. autocxx is unable to represent inner types if they are forward declarations.")]
+    ForwardDeclaredNestedType,
 }
 
 /// Ensures that error contexts are always created using the constructors in this
diff --git a/engine/src/conversion/mod.rs b/engine/src/conversion/mod.rs
index 3043dcf..aa639a2 100644
--- a/engine/src/conversion/mod.rs
+++ b/engine/src/conversion/mod.rs
@@ -157,7 +157,7 @@
                 // parameterized by a richer set of metadata.
                 Self::dump_apis("adding casts", &analyzed_apis);
                 let analyzed_apis =
-                    FnAnalyzer::analyze_functions(analyzed_apis, unsafe_policy, self.config);
+                    FnAnalyzer::analyze_functions(analyzed_apis, &unsafe_policy, self.config);
                 // If any of those functions turned out to be pure virtual, don't attempt
                 // to generate UniquePtr implementations for the type, since it can't
                 // be instantiated.
@@ -197,6 +197,7 @@
                 )?;
                 let rs = RsCodeGenerator::generate_rs_code(
                     analyzed_apis,
+                    &unsafe_policy,
                     self.include_list,
                     bindgen_mod,
                     self.config,
diff --git a/engine/src/conversion/parse/bindgen_semantic_attributes.rs b/engine/src/conversion/parse/bindgen_semantic_attributes.rs
index 8b789ae..a8de9ce 100644
--- a/engine/src/conversion/parse/bindgen_semantic_attributes.rs
+++ b/engine/src/conversion/parse/bindgen_semantic_attributes.rs
@@ -61,6 +61,11 @@
                 ConvertError::UnusedTemplateParam,
                 Some(ErrorContext::new_for_item(id_for_context.clone())),
             ))
+        } else if self.get_cpp_visibility() != CppVisibility::Public {
+            Err(ConvertErrorWithContext(
+                ConvertError::NonPublicNestedType,
+                Some(ErrorContext::new_for_item(id_for_context.clone())),
+            ))
         } else {
             Ok(())
         }
diff --git a/engine/src/conversion/parse/parse_bindgen.rs b/engine/src/conversion/parse/parse_bindgen.rs
index 0818aa5..2d4e3de 100644
--- a/engine/src/conversion/parse/parse_bindgen.rs
+++ b/engine/src/conversion/parse/parse_bindgen.rs
@@ -207,7 +207,7 @@
                 // cxx::bridge can't cope with type aliases to generic
                 // types at the moment.
                 let name = api_name_qualified(ns, s.ident.clone(), &annotations)?;
-                let err = annotations.check_for_fatal_attrs(&s.ident).err();
+                let mut err = annotations.check_for_fatal_attrs(&s.ident).err();
                 let api = if ns.is_empty() && self.config.is_rust_type(&s.ident) {
                     None
                 } else if Self::spot_forward_declaration(&s.fields)
@@ -219,6 +219,15 @@
                     // we spot in the previous clause) but instead with an _address field.
                     // So, solely in the case where we're storing up an error about such
                     // a templated type, we'll also treat such cases as forward declarations.
+                    //
+                    // We'll also at this point check for one specific problem with
+                    // forward declarations.
+                    if err.is_none() && name.cpp_name().contains("::") {
+                        err = Some(ConvertErrorWithContext(
+                            ConvertError::ForwardDeclaredNestedType,
+                            Some(ErrorContext::new_for_item(s.ident)),
+                        ));
+                    }
                     Some(UnanalyzedApi::ForwardDeclaration { name, err })
                 } else {
                     let has_rvalue_reference_fields = s.fields.iter().any(|f| {
@@ -227,7 +236,6 @@
                     Some(UnanalyzedApi::Struct {
                         name,
                         details: Box::new(StructDetails {
-                            vis: annotations.get_cpp_visibility(),
                             layout: annotations.get_layout(),
                             item: s,
                             has_rvalue_reference_fields,
diff --git a/engine/src/lib.rs b/engine/src/lib.rs
index 4edc4a4..86a31ea 100644
--- a/engine/src/lib.rs
+++ b/engine/src/lib.rs
@@ -304,6 +304,7 @@
             .default_enum_style(bindgen::EnumVariation::Rust {
                 non_exhaustive: false,
             })
+            .rustfmt_bindings(log::log_enabled!(log::Level::Info))
             .size_t_is_usize(true)
             .enable_cxx_namespaces()
             .generate_inline_functions(true)
diff --git a/engine/src/types.rs b/engine/src/types.rs
index 0d11895..337da14 100644
--- a/engine/src/types.rs
+++ b/engine/src/types.rs
@@ -195,6 +195,16 @@
         }
     }
 
+    pub(crate) fn type_path_from_root(&self) -> TypePath {
+        let segs = self
+            .ns_segment_iter()
+            .chain(std::iter::once(&self.1))
+            .map(make_ident);
+        parse_quote! {
+            #(#segs)::*
+        }
+    }
+
     /// Iterator over segments in the namespace of this name.
     pub(crate) fn ns_segment_iter(&self) -> impl Iterator<Item = &String> {
         self.0.iter()
diff --git a/examples/chromium-fake-render-frame-host/Cargo.toml b/examples/chromium-fake-render-frame-host/Cargo.toml
index a961708..3862f2c 100644
--- a/examples/chromium-fake-render-frame-host/Cargo.toml
+++ b/examples/chromium-fake-render-frame-host/Cargo.toml
@@ -13,9 +13,9 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.54"
-autocxx = { path = "../..", version="0.22.0" }
+cxx = "1.0.68"
+autocxx = { path = "../..", version="0.22.3" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.0" }
+autocxx-build = { path = "../../gen/build", version="0.22.3" }
 miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/llvm/Cargo.toml b/examples/llvm/Cargo.toml
index d8806c8..4c03db7 100644
--- a/examples/llvm/Cargo.toml
+++ b/examples/llvm/Cargo.toml
@@ -13,7 +13,7 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.54"
+cxx = "1.0.68"
 autocxx = { path = "../..", version="0.17.2" }
 
 [build-dependencies]
diff --git a/examples/non-trivial-type-on-stack/Cargo.toml b/examples/non-trivial-type-on-stack/Cargo.toml
index ff227d4..110bd1b 100644
--- a/examples/non-trivial-type-on-stack/Cargo.toml
+++ b/examples/non-trivial-type-on-stack/Cargo.toml
@@ -13,9 +13,9 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.54"
-autocxx = { path = "../..", version="0.22.0" }
+cxx = "1.0.68"
+autocxx = { path = "../..", version="0.22.3" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.0" }
+autocxx-build = { path = "../../gen/build", version="0.22.3" }
 miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/pod/Cargo.toml b/examples/pod/Cargo.toml
index 71bd5a0..9f4ee5a 100644
--- a/examples/pod/Cargo.toml
+++ b/examples/pod/Cargo.toml
@@ -13,9 +13,9 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.54"
-autocxx = { path = "../..", version="0.22.0" }
+cxx = "1.0.68"
+autocxx = { path = "../..", version="0.22.3" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.0" }
+autocxx-build = { path = "../../gen/build", version="0.22.3" }
 miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/reference-wrappers/Cargo.toml b/examples/reference-wrappers/Cargo.toml
new file mode 100644
index 0000000..cb85e80
--- /dev/null
+++ b/examples/reference-wrappers/Cargo.toml
@@ -0,0 +1,21 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-reference-wrapper-example"
+version = "0.22.1"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+
+[dependencies]
+cxx = "1.0.68"
+autocxx = { path = "../..", version="0.22.3" }
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.22.3" }
+miette = { version="4.3", features=["fancy"]}
diff --git a/examples/reference-wrappers/build.rs b/examples/reference-wrappers/build.rs
new file mode 100644
index 0000000..64c573d
--- /dev/null
+++ b/examples/reference-wrappers/build.rs
@@ -0,0 +1,18 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() -> miette::Result<()> {
+    let path = std::path::PathBuf::from("src");
+    let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).build()?;
+    b.flag_if_supported("-std=c++14")
+    .file("src/input.cc").compile("autocxx-reference-wrapper-example");
+
+    println!("cargo:rerun-if-changed=src/main.rs");
+    println!("cargo:rerun-if-changed=src/input.h");
+    Ok(())
+}
diff --git a/examples/reference-wrappers/src/input.cc b/examples/reference-wrappers/src/input.cc
new file mode 100644
index 0000000..afb7eb2
--- /dev/null
+++ b/examples/reference-wrappers/src/input.cc
@@ -0,0 +1,11 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "input.h"
+
+Goat the_goat;
diff --git a/examples/reference-wrappers/src/input.h b/examples/reference-wrappers/src/input.h
new file mode 100644
index 0000000..5e3c6e9
--- /dev/null
+++ b/examples/reference-wrappers/src/input.h
@@ -0,0 +1,42 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#pragma once
+
+#include <cstdint>
+#include <sstream>
+#include <stdint.h>
+#include <string>
+
+class Goat {
+public:
+    Goat() : horns(0) {}
+    void add_a_horn();
+    std::string describe() const;
+private:
+    uint32_t horns;
+};
+
+
+inline void Goat::add_a_horn() { horns++; }
+inline std::string Goat::describe() const {
+    std::ostringstream oss;
+    std::string plural = horns == 1 ? "" : "s";
+    oss << "This goat has " << horns << " horn" << plural << ".";
+    return oss.str();
+}
+
+class Field {
+public:
+    const Goat& get_goat() const {
+        return the_goat;
+    }
+
+private:
+    Goat the_goat;
+};
diff --git a/examples/reference-wrappers/src/main.rs b/examples/reference-wrappers/src/main.rs
new file mode 100644
index 0000000..6bea2ff
--- /dev/null
+++ b/examples/reference-wrappers/src/main.rs
@@ -0,0 +1,67 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This example serves to demonstrate the experimental C++
+// reference wrappers. They exist because C++ references are not
+// the same as Rust references: C++ references may alias, whereas
+// Rust references may not.
+//
+// Standard autocxx behavior therefore introduces unsoundness when
+// C++ references are encountered and treated like Rust references.
+// (cxx has this soundness problem for Trivial types; autocxx
+// makes it worse in that the same problem applies even for
+// opaque types, because we make them sized such that we can allocate
+// them on the stack).
+//
+// Reference wrappers solve that problem because internally, they're
+// just pointers. On the other hand, they're awkward to use,
+// especially in the absence of the Rust "arbitrary self types"
+// feature.
+
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    // This next line enables C++ reference wrappers
+    safety!(unsafe_references_wrapped)
+    generate!("Goat")
+    generate!("Field")
+}
+
+fn main() {
+    // Create a cxx::UniquePtr as normal for a Field object.
+    let field = ffi::Field::new().within_unique_ptr();
+    // We assume at this point that C++ has had no opportunity
+    // to retain any reference to the Field. That's not strictly
+    // true, due to RVO, but under all reasonable circumstances
+    // Rust currently has exclusive ownership of the Field we've
+    // been given.
+    // Therefore, at this point in the program, it's still
+    // OK to take Rust references to this Field.
+    let _field_rust_ref = field.as_ref();
+    // However, as soon as we want to pass a reference to the field
+    // back to C++, we have to ensure we have no Rust references
+    // in existence. So: we imprison the object in a "CppPin":
+    let field = ffi::cpp_pin_uniqueptr(field);
+    // We can no longer take Rust references to the field...
+    //   let _field_rust_ref = field.as_ref();
+    // However, we can take C++ references. And use such references
+    // to call methods...
+    let another_goat = field.as_cpp_ref().get_goat();
+    // The 'get_goat' method in C++ returns a reference, so this is
+    // another CppRef, not a Rust reference.
+    assert_eq!(
+        another_goat
+            .describe() // returns a UniquePtr<CxxString>, there
+                // are no Rust or C++ references involved at this point.
+            .as_ref()
+            .unwrap()
+            .to_string_lossy(),
+        "This goat has 0 horns."
+    );
+}
diff --git a/examples/s2/Cargo.toml b/examples/s2/Cargo.toml
index 994a381..04524b1 100644
--- a/examples/s2/Cargo.toml
+++ b/examples/s2/Cargo.toml
@@ -15,9 +15,9 @@
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-cxx = "1.0.54"
-autocxx = { path = "../..", version="0.22.0" }
+cxx = "1.0.68"
+autocxx = { path = "../..", version="0.22.3" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.0" }
+autocxx-build = { path = "../../gen/build", version="0.22.3" }
 miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/steam-mini/Cargo.toml b/examples/steam-mini/Cargo.toml
index b292f25..ceb81ff 100644
--- a/examples/steam-mini/Cargo.toml
+++ b/examples/steam-mini/Cargo.toml
@@ -15,9 +15,9 @@
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-cxx = "1.0.54"
-autocxx = { path = "../..", version="0.22.0" }
+cxx = "1.0.68"
+autocxx = { path = "../..", version="0.22.3" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.0" }
+autocxx-build = { path = "../../gen/build", version="0.22.3" }
 miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/subclass/Cargo.toml b/examples/subclass/Cargo.toml
index 8a01205..dc93f53 100644
--- a/examples/subclass/Cargo.toml
+++ b/examples/subclass/Cargo.toml
@@ -15,13 +15,13 @@
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-cxx = "1.0.54"
-autocxx = { path = "../..", version="0.22.0" }
+cxx = "1.0.68"
+autocxx = { path = "../..", version="0.22.3" }
 uwuify = "0.2.2"
 textwrap = "0.14"
 fastrand = "1.5.0"
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.0" }
+autocxx-build = { path = "../../gen/build", version="0.22.3" }
 regex = "1.5.4"
 miette = { version="4.3", features = [ "fancy" ] }
diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml
index 2895526..678a844 100644
--- a/gen/build/Cargo.toml
+++ b/gen/build/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-build"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2021"
 license = "MIT OR Apache-2.0"
@@ -22,7 +22,7 @@
 static = [ "autocxx-engine/static" ]
 
 [dependencies]
-autocxx-engine = { version="=0.22.0", path="../../engine", features = ["build"] }
+autocxx-engine = { version="=0.22.3", path="../../engine", features = ["build"] }
 env_logger = "0.9.0"
 indexmap = "1.8"
 
diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml
index 744d29a..51eb56c 100644
--- a/gen/cmd/Cargo.toml
+++ b/gen/cmd/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-gen"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2021"
 license = "MIT OR Apache-2.0"
@@ -22,7 +22,7 @@
 static = [ "autocxx-engine/static" ]
 
 [dependencies]
-autocxx-engine = { version="=0.22.0", path="../../engine" }
+autocxx-engine = { version="=0.22.3", path="../../engine" }
 clap = { version = "3.1.2", features = ["cargo"] }
 proc-macro2 = "1.0"
 env_logger = "0.9.0"
@@ -32,10 +32,10 @@
 
 [dev-dependencies]
 assert_cmd = "1.0.3"
-tempdir = "0.3.7"
-autocxx-integration-tests = { path = "../../integration-tests", version="=0.22.0" }
+tempfile = "3.1"
+autocxx-integration-tests = { path = "../../integration-tests", version="=0.22.3" }
 # This is necessary for building the projects created
 # by the trybuild test system...
 autocxx = { path="../.." }
-cxx = "1.0.54"
+cxx = "1.0.68"
 itertools = "0.10.3"
\ No newline at end of file
diff --git a/gen/cmd/src/depfile.rs b/gen/cmd/src/depfile.rs
index 5bb7a66..d8d3626 100644
--- a/gen/cmd/src/depfile.rs
+++ b/gen/cmd/src/depfile.rs
@@ -63,13 +63,13 @@
 mod tests {
     use std::{fs::File, io::Read};
 
-    use tempdir::TempDir;
+    use tempfile::tempdir;
 
     use super::Depfile;
 
     #[test]
     fn test_simple_depfile() {
-        let tmp_dir = TempDir::new("depfile-test").unwrap();
+        let tmp_dir = tempdir().unwrap();
         let f = tmp_dir.path().join("depfile.d");
         let mut df = Depfile::new(&f).unwrap();
         df.add_output(&tmp_dir.path().join("a/b"));
@@ -85,7 +85,7 @@
 
     #[test]
     fn test_multiple_outputs() {
-        let tmp_dir = TempDir::new("depfile-test").unwrap();
+        let tmp_dir = tempdir().unwrap();
         let f = tmp_dir.path().join("depfile.d");
         let mut df = Depfile::new(&f).unwrap();
         df.add_output(&tmp_dir.path().join("a/b"));
diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs
index 20f278b..8b109c3 100644
--- a/gen/cmd/src/main.rs
+++ b/gen/cmd/src/main.rs
@@ -316,27 +316,33 @@
             name_autocxxgen_h,
         )?;
     }
-    //writer.write_placeholders(header_counter.into_inner(), desired_number, "h")?;
     if matches.is_present("gen-rs-include") {
+        if !matches.is_present("fix-rs-include-name") && desired_number.is_some() {
+            return Err(miette::Report::msg(
+                "gen-rs-include and generate-exact requires fix-rs-include-name.",
+            ));
+        }
+        let mut counter = 0usize;
         let rust_buildables = parsed_files
             .iter()
             .flat_map(|parsed_file| parsed_file.get_rs_outputs());
-        for (counter, include_cxx) in rust_buildables.enumerate() {
+        for include_cxx in rust_buildables {
             let rs_code = generate_rs_single(include_cxx);
             let fname = if matches.is_present("fix-rs-include-name") {
-                format!("gen{}.include.rs", counter)
+                name_include_rs(counter)
             } else {
                 rs_code.filename
             };
             writer.write_to_file(fname, rs_code.code.as_bytes())?;
+            counter += 1;
         }
+        writer.write_placeholders(counter, desired_number, name_include_rs)?;
     }
     if matches.is_present("gen-rs-archive") {
         let rust_buildables = parsed_files
             .iter()
             .flat_map(|parsed_file| parsed_file.get_rs_outputs());
         let json = generate_rs_archive(rust_buildables);
-        eprintln!("Writing to gen.rs.json in {:?}", outdir);
         writer.write_to_file("gen.rs.json".into(), json.as_bytes())?;
     }
     if let Some(depfile) = depfile {
@@ -353,6 +359,10 @@
     format!("gen{}.h", counter)
 }
 
+fn name_include_rs(counter: usize) -> String {
+    format!("gen{}.include.rs", counter)
+}
+
 fn get_dependency_recorder(depfile: Rc<RefCell<Depfile>>) -> Box<dyn RebuildDependencyRecorder> {
     Box::new(RecordIntoDepfile(depfile))
 }
diff --git a/gen/cmd/tests/cmd_test.rs b/gen/cmd/tests/cmd_test.rs
index d0e671e..7e455a2 100644
--- a/gen/cmd/tests/cmd_test.rs
+++ b/gen/cmd/tests/cmd_test.rs
@@ -13,7 +13,7 @@
 use assert_cmd::Command;
 use autocxx_integration_tests::{build_from_folder, RsFindMode};
 use itertools::Itertools;
-use tempdir::TempDir;
+use tempfile::{tempdir, TempDir};
 
 static MAIN_RS: &str = concat!(
     include_str!("../../../demo/src/main.rs"),
@@ -31,7 +31,7 @@
 static INPUT2_H: &str = include_str!("data/input2.h");
 static INPUT3_H: &str = include_str!("data/input3.h");
 
-const KEEP_TEMPDIRS: bool = false;
+const KEEP_TEMPDIRS: bool = true;
 
 #[test]
 fn test_help() -> Result<(), Box<dyn std::error::Error>> {
@@ -107,7 +107,7 @@
 
 #[test]
 fn test_gen() -> Result<(), Box<dyn std::error::Error>> {
-    let tmp_dir = TempDir::new("example")?;
+    let tmp_dir = tempdir()?;
     base_test(&tmp_dir, RsGenMode::Single, |_| {})?;
     File::create(tmp_dir.path().join("cxx.h"))
         .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
@@ -128,7 +128,7 @@
 
 #[test]
 fn test_gen_archive() -> Result<(), Box<dyn std::error::Error>> {
-    let tmp_dir = TempDir::new("example")?;
+    let tmp_dir = tempdir()?;
     base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
     File::create(tmp_dir.path().join("cxx.h"))
         .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
@@ -148,7 +148,7 @@
 
 #[test]
 fn test_gen_multiple_in_archive() -> Result<(), Box<dyn std::error::Error>> {
-    let tmp_dir = TempDir::new("example")?;
+    let tmp_dir = tempdir()?;
 
     let mut files = HashMap::new();
     files.insert("input2.h", INPUT2_H.as_bytes());
@@ -186,14 +186,15 @@
 
 #[test]
 fn test_include_prefixes() -> Result<(), Box<dyn std::error::Error>> {
-    let tmp_dir = TempDir::new("example")?;
+    let tmp_dir = tempdir()?;
     base_test(&tmp_dir, RsGenMode::Single, |cmd| {
         cmd.arg("--cxx-h-path")
             .arg("foo/")
             .arg("--cxxgen-h-path")
             .arg("bar/")
             .arg("--generate-exact")
-            .arg("3");
+            .arg("3")
+            .arg("--fix-rs-include-name");
     })?;
     assert_contains(&tmp_dir, "autocxxgen0.h", "foo/cxx.h");
     // Currently we don't test cxxgen-h-path because we build the demo code
@@ -203,11 +204,12 @@
 
 #[test]
 fn test_gen_fixed_num() -> Result<(), Box<dyn std::error::Error>> {
-    let tmp_dir = TempDir::new("example")?;
+    let tmp_dir = tempdir()?;
     let depfile = tmp_dir.path().join("test.d");
     base_test(&tmp_dir, RsGenMode::Single, |cmd| {
         cmd.arg("--generate-exact")
             .arg("2")
+            .arg("--fix-rs-include-name")
             .arg("--depfile")
             .arg(depfile);
     })?;
@@ -217,16 +219,16 @@
     assert_contentful(&tmp_dir, "autocxxgen0.h");
     assert_not_contentful(&tmp_dir, "gen1.h");
     assert_not_contentful(&tmp_dir, "autocxxgen1.h");
-    assert_contentful(&tmp_dir, "autocxx-ffi-default-gen.rs");
+    assert_contentful(&tmp_dir, "gen0.include.rs");
     assert_contentful(&tmp_dir, "test.d");
     File::create(tmp_dir.path().join("cxx.h"))
         .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
     let r = build_from_folder(
         tmp_dir.path(),
         &tmp_dir.path().join("demo/main.rs"),
-        vec![tmp_dir.path().join("autocxx-ffi-default-gen.rs")],
+        vec![tmp_dir.path().join("gen0.include.rs")],
         &["gen0.cc"],
-        RsFindMode::AutocxxRs,
+        RsFindMode::AutocxxRsFile,
     );
     if KEEP_TEMPDIRS {
         println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
@@ -237,7 +239,7 @@
 
 #[test]
 fn test_gen_preprocess() -> Result<(), Box<dyn std::error::Error>> {
-    let tmp_dir = TempDir::new("example")?;
+    let tmp_dir = tempdir()?;
     let prepro_path = tmp_dir.path().join("preprocessed.h");
     base_test(&tmp_dir, RsGenMode::Single, |cmd| {
         cmd.env("AUTOCXX_PREPROCESS", prepro_path.to_str().unwrap());
@@ -251,7 +253,7 @@
 
 #[test]
 fn test_gen_repro() -> Result<(), Box<dyn std::error::Error>> {
-    let tmp_dir = TempDir::new("example")?;
+    let tmp_dir = tempdir()?;
     let repro_path = tmp_dir.path().join("repro.json");
     base_test(&tmp_dir, RsGenMode::Single, |cmd| {
         cmd.env("AUTOCXX_REPRO_CASE", repro_path.to_str().unwrap());
diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml
index c139479..7791ea5 100644
--- a/integration-tests/Cargo.toml
+++ b/integration-tests/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-integration-tests"
-version = "0.22.0"
+version = "0.22.3"
 autotests = false
 edition = "2021"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
@@ -27,15 +27,15 @@
 cc = "1.0"
 quote = "1.0"
 once_cell = "1.7"
-autocxx-engine = { version="=0.22.0", path="../engine", features = ["build"] }
+autocxx-engine = { version="=0.22.3", path="../engine", features = ["build"] }
 # This is necessary for building the projects created
 # by the trybuild test system...
-autocxx = { path="..", version="=0.22.0" }
+autocxx = { path="..", version="=0.22.3" }
 link-cplusplus = "1.0"
 tempfile = "3.1"
 indoc = "1.0"
 log = "0.4"
-cxx = "1.0.54"
+cxx = "1.0.68"
 itertools = "0.10"
 
 [dependencies.syn]
diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs
index 3499af8..352c8ab 100644
--- a/integration-tests/src/lib.rs
+++ b/integration-tests/src/lib.rs
@@ -21,7 +21,7 @@
 use log::info;
 use once_cell::sync::OnceCell;
 use proc_macro2::{Span, TokenStream};
-use quote::{quote, TokenStreamExt};
+use quote::{format_ident, quote, TokenStreamExt};
 use syn::Token;
 use tempfile::{tempdir, TempDir};
 
@@ -56,6 +56,7 @@
 pub enum RsFindMode {
     AutocxxRs,
     AutocxxRsArchive,
+    AutocxxRsFile,
 }
 
 /// API to test building pre-generated files.
@@ -169,6 +170,10 @@
                 "AUTOCXX_RS_JSON_ARCHIVE",
                 self.temp_dir.path().join("gen.rs.json"),
             ),
+            RsFindMode::AutocxxRsFile => std::env::set_var(
+                "AUTOCXX_RS_FILE",
+                self.temp_dir.path().join("gen0.include.rs"),
+            ),
         };
         std::panic::catch_unwind(|| {
             let test_cases = trybuild::TestCases::new();
@@ -200,6 +205,7 @@
         None,
         None,
         None,
+        "unsafe_ffi",
     )
     .unwrap()
 }
@@ -254,10 +260,23 @@
         builder_modifier,
         code_checker,
         extra_rust,
+        "unsafe_ffi",
     )
     .unwrap()
 }
 
+pub fn run_generate_all_test(header_code: &str) {
+    run_test_ex(
+        "",
+        header_code,
+        quote! {},
+        quote! { generate_all!() },
+        None,
+        None,
+        None,
+    );
+}
+
 pub fn run_test_expect_fail(
     cxx_code: &str,
     header_code: &str,
@@ -273,6 +292,7 @@
         None,
         None,
         None,
+        "unsafe_ffi",
     )
     .expect_err("Unexpected success");
 }
@@ -294,6 +314,7 @@
         builder_modifier,
         code_checker,
         extra_rust,
+        "unsafe_ffi",
     )
     .expect_err("Unexpected success");
 }
@@ -343,14 +364,16 @@
     builder_modifier: Option<BuilderModifier>,
     rust_code_checker: Option<CodeChecker>,
     extra_rust: Option<TokenStream>,
+    safety_policy: &str,
 ) -> Result<(), TestError> {
     let hexathorpe = Token![#](Span::call_site());
+    let safety_policy = format_ident!("{}", safety_policy);
     let unexpanded_rust = quote! {
             use autocxx::prelude::*;
 
             include_cpp!(
                 #hexathorpe include "input.h"
-                safety!(unsafe_ffi)
+                safety!(#safety_policy)
                 #directives
             );
 
diff --git a/integration-tests/tests/cpprefs_test.rs b/integration-tests/tests/cpprefs_test.rs
new file mode 100644
index 0000000..4241dec
--- /dev/null
+++ b/integration-tests/tests/cpprefs_test.rs
@@ -0,0 +1,96 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Tests specific to reference wrappers.
+
+use autocxx_integration_tests::{directives_from_lists, do_run_test};
+use indoc::indoc;
+use proc_macro2::TokenStream;
+use quote::quote;
+
+/// A positive test, we expect to pass.
+fn run_cpprefs_test(
+    cxx_code: &str,
+    header_code: &str,
+    rust_code: TokenStream,
+    generate: &[&str],
+    generate_pods: &[&str],
+) {
+    do_run_test(
+        cxx_code,
+        header_code,
+        rust_code,
+        directives_from_lists(generate, generate_pods, None),
+        None,
+        None,
+        None,
+        "unsafe_references_wrapped",
+    )
+    .unwrap()
+}
+
+#[test]
+fn test_method_call_mut() {
+    run_cpprefs_test(
+        "",
+        indoc! {"
+        #include <string>
+        #include <sstream>
+
+        class Goat {
+            public:
+                Goat() : horns(0) {}
+                void add_a_horn();
+            private:
+                uint32_t horns;
+        };
+
+        inline void Goat::add_a_horn() { horns++; }
+    "},
+        quote! {
+            let goat = ffi::Goat::new().within_unique_ptr();
+            let mut goat = ffi::CppUniquePtrPin::new(goat);
+            goat.as_cpp_mut_ref().add_a_horn();
+        },
+        &["Goat"],
+        &[],
+    )
+}
+
+#[test]
+fn test_method_call_const() {
+    run_cpprefs_test(
+        "",
+        indoc! {"
+        #include <string>
+        #include <sstream>
+
+        class Goat {
+            public:
+                Goat() : horns(0) {}
+                std::string describe() const;
+            private:
+                uint32_t horns;
+        };
+
+        inline std::string Goat::describe() const {
+            std::ostringstream oss;
+            std::string plural = horns == 1 ? \"\" : \"s\";
+            oss << \"This goat has \" << horns << \" horn\" << plural << \".\";
+            return oss.str();
+        }
+    "},
+        quote! {
+            let goat = ffi::Goat::new().within_unique_ptr();
+            let goat = ffi::cpp_pin_uniqueptr(goat);
+            goat.as_cpp_ref().describe();
+        },
+        &["Goat"],
+        &[],
+    )
+}
diff --git a/integration-tests/tests/integration_test.rs b/integration-tests/tests/integration_test.rs
index 2a3340a..16c792b8 100644
--- a/integration-tests/tests/integration_test.rs
+++ b/integration-tests/tests/integration_test.rs
@@ -16,8 +16,8 @@
     },
 };
 use autocxx_integration_tests::{
-    directives_from_lists, do_run_test, do_run_test_manual, run_test, run_test_ex,
-    run_test_expect_fail, run_test_expect_fail_ex, TestError,
+    directives_from_lists, do_run_test, do_run_test_manual, run_generate_all_test, run_test,
+    run_test_ex, run_test_expect_fail, run_test_expect_fail_ex, TestError,
 };
 use indoc::indoc;
 use itertools::Itertools;
@@ -3541,20 +3541,7 @@
         template <class _Ty>
         using _Remove_cvref_t = remove_cv_t<remove_reference_t<_Ty>>;
     "};
-
-    let rs = quote! {};
-
-    run_test_ex(
-        "",
-        hdr,
-        rs,
-        quote! {
-            generate_all!()
-        },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -5044,15 +5031,7 @@
         inline void a() {}
     };
     "};
-    run_test_ex(
-        "",
-        hdr,
-        quote! {},
-        quote! { generate_all!() },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -5941,6 +5920,7 @@
         None,
         None,
         None,
+        "unsafe_ffi",
     ) {
         Err(TestError::CppBuild(_)) => {} // be sure this fails due to a static_assert
         // rather than some runtime problem
@@ -6232,20 +6212,7 @@
 
         typedef bitset<1> mybitset;
     "};
-
-    let rs = quote! {};
-
-    run_test_ex(
-        "",
-        hdr,
-        rs,
-        quote! {
-            generate_all!()
-        },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -6433,18 +6400,7 @@
             typedef std::a<b<d>::c, int, int> e;
         };
     "};
-    let rs = quote! {};
-    run_test_ex(
-        "",
-        hdr,
-        rs,
-        quote! {
-            generate_all!()
-        },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -6505,18 +6461,7 @@
         }
         typedef char daft;
     "};
-    let rs = quote! {};
-    run_test_ex(
-        "",
-        hdr,
-        rs,
-        quote! {
-            generate_all!()
-        },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -6738,6 +6683,50 @@
 }
 
 #[test]
+/// Tests extern_cpp_type with a type inside a namespace.
+fn test_extern_cpp_type_namespace() {
+    let hdr = indoc! {"
+        #include <cstdint>
+        namespace b {
+        struct B {
+            B() {}
+        };
+        }  // namespace b
+        struct A {
+            A() {}
+            b::B make_b() { return b::B(); }
+        };
+    "};
+    let hexathorpe = Token![#](Span::call_site());
+    let rs = quote! {
+        pub mod b {
+            autocxx::include_cpp! {
+                #hexathorpe include "input.h"
+                safety!(unsafe_ffi)
+                name!(ffi_b)
+                generate_pod!("b::B")
+            }
+            pub use ffi_b::b::B;
+        }
+        pub mod a {
+            autocxx::include_cpp! {
+                #hexathorpe include "input.h"
+                safety!(unsafe_ffi)
+                name!(ffi_a)
+                generate_pod!("A")
+                extern_cpp_type!("b::B", crate::b::B)
+            }
+            pub use ffi_a::A;
+        }
+        fn main() {
+            use autocxx::prelude::*;
+            let _ = crate::a::A::new().within_unique_ptr().as_mut().unwrap().make_b();
+        }
+    };
+    do_run_test_manual("", hdr, rs, None, None).unwrap();
+}
+
+#[test]
 #[ignore] // because we currently require UniquePtrTarget which this can't implement
 fn test_extern_cpp_type_manual() {
     let hdr = indoc! {"
@@ -7600,6 +7589,49 @@
 }
 
 #[test]
+/// Tests the Rust code generated for subclasses when there's a `std` module in scope representing
+/// the C++ `std` namespace. This breaks if any of the generated Rust code fails to fully qualify
+/// its references to the Rust `std`.
+fn test_subclass_with_std() {
+    let hdr = indoc! {"
+    #include <cstdint>
+    #include <chrono>
+
+    class Observer {
+    public:
+        Observer() {}
+        virtual void foo() const {}
+        virtual ~Observer() {}
+
+        void unused(std::chrono::nanoseconds) {}
+    };
+    "};
+    run_test_ex(
+        "",
+        hdr,
+        quote! {
+            let obs = MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
+            obs.borrow().foo();
+        },
+        quote! {
+            subclass!("Observer",MyObserver)
+        },
+        None,
+        None,
+        Some(quote! {
+            use autocxx::subclass::CppSubclass;
+            use ffi::Observer_methods;
+            #[autocxx::subclass::subclass]
+            pub struct MyObserver {
+                a: u32
+            }
+            impl Observer_methods for MyObserver {
+            }
+        }),
+    );
+}
+
+#[test]
 fn test_two_subclasses() {
     let hdr = indoc! {"
     #include <cstdint>
@@ -7704,6 +7736,54 @@
 }
 
 #[test]
+fn test_subclass_no_safety() {
+    let hdr = indoc! {"
+    #include <cstdint>
+
+    class Observer {
+    public:
+        Observer() {}
+        virtual void foo() = 0;
+        virtual ~Observer() {}
+    };
+    "};
+    let hexathorpe = Token![#](Span::call_site());
+    let unexpanded_rust = quote! {
+        use autocxx::prelude::*;
+
+        include_cpp!(
+            #hexathorpe include "input.h"
+            subclass!("Observer",MyObserver)
+        );
+
+        use ffi::Observer_methods;
+        #hexathorpe [autocxx::subclass::subclass]
+        pub struct MyObserver;
+        impl Observer_methods for MyObserver {
+            unsafe fn foo(&mut self) {}
+        }
+
+        use autocxx::subclass::{CppSubclass, CppPeerConstructor, CppSubclassRustPeerHolder};
+        use cxx::UniquePtr;
+        impl CppPeerConstructor<ffi::MyObserverCpp> for MyObserver {
+            fn make_peer(
+                &mut self,
+                peer_holder: CppSubclassRustPeerHolder<Self>,
+            ) -> UniquePtr<ffi::MyObserverCpp> {
+                UniquePtr::emplace(unsafe { ffi::MyObserverCpp::new(peer_holder) })
+            }
+        }
+
+        fn main() {
+            let obs = MyObserver::new_rust_owned(MyObserver { cpp_peer: Default::default() });
+            unsafe { obs.borrow_mut().foo() };
+        }
+    };
+
+    do_run_test_manual("", hdr, unexpanded_rust, None, None).unwrap()
+}
+
+#[test]
 fn test_pv_protected_constructor() {
     let hdr = indoc! {"
     #include <cstdint>
@@ -8291,6 +8371,74 @@
 }
 
 #[test]
+fn test_pv_subclass_as_superclass() {
+    let hdr = indoc! {"
+    #include <cstdint>
+    #include <memory>
+
+    class TestObserver {
+    public:
+        TestObserver() {}
+        virtual void a() const = 0;
+        virtual ~TestObserver() {}
+    };
+
+    inline void call_observer(std::unique_ptr<TestObserver> obs) { obs->a(); }
+    "};
+    run_test_ex(
+        "",
+        hdr,
+        quote! {
+            use autocxx::subclass::CppSubclass;
+            let obs = MyTestObserver::new_cpp_owned(
+                MyTestObserver::default()
+            );
+            let obs = MyTestObserver::as_TestObserver_unique_ptr(obs);
+            assert!(!Lazy::force(&STATUS).lock().unwrap().dropped);
+            ffi::call_observer(obs);
+            assert!(Lazy::force(&STATUS).lock().unwrap().sub_a_called);
+            assert!(Lazy::force(&STATUS).lock().unwrap().dropped);
+            *Lazy::force(&STATUS).lock().unwrap() = Default::default();
+        },
+        quote! {
+            generate!("call_observer")
+            subclass!("TestObserver",MyTestObserver)
+        },
+        None,
+        None,
+        Some(quote! {
+            use once_cell::sync::Lazy;
+            use std::sync::Mutex;
+
+            use ffi::TestObserver_methods;
+            #[autocxx::subclass::subclass]
+            #[derive(Default)]
+            pub struct MyTestObserver {
+            }
+            impl TestObserver_methods for MyTestObserver {
+                fn a(&self) {
+                    assert!(!Lazy::force(&STATUS).lock().unwrap().dropped);
+                    Lazy::force(&STATUS).lock().unwrap().sub_a_called = true;
+                }
+            }
+            impl Drop for MyTestObserver {
+                fn drop(&mut self) {
+                    Lazy::force(&STATUS).lock().unwrap().dropped = true;
+                }
+            }
+
+            #[derive(Default)]
+            struct Status {
+                sub_a_called: bool,
+                dropped: bool,
+            }
+
+            static STATUS: Lazy<Mutex<Status>> = Lazy::new(|| Mutex::new(Status::default()));
+        }),
+    );
+}
+
+#[test]
 fn test_cycle_nonpod_simple() {
     let hdr = indoc! {"
     #include <string>
@@ -10925,15 +11073,7 @@
           d e();
         };
     "};
-    run_test_ex(
-        "",
-        hdr,
-        quote! {},
-        quote! { generate_all!() },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -10948,15 +11088,7 @@
         };
         } // namespace
     "};
-    run_test_ex(
-        "",
-        hdr,
-        quote! {},
-        quote! { generate_all!() },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -11015,15 +11147,7 @@
         };
         } // namespace a
     "};
-    run_test_ex(
-        "",
-        hdr,
-        quote! {},
-        quote! { generate_all!() },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -11044,15 +11168,7 @@
           _CharT b;
         };
     "};
-    run_test_ex(
-        "",
-        hdr,
-        quote! {},
-        quote! { generate_all!() },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -11063,15 +11179,7 @@
           b c;
         };
     "};
-    run_test_ex(
-        "",
-        hdr,
-        quote! {},
-        quote! { generate_all!() },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -11086,15 +11194,7 @@
         }
         }
     "};
-    run_test_ex(
-        "",
-        hdr,
-        quote! {},
-        quote! { generate_all!() },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -11116,15 +11216,171 @@
         } // namespace
         } // namespace a
     "};
-    run_test_ex(
-        "",
-        hdr,
-        quote! {},
-        quote! { generate_all!() },
-        None,
-        None,
-        None,
-    );
+    run_generate_all_test(hdr);
+}
+
+/// The problem here is that 'g' doesn't get annotated with
+/// the unused_template semantic attribute.
+/// This seems to be because both g and f have template
+/// parameters, so they're all "used", but effectively cancel
+/// out and thus bindgen generates
+///   pub type g = root::b::f;
+/// So, what we should do here is spot any typedef depending
+/// on a template which takes template args, and reject that too.
+/// Probably.
+#[test]
+#[ignore] // https://github.com/google/autocxx/pull/1094
+fn test_issue_1094() {
+    let hdr = indoc! {"
+        namespace {
+        typedef int a;
+        }
+        namespace b {
+        template <typename> struct c;
+        template <typename d, d e> using f = __make_integer_seq<c, d, e>;
+        template <a e> using g = f<a, e>;
+        } // namespace b
+    "};
+    run_generate_all_test(hdr);
+}
+
+#[test]
+fn test_issue_1096a() {
+    let hdr = indoc! {"
+        namespace a {
+        class b {
+          class c;
+        };
+        } // namespace a
+    "};
+    run_generate_all_test(hdr);
+}
+
+#[test]
+fn test_issue_1096b() {
+    let hdr = indoc! {"
+        namespace a {
+        class b {
+        public:
+          class c;
+        };
+        } // namespace a
+    "};
+    run_generate_all_test(hdr);
+}
+
+#[test]
+fn test_issue_1096c() {
+    let hdr = indoc! {"
+        namespace a {
+        class b {
+        public:
+          class c {
+          public:
+            int d;
+          };
+        };
+        } // namespace a
+    "};
+    run_generate_all_test(hdr);
+}
+
+#[test]
+fn test_issue_1096d() {
+    let hdr = indoc! {"
+        namespace a {
+        class b {
+        private:
+          class c {
+          public:
+            int d;
+          };
+        };
+        } // namespace a
+    "};
+    run_generate_all_test(hdr);
+}
+
+#[test]
+fn test_issue_1096e() {
+    let hdr = indoc! {"
+        namespace a {
+        class b {
+        private:
+          enum c {
+              D,
+          };
+        };
+        } // namespace a
+    "};
+    run_generate_all_test(hdr);
+}
+
+/// Unclear why minimization resulted in this particular test case.
+#[test]
+#[ignore] // https://github.com/google/autocxx/pull/1097
+fn test_issue_1097() {
+    let hdr = indoc! {"
+        namespace rust {
+        inline namespace a {
+        class Str {
+        public:
+          ~Str();
+        };
+        } // namespace a
+        } // namespace rust
+    "};
+    run_generate_all_test(hdr);
+}
+
+#[test]
+fn test_issue_1098a() {
+    let hdr = indoc! {"
+        namespace {
+        namespace {
+        template <typename _CharT> class a {
+          typedef _CharT b;
+          b c;
+        };
+        template <typename _CharT> class d : a<_CharT> {};
+        } // namespace
+        } // namespace
+    "};
+    run_generate_all_test(hdr);
+}
+
+/// Need to spot structs like this:
+/// pub struct d<_CharT> {
+///  _base: root::a<_CharT>,
+/// }
+/// and not create concrete types where the inner type is something from
+/// the outer context.
+#[test]
+fn test_issue_1098b() {
+    let hdr = indoc! {"
+        template <typename _CharT> class a {
+          typedef _CharT b;
+          b c;
+        };
+        template <typename _CharT> class d : a<_CharT> {};
+    "};
+    run_generate_all_test(hdr);
+}
+
+#[test]
+fn test_issue_1098c() {
+    let hdr = indoc! {"
+        namespace {
+        namespace {
+        struct A {
+            int a;
+        };
+        typedef A B;
+        } // namespace
+        } // namespace
+        inline void take_b(const B&) {}
+    "};
+    run_generate_all_test(hdr);
 }
 
 #[test]
@@ -11185,6 +11441,58 @@
     run_test("", hdr, rs, &["RenderFrameHost"], &[]);
 }
 
+#[test]
+fn test_issue_1081() {
+    let hdr = indoc! {"
+        namespace libtorrent {
+        char version;
+        }
+        namespace libtorrent {
+        struct session;
+        }
+    "};
+    let rs = quote! {};
+    run_test("", hdr, rs, &["libtorrent::session"], &[]);
+}
+
+#[test]
+#[ignore] // This test passes under all normal builds. However
+          // it triggers a stack use-after-return in older versions of
+          // libclang which is only detected under ASAN (obviously it
+          // sometimes causes crashes the rest of the time).
+          // This UaR does not occur when the same code is processed
+          // with a HEAD version of clang itself as of June 2022. This
+          // may mean that the UaR has been fixed in later versions of
+          // the clang code, or that it only occurs when the code is used
+          // in a libclang context (not a plain clang compilation context).
+          // If the problem recurs, we should work out which of these is
+          // the case.
+fn test_issue_1125() {
+    let hdr = indoc! {"
+        namespace {
+        namespace {
+        template <class a> class b {
+          typedef a c;
+          struct {
+            c : sizeof(c);
+          };
+        };
+        } // namespace
+        } // namespace
+    "};
+    run_test_ex(
+        "",
+        hdr,
+        quote! {},
+        quote! {
+            generate_all!()
+        },
+        make_cpp17_adder(),
+        None,
+        None,
+    );
+}
+
 // Yet to test:
 // - Ifdef
 // - Out param pointers
diff --git a/integration-tests/tests/lib.rs b/integration-tests/tests/lib.rs
index 17f076a..8d9eba9 100644
--- a/integration-tests/tests/lib.rs
+++ b/integration-tests/tests/lib.rs
@@ -8,4 +8,5 @@
 
 mod builder_modifiers;
 mod code_checkers;
+mod cpprefs_test;
 mod integration_test;
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index d1d633a..1f81ab1 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-macro"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
@@ -21,7 +21,7 @@
 proc-macro = true
 
 [dependencies]
-autocxx-parser = { path="../parser", version="=0.22.0" }
+autocxx-parser = { path="../parser", version="=0.22.3" }
 proc-macro-error = "1.0"
 proc-macro2 = "1.0.11"
 quote = "1.0"
diff --git a/parser/Cargo.toml b/parser/Cargo.toml
index 481f21d..3668e65 100644
--- a/parser/Cargo.toml
+++ b/parser/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-parser"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
diff --git a/parser/src/config.rs b/parser/src/config.rs
index 4204acf..8d30249 100644
--- a/parser/src/config.rs
+++ b/parser/src/config.rs
@@ -33,6 +33,7 @@
 pub enum UnsafePolicy {
     AllFunctionsSafe,
     AllFunctionsUnsafe,
+    ReferencesWrappedAllFunctionsSafe,
 }
 
 impl Default for UnsafePolicy {
@@ -50,8 +51,13 @@
             Some(id) => {
                 if id == "unsafe_ffi" {
                     Ok(UnsafePolicy::AllFunctionsSafe)
+                } else if id == "unsafe_references_wrapped" {
+                    Ok(UnsafePolicy::ReferencesWrappedAllFunctionsSafe)
                 } else {
-                    Err(syn::Error::new(id.span(), "expected unsafe_ffi"))
+                    Err(syn::Error::new(
+                        id.span(),
+                        "expected unsafe_ffi or unsafe_references_wrapped",
+                    ))
                 }
             }
             None => Ok(UnsafePolicy::AllFunctionsUnsafe),
@@ -70,10 +76,20 @@
     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
         if *self == UnsafePolicy::AllFunctionsSafe {
             tokens.extend(quote! { unsafe })
+        } else if *self == UnsafePolicy::ReferencesWrappedAllFunctionsSafe {
+            tokens.extend(quote! { unsafe_references_wrapped })
         }
     }
 }
 
+impl UnsafePolicy {
+    /// Whether we are treating C++ references as a different thing from Rust
+    /// references and therefore have to generate lots of code for a CppRef type
+    pub fn requires_cpprefs(&self) -> bool {
+        matches!(self, Self::ReferencesWrappedAllFunctionsSafe)
+    }
+}
+
 /// An entry in the allowlist.
 #[derive(Hash, Debug)]
 pub enum AllowlistEntry {
diff --git a/parser/src/directives.rs b/parser/src/directives.rs
index 160829e..70c88fc 100644
--- a/parser/src/directives.rs
+++ b/parser/src/directives.rs
@@ -268,10 +268,8 @@
     ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
         let policy = &config.unsafe_policy;
         match config.unsafe_policy {
-            crate::UnsafePolicy::AllFunctionsSafe => {
-                Box::new(std::iter::once(policy.to_token_stream()))
-            }
             crate::UnsafePolicy::AllFunctionsUnsafe => Box::new(std::iter::empty()),
+            _ => Box::new(std::iter::once(policy.to_token_stream())),
         }
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 6592011..4b9f26b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,10 +14,13 @@
 // do anything - all the magic is handled entirely by
 // autocxx_macro::include_cpp_impl.
 
+mod reference_wrapper;
 mod rvalue_param;
 pub mod subclass;
 mod value_param;
 
+pub use reference_wrapper::{CppMutRef, CppPin, CppRef};
+
 #[cfg_attr(doc, aquamarine::aquamarine)]
 /// Include some C++ headers in your Rust project.
 ///
@@ -257,6 +260,14 @@
 ///
 /// Generated C++ APIs which use raw pointers remain `unsafe`
 /// no matter what policy you choose.
+///
+/// There's an additional possible experimental safety
+/// policy available here:
+/// `safety!(unsafe_references_wrapped)`
+/// This policy treats C++ references as scary and requires
+/// them to be wrapped in a `CppRef` type. This `CppRef`
+/// type is implemented within the generated bindings but
+/// follows the contract of [`CppRef`].
 #[macro_export]
 macro_rules! safety {
     ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
@@ -613,6 +624,9 @@
     pub use crate::c_void;
     pub use crate::cpp_semantics;
     pub use crate::include_cpp;
+    pub use crate::CppMutRef;
+    pub use crate::CppPin;
+    pub use crate::CppRef;
     pub use crate::PinMut;
     pub use crate::RValueParam;
     pub use crate::ValueParam;
diff --git a/src/reference_wrapper.rs b/src/reference_wrapper.rs
new file mode 100644
index 0000000..ed943d3
--- /dev/null
+++ b/src/reference_wrapper.rs
@@ -0,0 +1,109 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// A C++ const reference. These are different from Rust's `&T` in that
+/// these may exist even while the object is mutated elsewhere.
+///
+/// This is a trait not a struct due to the nuances of Rust's orphan rule
+/// - implemntations of this trait are found in each set of generated bindings
+/// but they are essentially the same.
+pub trait CppRef<'a, T> {
+    /// Retrieve the underlying C++ pointer.
+    fn as_ptr(&self) -> *const T;
+
+    /// Get a regular Rust reference out of this C++ reference.
+    ///
+    /// # Safety
+    ///
+    /// Callers must guarantee that the referent is not modified by any other
+    /// C++ or Rust code while the returned reference exists. Callers must
+    /// also guarantee that no mutable Rust reference is created to the
+    /// referent while the returned reference exists.
+    unsafe fn as_ref(&self) -> &T {
+        &*self.as_ptr()
+    }
+}
+
+/// A C++ non-const reference. These are different from Rust's `&mut T` in that
+/// several C++ references can exist to the same underlying data ("aliasing")
+/// and that's not permitted in Rust.
+///
+/// This is a trait not a struct due to the nuances of Rust's orphan rule
+/// - implemntations of this trait are found in each set of generated bindings
+/// but they are essentially the same.
+pub trait CppMutRef<'a, T>: CppRef<'a, T> {
+    /// Retrieve the underlying C++ pointer.
+    fn as_mut_ptr(&self) -> *mut T;
+
+    /// Get a regular Rust mutable reference out of this C++ reference.
+    ///
+    /// # Safety
+    ///
+    /// Callers must guarantee that the referent is not modified by any other
+    /// C++ or Rust code while the returned reference exists. Callers must
+    /// also guarantee that no other Rust reference is created to the referent
+    /// while the returned reference exists.
+    unsafe fn as_mut(&mut self) -> &mut T {
+        &mut *self.as_mut_ptr()
+    }
+}
+
+/// Any newtype wrapper which causes the contained object to obey C++ reference
+/// semantics rather than Rust reference semantics.
+///
+/// The complex generics here are working around the orphan rule - the only
+/// important generic is `T` which is the underlying stored type.
+///
+/// C++ references are permitted to alias one another, and commonly do.
+/// Rust references must alias according only to the narrow rules of the
+/// borrow checker.
+///
+/// If you need C++ to access your Rust object, first imprison it in one of these
+/// objects, then use [`Self::as_cpp_ref`] to obtain C++ references to it.
+pub trait CppPin<'a, T: 'a> {
+    /// The type of C++ reference created to the contained object.
+    type CppRef: CppRef<'a, T>;
+
+    /// The type of C++ mutable reference created to the contained object..
+    type CppMutRef: CppMutRef<'a, T>;
+
+    /// Get an immutable pointer to the underlying object.
+    fn as_ptr(&self) -> *const T;
+
+    /// Get a mutable pointer to the underlying object.
+    fn as_mut_ptr(&mut self) -> *mut T;
+
+    /// Returns a reference which obeys C++ reference semantics
+    fn as_cpp_ref(&self) -> Self::CppRef;
+
+    /// Returns a mutable reference which obeys C++ reference semantics.
+    ///
+    /// Note that this requires unique ownership of `self`, but this is
+    /// advisory since the resulting reference can be cloned.
+    fn as_cpp_mut_ref(&mut self) -> Self::CppMutRef;
+
+    /// Get a normal Rust reference to the underlying object. This is unsafe.
+    ///
+    /// # Safety
+    ///
+    /// You must guarantee that C++ will not mutate the object while the
+    /// reference exists.
+    unsafe fn as_ref(&self) -> &T {
+        &*self.as_ptr()
+    }
+
+    /// Get a normal Rust mutable reference to the underlying object. This is unsafe.
+    ///
+    /// # Safety
+    ///
+    /// You must guarantee that C++ will not mutate the object while the
+    /// reference exists.
+    unsafe fn as_mut(&mut self) -> &mut T {
+        &mut *self.as_mut_ptr()
+    }
+}
diff --git a/src/subclass.rs b/src/subclass.rs
index d2827ca..6c6ee31 100644
--- a/src/subclass.rs
+++ b/src/subclass.rs
@@ -207,8 +207,9 @@
 /// * You _may_ need to implement [`CppPeerConstructor`] for your subclass,
 ///   but only if autocxx determines that there are multiple possible superclass
 ///   constructors so you need to call one explicitly (or if there's a single
-///   non-trivial superclass constructor.) autocxx will implemente this trait
-///   for you if there's no ambiguity.
+///   non-trivial superclass constructor.) autocxx will implement this trait
+///   for you if there's no ambiguity and FFI functions are safe to call due to
+///   `autocxx::safety!` being used.
 ///
 /// # How to access your Rust structure from outside
 ///
diff --git a/tools/mdbook-preprocessor/Cargo.toml b/tools/mdbook-preprocessor/Cargo.toml
index 19d775d..f88e833 100644
--- a/tools/mdbook-preprocessor/Cargo.toml
+++ b/tools/mdbook-preprocessor/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-mdbook-preprocessor"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["adetaylor <adetaylor@chromium.org>"]
 edition = "2021"
 
@@ -18,7 +18,7 @@
 itertools = "0.10"
 anyhow = "1"
 regex = "1"
-autocxx-integration-tests = { path = "../../integration-tests", version="=0.22.0"}
+autocxx-integration-tests = { path = "../../integration-tests", version="=0.22.3"}
 rayon = "1.5"
 gag = "1.0"
 env_logger = "0.9.0"
diff --git a/tools/reduce/Cargo.toml b/tools/reduce/Cargo.toml
index 092ccac..83e3916 100644
--- a/tools/reduce/Cargo.toml
+++ b/tools/reduce/Cargo.toml
@@ -8,15 +8,15 @@
 
 [package]
 name = "autocxx-reduce"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["adetaylor <adetaylor@chromium.org>"]
 edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-autocxx-engine = { version = "=0.22.0", path="../../engine" }
-autocxx-parser = { version = "=0.22.0", path="../../parser", features = [ "reproduction_case" ] }
+autocxx-engine = { version = "=0.22.3", path="../../engine" }
+autocxx-parser = { version = "=0.22.3", path="../../parser", features = [ "reproduction_case" ] }
 clap = { version = "3.1.2", features = ["cargo"] }
 tempfile = "3.1"
 indoc = "1.0"
@@ -26,12 +26,12 @@
 serde_derive = "1.0"
 syn = "1.0.39"
 quote = "1.0"
-cxx-gen = "0.7.54"
+cxx-gen = "0.7.68"
 regex = "1.5"
 indexmap = "1.8"
 
 [dev-dependencies]
 assert_cmd = "1.0.3"
-tempdir = "0.3.7"
+tempfile = "3.1"
 indoc = "1.0"
 proc-macro2 = "1.0"
\ No newline at end of file
diff --git a/tools/reduce/src/main.rs b/tools/reduce/src/main.rs
index 17c5bf0..06110cd 100644
--- a/tools/reduce/src/main.rs
+++ b/tools/reduce/src/main.rs
@@ -482,7 +482,7 @@
         mv concat.h concat-body.h
         echo Codegen
         (echo \"#ifndef __CONCAT_H__\"; echo \"#define __CONCAT_H__\"; echo '#include \"concat-body.h\"'; echo \"#endif\") > concat.h
-        ({} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} 2>&1 ) {}
+        (trap \"if [[ \\$? -eq 139 ]]; then echo Segfault; fi\" CHLD; {} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} 2>&1 ) {}
         echo Remove
         rm concat.h
         echo Swap back
diff --git a/tools/reduce/tests/reduce_test.rs b/tools/reduce/tests/reduce_test.rs
index ceac5f4..b277585 100644
--- a/tools/reduce/tests/reduce_test.rs
+++ b/tools/reduce/tests/reduce_test.rs
@@ -16,7 +16,7 @@
     path::{Path, PathBuf},
 };
 use syn::Token;
-use tempdir::TempDir;
+use tempfile::tempdir;
 
 static INPUT_H: &str = indoc::indoc! {"
     inline int DoMath(int a) {
@@ -169,7 +169,7 @@
     if creduce_is_broken() {
         return Ok(());
     }
-    let tmp_dir = TempDir::new("example")?;
+    let tmp_dir = tempdir()?;
     let demo_code_dir = tmp_dir.path().join("demo");
     std::fs::create_dir(&demo_code_dir).unwrap();
     let input_header = if include_cxx_h {
diff --git a/tools/stress-test/Cargo.toml b/tools/stress-test/Cargo.toml
index 6f15e42..a651f60 100644
--- a/tools/stress-test/Cargo.toml
+++ b/tools/stress-test/Cargo.toml
@@ -8,14 +8,14 @@
 
 [package]
 name = "autocxx-stress-test"
-version = "0.22.0"
+version = "0.22.3"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.54"
-autocxx = { path = "../..", version="0.22.0" }
+cxx = "1.0.68"
+autocxx = { path = "../..", version="0.22.3" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.0" }
+autocxx-build = { path = "../../gen/build", version="0.22.3" }
 miette = { version="4.3", features=["fancy"]}