Merge "Add rust LSP utilities"
diff --git a/Cargo.Bazel.lock b/Cargo.Bazel.lock
index 38f8003..e70a4ca 100644
--- a/Cargo.Bazel.lock
+++ b/Cargo.Bazel.lock
@@ -1,5 +1,5 @@
 {
-  "checksum": "9496d946b25ee32f2e88d66571320aad6d52655f5f4e1f646ddb64b89eca1136",
+  "checksum": "7305dfa1e83bfd0222912e22f8294905db9c35e036a34182aaaf7bafa13c28ac",
   "crates": {
     "addr2line 0.20.0": {
       "name": "addr2line",
@@ -233,6 +233,43 @@
       },
       "license": "MIT"
     },
+    "anstyle 1.0.1": {
+      "name": "anstyle",
+      "version": "1.0.1",
+      "repository": {
+        "Http": {
+          "url": "https://crates.io/api/v1/crates/anstyle/1.0.1/download",
+          "sha256": "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "anstyle",
+            "crate_root": "src/lib.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        }
+      ],
+      "library_target_name": "anstyle",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default",
+            "std"
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "1.0.1"
+      },
+      "license": "MIT OR Apache-2.0"
+    },
     "anyhow 1.0.71": {
       "name": "anyhow",
       "version": "1.0.71",
@@ -348,13 +385,13 @@
       },
       "license": "MIT"
     },
-    "assert_cmd 1.0.8": {
+    "assert_cmd 2.0.12": {
       "name": "assert_cmd",
-      "version": "1.0.8",
+      "version": "2.0.12",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/assert_cmd/1.0.8/download",
-          "sha256": "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe"
+          "url": "https://crates.io/api/v1/crates/assert_cmd/2.0.12/download",
+          "sha256": "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6"
         }
       },
       "targets": [
@@ -376,7 +413,11 @@
         "deps": {
           "common": [
             {
-              "id": "bstr 0.2.17",
+              "id": "anstyle 1.0.1",
+              "target": "anstyle"
+            },
+            {
+              "id": "bstr 1.6.0",
               "target": "bstr"
             },
             {
@@ -384,7 +425,7 @@
               "target": "doc_comment"
             },
             {
-              "id": "predicates 2.1.5",
+              "id": "predicates 3.0.3",
               "target": "predicates"
             },
             {
@@ -402,8 +443,8 @@
           ],
           "selects": {}
         },
-        "edition": "2018",
-        "version": "1.0.8"
+        "edition": "2021",
+        "version": "2.0.12"
       },
       "license": "MIT OR Apache-2.0"
     },
@@ -490,9 +531,9 @@
       },
       "license": "Apache-2.0 OR MIT"
     },
-    "autocxx 0.22.3": {
+    "autocxx 0.26.0": {
       "name": "autocxx",
-      "version": "0.22.3",
+      "version": "0.26.0",
       "repository": null,
       "targets": [
         {
@@ -503,6 +544,15 @@
               "**/*.rs"
             ]
           }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
         }
       ],
       "library_target_name": "autocxx",
@@ -513,11 +563,15 @@
         "deps": {
           "common": [
             {
+              "id": "autocxx 0.26.0",
+              "target": "build_script_build"
+            },
+            {
               "id": "cxx 1.0.97",
               "target": "cxx"
             },
             {
-              "id": "moveit 0.5.1",
+              "id": "moveit 0.6.0",
               "target": "moveit"
             }
           ],
@@ -533,24 +587,29 @@
           ],
           "selects": {}
         },
-        "version": "0.22.3"
+        "version": "0.26.0"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ]
       },
       "license": "MIT OR Apache-2.0"
     },
-    "autocxx-bindgen 0.59.17": {
+    "autocxx-bindgen 0.65.1": {
       "name": "autocxx-bindgen",
-      "version": "0.59.17",
+      "version": "0.65.1",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/autocxx-bindgen/0.59.17/download",
-          "sha256": "f9a9a26dd38d385d23b1bf61bd231b77f690c4368aef4c77cee1b7a6da2e2042"
+          "url": "https://crates.io/api/v1/crates/autocxx-bindgen/0.65.1/download",
+          "sha256": "6c9fb7b8dd83a582e12157367773d8d1195f2dea54d4250aaf3426abae3237aa"
         }
       },
       "targets": [
         {
           "Library": {
             "crate_name": "autocxx_bindgen",
-            "crate_root": "src/lib.rs",
+            "crate_root": "lib.rs",
             "srcs": [
               "**/*.rs"
             ]
@@ -573,9 +632,6 @@
         ],
         "crate_features": {
           "common": [
-            "clap",
-            "default",
-            "env_logger",
             "log",
             "logging",
             "runtime",
@@ -587,7 +643,7 @@
         "deps": {
           "common": [
             {
-              "id": "autocxx-bindgen 0.59.17",
+              "id": "autocxx-bindgen 0.65.1",
               "target": "build_script_build"
             },
             {
@@ -603,14 +659,6 @@
               "target": "clang_sys"
             },
             {
-              "id": "clap 2.34.0",
-              "target": "clap"
-            },
-            {
-              "id": "env_logger 0.9.3",
-              "target": "env_logger"
-            },
-            {
               "id": "itertools 0.10.5",
               "target": "itertools"
             },
@@ -631,6 +679,10 @@
               "target": "peeking_take_while"
             },
             {
+              "id": "prettyplease 0.2.12",
+              "target": "prettyplease"
+            },
+            {
               "id": "proc-macro2 1.0.63",
               "target": "proc_macro2"
             },
@@ -651,6 +703,10 @@
               "target": "shlex"
             },
             {
+              "id": "syn 2.0.28",
+              "target": "syn"
+            },
+            {
               "id": "which 4.4.0",
               "target": "which"
             }
@@ -658,7 +714,7 @@
           "selects": {}
         },
         "edition": "2018",
-        "version": "0.59.17"
+        "version": "0.65.1"
       },
       "build_script_attrs": {
         "data_glob": [
@@ -667,9 +723,9 @@
       },
       "license": "BSD-3-Clause"
     },
-    "autocxx-engine 0.22.3": {
+    "autocxx-engine 0.26.0": {
       "name": "autocxx-engine",
-      "version": "0.22.3",
+      "version": "0.26.0",
       "repository": null,
       "targets": [
         {
@@ -693,6 +749,7 @@
             "cc",
             "default",
             "reproduction_case",
+            "runtime",
             "serde_json"
           ],
           "selects": {}
@@ -700,7 +757,7 @@
         "deps": {
           "common": [
             {
-              "id": "autocxx-bindgen 0.59.17",
+              "id": "autocxx-bindgen 0.65.1",
               "target": "autocxx_bindgen"
             },
             {
@@ -724,7 +781,7 @@
               "target": "log"
             },
             {
-              "id": "miette 4.7.1",
+              "id": "miette 5.10.0",
               "target": "miette"
             },
             {
@@ -732,6 +789,10 @@
               "target": "once_cell"
             },
             {
+              "id": "prettyplease 0.2.12",
+              "target": "prettyplease"
+            },
+            {
               "id": "proc-macro2 1.0.63",
               "target": "proc_macro2"
             },
@@ -748,7 +809,7 @@
               "target": "serde_json"
             },
             {
-              "id": "syn 1.0.109",
+              "id": "syn 2.0.28",
               "target": "syn"
             },
             {
@@ -778,19 +839,19 @@
               "target": "indoc"
             },
             {
-              "id": "strum_macros 0.24.3",
-              "target": "strum_macros"
+              "id": "rustversion 1.0.13",
+              "target": "rustversion"
             }
           ],
           "selects": {}
         },
-        "version": "0.22.3"
+        "version": "0.26.0"
       },
       "license": "MIT OR Apache-2.0"
     },
-    "autocxx-gen 0.22.3": {
+    "autocxx-gen 0.26.0": {
       "name": "autocxx-gen",
-      "version": "0.22.3",
+      "version": "0.26.0",
       "repository": null,
       "targets": [],
       "library_target_name": null,
@@ -813,7 +874,7 @@
               "target": "indexmap"
             },
             {
-              "id": "miette 4.7.1",
+              "id": "miette 5.10.0",
               "target": "miette"
             },
             {
@@ -830,7 +891,7 @@
         "deps_dev": {
           "common": [
             {
-              "id": "assert_cmd 1.0.8",
+              "id": "assert_cmd 2.0.12",
               "target": "assert_cmd"
             },
             {
@@ -849,13 +910,13 @@
           "selects": {}
         },
         "edition": "2021",
-        "version": "0.22.3"
+        "version": "0.26.0"
       },
       "license": "MIT OR Apache-2.0"
     },
-    "autocxx-integration-tests 0.22.3": {
+    "autocxx-integration-tests 0.26.0": {
       "name": "autocxx-integration-tests",
-      "version": "0.22.3",
+      "version": "0.26.0",
       "repository": null,
       "targets": [
         {
@@ -900,6 +961,10 @@
               "target": "log"
             },
             {
+              "id": "moveit 0.6.0",
+              "target": "moveit"
+            },
+            {
               "id": "once_cell 1.18.0",
               "target": "once_cell"
             },
@@ -916,6 +981,10 @@
               "target": "rust_info"
             },
             {
+              "id": "static_assertions 1.1.0",
+              "target": "static_assertions"
+            },
+            {
               "id": "syn 1.0.109",
               "target": "syn"
             },
@@ -938,19 +1007,23 @@
               "target": "indoc"
             },
             {
+              "id": "rustversion 1.0.13",
+              "target": "rustversion"
+            },
+            {
               "id": "test-log 0.2.12",
               "target": "test_log"
             }
           ],
           "selects": {}
         },
-        "version": "0.22.3"
+        "version": "0.26.0"
       },
       "license": "MIT OR Apache-2.0"
     },
-    "autocxx-macro 0.22.3": {
+    "autocxx-macro 0.26.0": {
       "name": "autocxx-macro",
-      "version": "0.22.3",
+      "version": "0.26.0",
       "repository": null,
       "targets": [
         {
@@ -983,20 +1056,20 @@
               "target": "quote"
             },
             {
-              "id": "syn 1.0.109",
+              "id": "syn 2.0.28",
               "target": "syn"
             }
           ],
           "selects": {}
         },
         "edition": "2021",
-        "version": "0.22.3"
+        "version": "0.26.0"
       },
       "license": "MIT OR Apache-2.0"
     },
-    "autocxx-parser 0.22.3": {
+    "autocxx-parser 0.26.0": {
       "name": "autocxx-parser",
-      "version": "0.22.3",
+      "version": "0.26.0",
       "repository": null,
       "targets": [
         {
@@ -1055,7 +1128,7 @@
               "target": "serde_json"
             },
             {
-              "id": "syn 1.0.109",
+              "id": "syn 2.0.28",
               "target": "syn"
             },
             {
@@ -1066,7 +1139,7 @@
           "selects": {}
         },
         "edition": "2021",
-        "version": "0.22.3"
+        "version": "0.26.0"
       },
       "license": "MIT OR Apache-2.0"
     },
@@ -1163,6 +1236,45 @@
       },
       "license": "MIT OR Apache-2.0"
     },
+    "backtrace-ext 0.2.1": {
+      "name": "backtrace-ext",
+      "version": "0.2.1",
+      "repository": {
+        "Http": {
+          "url": "https://crates.io/api/v1/crates/backtrace-ext/0.2.1/download",
+          "sha256": "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "backtrace_ext",
+            "crate_root": "src/lib.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        }
+      ],
+      "library_target_name": "backtrace_ext",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "backtrace 0.3.68",
+              "target": "backtrace"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.2.1"
+      },
+      "license": "MIT OR Apache-2.0"
+    },
     "basic-toml 0.1.3": {
       "name": "basic-toml",
       "version": "0.1.3",
@@ -1364,13 +1476,49 @@
       },
       "license": "MIT/Apache-2.0"
     },
-    "bstr 0.2.17": {
-      "name": "bstr",
-      "version": "0.2.17",
+    "bitflags 2.3.3": {
+      "name": "bitflags",
+      "version": "2.3.3",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/bstr/0.2.17/download",
-          "sha256": "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+          "url": "https://crates.io/api/v1/crates/bitflags/2.3.3/download",
+          "sha256": "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "bitflags",
+            "crate_root": "src/lib.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        }
+      ],
+      "library_target_name": "bitflags",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "std"
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "2.3.3"
+      },
+      "license": "MIT OR Apache-2.0"
+    },
+    "bstr 1.6.0": {
+      "name": "bstr",
+      "version": "1.6.0",
+      "repository": {
+        "Http": {
+          "url": "https://crates.io/api/v1/crates/bstr/1.6.0/download",
+          "sha256": "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
         }
       },
       "targets": [
@@ -1391,9 +1539,8 @@
         ],
         "crate_features": {
           "common": [
+            "alloc",
             "default",
-            "lazy_static",
-            "regex-automata",
             "std",
             "unicode"
           ],
@@ -1402,22 +1549,18 @@
         "deps": {
           "common": [
             {
-              "id": "lazy_static 1.4.0",
-              "target": "lazy_static"
-            },
-            {
               "id": "memchr 2.5.0",
               "target": "memchr"
             },
             {
-              "id": "regex-automata 0.1.10",
+              "id": "regex-automata 0.3.2",
               "target": "regex_automata"
             }
           ],
           "selects": {}
         },
-        "edition": "2018",
-        "version": "0.2.17"
+        "edition": "2021",
+        "version": "1.6.0"
       },
       "license": "MIT OR Apache-2.0"
     },
@@ -2107,7 +2250,7 @@
               "target": "quote"
             },
             {
-              "id": "syn 2.0.22",
+              "id": "syn 2.0.28",
               "target": "syn"
             }
           ],
@@ -2190,7 +2333,7 @@
               "target": "quote"
             },
             {
-              "id": "syn 2.0.22",
+              "id": "syn 2.0.28",
               "target": "syn"
             }
           ],
@@ -3020,7 +3163,7 @@
               "target": "quote"
             },
             {
-              "id": "syn 2.0.22",
+              "id": "syn 2.0.28",
               "target": "syn"
             }
           ],
@@ -3411,42 +3554,6 @@
       },
       "license": "MIT OR Apache-2.0"
     },
-    "heck 0.4.1": {
-      "name": "heck",
-      "version": "0.4.1",
-      "repository": {
-        "Http": {
-          "url": "https://crates.io/api/v1/crates/heck/0.4.1/download",
-          "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-        }
-      },
-      "targets": [
-        {
-          "Library": {
-            "crate_name": "heck",
-            "crate_root": "src/lib.rs",
-            "srcs": [
-              "**/*.rs"
-            ]
-          }
-        }
-      ],
-      "library_target_name": "heck",
-      "common_attrs": {
-        "compile_data_glob": [
-          "**"
-        ],
-        "crate_features": {
-          "common": [
-            "default"
-          ],
-          "selects": {}
-        },
-        "edition": "2018",
-        "version": "0.4.1"
-      },
-      "license": "MIT OR Apache-2.0"
-    },
     "hermit-abi 0.1.19": {
       "name": "hermit-abi",
       "version": "0.1.19",
@@ -3773,6 +3880,59 @@
       },
       "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
     },
+    "is-terminal 0.4.9": {
+      "name": "is-terminal",
+      "version": "0.4.9",
+      "repository": {
+        "Http": {
+          "url": "https://crates.io/api/v1/crates/is-terminal/0.4.9/download",
+          "sha256": "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "is_terminal",
+            "crate_root": "src/lib.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        }
+      ],
+      "library_target_name": "is_terminal",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [],
+          "selects": {
+            "cfg(not(any(windows, target_os = \"hermit\", target_os = \"unknown\")))": [
+              {
+                "id": "rustix 0.38.7",
+                "target": "rustix"
+              }
+            ],
+            "cfg(target_os = \"hermit\")": [
+              {
+                "id": "hermit-abi 0.3.2",
+                "target": "hermit_abi"
+              }
+            ],
+            "cfg(windows)": [
+              {
+                "id": "windows-sys 0.48.0",
+                "target": "windows_sys"
+              }
+            ]
+          }
+        },
+        "edition": "2018",
+        "version": "0.4.9"
+      },
+      "license": "MIT"
+    },
     "is_ci 1.1.1": {
       "name": "is_ci",
       "version": "1.1.1",
@@ -4269,6 +4429,45 @@
       },
       "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
     },
+    "linux-raw-sys 0.4.5": {
+      "name": "linux-raw-sys",
+      "version": "0.4.5",
+      "repository": {
+        "Http": {
+          "url": "https://crates.io/api/v1/crates/linux-raw-sys/0.4.5/download",
+          "sha256": "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "linux_raw_sys",
+            "crate_root": "src/lib.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        }
+      ],
+      "library_target_name": "linux_raw_sys",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "errno",
+            "general",
+            "ioctl",
+            "no_std"
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.4.5"
+      },
+      "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
+    },
     "log 0.4.19": {
       "name": "log",
       "version": "0.4.19",
@@ -4369,13 +4568,13 @@
       },
       "license": "Unlicense/MIT"
     },
-    "miette 4.7.1": {
+    "miette 5.10.0": {
       "name": "miette",
-      "version": "4.7.1",
+      "version": "5.10.0",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/miette/4.7.1/download",
-          "sha256": "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c"
+          "url": "https://crates.io/api/v1/crates/miette/5.10.0/download",
+          "sha256": "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
         }
       },
       "targets": [
@@ -4396,11 +4595,12 @@
         ],
         "crate_features": {
           "common": [
-            "atty",
             "backtrace",
+            "backtrace-ext",
             "default",
             "fancy",
             "fancy-no-backtrace",
+            "is-terminal",
             "owo-colors",
             "supports-color",
             "supports-hyperlinks",
@@ -4413,14 +4613,18 @@
         "deps": {
           "common": [
             {
-              "id": "atty 0.2.14",
-              "target": "atty"
-            },
-            {
               "id": "backtrace 0.3.68",
               "target": "backtrace"
             },
             {
+              "id": "backtrace-ext 0.2.1",
+              "target": "backtrace_ext"
+            },
+            {
+              "id": "is-terminal 0.4.9",
+              "target": "is_terminal"
+            },
+            {
               "id": "once_cell 1.18.0",
               "target": "once_cell"
             },
@@ -4429,15 +4633,15 @@
               "target": "owo_colors"
             },
             {
-              "id": "supports-color 1.3.1",
+              "id": "supports-color 2.0.0",
               "target": "supports_color"
             },
             {
-              "id": "supports-hyperlinks 1.2.0",
+              "id": "supports-hyperlinks 2.1.0",
               "target": "supports_hyperlinks"
             },
             {
-              "id": "supports-unicode 1.0.2",
+              "id": "supports-unicode 2.0.0",
               "target": "supports_unicode"
             },
             {
@@ -4463,23 +4667,23 @@
         "proc_macro_deps": {
           "common": [
             {
-              "id": "miette-derive 4.7.1",
+              "id": "miette-derive 5.10.0",
               "target": "miette_derive"
             }
           ],
           "selects": {}
         },
-        "version": "4.7.1"
+        "version": "5.10.0"
       },
       "license": "Apache-2.0"
     },
-    "miette-derive 4.7.1": {
+    "miette-derive 5.10.0": {
       "name": "miette-derive",
-      "version": "4.7.1",
+      "version": "5.10.0",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/miette-derive/4.7.1/download",
-          "sha256": "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c"
+          "url": "https://crates.io/api/v1/crates/miette-derive/5.10.0/download",
+          "sha256": "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
         }
       },
       "targets": [
@@ -4509,14 +4713,14 @@
               "target": "quote"
             },
             {
-              "id": "syn 1.0.109",
+              "id": "syn 2.0.28",
               "target": "syn"
             }
           ],
           "selects": {}
         },
         "edition": "2018",
-        "version": "4.7.1"
+        "version": "5.10.0"
       },
       "license": "Apache-2.0"
     },
@@ -4595,13 +4799,13 @@
       },
       "license": "MIT OR Zlib OR Apache-2.0"
     },
-    "moveit 0.5.1": {
+    "moveit 0.6.0": {
       "name": "moveit",
-      "version": "0.5.1",
+      "version": "0.6.0",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/moveit/0.5.1/download",
-          "sha256": "d7d756ffe4e38013507d35bf726a93fcdae2cae043ab5ce477f13857a335030d"
+          "url": "https://crates.io/api/v1/crates/moveit/0.6.0/download",
+          "sha256": "87d7335204cb6ef7bd647fa6db0be3e4d7aa25b5823a7aa030027ddf512cefba"
         }
       },
       "targets": [
@@ -4638,7 +4842,7 @@
           "selects": {}
         },
         "edition": "2018",
-        "version": "0.5.1"
+        "version": "0.6.0"
       },
       "license": "Apache-2.0 OR MIT"
     },
@@ -5041,13 +5245,13 @@
       },
       "license": "MIT OR Apache-2.0"
     },
-    "predicates 2.1.5": {
+    "predicates 3.0.3": {
       "name": "predicates",
-      "version": "2.1.5",
+      "version": "3.0.3",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/predicates/2.1.5/download",
-          "sha256": "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
+          "url": "https://crates.io/api/v1/crates/predicates/3.0.3/download",
+          "sha256": "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9"
         }
       },
       "targets": [
@@ -5075,6 +5279,10 @@
         "deps": {
           "common": [
             {
+              "id": "anstyle 1.0.1",
+              "target": "anstyle"
+            },
+            {
               "id": "difflib 0.4.0",
               "target": "difflib"
             },
@@ -5090,7 +5298,7 @@
           "selects": {}
         },
         "edition": "2021",
-        "version": "2.1.5"
+        "version": "3.0.3"
       },
       "license": "MIT OR Apache-2.0"
     },
@@ -5167,6 +5375,74 @@
       },
       "license": "MIT OR Apache-2.0"
     },
+    "prettyplease 0.2.12": {
+      "name": "prettyplease",
+      "version": "0.2.12",
+      "repository": {
+        "Http": {
+          "url": "https://crates.io/api/v1/crates/prettyplease/0.2.12/download",
+          "sha256": "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "prettyplease",
+            "crate_root": "src/lib.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        }
+      ],
+      "library_target_name": "prettyplease",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "verbatim"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "prettyplease 0.2.12",
+              "target": "build_script_build"
+            },
+            {
+              "id": "proc-macro2 1.0.63",
+              "target": "proc_macro2"
+            },
+            {
+              "id": "syn 2.0.28",
+              "target": "syn"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.2.12"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ],
+        "links": "prettyplease02"
+      },
+      "license": "MIT OR Apache-2.0"
+    },
     "proc-macro-error 1.0.4": {
       "name": "proc-macro-error",
       "version": "1.0.4",
@@ -5569,36 +5845,6 @@
       },
       "license": "MIT OR Apache-2.0"
     },
-    "regex-automata 0.1.10": {
-      "name": "regex-automata",
-      "version": "0.1.10",
-      "repository": {
-        "Http": {
-          "url": "https://crates.io/api/v1/crates/regex-automata/0.1.10/download",
-          "sha256": "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-        }
-      },
-      "targets": [
-        {
-          "Library": {
-            "crate_name": "regex_automata",
-            "crate_root": "src/lib.rs",
-            "srcs": [
-              "**/*.rs"
-            ]
-          }
-        }
-      ],
-      "library_target_name": "regex_automata",
-      "common_attrs": {
-        "compile_data_glob": [
-          "**"
-        ],
-        "edition": "2015",
-        "version": "0.1.10"
-      },
-      "license": "Unlicense/MIT"
-    },
     "regex-automata 0.3.2": {
       "name": "regex-automata",
       "version": "0.3.2",
@@ -5628,6 +5874,7 @@
           "common": [
             "alloc",
             "dfa-onepass",
+            "dfa-search",
             "hybrid",
             "meta",
             "nfa-backtrack",
@@ -5965,6 +6212,107 @@
       },
       "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
     },
+    "rustix 0.38.7": {
+      "name": "rustix",
+      "version": "0.38.7",
+      "repository": {
+        "Http": {
+          "url": "https://crates.io/api/v1/crates/rustix/0.38.7/download",
+          "sha256": "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "rustix",
+            "crate_root": "src/lib.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        }
+      ],
+      "library_target_name": "rustix",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default",
+            "std",
+            "termios",
+            "use-libc-auxv"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "bitflags 2.3.3",
+              "target": "bitflags"
+            },
+            {
+              "id": "rustix 0.38.7",
+              "target": "build_script_build"
+            }
+          ],
+          "selects": {
+            "cfg(all(any(target_os = \"android\", target_os = \"linux\"), any(rustix_use_libc, miri, not(all(target_os = \"linux\", target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))": [
+              {
+                "id": "linux-raw-sys 0.4.5",
+                "target": "linux_raw_sys"
+              }
+            ],
+            "cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))": [
+              {
+                "id": "linux-raw-sys 0.4.5",
+                "target": "linux_raw_sys"
+              }
+            ],
+            "cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))": [
+              {
+                "id": "errno 0.3.1",
+                "target": "errno",
+                "alias": "libc_errno"
+              },
+              {
+                "id": "libc 0.2.147",
+                "target": "libc"
+              }
+            ],
+            "cfg(windows)": [
+              {
+                "id": "errno 0.3.1",
+                "target": "errno",
+                "alias": "libc_errno"
+              },
+              {
+                "id": "windows-sys 0.48.0",
+                "target": "windows_sys"
+              }
+            ]
+          }
+        },
+        "edition": "2021",
+        "version": "0.38.7"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ]
+      },
+      "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
+    },
     "rustversion 1.0.13": {
       "name": "rustversion",
       "version": "1.0.13",
@@ -6221,7 +6569,7 @@
               "target": "quote"
             },
             {
-              "id": "syn 2.0.22",
+              "id": "syn 2.0.28",
               "target": "syn"
             }
           ],
@@ -6470,6 +6818,36 @@
       },
       "license": "MIT"
     },
+    "static_assertions 1.1.0": {
+      "name": "static_assertions",
+      "version": "1.1.0",
+      "repository": {
+        "Http": {
+          "url": "https://crates.io/api/v1/crates/static_assertions/1.1.0/download",
+          "sha256": "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "static_assertions",
+            "crate_root": "src/lib.rs",
+            "srcs": [
+              "**/*.rs"
+            ]
+          }
+        }
+      ],
+      "library_target_name": "static_assertions",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2015",
+        "version": "1.1.0"
+      },
+      "license": "MIT OR Apache-2.0"
+    },
     "strsim 0.10.0": {
       "name": "strsim",
       "version": "0.10.0",
@@ -6530,73 +6908,13 @@
       },
       "license": "MIT"
     },
-    "strum_macros 0.24.3": {
-      "name": "strum_macros",
-      "version": "0.24.3",
-      "repository": {
-        "Http": {
-          "url": "https://crates.io/api/v1/crates/strum_macros/0.24.3/download",
-          "sha256": "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
-        }
-      },
-      "targets": [
-        {
-          "ProcMacro": {
-            "crate_name": "strum_macros",
-            "crate_root": "src/lib.rs",
-            "srcs": [
-              "**/*.rs"
-            ]
-          }
-        }
-      ],
-      "library_target_name": "strum_macros",
-      "common_attrs": {
-        "compile_data_glob": [
-          "**"
-        ],
-        "deps": {
-          "common": [
-            {
-              "id": "heck 0.4.1",
-              "target": "heck"
-            },
-            {
-              "id": "proc-macro2 1.0.63",
-              "target": "proc_macro2"
-            },
-            {
-              "id": "quote 1.0.29",
-              "target": "quote"
-            },
-            {
-              "id": "syn 1.0.109",
-              "target": "syn"
-            }
-          ],
-          "selects": {}
-        },
-        "edition": "2018",
-        "proc_macro_deps": {
-          "common": [
-            {
-              "id": "rustversion 1.0.13",
-              "target": "rustversion"
-            }
-          ],
-          "selects": {}
-        },
-        "version": "0.24.3"
-      },
-      "license": "MIT"
-    },
-    "supports-color 1.3.1": {
+    "supports-color 2.0.0": {
       "name": "supports-color",
-      "version": "1.3.1",
+      "version": "2.0.0",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/supports-color/1.3.1/download",
-          "sha256": "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f"
+          "url": "https://crates.io/api/v1/crates/supports-color/2.0.0/download",
+          "sha256": "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354"
         }
       },
       "targets": [
@@ -6618,8 +6936,8 @@
         "deps": {
           "common": [
             {
-              "id": "atty 0.2.14",
-              "target": "atty"
+              "id": "is-terminal 0.4.9",
+              "target": "is_terminal"
             },
             {
               "id": "is_ci 1.1.1",
@@ -6629,17 +6947,17 @@
           "selects": {}
         },
         "edition": "2018",
-        "version": "1.3.1"
+        "version": "2.0.0"
       },
       "license": "Apache-2.0"
     },
-    "supports-hyperlinks 1.2.0": {
+    "supports-hyperlinks 2.1.0": {
       "name": "supports-hyperlinks",
-      "version": "1.2.0",
+      "version": "2.1.0",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/supports-hyperlinks/1.2.0/download",
-          "sha256": "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406"
+          "url": "https://crates.io/api/v1/crates/supports-hyperlinks/2.1.0/download",
+          "sha256": "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
         }
       },
       "targets": [
@@ -6661,24 +6979,24 @@
         "deps": {
           "common": [
             {
-              "id": "atty 0.2.14",
-              "target": "atty"
+              "id": "is-terminal 0.4.9",
+              "target": "is_terminal"
             }
           ],
           "selects": {}
         },
-        "edition": "2018",
-        "version": "1.2.0"
+        "edition": "2021",
+        "version": "2.1.0"
       },
       "license": "Apache-2.0"
     },
-    "supports-unicode 1.0.2": {
+    "supports-unicode 2.0.0": {
       "name": "supports-unicode",
-      "version": "1.0.2",
+      "version": "2.0.0",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/supports-unicode/1.0.2/download",
-          "sha256": "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2"
+          "url": "https://crates.io/api/v1/crates/supports-unicode/2.0.0/download",
+          "sha256": "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
         }
       },
       "targets": [
@@ -6700,14 +7018,14 @@
         "deps": {
           "common": [
             {
-              "id": "atty 0.2.14",
-              "target": "atty"
+              "id": "is-terminal 0.4.9",
+              "target": "is_terminal"
             }
           ],
           "selects": {}
         },
         "edition": "2018",
-        "version": "1.0.2"
+        "version": "2.0.0"
       },
       "license": "Apache-2.0"
     },
@@ -6790,13 +7108,13 @@
       },
       "license": "MIT OR Apache-2.0"
     },
-    "syn 2.0.22": {
+    "syn 2.0.28": {
       "name": "syn",
-      "version": "2.0.22",
+      "version": "2.0.28",
       "repository": {
         "Http": {
-          "url": "https://crates.io/api/v1/crates/syn/2.0.22/download",
-          "sha256": "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
+          "url": "https://crates.io/api/v1/crates/syn/2.0.28/download",
+          "sha256": "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
         }
       },
       "targets": [
@@ -6820,11 +7138,13 @@
             "clone-impls",
             "default",
             "derive",
+            "extra-traits",
             "full",
             "parsing",
             "printing",
             "proc-macro",
-            "quote"
+            "quote",
+            "visit-mut"
           ],
           "selects": {}
         },
@@ -6846,7 +7166,7 @@
           "selects": {}
         },
         "edition": "2021",
-        "version": "2.0.22"
+        "version": "2.0.28"
       },
       "license": "MIT OR Apache-2.0"
     },
@@ -7334,7 +7654,7 @@
               "target": "quote"
             },
             {
-              "id": "syn 2.0.22",
+              "id": "syn 2.0.28",
               "target": "syn"
             }
           ],
@@ -8568,25 +8888,33 @@
   },
   "binary_crates": [],
   "workspace_members": {
-    "autocxx 0.22.3": "third_party/autocxx",
-    "autocxx-engine 0.22.3": "third_party/autocxx/engine",
-    "autocxx-gen 0.22.3": "third_party/autocxx/gen/cmd",
-    "autocxx-integration-tests 0.22.3": "third_party/autocxx/integration-tests",
-    "autocxx-macro 0.22.3": "third_party/autocxx/macro",
-    "autocxx-parser 0.22.3": "third_party/autocxx/parser",
+    "autocxx 0.26.0": "third_party/autocxx",
+    "autocxx-engine 0.26.0": "third_party/autocxx/engine",
+    "autocxx-gen 0.26.0": "third_party/autocxx/gen/cmd",
+    "autocxx-integration-tests 0.26.0": "third_party/autocxx/integration-tests",
+    "autocxx-macro 0.26.0": "third_party/autocxx/macro",
+    "autocxx-parser 0.26.0": "third_party/autocxx/parser",
     "compile_with_bazel 0.0.0": "",
     "flatbuffers 22.10.26": "third_party/flatbuffers/rust/flatbuffers"
   },
   "conditions": {
     "aarch64-pc-windows-gnullvm": [],
     "cfg(all(any(target_os = \"android\", target_os = \"linux\"), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"), all(target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"powerpc64\", target_arch = \"riscv64\", target_arch = \"mips\", target_arch = \"mips64\"))))))))": [],
+    "cfg(all(any(target_os = \"android\", target_os = \"linux\"), any(rustix_use_libc, miri, not(all(target_os = \"linux\", target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))": [],
     "cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"), all(target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"powerpc64\", target_arch = \"riscv64\", target_arch = \"mips\", target_arch = \"mips64\")))))": [
       "aarch64-unknown-linux-gnu",
       "arm-unknown-linux-gnueabi",
       "armv7-unknown-linux-gnueabihf",
       "x86_64-unknown-linux-gnu"
     ],
+    "cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))": [
+      "aarch64-unknown-linux-gnu",
+      "arm-unknown-linux-gnueabi",
+      "armv7-unknown-linux-gnueabihf",
+      "x86_64-unknown-linux-gnu"
+    ],
     "cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"), all(target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"powerpc64\", target_arch = \"riscv64\", target_arch = \"mips\", target_arch = \"mips64\"))))))))": [],
+    "cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", target_endian = \"little\", any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))": [],
     "cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))": [],
     "cfg(all(target_arch = \"wasm32\", not(target_os = \"wasi\")))": [],
     "cfg(all(target_arch = \"x86\", target_env = \"gnu\", not(windows_raw_dylib)))": [],
@@ -8613,6 +8941,12 @@
       "armv7-unknown-linux-gnueabihf",
       "x86_64-unknown-linux-gnu"
     ],
+    "cfg(not(any(windows, target_os = \"hermit\", target_os = \"unknown\")))": [
+      "aarch64-unknown-linux-gnu",
+      "arm-unknown-linux-gnueabi",
+      "armv7-unknown-linux-gnueabihf",
+      "x86_64-unknown-linux-gnu"
+    ],
     "cfg(not(windows))": [
       "aarch64-unknown-linux-gnu",
       "arm-unknown-linux-gnueabi",
diff --git a/Cargo.lock b/Cargo.lock
index cd97ba0..5fcc5eb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -47,6 +47,12 @@
 ]
 
 [[package]]
+name = "anstyle"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+
+[[package]]
 name = "anyhow"
 version = "1.0.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -67,10 +73,11 @@
 
 [[package]]
 name = "assert_cmd"
-version = "1.0.8"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe"
+checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6"
 dependencies = [
+ "anstyle",
  "bstr",
  "doc-comment",
  "predicates",
@@ -98,7 +105,7 @@
 
 [[package]]
 name = "autocxx"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "aquamarine",
  "autocxx-macro",
@@ -108,31 +115,31 @@
 
 [[package]]
 name = "autocxx-bindgen"
-version = "0.59.17"
+version = "0.65.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a9a26dd38d385d23b1bf61bd231b77f690c4368aef4c77cee1b7a6da2e2042"
+checksum = "6c9fb7b8dd83a582e12157367773d8d1195f2dea54d4250aaf3426abae3237aa"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "cexpr 0.6.0",
  "clang-sys",
- "clap 2.34.0",
- "env_logger 0.9.3",
  "itertools 0.10.5",
  "lazy_static",
  "lazycell",
  "log",
  "peeking_take_while",
+ "prettyplease",
  "proc-macro2",
  "quote",
  "regex",
  "rustc-hash",
  "shlex",
+ "syn 2.0.28",
  "which 4.4.0",
 ]
 
 [[package]]
 name = "autocxx-engine"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "aquamarine",
  "autocxx-bindgen",
@@ -145,12 +152,13 @@
  "log",
  "miette",
  "once_cell",
+ "prettyplease",
  "proc-macro2",
  "quote",
  "regex",
+ "rustversion",
  "serde_json",
- "strum_macros",
- "syn 1.0.109",
+ "syn 2.0.28",
  "tempfile",
  "thiserror",
  "version_check",
@@ -158,7 +166,7 @@
 
 [[package]]
 name = "autocxx-gen"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "assert_cmd",
  "autocxx",
@@ -177,7 +185,7 @@
 
 [[package]]
 name = "autocxx-integration-tests"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "autocxx",
  "autocxx-engine",
@@ -188,10 +196,13 @@
  "itertools 0.10.5",
  "link-cplusplus",
  "log",
+ "moveit",
  "once_cell",
  "proc-macro2",
  "quote",
  "rust_info",
+ "rustversion",
+ "static_assertions",
  "syn 1.0.109",
  "tempfile",
  "test-log",
@@ -200,18 +211,18 @@
 
 [[package]]
 name = "autocxx-macro"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "autocxx-parser",
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.28",
 ]
 
 [[package]]
 name = "autocxx-parser"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "indexmap",
  "itertools 0.10.5",
@@ -221,7 +232,7 @@
  "quote",
  "serde",
  "serde_json",
- "syn 1.0.109",
+ "syn 2.0.28",
  "thiserror",
 ]
 
@@ -241,6 +252,15 @@
 ]
 
 [[package]]
+name = "backtrace-ext"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
 name = "basic-toml"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -255,7 +275,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "cexpr 0.4.0",
  "clang-sys",
  "clap 2.34.0",
@@ -279,14 +299,20 @@
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
-name = "bstr"
-version = "0.2.17"
+name = "bitflags"
+version = "2.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+
+[[package]]
+name = "bstr"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
 dependencies = [
- "lazy_static",
  "memchr",
- "regex-automata 0.1.10",
+ "regex-automata",
+ "serde",
 ]
 
 [[package]]
@@ -344,7 +370,7 @@
 dependencies = [
  "ansi_term",
  "atty",
- "bitflags",
+ "bitflags 1.3.2",
  "strsim 0.8.0",
  "textwrap 0.11.0",
  "unicode-width",
@@ -358,7 +384,7 @@
 checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
 dependencies = [
  "atty",
- "bitflags",
+ "bitflags 1.3.2",
  "clap_lex",
  "indexmap",
  "once_cell",
@@ -392,7 +418,7 @@
 dependencies = [
  "anyhow",
  "bindgen",
- "bitflags",
+ "bitflags 1.3.2",
  "cxx",
  "cxxbridge-macro",
  "futures",
@@ -426,7 +452,7 @@
  "codespan-reporting",
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.28",
 ]
 
 [[package]]
@@ -443,7 +469,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.28",
 ]
 
 [[package]]
@@ -524,7 +550,7 @@
 name = "flatbuffers"
 version = "22.10.26"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "rustc_version",
  "serde",
 ]
@@ -585,7 +611,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.28",
 ]
 
 [[package]]
@@ -651,12 +677,6 @@
 ]
 
 [[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
-[[package]]
 name = "hermit-abi"
 version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -715,6 +735,17 @@
 ]
 
 [[package]]
+name = "is-terminal"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+dependencies = [
+ "hermit-abi 0.3.2",
+ "rustix 0.38.7",
+ "windows-sys",
+]
+
+[[package]]
 name = "is_ci"
 version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -798,6 +829,12 @@
 checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
 
 [[package]]
+name = "linux-raw-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+
+[[package]]
 name = "log"
 version = "0.4.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -811,12 +848,13 @@
 
 [[package]]
 name = "miette"
-version = "4.7.1"
+version = "5.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c"
+checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
 dependencies = [
- "atty",
  "backtrace",
+ "backtrace-ext",
+ "is-terminal",
  "miette-derive",
  "once_cell",
  "owo-colors",
@@ -831,13 +869,13 @@
 
 [[package]]
 name = "miette-derive"
-version = "4.7.1"
+version = "5.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c"
+checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.28",
 ]
 
 [[package]]
@@ -857,9 +895,9 @@
 
 [[package]]
 name = "moveit"
-version = "0.5.1"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7d756ffe4e38013507d35bf726a93fcdae2cae043ab5ce477f13857a335030d"
+checksum = "87d7335204cb6ef7bd647fa6db0be3e4d7aa25b5823a7aa030027ddf512cefba"
 dependencies = [
  "cxx",
 ]
@@ -937,10 +975,11 @@
 
 [[package]]
 name = "predicates"
-version = "2.1.5"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
+checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9"
 dependencies = [
+ "anstyle",
  "difflib",
  "itertools 0.10.5",
  "predicates-core",
@@ -963,6 +1002,16 @@
 ]
 
 [[package]]
+name = "prettyplease"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.28",
+]
+
+[[package]]
 name = "proc-macro-error"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1010,7 +1059,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
@@ -1021,18 +1070,12 @@
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-automata 0.3.2",
+ "regex-automata",
  "regex-syntax",
 ]
 
 [[package]]
 name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-
-[[package]]
-name = "regex-automata"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf"
@@ -1081,11 +1124,24 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "errno",
  "io-lifetimes",
  "libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.3.8",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399"
+dependencies = [
+ "bitflags 2.3.3",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.5",
  "windows-sys",
 ]
 
@@ -1124,7 +1180,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.28",
 ]
 
 [[package]]
@@ -1166,6 +1222,12 @@
 checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
 
 [[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
 name = "strsim"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1178,44 +1240,31 @@
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
-name = "strum_macros"
-version = "0.24.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn 1.0.109",
-]
-
-[[package]]
 name = "supports-color"
-version = "1.3.1"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f"
+checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354"
 dependencies = [
- "atty",
+ "is-terminal",
  "is_ci",
 ]
 
 [[package]]
 name = "supports-hyperlinks"
-version = "1.2.0"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406"
+checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
 dependencies = [
- "atty",
+ "is-terminal",
 ]
 
 [[package]]
 name = "supports-unicode"
-version = "1.0.2"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2"
+checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
 dependencies = [
- "atty",
+ "is-terminal",
 ]
 
 [[package]]
@@ -1231,9 +1280,9 @@
 
 [[package]]
 name = "syn"
-version = "2.0.22"
+version = "2.0.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1250,7 +1299,7 @@
  "cfg-if 1.0.0",
  "fastrand",
  "redox_syscall",
- "rustix",
+ "rustix 0.37.23",
  "windows-sys",
 ]
 
@@ -1333,7 +1382,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.28",
 ]
 
 [[package]]
diff --git a/WORKSPACE b/WORKSPACE
index f65e4ee..f98a2da 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -724,29 +724,29 @@
 )
 
 http_archive(
-    name = "ctre_phoenixpro_api_cpp_headers",
+    name = "ctre_phoenix6_api_cpp_headers",
     build_file_content = """
 cc_library(
     name = 'api-cpp',
     visibility = ['//visibility:public'],
-    hdrs = glob(['ctre/phoenixpro/**/*.hpp', 'units/*.h']),
+    hdrs = glob(['ctre/phoenix6/**/*.hpp', 'units/*.h']),
     includes = ["."],
     deps = ["@//third_party/allwpilib/wpimath"],
 )
 """,
-    sha256 = "340a9c8e726e2eb365b7a40a722df05fe7c7072c5c4a617fa0218eb6d074ad9f",
+    sha256 = "3c4da9f46c751d4981697da26d3c8680f40c87090782f5adf63412e34508f372",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenixpro/api-cpp/23.0.11/api-cpp-23.0.11-headers.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/api-cpp/23.2.2/api-cpp-23.2.2-headers.zip",
     ],
 )
 
 http_archive(
-    name = "ctre_phoenixpro_api_cpp_athena",
+    name = "ctre_phoenix6_api_cpp_athena",
     build_file_content = """
 filegroup(
     name = 'shared_libraries',
     srcs = [
-        'linux/athena/shared/libCTRE_PhoenixPro.so',
+        'linux/athena/shared/libCTRE_Phoenix6.so',
     ],
     visibility = ['//visibility:public'],
 )
@@ -754,18 +754,18 @@
 cc_library(
     name = 'api-cpp',
     visibility = ['//visibility:public'],
-    srcs = ['linux/athena/shared/libCTRE_PhoenixPro.so'],
+    srcs = ['linux/athena/shared/libCTRE_Phoenix6.so'],
     target_compatible_with = ['@//tools/platforms/hardware:roborio'],
 )
 """,
-    sha256 = "11f392bebfe54f512be9ef59809e1a10c4497e0ce92970645f054e7e04fe7ef6",
+    sha256 = "8391cbd24582c951a8fdbcff533243be718fc54e091c068d5441bb0c18ff822c",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenixpro/api-cpp/23.0.11/api-cpp-23.0.11-linuxathena.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/api-cpp/23.2.2/api-cpp-23.2.2-linuxathena.zip",
     ],
 )
 
 http_archive(
-    name = "ctre_phoenixpro_tools_headers",
+    name = "ctre_phoenix6_tools_headers",
     build_file_content = """
 cc_library(
     name = 'tools',
@@ -773,14 +773,14 @@
     hdrs = glob(['ctre/**/*.h', 'ctre/**/*.hpp']),
 )
 """,
-    sha256 = "7585e1bd9e581dd745e7f040ab521b966b40a04d05bc7fa82d6dafe2fb65764e",
+    sha256 = "33781c9db0a204e257928351c700295aec2bf6e2abb6a49ef237a95e98442a18",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenixpro/tools/23.0.11/tools-23.0.11-headers.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/tools/23.2.2/tools-23.2.2-headers.zip",
     ],
 )
 
 http_archive(
-    name = "ctre_phoenixpro_tools_athena",
+    name = "ctre_phoenix6_tools_athena",
     build_file_content = """
 filegroup(
     name = 'shared_libraries',
@@ -797,9 +797,9 @@
     target_compatible_with = ['@//tools/platforms/hardware:roborio'],
 )
 """,
-    sha256 = "b1daadfe782c43ed32c2e1a3956998f9604a3fc9282ef866fd8dc1482f3b8cc9",
+    sha256 = "75ec607f81ab470bc7c01fda2b8ca7b71b7dc3378b370f806f8646db27600504",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenixpro/tools/23.0.11/tools-23.0.11-linuxathena.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/tools/23.2.2/tools-23.2.2-linuxathena.zip",
     ],
 )
 
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 07c1f3a..d78df3a 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -3,7 +3,7 @@
 load("//aos:flatbuffers.bzl", "cc_static_flatbuffer")
 load("//aos:config.bzl", "aos_config")
 load("//tools/build_rules:autocxx.bzl", "autocxx_library")
-load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_test")
+load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_doc", "rust_doc_test", "rust_test")
 
 package(default_visibility = ["//visibility:public"])
 
@@ -181,6 +181,11 @@
     ],
 )
 
+rust_doc(
+    name = "event_loop_runtime_doc",
+    crate = ":event_loop_runtime",
+)
+
 rust_doc_test(
     name = "event_loop_runtime_doc_test",
     crate = ":event_loop_runtime",
@@ -259,6 +264,60 @@
     ],
 )
 
+rust_binary(
+    name = "ping_rs",
+    srcs = [
+        "ping.rs",
+    ],
+    data = [
+        ":pingpong_config",
+    ],
+    rustc_flags = ["-Crelocation-model=static"],
+    target_compatible_with = select({
+        "//conditions:default": ["//tools/platforms/rust:has_support"],
+        "//tools:has_msan": ["@platforms//:incompatible"],
+    }),
+    deps = [
+        ":event_loop_runtime",
+        ":ping_rust_fbs",
+        ":pong_rust_fbs",
+        ":shm_event_loop_rs",
+        "//aos:configuration_rs",
+        "//aos:configuration_rust_fbs",
+        "//aos:flatbuffers_rs",
+        "//aos:init_rs",
+        "@com_github_google_flatbuffers//rust",
+        "@crate_index//:futures",
+    ],
+)
+
+rust_binary(
+    name = "pong_rs",
+    srcs = [
+        "pong.rs",
+    ],
+    data = [
+        ":pingpong_config",
+    ],
+    rustc_flags = ["-Crelocation-model=static"],
+    target_compatible_with = select({
+        "//conditions:default": ["//tools/platforms/rust:has_support"],
+        "//tools:has_msan": ["@platforms//:incompatible"],
+    }),
+    deps = [
+        ":event_loop_runtime",
+        ":ping_rust_fbs",
+        ":pong_rust_fbs",
+        ":shm_event_loop_rs",
+        "//aos:configuration_rs",
+        "//aos:configuration_rust_fbs",
+        "//aos:flatbuffers_rs",
+        "//aos:init_rs",
+        "@com_github_google_flatbuffers//rust",
+        "@crate_index//:futures",
+    ],
+)
+
 aos_config(
     name = "aos_config",
     src = "aos.json",
@@ -520,6 +579,15 @@
     ],
 )
 
+cc_library(
+    name = "shm_event_loop_for_rust",
+    hdrs = ["shm_event_loop_for_rust.h"],
+    deps = [
+        ":event_loop",
+        ":simulated_event_loop",
+    ],
+)
+
 autocxx_library(
     name = "simulated_event_loop_rs",
     srcs = ["simulated_event_loop.rs"],
@@ -560,6 +628,66 @@
     ],
 )
 
+autocxx_library(
+    name = "shm_event_loop_rs",
+    srcs = ["shm_event_loop.rs"],
+    crate_name = "aos_events_shm_event_loop",
+    libs = [
+        ":shm_event_loop",
+        ":shm_event_loop_for_rust",
+    ],
+    rs_deps = [
+        "@com_github_google_flatbuffers//rust",
+        "@crate_index//:futures",
+        "//aos:configuration_rust_fbs",
+        "//aos:flatbuffers_rs",
+    ],
+    target_compatible_with = select({
+        "//conditions:default": ["//tools/platforms/rust:has_support"],
+        "//tools:has_msan": ["@platforms//:incompatible"],
+    }),
+    visibility = ["//visibility:public"],
+    deps = [
+        ":event_loop_runtime",
+        "//aos:configuration_rs",
+    ],
+)
+
+rust_doc(
+    name = "shm_event_loop_rs_doc",
+    crate = ":shm_event_loop_rs",
+    target_compatible_with = ["@platforms//cpu:x86_64"],
+)
+
+rust_test(
+    name = "shm_event_loop_rs_test",
+    crate = ":shm_event_loop_rs",
+    data = [":pingpong_config"],
+    # TODO: Make Rust play happy with pic vs nopic. Details at:
+    # https://github.com/bazelbuild/rules_rust/issues/118
+    rustc_flags = ["-Crelocation-model=static"],
+    target_compatible_with = select({
+        "//conditions:default": ["//tools/platforms/rust:has_support"],
+        "//tools:has_msan": ["@platforms//:incompatible"],
+    }),
+    deps = [
+        ":ping_rust_fbs",
+        "//aos:init_rs",
+        "@crate_index//:futures",
+        "@rules_rust//tools/runfiles",
+    ],
+)
+
+rust_doc_test(
+    name = "shm_event_loop_rs_doc_test",
+    crate = ":shm_event_loop_rs",
+    target_compatible_with = ["@platforms//cpu:x86_64"],
+    deps = [
+        ":ping_rust_fbs",
+        ":pong_rust_fbs",
+    ],
+)
+
 cc_test(
     name = "event_scheduler_test",
     srcs = ["event_scheduler_test.cc"],
diff --git a/aos/events/event_loop_runtime.cc b/aos/events/event_loop_runtime.cc
index 727d806..e3d73c1 100644
--- a/aos/events/event_loop_runtime.cc
+++ b/aos/events/event_loop_runtime.cc
@@ -8,4 +8,22 @@
 OnRunForRust::~OnRunForRust() { --runtime_->child_count_; }
 bool OnRunForRust::is_running() const { return runtime_->is_running(); }
 
+std::unique_ptr<TimerForRust> TimerForRust::Make(EventLoopRuntime *runtime) {
+  auto handler = std::unique_ptr<TimerForRust>(new TimerForRust());
+  TimerForRust *inner = handler.get();
+  handler->timer_ = runtime->event_loop()->AddTimer([inner, runtime] {
+    inner->expired_ = true;
+    runtime->DoPoll();
+  });
+  return handler;
+}
+
+bool TimerForRust::Poll() {
+  if (expired_) {
+    // Reset it for next poll.
+    expired_ = false;
+    return true;
+  }
+  return false;
+}
 }  // namespace aos
diff --git a/aos/events/event_loop_runtime.h b/aos/events/event_loop_runtime.h
index 325560f..7cc551f 100644
--- a/aos/events/event_loop_runtime.h
+++ b/aos/events/event_loop_runtime.h
@@ -6,6 +6,7 @@
 // particularly ergonomic for C++. See the Rust wrapper for detailed
 // documentation.
 
+#include <chrono>
 #include <memory>
 #include <optional>
 
@@ -139,6 +140,41 @@
   EventLoopRuntime *const runtime_;
 };
 
+class TimerForRust {
+ public:
+  static std::unique_ptr<TimerForRust> Make(EventLoopRuntime *runtime);
+
+  TimerForRust(const TimerForRust &) = delete;
+  TimerForRust(TimerForRust &&) = delete;
+
+  TimerForRust &operator=(const TimerForRust &) = delete;
+  TimerForRust &operator=(TimerForRust &&) = delete;
+
+  ~TimerForRust() { timer_->Disable(); }
+
+  void Schedule(int64_t base, int64_t repeat_offset) {
+    timer_->Schedule(
+        monotonic_clock::time_point(std::chrono::nanoseconds(base)),
+        std::chrono::nanoseconds(repeat_offset));
+  }
+
+  void Disable() { timer_->Disable(); }
+
+  bool IsDisabled() const { return timer_->IsDisabled(); }
+
+  void set_name(rust::Str name) { timer_->set_name(RustStrToStringView(name)); }
+  rust::Str name() const { return StringViewToRustStr(timer_->name()); }
+
+  // If true, the timer is expired.
+  bool Poll();
+
+ private:
+  TimerForRust() = default;
+
+  TimerHandler *timer_;
+  bool expired_ = false;
+};
+
 class EventLoopRuntime {
  public:
   EventLoopRuntime(EventLoop *event_loop) : event_loop_(event_loop) {}
@@ -199,8 +235,11 @@
 
   OnRunForRust MakeOnRun() { return OnRunForRust(this); }
 
+  std::unique_ptr<TimerForRust> AddTimer() { return TimerForRust::Make(this); }
+
  private:
   friend class OnRunForRust;
+  friend class TimerForRust;
 
   // Polls the top-level future once. This is what all the callbacks should do.
   void DoPoll() {
diff --git a/aos/events/event_loop_runtime.rs b/aos/events/event_loop_runtime.rs
index 76c1e2e..35a4225 100644
--- a/aos/events/event_loop_runtime.rs
+++ b/aos/events/event_loop_runtime.rs
@@ -47,6 +47,7 @@
     future::Future,
     marker::PhantomData,
     mem::ManuallyDrop,
+    ops::Add,
     panic::{catch_unwind, AssertUnwindSafe},
     pin::Pin,
     slice,
@@ -84,6 +85,7 @@
 generate!("aos::OnRunForRust")
 generate!("aos::EventLoopRuntime")
 generate!("aos::ExitHandle")
+generate!("aos::TimerForRust")
 
 subclass!("aos::ApplicationFuture", RustApplicationFuture)
 
@@ -681,6 +683,115 @@
     pub fn is_running(&self) -> bool {
         self.0.is_running()
     }
+
+    /// Returns an unarmed timer.
+    pub fn add_timer(&mut self) -> Timer {
+        Timer(self.0.as_mut().AddTimer())
+    }
+
+    /// Returns a timer that goes off every `duration`-long ticks.
+    pub fn add_interval(&mut self, duration: Duration) -> Timer {
+        let mut timer = self.add_timer();
+        timer.setup(self.monotonic_now(), Some(duration));
+        timer
+    }
+}
+
+/// An event loop primitive that allows sleeping asynchronously.
+///
+/// # Examples
+///
+/// ```no_run
+/// # use aos_events_event_loop_runtime::EventLoopRuntime;
+/// # use std::time::Duration;
+/// # fn compile_check(runtime: &mut EventLoopRuntime<'_>) {
+/// # let mut timer = runtime.add_timer();
+/// // Goes as soon as awaited.
+/// timer.setup(runtime.monotonic_now(), None);
+/// // Goes off once in 2 seconds.
+/// timer.setup(runtime.monotonic_now() + Duration::from_secs(2), None);
+/// // Goes off as soon as awaited and every 2 seconds afterwards.
+/// timer.setup(runtime.monotonic_now(), Some(Duration::from_secs(1)));
+/// async {
+///   for i in 0..10 {
+///     timer.tick().await;
+///   }
+///   // Timer won't off anymore. Next `tick` will never return.
+///   timer.disable();
+///   timer.tick().await;
+/// };
+/// # }
+/// ```
+pub struct Timer(UniquePtr<ffi::aos::TimerForRust>);
+
+/// A "tick" for a [`Timer`].
+///
+/// This is the raw future generated by the [`Timer::tick`] function.
+pub struct TimerTick<'a>(&'a mut Timer);
+
+impl Timer {
+    /// Arms the timer.
+    ///
+    /// The timer should sleep until `base`, `base + repeat`, `base + repeat * 2`, ...
+    /// If `repeat` is `None`, then the timer only expires once at `base`.
+    pub fn setup(&mut self, base: MonotonicInstant, repeat: Option<Duration>) {
+        self.0.pin_mut().Schedule(
+            base.0,
+            repeat
+                .unwrap_or(Duration::from_nanos(0))
+                .as_nanos()
+                .try_into()
+                .expect("Out of range: Internal clock uses 64 bits"),
+        );
+    }
+
+    /// Disarms the timer.
+    ///
+    /// Can be re-enabled by calling `setup` again.
+    pub fn disable(&mut self) {
+        self.0.pin_mut().Disable();
+    }
+
+    /// Returns `true` if the timer is enabled.
+    pub fn is_enabled(&self) -> bool {
+        !self.0.IsDisabled()
+    }
+
+    /// Sets the name of the timer.
+    ///
+    /// This can be useful to get a descriptive name in the timing reports.
+    pub fn set_name(&mut self, name: &str) {
+        self.0.pin_mut().set_name(name);
+    }
+
+    /// Gets the name of the timer.
+    pub fn name(&self) -> &str {
+        self.0.name()
+    }
+
+    /// Returns a tick which can be `.await`ed.
+    ///
+    /// This tick will resolve on the next timer expired.
+    pub fn tick(&mut self) -> TimerTick {
+        TimerTick(self)
+    }
+
+    /// Polls the timer, returning `[Poll::Ready]` only once the timer expired.
+    fn poll(&mut self) -> Poll<()> {
+        if self.0.pin_mut().Poll() {
+            Poll::Ready(())
+        } else {
+            Poll::Pending
+        }
+    }
+}
+
+impl Future for TimerTick<'_> {
+    type Output = ();
+
+    fn poll(mut self: Pin<&mut Self>, _: &mut std::task::Context) -> Poll<()> {
+        self.0.poll()
+    }
 }
 
 /// Provides async blocking access to messages on a channel. This will return every message on the
@@ -1342,6 +1453,14 @@
     }
 }
 
+impl Add<Duration> for MonotonicInstant {
+    type Output = MonotonicInstant;
+
+    fn add(self, rhs: Duration) -> Self::Output {
+        Self(self.0 + i64::try_from(rhs.as_nanos()).unwrap())
+    }
+}
+
 impl fmt::Debug for MonotonicInstant {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         self.duration_since_epoch().fmt(f)
diff --git a/aos/events/ping.rs b/aos/events/ping.rs
new file mode 100644
index 0000000..b9725b8
--- /dev/null
+++ b/aos/events/ping.rs
@@ -0,0 +1,87 @@
+use aos_configuration as config;
+use aos_events_event_loop_runtime::{EventLoopRuntime, Sender, Watcher};
+use aos_events_shm_event_loop::ShmEventLoop;
+use core::cell::Cell;
+use core::future::Future;
+use core::time::Duration;
+use futures::never::Never;
+use std::path::Path;
+
+use ping_rust_fbs::aos::examples as ping;
+use pong_rust_fbs::aos::examples as pong;
+
+fn main() {
+    aos_init::init();
+    let config = config::read_config_from(Path::new("pingpong_config.json")).unwrap();
+    let ping = PingTask::new();
+    ShmEventLoop::new(&config).run_with(|runtime| {
+        let task = ping.tasks(runtime);
+        runtime.spawn(task);
+    });
+}
+
+#[derive(Debug)]
+struct PingTask {
+    counter: Cell<i32>,
+}
+
+impl PingTask {
+    pub fn new() -> Self {
+        Self {
+            counter: Cell::new(0),
+        }
+    }
+
+    /// Returns a future with all the tasks for the ping process
+    pub fn tasks(&self, event_loop: &mut EventLoopRuntime) -> impl Future<Output = Never> + '_ {
+        let ping = self.ping(event_loop);
+        let handle_pong = self.handle_pong(event_loop);
+
+        async move {
+            futures::join!(ping, handle_pong);
+            unreachable!("Let's hope `never_type` gets stabilized soon :)");
+        }
+    }
+
+    fn ping(&self, event_loop: &mut EventLoopRuntime) -> impl Future<Output = Never> + '_ {
+        // The sender is used to send messages back to the pong channel.
+        let mut ping_sender: Sender<ping::Ping> = event_loop.make_sender("/test").unwrap();
+        let startup = event_loop.on_run();
+
+        let mut interval = event_loop.add_interval(Duration::from_secs(1));
+
+        async move {
+            // Wait for startup.
+            startup.await;
+            loop {
+                interval.tick().await;
+                self.counter.set(self.counter.get() + 1);
+                let mut builder = ping_sender.make_builder();
+                let mut ping = ping::PingBuilder::new(builder.fbb());
+                let iter = self.counter.get();
+                ping.add_value(iter);
+                let ping = ping.finish();
+                builder.send(ping).expect("Can't send ping");
+            }
+        }
+    }
+
+    fn handle_pong(&self, event_loop: &mut EventLoopRuntime) -> impl Future<Output = Never> + '_ {
+        // The watcher gives us incoming ping messages.
+        let mut pong_watcher: Watcher<pong::Pong> = event_loop.make_watcher("/test").unwrap();
+        let startup = event_loop.on_run();
+
+        async move {
+            // Wait for startup.
+            startup.await;
+            loop {
+                let pong = dbg!(pong_watcher.next().await);
+                assert_eq!(
+                    pong.message().unwrap().value(),
+                    self.counter.get(),
+                    "Missed a reply"
+                );
+            }
+        }
+    }
+}
diff --git a/aos/events/pong.rs b/aos/events/pong.rs
new file mode 100644
index 0000000..b817ac2
--- /dev/null
+++ b/aos/events/pong.rs
@@ -0,0 +1,42 @@
+use aos_configuration as config;
+use aos_events_event_loop_runtime::{EventLoopRuntime, Sender, Watcher};
+use aos_events_shm_event_loop::ShmEventLoop;
+use core::future::Future;
+use futures::never::Never;
+use std::path::Path;
+
+use ping_rust_fbs::aos::examples as ping;
+use pong_rust_fbs::aos::examples as pong;
+
+fn main() {
+    aos_init::init();
+    let config = config::read_config_from(Path::new("pingpong_config.json")).unwrap();
+    ShmEventLoop::new(&config).run_with(|runtime| {
+        let task = pong(runtime);
+        runtime.spawn(task);
+    });
+}
+
+/// Responds to ping messages with an equivalent pong.
+fn pong(event_loop: &mut EventLoopRuntime) -> impl Future<Output = Never> {
+    // The watcher gives us incoming ping messages.
+    let mut ping_watcher: Watcher<ping::Ping> = event_loop.make_watcher("/test").unwrap();
+
+    // The sender is used to send messages back to the pong channel.
+    let mut pong_sender: Sender<pong::Pong> = event_loop.make_sender("/test").unwrap();
+    // Wait for startup.
+    let startup = event_loop.on_run();
+
+    async move {
+        startup.await;
+        loop {
+            let ping = dbg!(ping_watcher.next().await);
+
+            let mut builder = pong_sender.make_builder();
+            let mut pong = pong::PongBuilder::new(builder.fbb());
+            pong.add_value(ping.message().unwrap().value());
+            let pong = pong.finish();
+            builder.send(pong).expect("Can't send pong reponse");
+        }
+    }
+}
diff --git a/aos/events/shm_event_loop.cc b/aos/events/shm_event_loop.cc
index 8ed6e0e..8523cdf 100644
--- a/aos/events/shm_event_loop.cc
+++ b/aos/events/shm_event_loop.cc
@@ -73,6 +73,14 @@
   return configuration::GetMyNode(configuration);
 }
 
+void IgnoreWakeupSignal() {
+  struct sigaction action;
+  action.sa_handler = SIG_IGN;
+  PCHECK(sigemptyset(&action.sa_mask) == 0);
+  action.sa_flags = 0;
+  PCHECK(sigaction(ipc_lib::kWakeupSignal, &action, nullptr) == 0);
+}
+
 }  // namespace
 
 ShmEventLoop::ShmEventLoop(const Configuration *configuration)
@@ -81,6 +89,11 @@
       shm_base_(FLAGS_shm_base),
       name_(FLAGS_application_name),
       node_(MaybeMyNode(configuration)) {
+  // Ignore the wakeup signal by default. Otherwise, we have race conditions on
+  // shutdown where a wakeup signal will uncleanly terminate the process.
+  // See LocklessQueueWakeUpper::Wakeup() for some more information.
+  IgnoreWakeupSignal();
+
   CHECK(IsInitialized()) << ": Need to initialize AOS first.";
   ClearContext();
   if (configuration->has_nodes()) {
diff --git a/aos/events/shm_event_loop.rs b/aos/events/shm_event_loop.rs
new file mode 100644
index 0000000..3e387ef
--- /dev/null
+++ b/aos/events/shm_event_loop.rs
@@ -0,0 +1,302 @@
+pub use aos_configuration::{Configuration, ConfigurationExt};
+pub use aos_events_event_loop_runtime::EventLoop;
+pub use aos_events_event_loop_runtime::{CppExitHandle, EventLoopRuntime, ExitHandle};
+
+use aos_configuration_fbs::aos::Configuration as RustConfiguration;
+use aos_flatbuffers::{transmute_table_to, Flatbuffer};
+use autocxx::WithinBox;
+use core::marker::PhantomData;
+use core::pin::Pin;
+use std::boxed::Box;
+use std::ops::{Deref, DerefMut};
+
+autocxx::include_cpp! (
+#include "aos/events/shm_event_loop.h"
+#include "aos/events/shm_event_loop_for_rust.h"
+
+safety!(unsafe)
+
+generate!("aos::ShmEventLoopForRust")
+
+extern_cpp_type!("aos::ExitHandle", crate::CppExitHandle)
+extern_cpp_type!("aos::Configuration", crate::Configuration)
+extern_cpp_type!("aos::EventLoop", crate::EventLoop)
+);
+
+/// A Rust-owned C++ `ShmEventLoop` object.
+pub struct ShmEventLoop<'config> {
+    inner: Pin<Box<ffi::aos::ShmEventLoopForRust>>,
+    _config: PhantomData<&'config Configuration>,
+}
+
+impl<'config> ShmEventLoop<'config> {
+    /// Creates a Rust-owned ShmEventLoop.
+    pub fn new(config: &'config impl Flatbuffer<RustConfiguration<'static>>) -> Self {
+        // SAFETY: The `_config` represents the lifetime of this pointer we're handing off to c++ to
+        // store.
+        let event_loop = unsafe {
+            ffi::aos::ShmEventLoopForRust::new(transmute_table_to::<Configuration>(
+                &config.message()._tab,
+            ))
+        }
+        .within_box();
+
+        Self {
+            inner: event_loop,
+            _config: PhantomData,
+        }
+    }
+
+    /// Provides a runtime to construct the application and runs the event loop.
+    ///
+    /// The runtime is the only way to interact with the event loop. It provides the functionality
+    /// to spawn a task, construct timers, watchers, fetchers, and so on.
+    ///
+    /// Making an [`EventLoopRuntime`] is tricky since the lifetime of the runtime is invariant
+    /// w.r.t the event loop. In other words, the runtime and the event loop must have the same
+    /// lifetime. By providing access to the runtime through an [`FnOnce`], we can guarantee
+    /// that the runtime and the event loop both have the same lifetime.
+    ///
+    /// # Examples
+    ///
+    /// A ping application might do something like the following
+    ///
+    /// ```no_run
+    /// # use aos_events_shm_event_loop::*;
+    /// use ping_rust_fbs::aos::examples as ping;
+    /// use pong_rust_fbs::aos::examples as pong;
+    /// use std::cell::Cell;
+    /// use std::path::Path;
+    /// use aos_configuration::read_config_from;
+    /// use aos_events_event_loop_runtime::{Sender, Watcher};
+    ///
+    /// let config = read_config_from(Path::new("path/to/aos_config.json")).unwrap();
+    /// let event_loop = ShmEventLoop::new(&config);
+    /// event_loop.run_with(|runtime| {
+    ///   // One task will send a ping, the other will listen to pong messages.
+    ///   let mut sender: Sender<ping::Ping> = runtime
+    ///       .make_sender("/test")
+    ///       .expect("Can't create `Ping` sender");
+    ///
+    ///   let on_run = runtime.on_run();
+    ///   // Sends a single ping message.
+    ///   let send_task = async move {
+    ///     on_run.await;
+    ///     let mut builder = sender.make_builder();
+    ///     let mut ping = ping::PingBuilder::new(builder.fbb());
+    ///     ping.add_value(10);
+    ///     let ping = ping.finish();
+    ///     builder.send(ping).expect("Can't send ping");
+    ///   };
+    ///
+    ///   let mut watcher: Watcher<pong::Pong> = runtime
+    ///       .make_watcher("/test")
+    ///       .expect("Can't create `Ping` watcher");
+    ///
+    ///   // Listens to pong messages and prints them.
+    ///   let receive_task = async move {
+    ///     loop {
+    ///       let pong = dbg!(watcher.next().await);
+    ///     }
+    ///   };
+    ///
+    ///   runtime.spawn(async move {
+    ///      futures::join!(send_task, receive_task);
+    ///      std::future::pending().await
+    ///   });
+    /// }); // Event loop starts runnning...
+    /// unreachable!("This can't be reached since no ExitHandle was made");
+    /// ```
+    ///
+    /// `run_with` can also borrow data from the outer scope that can be used in the async task.
+    ///
+    /// ```no_run
+    /// # use aos_events_shm_event_loop::*;
+    /// # use std::cell::Cell;
+    /// # use std::path::Path;
+    /// # use aos_configuration::read_config_from;
+    /// let config = read_config_from(Path::new("path/to/aos_config.json")).unwrap();
+    /// let shared_data = Cell::new(971);
+    /// let shared_data = &shared_data;
+    /// let event_loop = ShmEventLoop::new(&config);
+    /// event_loop.run_with(|runtime| {
+    ///   // Note how `Cell` is enough since the event loop is single threaded.
+    ///   let t1 = async move {
+    ///     shared_data.set(shared_data.get() + 1);
+    ///   };
+    ///   let t2 = async move {
+    ///     shared_data.set(shared_data.get() + 1);
+    ///   };
+    ///
+    ///   runtime.spawn(async move {
+    ///      futures::join!(t1, t2);
+    ///      std::future::pending().await
+    ///   });
+    /// });
+    /// unreachable!("This can't be reached since no ExitHandle was made");
+    /// ```
+    ///
+    /// However, the spawned future must outlive `run_with`.
+    ///
+    /// ```compile_fail
+    /// # use aos_events_shm_event_loop::*;
+    /// # use std::cell::Cell;
+    /// # use std::path::Path;
+    /// # use aos_configuration::read_config_from;
+    /// let config = read_config_from(Path::new("path/to/aos_config.json")).unwrap();
+    /// let event_loop = ShmEventLoop::new(&config);
+    /// event_loop.run_with(|runtime| {
+    ///   // ERROR: `shared_data` doesn't live long enough.
+    ///   let shared_data = Cell::new(971);
+    ///   let t1 = async {
+    ///     shared_data.set(shared_data.get() + 1);
+    ///   };
+    ///   let t2 = async {
+    ///     shared_data.set(shared_data.get() + 1);
+    ///   };
+    ///
+    ///   runtime.spawn(async move {
+    ///      futures::join!(t1, t2);
+    ///      std::future::pending().await
+    ///   });
+    /// });
+    /// ```
+    pub fn run_with<'env, F>(mut self, fun: F)
+    where
+        F: for<'event_loop> FnOnce(&mut Scoped<'event_loop, 'env, EventLoopRuntime<'event_loop>>),
+    {
+        // SAFETY: The runtime and the event loop (i.e. self) both get destroyed at the end of this
+        // scope: first the runtime followed by the event loop. The runtime gets exclusive access
+        // during initialization in `fun` while the event loop remains unused.
+        let runtime = unsafe { EventLoopRuntime::new(self.inner.as_mut().event_loop_mut()) };
+        let mut runtime = Scoped::new(runtime);
+        fun(&mut runtime);
+        self.run();
+    }
+
+    /// Makes an exit handle.
+    ///
+    /// Awaiting on the exit handle is the only way to actually exit the event loop
+    /// task, other than panicking.
+    pub fn make_exit_handle(&mut self) -> ExitHandle {
+        self.inner.as_mut().MakeExitHandle().into()
+    }
+
+    /// Runs the spawned task to completion.
+    fn run(&mut self) {
+        self.inner.as_mut().Run();
+    }
+}
+
+/// A wrapper over some data that lives for the duration of a scope.
+///
+/// This struct ensures the existence of some `'env` which outlives `'scope`. In
+/// the presence of higher-ranked trait bounds which require types that work for
+/// any `'scope`, this allows the compiler to propagate lifetime bounds which
+/// outlive any of the possible `'scope`. This is the simplest way to express
+/// this concept to the compiler right now.
+pub struct Scoped<'scope, 'env: 'scope, T: 'scope> {
+    data: T,
+    _env: PhantomData<fn(&'env ()) -> &'env ()>,
+    _scope: PhantomData<fn(&'scope ()) -> &'scope ()>,
+}
+
+impl<'scope, 'env: 'scope, T: 'scope> Scoped<'scope, 'env, T> {
+    /// Makes the [`Scoped`].
+    pub fn new(data: T) -> Self {
+        Self {
+            data,
+            _env: PhantomData,
+            _scope: PhantomData,
+        }
+    }
+}
+
+impl<'scope, 'env: 'scope, T: 'scope> Deref for Scoped<'scope, 'env, T> {
+    type Target = T;
+    fn deref(&self) -> &Self::Target {
+        &self.data
+    }
+}
+
+impl<'scope, 'env: 'scope, T: 'scope> DerefMut for Scoped<'scope, 'env, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.data
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use runfiles::Runfiles;
+
+    use aos_configuration::read_config_from;
+    use aos_events_event_loop_runtime::{Sender, Watcher};
+    use aos_init::test_init;
+    use ping_rust_fbs::aos::examples as ping;
+    use std::sync::atomic::{AtomicUsize, Ordering};
+    use std::sync::Barrier;
+
+    /// Tests basic functionality with 2 threads operating their own event loops.
+    #[test]
+    fn smoke_test() {
+        test_init();
+
+        let r = Runfiles::create().unwrap();
+        let config =
+            read_config_from(&r.rlocation("org_frc971/aos/events/pingpong_config.json")).unwrap();
+
+        const VALUE: i32 = 971;
+        let barrier = Barrier::new(2);
+        let count = AtomicUsize::new(0);
+
+        std::thread::scope(|s| {
+            let config = &config;
+            let barrier = &barrier;
+            let count = &count;
+            s.spawn(move || {
+                let mut event_loop = ShmEventLoop::new(config);
+                let exit_handle = event_loop.make_exit_handle();
+                event_loop.run_with(|runtime| {
+                    let mut watcher: Watcher<ping::Ping> = runtime
+                        .make_watcher("/test")
+                        .expect("Can't create `Ping` watcher");
+                    let on_run = runtime.on_run();
+                    runtime.spawn(async move {
+                        on_run.await;
+                        barrier.wait();
+                        let ping = watcher.next().await;
+                        assert_eq!(ping.message().unwrap().value(), VALUE);
+                        count.fetch_add(1, Ordering::Relaxed);
+                        exit_handle.exit().await
+                    });
+                });
+            });
+            s.spawn(move || {
+                let mut event_loop = ShmEventLoop::new(config);
+                let exit_handle = event_loop.make_exit_handle();
+                event_loop.run_with(|runtime| {
+                    let mut sender: Sender<ping::Ping> = runtime
+                        .make_sender("/test")
+                        .expect("Can't create `Ping` sender");
+                    let on_run = runtime.on_run();
+                    runtime.spawn(async move {
+                        on_run.await;
+                        // Give the waiting thread a chance to start.
+                        barrier.wait();
+                        let mut sender = sender.make_builder();
+                        let mut ping = ping::PingBuilder::new(sender.fbb());
+                        ping.add_value(VALUE);
+                        let ping = ping.finish();
+                        sender.send(ping).expect("send should succeed");
+                        count.fetch_add(1, Ordering::Relaxed);
+                        exit_handle.exit().await
+                    });
+                });
+            });
+        });
+
+        assert_eq!(count.into_inner(), 2, "Not all event loops ran.");
+    }
+}
diff --git a/aos/events/shm_event_loop_for_rust.h b/aos/events/shm_event_loop_for_rust.h
new file mode 100644
index 0000000..3a815e1
--- /dev/null
+++ b/aos/events/shm_event_loop_for_rust.h
@@ -0,0 +1,31 @@
+#ifndef AOS_EVENTS_SHM_EVENT_LOOP_FOR_RUST_H_
+#define AOS_EVENTS_SHM_EVENT_LOOP_FOR_RUST_H_
+
+#include <memory>
+
+#include "aos/events/event_loop.h"
+#include "aos/events/shm_event_loop.h"
+
+namespace aos {
+
+class ShmEventLoopForRust {
+ public:
+  ShmEventLoopForRust(const Configuration *configuration)
+      : event_loop_(configuration) {}
+
+  const EventLoop *event_loop() const { return &event_loop_; }
+  EventLoop *event_loop_mut() { return &event_loop_; }
+
+  std::unique_ptr<ExitHandle> MakeExitHandle() {
+    return event_loop_.MakeExitHandle();
+  }
+
+  void Run() { event_loop_.Run(); }
+
+ private:
+  ShmEventLoop event_loop_;
+};
+
+}  // namespace aos
+
+#endif  // AOS_EVENTS_SHM_EVENT_LOOP_FOR_RUST_H_
diff --git a/aos/init.rs b/aos/init.rs
index 8a5f262..9ac62f1 100644
--- a/aos/init.rs
+++ b/aos/init.rs
@@ -16,6 +16,12 @@
 ///
 /// Panics if non-test initialization has already been performed.
 pub fn test_init() {
+    init();
+    // TODO(Brian): Do we want any of the other stuff that `:gtest_main` has?
+    // TODO(Brian): Call `aos::SetShmBase` like `:gtest_main` does.
+}
+
+pub fn init() {
     static ONCE: Once = Once::new();
     ONCE.call_once(|| {
         let argv0 = std::env::args().next().expect("must have argv[0]");
@@ -23,7 +29,4 @@
         // SAFETY: argv0 is a NUL-terminated string.
         unsafe { ffi::aos::InitFromRust(argv0.as_ptr()) };
     });
-
-    // TODO(Brian): Do we want any of the other stuff that `:gtest_main` has?
-    // TODO(Brian): Call `aos::SetShmBase` like `:gtest_main` does.
 }
diff --git a/aos/network/message_bridge_client_lib.cc b/aos/network/message_bridge_client_lib.cc
index 8df81c6..e42eaa0 100644
--- a/aos/network/message_bridge_client_lib.cc
+++ b/aos/network/message_bridge_client_lib.cc
@@ -378,11 +378,15 @@
       client_status_(event_loop_),
       config_sha256_(std::move(config_sha256)),
       refresh_key_timer_(event_loop->AddTimer([this]() { RequestAuthKey(); })),
-      sctp_config_request_(event_loop_->MakeSender<SctpConfigRequest>("/aos")) {
+      sctp_config_request_(
+          event_loop_->TryMakeSender<SctpConfigRequest>("/aos")) {
   std::string_view node_name = event_loop->node()->name()->string_view();
 
   // Set up the SCTP configuration watcher and timer.
   if (requested_authentication == SctpAuthMethod::kAuth && HasSctpAuth()) {
+    CHECK(sctp_config_request_.valid())
+        << ": Must have SctpConfig channel configured to use SCTP "
+           "authentication.";
     event_loop->MakeWatcher("/aos", [this](const SctpConfig &config) {
       if (config.has_key()) {
         for (auto &conn : connections_) {
@@ -449,6 +453,7 @@
 }
 
 void MessageBridgeClient::RequestAuthKey() {
+  CHECK(sctp_config_request_.valid());
   auto sender = sctp_config_request_.MakeBuilder();
   auto builder = sender.MakeBuilder<SctpConfigRequest>();
   builder.add_request_key(true);
diff --git a/aos/network/message_bridge_server_lib.cc b/aos/network/message_bridge_server_lib.cc
index 9ac062e..35c8afe 100644
--- a/aos/network/message_bridge_server_lib.cc
+++ b/aos/network/message_bridge_server_lib.cc
@@ -418,12 +418,16 @@
       config_sha256_(std::move(config_sha256)),
       allocator_(0),
       refresh_key_timer_(event_loop->AddTimer([this]() { RequestAuthKey(); })),
-      sctp_config_request_(event_loop_->MakeSender<SctpConfigRequest>("/aos")) {
+      sctp_config_request_(
+          event_loop_->TryMakeSender<SctpConfigRequest>("/aos")) {
   CHECK_EQ(config_sha256_.size(), 64u) << ": Wrong length sha256sum";
   CHECK(event_loop_->node() != nullptr) << ": No nodes configured.";
 
   // Set up the SCTP configuration watcher and timer.
   if (requested_authentication == SctpAuthMethod::kAuth && HasSctpAuth()) {
+    CHECK(sctp_config_request_.valid())
+        << ": Must have SctpConfig channel configured to use SCTP "
+           "authentication.";
     event_loop_->MakeWatcher("/aos", [this](const SctpConfig &config) {
       if (config.has_key()) {
         server_.SetAuthKey(*config.key());
@@ -846,6 +850,7 @@
 }
 
 void MessageBridgeServer::RequestAuthKey() {
+  CHECK(sctp_config_request_.valid());
   auto sender = sctp_config_request_.MakeBuilder();
   auto builder = sender.MakeBuilder<SctpConfigRequest>();
   builder.add_request_key(true);
diff --git a/aos/realtime.cc b/aos/realtime.cc
index 8b34ada..94dfa4e 100644
--- a/aos/realtime.cc
+++ b/aos/realtime.cc
@@ -331,7 +331,9 @@
   if (FLAGS_die_on_malloc) {
     // tcmalloc redefines __libc_malloc, so use this as a feature test.
     if (&__libc_malloc == &tc_malloc) {
-      RAW_LOG(INFO, "Hooking tcmalloc for die_on_malloc");
+      if (VLOG_IS_ON(1)) {
+        RAW_LOG(INFO, "Hooking tcmalloc for die_on_malloc");
+      }
       if (&MallocHook_AddNewHook != nullptr) {
         CHECK(MallocHook_AddNewHook(&NewHook));
       } else {
diff --git a/frc971/BUILD b/frc971/BUILD
index d6a3af8..5613737 100644
--- a/frc971/BUILD
+++ b/frc971/BUILD
@@ -1,3 +1,5 @@
+load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
+
 package(default_visibility = ["//visibility:public"])
 
 cc_library(
@@ -21,3 +23,12 @@
     target_compatible_with = ["@platforms//os:linux"],
     visibility = ["//visibility:public"],
 )
+
+flatbuffer_cc_library(
+    name = "can_configuration_fbs",
+    srcs = [
+        ":can_configuration.fbs",
+    ],
+    gen_reflections = 1,
+    visibility = ["//visibility:public"],
+)
diff --git a/y2023/can_configuration.fbs b/frc971/can_configuration.fbs
similarity index 93%
rename from y2023/can_configuration.fbs
rename to frc971/can_configuration.fbs
index 75e2691..4ce70ab 100644
--- a/y2023/can_configuration.fbs
+++ b/frc971/can_configuration.fbs
@@ -1,4 +1,4 @@
-namespace y2023;
+namespace frc971;
 
 // Message which triggers wpilib_interface to print out the current
 // configuration, and optionally re-apply it.
diff --git a/frc971/wpilib/BUILD b/frc971/wpilib/BUILD
index a9d4ebf..33a3cd1 100644
--- a/frc971/wpilib/BUILD
+++ b/frc971/wpilib/BUILD
@@ -506,11 +506,12 @@
     target_compatible_with = ["//tools/platforms/hardware:roborio"],
     deps = [
         "//aos:init",
+        "//aos:math",
         "//aos/events:shm_event_loop",
         "//aos/logging",
         "//frc971/control_loops/drivetrain:drivetrain_can_position_fbs",
         "//third_party:phoenix",
-        "//third_party:phoenixpro",
+        "//third_party:phoenix6",
         "//third_party:wpilib",
         "@com_github_google_glog//:glog",
     ],
diff --git a/frc971/wpilib/can_sensor_reader.cc b/frc971/wpilib/can_sensor_reader.cc
index 5a19e8f..ad244fb 100644
--- a/frc971/wpilib/can_sensor_reader.cc
+++ b/frc971/wpilib/can_sensor_reader.cc
@@ -5,18 +5,19 @@
 
 CANSensorReader::CANSensorReader(
     aos::EventLoop *event_loop,
-    std::vector<ctre::phoenixpro::BaseStatusSignalValue *> signals_registry,
-    std::vector<std::shared_ptr<Falcon>> falcons)
+    std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry,
+    std::vector<std::shared_ptr<Falcon>> falcons,
+    std::function<void(ctre::phoenix::StatusCode status)> flatbuffer_callback)
     : event_loop_(event_loop),
       signals_(signals_registry.begin(), signals_registry.end()),
-      can_position_sender_(
-          event_loop->MakeSender<control_loops::drivetrain::CANPosition>(
-              "/drivetrain")),
-      falcons_(falcons) {
+      falcons_(falcons),
+      flatbuffer_callback_(flatbuffer_callback) {
   event_loop->SetRuntimeRealtimePriority(40);
 
   // TODO(max): Decide if we want to keep this on this core.
   event_loop->SetRuntimeAffinity(aos::MakeCpusetFromCpus({1}));
+
+  CHECK(flatbuffer_callback_);
   timer_handler_ = event_loop->AddTimer([this]() { Loop(); });
   timer_handler_->set_name("CANSensorReader Loop");
 
@@ -28,37 +29,12 @@
 
 void CANSensorReader::Loop() {
   ctre::phoenix::StatusCode status =
-      ctre::phoenixpro::BaseStatusSignalValue::WaitForAll(20_ms, signals_);
+      ctre::phoenix6::BaseStatusSignal::WaitForAll(20_ms, signals_);
 
   if (!status.IsOK()) {
     AOS_LOG(ERROR, "Failed to read signals from falcons: %s: %s",
             status.GetName(), status.GetDescription());
   }
 
-  auto builder = can_position_sender_.MakeBuilder();
-
-  for (auto falcon : falcons_) {
-    falcon->RefreshNontimesyncedSignals();
-    falcon->SerializePosition(builder.fbb());
-  }
-
-  auto falcon_offsets =
-      builder.fbb()
-          ->CreateVector<flatbuffers::Offset<control_loops::CANFalcon>>(
-              falcons_.size(), [this](size_t index) {
-                auto offset = falcons_.at(index)->TakeOffset();
-                CHECK(offset.has_value());
-                return offset.value();
-              });
-
-  control_loops::drivetrain::CANPosition::Builder can_position_builder =
-      builder.MakeBuilder<control_loops::drivetrain::CANPosition>();
-
-  can_position_builder.add_falcons(falcon_offsets);
-  if (!falcons_.empty()) {
-    can_position_builder.add_timestamp(falcons_.at(0)->GetTimestamp());
-  }
-  can_position_builder.add_status(static_cast<int>(status));
-
-  builder.CheckOk(builder.Send(can_position_builder.Finish()));
+  flatbuffer_callback_(status);
 }
diff --git a/frc971/wpilib/can_sensor_reader.h b/frc971/wpilib/can_sensor_reader.h
index 2e1a406..764a041 100644
--- a/frc971/wpilib/can_sensor_reader.h
+++ b/frc971/wpilib/can_sensor_reader.h
@@ -15,16 +15,17 @@
  public:
   CANSensorReader(
       aos::EventLoop *event_loop,
-      std::vector<ctre::phoenixpro::BaseStatusSignalValue *> signals_registry,
-      std::vector<std::shared_ptr<Falcon>> falcons);
+      std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry,
+      std::vector<std::shared_ptr<Falcon>> falcons,
+      std::function<void(ctre::phoenix::StatusCode status)>
+          flatbuffer_callback);
 
  private:
   void Loop();
 
   aos::EventLoop *event_loop_;
 
-  const std::vector<ctre::phoenixpro::BaseStatusSignalValue *> signals_;
-  aos::Sender<control_loops::drivetrain::CANPosition> can_position_sender_;
+  const std::vector<ctre::phoenix6::BaseStatusSignal *> signals_;
 
   // This is a vector of falcons becuase we don't need to care
   // about falcons individually.
@@ -32,6 +33,9 @@
 
   // Pointer to the timer handler used to modify the wakeup.
   ::aos::TimerHandler *timer_handler_;
+
+  // Callback used to send the CANPosition flatbuffer
+  std::function<void(ctre::phoenix::StatusCode status)> flatbuffer_callback_;
 };
 }  // namespace wpilib
 }  // namespace frc971
diff --git a/frc971/wpilib/falcon.cc b/frc971/wpilib/falcon.cc
index 2dee390..6be83aa 100644
--- a/frc971/wpilib/falcon.cc
+++ b/frc971/wpilib/falcon.cc
@@ -1,9 +1,10 @@
 #include "frc971/wpilib/falcon.h"
 
 using frc971::wpilib::Falcon;
+using frc971::wpilib::kMaxBringupPower;
 
 Falcon::Falcon(int device_id, std::string canbus,
-               std::vector<ctre::phoenixpro::BaseStatusSignalValue *> *signals,
+               std::vector<ctre::phoenix6::BaseStatusSignal *> *signals,
                double stator_current_limit, double supply_current_limit)
     : talon_(device_id, canbus),
       device_id_(device_id),
@@ -37,7 +38,7 @@
 }
 
 void Falcon::PrintConfigs() {
-  ctre::phoenixpro::configs::TalonFXConfiguration configuration;
+  ctre::phoenix6::configs::TalonFXConfiguration configuration;
   ctre::phoenix::StatusCode status =
       talon_.GetConfigurator().Refresh(configuration);
   if (!status.IsOK()) {
@@ -47,23 +48,22 @@
   AOS_LOG(INFO, "configuration: %s", configuration.ToString().c_str());
 }
 
-void Falcon::WriteConfigs(ctre::phoenixpro::signals::InvertedValue invert) {
+void Falcon::WriteConfigs(ctre::phoenix6::signals::InvertedValue invert) {
   inverted_ = invert;
 
-  ctre::phoenixpro::configs::CurrentLimitsConfigs current_limits;
+  ctre::phoenix6::configs::CurrentLimitsConfigs current_limits;
   current_limits.StatorCurrentLimit = stator_current_limit_;
   current_limits.StatorCurrentLimitEnable = true;
   current_limits.SupplyCurrentLimit = supply_current_limit_;
   current_limits.SupplyCurrentLimitEnable = true;
 
-  ctre::phoenixpro::configs::MotorOutputConfigs output_configs;
-  output_configs.NeutralMode =
-      ctre::phoenixpro::signals::NeutralModeValue::Brake;
+  ctre::phoenix6::configs::MotorOutputConfigs output_configs;
+  output_configs.NeutralMode = ctre::phoenix6::signals::NeutralModeValue::Brake;
   output_configs.DutyCycleNeutralDeadband = 0;
 
   output_configs.Inverted = inverted_;
 
-  ctre::phoenixpro::configs::TalonFXConfiguration configuration;
+  ctre::phoenix6::configs::TalonFXConfiguration configuration;
   configuration.CurrentLimits = current_limits;
   configuration.MotorOutput = output_configs;
 
@@ -77,6 +77,23 @@
   PrintConfigs();
 }
 
+ctre::phoenix::StatusCode Falcon::WriteCurrent(double current,
+                                               double max_voltage) {
+  ctre::phoenix6::controls::TorqueCurrentFOC control(
+      static_cast<units::current::ampere_t>(current));
+  // Using 0_Hz here makes it a one-shot update.
+  control.UpdateFreqHz = 0_Hz;
+  control.MaxAbsDutyCycle =
+      ::aos::Clip(max_voltage, -kMaxBringupPower, kMaxBringupPower) / 12.0;
+  ctre::phoenix::StatusCode status = talon()->SetControl(control);
+  if (!status.IsOK()) {
+    AOS_LOG(ERROR, "Failed to write control to falcon %d: %s: %s", device_id(),
+            status.GetName(), status.GetDescription());
+  }
+
+  return status;
+}
+
 void Falcon::SerializePosition(flatbuffers::FlatBufferBuilder *fbb) {
   control_loops::CANFalcon::Builder builder(*fbb);
   builder.add_id(device_id_);
diff --git a/frc971/wpilib/falcon.h b/frc971/wpilib/falcon.h
index 7137d82..8f0f1f0 100644
--- a/frc971/wpilib/falcon.h
+++ b/frc971/wpilib/falcon.h
@@ -5,9 +5,10 @@
 #include <cinttypes>
 #include <vector>
 
-#include "ctre/phoenixpro/TalonFX.hpp"
+#include "ctre/phoenix6/TalonFX.hpp"
 #include "glog/logging.h"
 
+#include "aos/commonmath.h"
 #include "aos/init.h"
 #include "aos/logging/logging.h"
 #include "frc971/control_loops/drivetrain/drivetrain_can_position_generated.h"
@@ -18,19 +19,21 @@
 namespace wpilib {
 
 static constexpr units::frequency::hertz_t kCANUpdateFreqHz = 200_Hz;
+static constexpr double kMaxBringupPower = 12.0;
 
 // Gets info from and writes to falcon motors using the TalonFX controller.
 class Falcon {
  public:
   Falcon(int device_id, std::string canbus,
-         std::vector<ctre::phoenixpro::BaseStatusSignalValue *> *signals,
+         std::vector<ctre::phoenix6::BaseStatusSignal *> *signals,
          double stator_current_limit, double supply_current_limit);
 
   void PrintConfigs();
 
-  void WriteConfigs(ctre::phoenixpro::signals::InvertedValue invert);
+  void WriteConfigs(ctre::phoenix6::signals::InvertedValue invert);
+  ctre::phoenix::StatusCode WriteCurrent(double current, double max_voltage);
 
-  ctre::phoenixpro::hardware::TalonFX *talon() { return &talon_; }
+  ctre::phoenix6::hardware::TalonFX *talon() { return &talon_; }
 
   void SerializePosition(flatbuffers::FlatBufferBuilder *fbb);
 
@@ -66,19 +69,17 @@
   }
 
  private:
-  ctre::phoenixpro::hardware::TalonFX talon_;
+  ctre::phoenix6::hardware::TalonFX talon_;
   int device_id_;
 
-  ctre::phoenixpro::signals::InvertedValue inverted_;
+  ctre::phoenix6::signals::InvertedValue inverted_;
 
-  ctre::phoenixpro::StatusSignalValue<units::temperature::celsius_t>
-      device_temp_;
-  ctre::phoenixpro::StatusSignalValue<units::voltage::volt_t> supply_voltage_;
-  ctre::phoenixpro::StatusSignalValue<units::current::ampere_t> supply_current_,
+  ctre::phoenix6::StatusSignal<units::temperature::celsius_t> device_temp_;
+  ctre::phoenix6::StatusSignal<units::voltage::volt_t> supply_voltage_;
+  ctre::phoenix6::StatusSignal<units::current::ampere_t> supply_current_,
       torque_current_;
-  ctre::phoenixpro::StatusSignalValue<units::angle::turn_t> position_;
-  ctre::phoenixpro::StatusSignalValue<units::dimensionless::scalar_t>
-      duty_cycle_;
+  ctre::phoenix6::StatusSignal<units::angle::turn_t> position_;
+  ctre::phoenix6::StatusSignal<units::dimensionless::scalar_t> duty_cycle_;
 
   double stator_current_limit_;
   double supply_current_limit_;
diff --git a/frc971/wpilib/swerve/BUILD b/frc971/wpilib/swerve/BUILD
new file mode 100644
index 0000000..9f559f6
--- /dev/null
+++ b/frc971/wpilib/swerve/BUILD
@@ -0,0 +1,30 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+    name = "swerve_drivetrain_writer",
+    srcs = [
+        "swerve_drivetrain_writer.cc",
+    ],
+    hdrs = [
+        "swerve_drivetrain_writer.h",
+    ],
+    deps = [
+        ":swerve_module",
+        "//aos/logging",
+        "//frc971:can_configuration_fbs",
+        "//frc971/control_loops/drivetrain/swerve:swerve_drivetrain_output_fbs",
+        "//frc971/wpilib:falcon",
+        "//frc971/wpilib:loop_output_handler",
+        "//third_party:phoenix6",
+    ],
+)
+
+cc_library(
+    name = "swerve_module",
+    hdrs = [
+        "swerve_module.h",
+    ],
+    deps = [
+        "//frc971/wpilib:falcon",
+    ],
+)
diff --git a/frc971/wpilib/swerve/swerve_drivetrain_writer.cc b/frc971/wpilib/swerve/swerve_drivetrain_writer.cc
new file mode 100644
index 0000000..2b6ef9e
--- /dev/null
+++ b/frc971/wpilib/swerve/swerve_drivetrain_writer.cc
@@ -0,0 +1,60 @@
+#include "frc971/wpilib/swerve/swerve_drivetrain_writer.h"
+
+using frc971::wpilib::swerve::DrivetrainWriter;
+
+DrivetrainWriter::DrivetrainWriter(::aos::EventLoop *event_loop,
+                                   int drivetrain_writer_priority,
+                                   double max_voltage)
+    : ::frc971::wpilib::LoopOutputHandler<
+          ::frc971::control_loops::drivetrain::swerve::Output>(event_loop,
+                                                               "/drivetrain"),
+      max_voltage_(max_voltage) {
+  event_loop->SetRuntimeRealtimePriority(drivetrain_writer_priority);
+
+  event_loop->OnRun([this]() { WriteConfigs(); });
+}
+
+void DrivetrainWriter::set_falcons(std::shared_ptr<SwerveModule> front_left,
+                                   std::shared_ptr<SwerveModule> front_right,
+                                   std::shared_ptr<SwerveModule> back_left,
+                                   std::shared_ptr<SwerveModule> back_right) {
+  front_left_ = std::move(front_left);
+  front_right_ = std::move(front_right);
+  back_left_ = std::move(back_left);
+  back_right_ = std::move(back_right);
+}
+
+void DrivetrainWriter::HandleCANConfiguration(
+    const CANConfiguration &configuration) {
+  for (auto module : {front_left_, front_right_, back_left_, back_right_}) {
+    module->rotation->PrintConfigs();
+    module->translation->PrintConfigs();
+  }
+  if (configuration.reapply()) {
+    WriteConfigs();
+  }
+}
+
+void DrivetrainWriter::WriteConfigs() {
+  for (auto module : {front_left_, front_right_, back_left_, back_right_}) {
+    module->rotation->WriteConfigs(false);
+    module->translation->WriteConfigs(false);
+  }
+}
+
+void DrivetrainWriter::Write(
+    const ::frc971::control_loops::drivetrain::swerve::Output &output) {
+  front_left_->WriteModule(output.front_left_output(), max_voltage_);
+  front_right_->WriteModule(output.front_right_output(), max_voltage_);
+  back_left_->WriteModule(output.back_left_output(), max_voltage_);
+  back_right_->WriteModule(output.back_right_output(), max_voltage_);
+}
+
+void DrivetrainWriter::Stop() {
+  AOS_LOG(WARNING, "drivetrain output too old\n");
+
+  for (auto module : {front_left_, front_right_, back_left_, back_right_}) {
+    module->rotation->WriteCurrent(0, 0);
+    module->translation->WriteCurrent(0, 0);
+  }
+}
diff --git a/frc971/wpilib/swerve/swerve_drivetrain_writer.h b/frc971/wpilib/swerve/swerve_drivetrain_writer.h
new file mode 100644
index 0000000..4bd6639
--- /dev/null
+++ b/frc971/wpilib/swerve/swerve_drivetrain_writer.h
@@ -0,0 +1,52 @@
+#ifndef FRC971_WPILIB_SWERVE_DRIVETRAIN_WRITER_H_
+#define FRC971_WPILIB_SWERVE_DRIVETRAIN_WRITER_H_
+
+#include "ctre/phoenix6/TalonFX.hpp"
+
+#include "frc971/can_configuration_generated.h"
+#include "frc971/control_loops/drivetrain/swerve/swerve_drivetrain_output_generated.h"
+#include "frc971/wpilib/falcon.h"
+#include "frc971/wpilib/loop_output_handler.h"
+#include "frc971/wpilib/swerve/swerve_module.h"
+
+namespace frc971 {
+namespace wpilib {
+namespace swerve {
+
+// Reads from the swerve output flatbuffer and uses wpilib to set the current
+// for each motor.
+class DrivetrainWriter
+    : public ::frc971::wpilib::LoopOutputHandler<
+          ::frc971::control_loops::drivetrain::swerve::Output> {
+ public:
+  DrivetrainWriter(::aos::EventLoop *event_loop, int drivetrain_writer_priority,
+                   double max_voltage);
+
+  void set_falcons(std::shared_ptr<SwerveModule> front_left,
+                   std::shared_ptr<SwerveModule> front_right,
+                   std::shared_ptr<SwerveModule> back_left,
+                   std::shared_ptr<SwerveModule> back_right);
+
+  void HandleCANConfiguration(const CANConfiguration &configuration);
+
+ private:
+  void WriteConfigs();
+
+  void Write(const ::frc971::control_loops::drivetrain::swerve::Output &output)
+      override;
+
+  void Stop() override;
+
+  double SafeSpeed(double voltage);
+
+  std::shared_ptr<SwerveModule> front_left_, front_right_, back_left_,
+      back_right_;
+
+  double max_voltage_;
+};
+
+}  // namespace swerve
+}  // namespace wpilib
+}  // namespace frc971
+
+#endif  // FRC971_WPILIB_SWERVE_DRIVETRAIN_WRITER_H_
diff --git a/frc971/wpilib/swerve/swerve_module.h b/frc971/wpilib/swerve/swerve_module.h
new file mode 100644
index 0000000..534f0ce
--- /dev/null
+++ b/frc971/wpilib/swerve/swerve_module.h
@@ -0,0 +1,44 @@
+#ifndef FRC971_WPILIB_SWERVE_SWERVE_MODULE_H_
+#define FRC971_WPILIB_SWERVE_SWERVE_MODULE_H_
+
+#include "frc971/wpilib/falcon.h"
+
+namespace frc971 {
+namespace wpilib {
+namespace swerve {
+
+struct SwerveModule {
+  SwerveModule(int rotation_id, int translation_id, std::string canbus,
+               std::vector<ctre::phoenix6::BaseStatusSignal *> *signals,
+               double stator_current_limit, double supply_current_limit)
+      : rotation(std::make_shared<Falcon>(rotation_id, canbus, signals,
+                                          stator_current_limit,
+                                          supply_current_limit)),
+        translation(std::make_shared<Falcon>(translation_id, canbus, signals,
+                                             stator_current_limit,
+                                             supply_current_limit)) {}
+
+  void WriteModule(
+      const frc971::control_loops::drivetrain::swerve::SwerveModuleOutput
+          *module_output,
+      double max_voltage) {
+    double rotation_current = 0.0;
+    double translation_current = 0.0;
+
+    if (module_output != nullptr) {
+      rotation_current = module_output->rotation_current();
+      translation_current = module_output->translation_current();
+    }
+
+    rotation->WriteCurrent(rotation_current, max_voltage);
+    translation->WriteCurrent(translation_current, max_voltage);
+  }
+
+  std::shared_ptr<Falcon> rotation;
+  std::shared_ptr<Falcon> translation;
+};
+
+}  // namespace swerve
+}  // namespace wpilib
+}  // namespace frc971
+#endif  // FRC971_WPILIB_SWERVE_SWERVE_MODULE_H_
diff --git a/scouting/BUILD b/scouting/BUILD
index d6c09b2..b7e50fa 100644
--- a/scouting/BUILD
+++ b/scouting/BUILD
@@ -19,6 +19,11 @@
     ],
 )
 
+# TODO(philipp): Sandbox the following:
+# - libnss3
+# - libdrm2
+# - libgbm1
+# - x11-xkb-utils (See TODO in scouting/scouting_test_runner.js)
 cypress_test(
     name = "scouting_test",
     data = [
diff --git a/scouting/scouting_test_runner.js b/scouting/scouting_test_runner.js
index 3106ee7..c2ef027 100644
--- a/scouting/scouting_test_runner.js
+++ b/scouting/scouting_test_runner.js
@@ -4,9 +4,13 @@
 const cypress = require('cypress');
 
 // Set up the xvfb binary.
-process.env[
-  'PATH'
-] = `${process.env.RUNFILES_DIR}/xvfb_amd64/wrapped_bin:${process.env.PATH}`;
+// TODO(philipp): Figure out how to point Xvfb at the sandboxed usr/bin
+// directory. Currently impossible as it's hardcoded to use /usr/bin.
+process.env['PATH'] = [
+  `${process.env.RUNFILES_DIR}/xvfb_amd64/wrapped_bin`,
+  `${process.env.RUNFILES_DIR}/xvfb_amd64/usr/bin`,
+  process.env.PATH,
+].join(':');
 
 // Start the web server, database, and fake TBA server.
 // We use file descriptor 3 ('pipe') for the test server to let us know when
diff --git a/third_party/BUILD b/third_party/BUILD
index 53b5d24..7195123 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -31,17 +31,17 @@
     target_compatible_with = ["//tools/platforms/hardware:roborio"],
     visibility = ["//visibility:public"],
     deps = [
+        "@ctre_phoenix6_tools_athena//:tools",
+        "@ctre_phoenix6_tools_headers//:tools",
         "@ctre_phoenix_api_cpp_athena//:api-cpp",
         "@ctre_phoenix_api_cpp_headers//:api-cpp",
         "@ctre_phoenix_cci_athena//:cci",
         "@ctre_phoenix_cci_headers//:cci",
-        "@ctre_phoenixpro_tools_athena//:tools",
-        "@ctre_phoenixpro_tools_headers//:tools",
     ],
 )
 
 cc_library(
-    name = "phoenixpro",
+    name = "phoenix6",
     linkopts = [
         "-Wl,-rpath",
         "-Wl,.",
@@ -49,10 +49,10 @@
     target_compatible_with = ["//tools/platforms/hardware:roborio"],
     visibility = ["//visibility:public"],
     deps = [
-        "@ctre_phoenixpro_api_cpp_athena//:api-cpp",
-        "@ctre_phoenixpro_api_cpp_headers//:api-cpp",
-        "@ctre_phoenixpro_tools_athena//:tools",
-        "@ctre_phoenixpro_tools_headers//:tools",
+        "@ctre_phoenix6_api_cpp_athena//:api-cpp",
+        "@ctre_phoenix6_api_cpp_headers//:api-cpp",
+        "@ctre_phoenix6_tools_athena//:tools",
+        "@ctre_phoenix6_tools_headers//:tools",
     ],
 )
 
diff --git a/third_party/autocxx/.github/ISSUE_TEMPLATE/bug_report.md b/third_party/autocxx/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..59bdb4d
--- /dev/null
+++ b/third_party/autocxx/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,22 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior with appropriate Rust and C++ code snippets. Ideally, please raise a new pull request with a failing test - instructions are [here](https://google.github.io/autocxx/contributing.html#reporting-bugs). If that isn't possible, a few other options are listed too.
+
+About half of the time people report bugs, they're not readily reproducible. That wastes time for everyone. That's why it's incredibly useful to have a test case which definitely fails. Thanks!
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/third_party/autocxx/.github/workflows/ci.yml b/third_party/autocxx/.github/workflows/ci.yml
index 367b729..563b8ff 100644
--- a/third_party/autocxx/.github/workflows/ci.yml
+++ b/third_party/autocxx/.github/workflows/ci.yml
@@ -32,9 +32,10 @@
           - name: macOS
             rust: nightly
             os: macos
-          - name: Windows (gnu)
-            rust: nightly-x86_64-pc-windows-gnu
-            os: windows
+          # Windows GNU disabled due to https://github.com/google/autocxx/issues/1134
+          # - name: Windows (gnu)
+          #    rust: nightly-x86_64-pc-windows-gnu
+          #    os: windows
           - name: Windows (msvc)
             rust: nightly-x86_64-pc-windows-msvc
             os: windows
@@ -141,9 +142,16 @@
         run: cargo build
       - name: Build reference-wrappers example
         working-directory: ./examples/reference-wrappers
+        if: matrix.rust == 'nightly'
         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.
+      - name: Build cpp_calling_rust example
+        working-directory: ./examples/cpp_calling_rust
+        run: cargo build
+      - name: Build llvm example
+        working-directory: ./examples/llvm
+        # llvm example needs to install LLVM 13 headers via apt-get.
+        if: matrix.os == ''
+        run: sudo apt-get install llvm-13-dev && cargo build
 
   sanitizer:
     name: Address Sanitizer
@@ -169,6 +177,17 @@
           RUST_BACKTRACE: "0"
         run: cargo -Z build-std test --workspace --target x86_64-unknown-linux-gnu
 
+  force-wrapper-generation:
+    name: Test forcing wrapper generation
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: Swatinem/rust-cache@v1
+      - name: Tests generating all possible shims
+        env:
+          AUTOCXX_FORCE_WRAPPER_GENERATION: 1
+        run: cargo test --workspace
+
   # Clippy check
   clippy:
     name: Clippy
diff --git a/third_party/autocxx/Cargo.lock b/third_party/autocxx/Cargo.lock
index 4f58182..a7872e3 100644
--- a/third_party/autocxx/Cargo.lock
+++ b/third_party/autocxx/Cargo.lock
@@ -4,9 +4,9 @@
 
 [[package]]
 name = "addr2line"
-version = "0.17.0"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
 dependencies = [
  "gimli",
 ]
@@ -18,48 +18,115 @@
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
-name = "aho-corasick"
-version = "0.7.18"
+name = "ahash"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
-name = "ansi_term"
-version = "0.12.1"
+name = "android-tzdata"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
 dependencies = [
- "winapi",
+ "libc",
+]
+
+[[package]]
+name = "anstream"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+dependencies = [
+ "anstyle",
+ "windows-sys",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.56"
+version = "1.0.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
 
 [[package]]
 name = "aquamarine"
-version = "0.1.11"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96e14cb2a51c8b45d26a4219981985c7350fc05eacb7b5b2939bceb2ffefdf3e"
+checksum = "a941c39708478e8eea39243b5983f1c42d2717b3620ee91f4a52115fd02ac43f"
 dependencies = [
  "itertools 0.9.0",
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
 name = "assert_cmd"
-version = "1.0.8"
+version = "2.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe"
+checksum = "86d6b683edf8d1119fe420a94f8a7e389239666aa72e65495d91c00462510151"
 dependencies = [
+ "anstyle",
  "bstr",
  "doc-comment",
  "predicates",
@@ -74,7 +141,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.1.19",
  "libc",
  "winapi",
 ]
@@ -87,7 +154,7 @@
 
 [[package]]
 name = "autocxx"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "aquamarine",
  "autocxx-macro",
@@ -97,41 +164,41 @@
 
 [[package]]
 name = "autocxx-bindgen"
-version = "0.59.17"
+version = "0.65.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a9a26dd38d385d23b1bf61bd231b77f690c4368aef4c77cee1b7a6da2e2042"
+checksum = "6c9fb7b8dd83a582e12157367773d8d1195f2dea54d4250aaf3426abae3237aa"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "cexpr",
  "clang-sys",
- "clap 2.34.0",
- "env_logger 0.9.0",
- "itertools 0.10.3",
+ "itertools 0.10.5",
  "lazy_static",
  "lazycell",
  "log",
  "peeking_take_while",
+ "prettyplease",
  "proc-macro2",
  "quote",
  "regex",
  "rustc-hash",
  "shlex",
+ "syn 2.0.23",
  "which",
 ]
 
 [[package]]
 name = "autocxx-build"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "autocxx-engine",
- "env_logger 0.9.0",
+ "env_logger 0.9.3",
  "indexmap",
- "syn",
+ "syn 2.0.23",
 ]
 
 [[package]]
 name = "autocxx-demo"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "autocxx",
  "autocxx-build",
@@ -141,7 +208,7 @@
 
 [[package]]
 name = "autocxx-engine"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "aquamarine",
  "autocxx-bindgen",
@@ -150,16 +217,17 @@
  "cxx-gen",
  "indexmap",
  "indoc",
- "itertools 0.10.3",
+ "itertools 0.10.5",
  "log",
  "miette",
  "once_cell",
+ "prettyplease",
  "proc-macro2",
  "quote",
  "regex",
+ "rustversion",
  "serde_json",
- "strum_macros",
- "syn",
+ "syn 2.0.23",
  "tempfile",
  "thiserror",
  "version_check",
@@ -167,17 +235,17 @@
 
 [[package]]
 name = "autocxx-gen"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "assert_cmd",
  "autocxx",
  "autocxx-engine",
  "autocxx-integration-tests",
- "clap 3.1.9",
+ "clap 3.2.25",
  "cxx",
- "env_logger 0.9.0",
+ "env_logger 0.9.3",
  "indexmap",
- "itertools 0.10.3",
+ "itertools 0.10.5",
  "miette",
  "pathdiff",
  "proc-macro2",
@@ -186,22 +254,25 @@
 
 [[package]]
 name = "autocxx-integration-tests"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "autocxx",
  "autocxx-engine",
  "cc",
  "cxx",
- "env_logger 0.9.0",
+ "env_logger 0.9.3",
  "indoc",
- "itertools 0.10.3",
+ "itertools 0.10.5",
  "link-cplusplus",
  "log",
+ "moveit",
  "once_cell",
  "proc-macro2",
  "quote",
  "rust_info",
- "syn",
+ "rustversion",
+ "static_assertions",
+ "syn 1.0.109",
  "tempfile",
  "test-log",
  "trybuild",
@@ -209,76 +280,76 @@
 
 [[package]]
 name = "autocxx-macro"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "autocxx-parser",
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.23",
 ]
 
 [[package]]
 name = "autocxx-mdbook-preprocessor"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "anyhow",
  "autocxx-integration-tests",
- "clap 3.1.9",
- "env_logger 0.9.0",
+ "clap 3.2.25",
+ "env_logger 0.9.3",
  "gag",
- "itertools 0.10.3",
+ "itertools 0.10.5",
  "mdbook",
  "proc-macro2",
  "rayon",
  "regex",
  "serde_json",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
 name = "autocxx-parser"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "indexmap",
- "itertools 0.10.3",
+ "itertools 0.10.5",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
  "serde",
  "serde_json",
- "syn",
+ "syn 2.0.23",
  "thiserror",
 ]
 
 [[package]]
 name = "autocxx-reduce"
-version = "0.22.3"
+version = "0.26.0"
 dependencies = [
  "assert_cmd",
  "autocxx-engine",
  "autocxx-parser",
- "clap 3.1.9",
+ "clap 3.2.25",
  "cxx-gen",
  "indexmap",
  "indoc",
- "itertools 0.10.3",
+ "itertools 0.10.5",
  "proc-macro2",
  "quote",
  "regex",
  "serde",
  "serde_derive",
  "serde_json",
- "syn",
+ "syn 2.0.23",
  "tempfile",
 ]
 
 [[package]]
 name = "backtrace"
-version = "0.3.64"
+version = "0.3.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f"
+checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
 dependencies = [
  "addr2line",
  "cc",
@@ -290,60 +361,67 @@
 ]
 
 [[package]]
+name = "backtrace-ext"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
+name = "basic-toml"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f838d03a705d72b12389b8930bd14cacf493be1380bfb15720d4d12db5ab03ac"
+dependencies = [
+ "serde",
+]
+
+[[package]]
 name = "bitflags"
 version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
-name = "block-buffer"
-version = "0.7.3"
+name = "bitflags"
+version = "2.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
 dependencies = [
- "block-padding",
- "byte-tools",
- "byteorder",
  "generic-array",
 ]
 
 [[package]]
-name = "block-padding"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
-dependencies = [
- "byte-tools",
-]
-
-[[package]]
 name = "bstr"
-version = "0.2.17"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5"
 dependencies = [
- "lazy_static",
  "memchr",
- "regex-automata",
+ "once_cell",
+ "regex-automata 0.1.10",
+ "serde",
 ]
 
 [[package]]
-name = "byte-tools"
-version = "0.3.1"
+name = "bumpalo"
+version = "3.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
-
-[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
 
 [[package]]
 name = "cc"
-version = "1.0.73"
+version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
 
 [[package]]
 name = "cexpr"
@@ -362,22 +440,21 @@
 
 [[package]]
 name = "chrono"
-version = "0.4.19"
+version = "0.4.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
 dependencies = [
- "libc",
- "num-integer",
+ "android-tzdata",
+ "iana-time-zone",
  "num-traits",
- "time",
  "winapi",
 ]
 
 [[package]]
 name = "clang-sys"
-version = "1.3.1"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21"
+checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
 dependencies = [
  "glob",
  "libc",
@@ -386,54 +463,68 @@
 
 [[package]]
 name = "clap"
-version = "2.34.0"
+version = "3.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
 dependencies = [
- "ansi_term",
  "atty",
- "bitflags",
- "strsim 0.8.0",
- "textwrap 0.11.0",
- "unicode-width",
- "vec_map",
+ "bitflags 1.3.2",
+ "clap_lex 0.2.4",
+ "indexmap",
+ "once_cell",
+ "strsim",
+ "termcolor",
+ "textwrap 0.16.0",
 ]
 
 [[package]]
 name = "clap"
-version = "3.1.9"
+version = "4.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6aad2534fad53df1cc12519c5cda696dd3e20e6118a027e24054aea14a0bdcbe"
+checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a"
 dependencies = [
- "atty",
- "bitflags",
- "clap_lex",
- "indexmap",
- "lazy_static",
- "strsim 0.10.0",
- "termcolor",
- "textwrap 0.15.0",
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex 0.5.0",
+ "once_cell",
+ "strsim",
+ "terminal_size 0.2.6",
 ]
 
 [[package]]
 name = "clap_complete"
-version = "3.1.1"
+version = "4.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25"
+checksum = "7f6b5c519bab3ea61843a7923d074b04245624bb84a64a8c150f5deb014e388b"
 dependencies = [
- "clap 3.1.9",
+ "clap 4.3.10",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.1.1"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "189ddd3b5d32a70b35e7686054371742a937b0d99128e76dde6340210e966669"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
 dependencies = [
  "os_str_bytes",
 ]
 
 [[package]]
+name = "clap_lex"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
 name = "codespan-reporting"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -444,10 +535,31 @@
 ]
 
 [[package]]
-name = "crossbeam-channel"
-version = "0.5.4"
+name = "colorchoice"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -455,9 +567,9 @@
 
 [[package]]
 name = "crossbeam-deque"
-version = "0.8.1"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
 dependencies = [
  "cfg-if",
  "crossbeam-epoch",
@@ -466,33 +578,41 @@
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.8"
+version = "0.9.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
 dependencies = [
  "autocfg",
  "cfg-if",
  "crossbeam-utils",
- "lazy_static",
  "memoffset",
  "scopeguard",
 ]
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.8"
+version = "0.8.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
 dependencies = [
  "cfg-if",
- "lazy_static",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
 ]
 
 [[package]]
 name = "cxx"
-version = "1.0.68"
+version = "1.0.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e599641dff337570f6aa9c304ecca92341d30bf72e1c50287869ed6a36615a6"
+checksum = "e88abab2f5abbe4c56e8f1fb431b784d710b709888f35755a160e62e33fe38e8"
 dependencies = [
  "cc",
  "cxxbridge-flags",
@@ -502,31 +622,31 @@
 
 [[package]]
 name = "cxx-gen"
-version = "0.7.68"
+version = "0.7.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e2c726d93799c3129c65224ab09eae1a31276bc593d4f7344be1c592c16a1ec"
+checksum = "83f6f8cddb97c1510ef1e7e849a40d60cd97377766187633ac6b9162dd862fd8"
 dependencies = [
  "codespan-reporting",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.23",
 ]
 
 [[package]]
 name = "cxxbridge-flags"
-version = "1.0.68"
+version = "1.0.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3894ad0c6d517cb5a4ce8ec20b37cd0ea31b480fe582a104c5db67ae21270853"
+checksum = "8d3816ed957c008ccd4728485511e3d9aaf7db419aa321e3d2c5a2f3411e36c8"
 
 [[package]]
 name = "cxxbridge-macro"
-version = "1.0.68"
+version = "1.0.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34fa7e395dc1c001083c7eed28c8f0f0b5a225610f3b6284675f444af6fab86b"
+checksum = "a26acccf6f445af85ea056362561a24ef56cdc15fcc685f03aec50b9c702cb6d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.23",
 ]
 
 [[package]]
@@ -537,11 +657,12 @@
 
 [[package]]
 name = "digest"
-version = "0.8.1"
+version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 dependencies = [
- "generic-array",
+ "block-buffer",
+ "crypto-common",
 ]
 
 [[package]]
@@ -552,18 +673,18 @@
 
 [[package]]
 name = "either"
-version = "1.6.1"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 
 [[package]]
 name = "env_logger"
-version = "0.7.1"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
 dependencies = [
  "atty",
- "humantime 1.3.0",
+ "humantime",
  "log",
  "regex",
  "termcolor",
@@ -571,28 +692,43 @@
 
 [[package]]
 name = "env_logger"
-version = "0.9.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
 dependencies = [
- "atty",
- "humantime 2.1.0",
+ "humantime",
+ "is-terminal",
  "log",
  "regex",
  "termcolor",
 ]
 
 [[package]]
-name = "fake-simd"
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
 
 [[package]]
 name = "fastrand"
-version = "1.7.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
 dependencies = [
  "instant",
 ]
@@ -620,50 +756,59 @@
 
 [[package]]
 name = "generic-array"
-version = "0.12.4"
+version = "0.14.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
 dependencies = [
  "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
 ]
 
 [[package]]
 name = "gimli"
-version = "0.26.1"
+version = "0.27.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
+checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
 
 [[package]]
 name = "glob"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "handlebars"
-version = "4.2.2"
+version = "4.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b"
+checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d"
 dependencies = [
  "log",
  "pest",
  "pest_derive",
- "quick-error 2.0.1",
  "serde",
  "serde_json",
+ "thiserror",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.11.2"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
-
-[[package]]
-name = "heck"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
 
 [[package]]
 name = "hermit-abi"
@@ -675,13 +820,10 @@
 ]
 
 [[package]]
-name = "humantime"
-version = "1.3.0"
+name = "hermit-abi"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
-dependencies = [
- "quick-error 1.2.3",
-]
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
 
 [[package]]
 name = "humantime"
@@ -690,10 +832,33 @@
 checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
-name = "indexmap"
-version = "1.8.1"
+name = "iana-time-zone"
+version = "0.1.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
+checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -702,12 +867,9 @@
 
 [[package]]
 name = "indoc"
-version = "1.0.4"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
-dependencies = [
- "unindent",
-]
+checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
 
 [[package]]
 name = "instant"
@@ -719,6 +881,28 @@
 ]
 
 [[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi 0.3.2",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb"
+dependencies = [
+ "hermit-abi 0.3.2",
+ "rustix 0.38.3",
+ "windows-sys",
+]
+
+[[package]]
 name = "is_ci"
 version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -735,18 +919,27 @@
 
 [[package]]
 name = "itertools"
-version = "0.10.3"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
 dependencies = [
  "either",
 ]
 
 [[package]]
 name = "itoa"
-version = "1.0.1"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
+checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
 
 [[package]]
 name = "lazy_static"
@@ -762,15 +955,15 @@
 
 [[package]]
 name = "libc"
-version = "0.2.123"
+version = "0.2.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
 
 [[package]]
 name = "libloading"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
 dependencies = [
  "cfg-if",
  "winapi",
@@ -778,48 +971,50 @@
 
 [[package]]
 name = "link-cplusplus"
-version = "1.0.6"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cae2cd7ba2f3f63938b9c724475dfb7b9861b545a90324476324ed21dbc8c8"
+checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
 dependencies = [
  "cc",
 ]
 
 [[package]]
-name = "log"
-version = "0.4.16"
+name = "linux-raw-sys"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
-dependencies = [
- "cfg-if",
-]
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
 
 [[package]]
-name = "maplit"
-version = "1.0.2"
+name = "linux-raw-sys"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
 
 [[package]]
 name = "mdbook"
-version = "0.4.18"
+version = "0.4.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74612ae81a3e5ee509854049dfa4c7975ae033c06f5fc4735c7dfbe60ee2a39d"
+checksum = "7b67ee4a744f36e6280792016c17e69921b51df357181d1eb17d620fcc3609f3"
 dependencies = [
  "anyhow",
  "chrono",
- "clap 3.1.9",
+ "clap 4.3.10",
  "clap_complete",
- "env_logger 0.7.1",
+ "env_logger 0.10.0",
  "handlebars",
- "lazy_static",
  "log",
  "memchr",
+ "once_cell",
  "opener",
  "pulldown-cmark",
  "regex",
  "serde",
- "serde_derive",
  "serde_json",
  "shlex",
  "tempfile",
@@ -829,48 +1024,49 @@
 
 [[package]]
 name = "memchr"
-version = "2.4.1"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "memoffset"
-version = "0.6.5"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "miette"
-version = "4.4.0"
+version = "5.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a097de91d72c13382f60213ed9f7f7a26afd8bee0ea320b47f886a9a67ca5a1"
+checksum = "a236ff270093b0b67451bc50a509bd1bad302cb1d3c7d37d5efe931238581fa9"
 dependencies = [
- "atty",
  "backtrace",
+ "backtrace-ext",
+ "is-terminal",
  "miette-derive",
  "once_cell",
  "owo-colors",
  "supports-color",
  "supports-hyperlinks",
  "supports-unicode",
- "terminal_size",
- "textwrap 0.15.0",
+ "terminal_size 0.1.17",
+ "textwrap 0.15.2",
  "thiserror",
  "unicode-width",
 ]
 
 [[package]]
 name = "miette-derive"
-version = "4.4.0"
+version = "5.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45a95a48d0bc28f9af628286e8a4da09f96f34a97744a2e9a5a4db9814ad527d"
+checksum = "4901771e1d44ddb37964565c654a3223ba41a594d02b8da471cc4464912b5cfa"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.23",
 ]
 
 [[package]]
@@ -881,88 +1077,71 @@
 
 [[package]]
 name = "miniz_oxide"
-version = "0.4.4"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
 dependencies = [
  "adler",
- "autocfg",
 ]
 
 [[package]]
 name = "moveit"
-version = "0.5.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "815d5988a1dd22f08bad572a83ee654563bb422ece5d5bce41f31ec49399dcb5"
+checksum = "87d7335204cb6ef7bd647fa6db0be3e4d7aa25b5823a7aa030027ddf512cefba"
 dependencies = [
  "cxx",
 ]
 
 [[package]]
 name = "nom"
-version = "7.1.1"
+version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
 dependencies = [
  "memchr",
  "minimal-lexical",
 ]
 
 [[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
 name = "num-traits"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "num_cpus"
-version = "1.13.1"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.2",
  "libc",
 ]
 
 [[package]]
 name = "object"
-version = "0.27.1"
+version = "0.31.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
+checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.10.0"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
-
-[[package]]
-name = "opaque-debug"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
 name = "opener"
-version = "0.5.0"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
+checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005"
 dependencies = [
  "bstr",
  "winapi",
@@ -970,15 +1149,15 @@
 
 [[package]]
 name = "os_str_bytes"
-version = "6.0.0"
+version = "6.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
 
 [[package]]
 name = "owo-colors"
-version = "3.3.0"
+version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e72e30578e0d0993c8ae20823dd9cff2bc5517d2f586a8aef462a581e8a03eb"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
 
 [[package]]
 name = "pathdiff"
@@ -994,18 +1173,19 @@
 
 [[package]]
 name = "pest"
-version = "2.1.3"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9"
 dependencies = [
+ "thiserror",
  "ucd-trie",
 ]
 
 [[package]]
 name = "pest_derive"
-version = "2.1.0"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
+checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b"
 dependencies = [
  "pest",
  "pest_generator",
@@ -1013,56 +1193,67 @@
 
 [[package]]
 name = "pest_generator"
-version = "2.1.3"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
+checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190"
 dependencies = [
  "pest",
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.23",
 ]
 
 [[package]]
 name = "pest_meta"
-version = "2.1.3"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
+checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0"
 dependencies = [
- "maplit",
+ "once_cell",
  "pest",
- "sha-1",
+ "sha2",
 ]
 
 [[package]]
 name = "predicates"
-version = "2.1.1"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c"
+checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9"
 dependencies = [
+ "anstyle",
  "difflib",
- "itertools 0.10.3",
+ "itertools 0.10.5",
  "predicates-core",
 ]
 
 [[package]]
 name = "predicates-core"
-version = "1.0.3"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb"
+checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
 
 [[package]]
 name = "predicates-tree"
-version = "1.0.5"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032"
+checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
 dependencies = [
  "predicates-core",
  "termtree",
 ]
 
 [[package]]
+name = "prettyplease"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.23",
+]
+
+[[package]]
 name = "proc-macro-error"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1071,7 +1262,7 @@
  "proc-macro-error-attr",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
  "version_check",
 ]
 
@@ -1088,62 +1279,48 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.37"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
+checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
 dependencies = [
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
 name = "pulldown-cmark"
-version = "0.9.1"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6"
+checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "memchr",
  "unicase",
 ]
 
 [[package]]
-name = "quick-error"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-
-[[package]]
-name = "quick-error"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
-
-[[package]]
 name = "quote"
-version = "1.0.18"
+version = "1.0.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "rayon"
-version = "1.5.2"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
 dependencies = [
- "autocfg",
- "crossbeam-deque",
  "either",
  "rayon-core",
 ]
 
 [[package]]
 name = "rayon-core"
-version = "1.9.2"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
 dependencies = [
  "crossbeam-channel",
  "crossbeam-deque",
@@ -1153,21 +1330,22 @@
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.13"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.5"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484"
 dependencies = [
  "aho-corasick",
  "memchr",
+ "regex-automata 0.3.0",
  "regex-syntax",
 ]
 
@@ -1178,19 +1356,21 @@
 checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
 
 [[package]]
-name = "regex-syntax"
-version = "0.6.25"
+name = "regex-automata"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
 
 [[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+name = "regex-syntax"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
+checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846"
 
 [[package]]
 name = "rust_info"
@@ -1200,9 +1380,9 @@
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.21"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
 [[package]]
 name = "rustc-hash"
@@ -1211,16 +1391,43 @@
 checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
-name = "rustversion"
-version = "1.0.6"
+name = "rustix"
+version = "0.37.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
+checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
+dependencies = [
+ "bitflags 1.3.2",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys 0.3.8",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4"
+dependencies = [
+ "bitflags 2.3.3",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.3",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f"
 
 [[package]]
 name = "ryu"
-version = "1.0.9"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
 
 [[package]]
 name = "scopeguard"
@@ -1230,29 +1437,29 @@
 
 [[package]]
 name = "serde"
-version = "1.0.136"
+version = "1.0.166"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.136"
+version = "1.0.166"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.23",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.79"
+version = "1.0.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c"
 dependencies = [
  "itoa",
  "ryu",
@@ -1260,15 +1467,14 @@
 ]
 
 [[package]]
-name = "sha-1"
-version = "0.8.2"
+name = "sha2"
+version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
 dependencies = [
- "block-buffer",
+ "cfg-if",
+ "cpufeatures",
  "digest",
- "fake-simd",
- "opaque-debug",
 ]
 
 [[package]]
@@ -1284,10 +1490,10 @@
 checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
 
 [[package]]
-name = "strsim"
-version = "0.8.0"
+name = "static_assertions"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 
 [[package]]
 name = "strsim"
@@ -1296,76 +1502,74 @@
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
-name = "strum_macros"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn",
-]
-
-[[package]]
 name = "supports-color"
-version = "1.3.0"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9"
+checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354"
 dependencies = [
- "atty",
+ "is-terminal",
  "is_ci",
 ]
 
 [[package]]
 name = "supports-hyperlinks"
-version = "1.2.0"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406"
+checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
 dependencies = [
- "atty",
+ "is-terminal",
 ]
 
 [[package]]
 name = "supports-unicode"
-version = "1.0.2"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2"
+checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
 dependencies = [
- "atty",
+ "is-terminal",
 ]
 
 [[package]]
 name = "syn"
-version = "1.0.91"
+version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 dependencies = [
  "proc-macro2",
  "quote",
- "unicode-xid",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
 ]
 
 [[package]]
 name = "tempfile"
-version = "3.3.0"
+version = "3.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
 dependencies = [
+ "autocfg",
  "cfg-if",
  "fastrand",
- "libc",
  "redox_syscall",
- "remove_dir_all",
- "winapi",
+ "rustix 0.37.23",
+ "windows-sys",
 ]
 
 [[package]]
 name = "termcolor"
-version = "1.1.3"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
 dependencies = [
  "winapi-util",
 ]
@@ -1381,36 +1585,37 @@
 ]
 
 [[package]]
-name = "termtree"
-version = "0.2.4"
+name = "terminal_size"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
+checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
+dependencies = [
+ "rustix 0.37.23",
+ "windows-sys",
+]
+
+[[package]]
+name = "termtree"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
 
 [[package]]
 name = "test-log"
-version = "0.2.10"
+version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4235dbf7ea878b3ef12dea20a59c134b405a66aafc4fc2c7b9935916e289e735"
+checksum = "d9601d162c1d77e62c1ea0bc8116cd1caf143ce3af947536c3c9052a1677fe0c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
 name = "textwrap"
-version = "0.11.0"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
+checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
 dependencies = [
  "smawk",
  "unicode-linebreak",
@@ -1418,77 +1623,72 @@
 ]
 
 [[package]]
-name = "thiserror"
-version = "1.0.30"
+name = "textwrap"
+version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+
+[[package]]
+name = "thiserror"
+version = "1.0.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
-]
-
-[[package]]
-name = "time"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
-dependencies = [
- "libc",
- "wasi",
- "winapi",
+ "syn 2.0.23",
 ]
 
 [[package]]
 name = "toml"
-version = "0.5.9"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "topological-sort"
-version = "0.1.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"
+checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
 
 [[package]]
 name = "trybuild"
-version = "1.0.59"
+version = "1.0.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "606ab3fe0065741fdbb51f64bcb6ba76f13fad49f1723030041826c631782764"
+checksum = "04366e99ff743345622cd00af2af01d711dc2d1ef59250d7347698d21b546729"
 dependencies = [
+ "basic-toml",
  "glob",
  "once_cell",
  "serde",
  "serde_derive",
  "serde_json",
  "termcolor",
- "toml",
 ]
 
 [[package]]
 name = "typenum"
-version = "1.15.0"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
 
 [[package]]
 name = "ucd-trie"
-version = "0.1.3"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
 
 [[package]]
 name = "unicase"
@@ -1500,37 +1700,32 @@
 ]
 
 [[package]]
-name = "unicode-linebreak"
-version = "0.1.2"
+name = "unicode-ident"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f"
+checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
+
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
 dependencies = [
+ "hashbrown",
  "regex",
 ]
 
 [[package]]
 name = "unicode-width"
-version = "0.1.9"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
 
 [[package]]
-name = "unicode-xid"
-version = "0.2.2"
+name = "utf8parse"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
-name = "unindent"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
-
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
 [[package]]
 name = "version_check"
@@ -1549,19 +1744,73 @@
 
 [[package]]
 name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.23",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.23",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
 
 [[package]]
 name = "which"
-version = "4.2.5"
+version = "4.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
 dependencies = [
  "either",
- "lazy_static",
  "libc",
+ "once_cell",
 ]
 
 [[package]]
@@ -1594,3 +1843,78 @@
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/third_party/autocxx/Cargo.toml b/third_party/autocxx/Cargo.toml
index a741ece..7a30fc7 100644
--- a/third_party/autocxx/Cargo.toml
+++ b/third_party/autocxx/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
@@ -25,10 +25,14 @@
 resolver = "2"
 
 [dependencies]
-autocxx-macro = { path="macro", version="0.22.3" }
-cxx = "1.0.68" # ... also needed because expansion of type_id refers to ::cxx
+autocxx-macro = { path="macro", version="0.26.0" }
+cxx = "1.0.78" # ... also needed because expansion of type_id refers to ::cxx
 aquamarine = "0.1" # docs
-moveit = { version = "0.5", features = [ "cxx" ] }
+moveit = { version = "0.6", 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", "examples/reference-wrappers", "examples/cpp_calling_rust", "tools/stress-test"]
 
 #[patch.crates-io]
 #cxx = { path="../cxx" }
diff --git a/third_party/autocxx/book/src/building.md b/third_party/autocxx/book/src/building.md
index 4d5d919..d4abd09 100644
--- a/third_party/autocxx/book/src/building.md
+++ b/third_party/autocxx/book/src/building.md
@@ -12,7 +12,7 @@
     let mut b = autocxx_build::Builder::new("src/main.rs", &[&path])
         .extra_clang_args(&["-std=c++17"])
         .expect_build();
-    b.flag_if_supported("-std=c++17")
+    b.flag_if_supported("-std=c++17") // use "-std:c++17" here if using msvc on windows
         .compile("autocxx-demo"); // arbitrary library name, pick anything
     println!("cargo:rerun-if-changed=src/main.rs");
     // Add instructions to link to any C++ libraries you need.
@@ -58,3 +58,14 @@
 ```
 
 This interop inevitably involves lots of fiddly small functions. It's likely to perform far better if you can achieve cross-language link-time-optimization (LTO). [This issue](https://github.com/dtolnay/cxx/issues/371) may give some useful hints - see also all the build-related help in [the cxx manual](https://cxx.rs/) which all applies here too.
+
+## C++ versions and other compiler command-line flags
+
+The code generated by cxx and autocxx requires C++ 14, so it's not possible to use an earlier version of C++ than that.
+
+To use a later version, you need to:
+
+* Build the generated code with a later C++ version, for example using the clang argument `-std=c++17`. If you're using autocxx's cargo support, then you would do this by calling methods on the returned `cc::Build` object, for instance [`flag_if_supported`](https://docs.rs/cc/latest/cc/struct.Build.html#method.flag_if_supported).
+* _Also_ give similar directives to the C++ parsing which happens _within_ autocxx (specifically, by autocxx's version of bindgen). To do that, use [`Builder::extra_clang_args`](https://docs.rs/autocxx-engine/latest/autocxx_engine/struct.Builder.html#method.extra_clang_args).
+
+The same applies with the command-line `autocxx_gen` support - you'll need to pass such extra compiler options to `autocxx_gen` and also use them when building the generated C++ code.
diff --git a/third_party/autocxx/book/src/contributing.md b/third_party/autocxx/book/src/contributing.md
index e95ca5a..d82e64e 100644
--- a/third_party/autocxx/book/src/contributing.md
+++ b/third_party/autocxx/book/src/contributing.md
@@ -71,7 +71,11 @@
 This is especially valuable to see the `bindgen` output Rust code, and then the converted Rust code which we pass into cxx. Usually, most problems are due to some mis-conversion somewhere
 in `engine/src/conversion`. See [here](https://docs.rs/autocxx-engine/latest/autocxx_engine/struct.IncludeCppEngine.html) for documentation and diagrams on how the engine works.
 
-You may also wish to set `AUTOCXX_ASAN=1` on Linux when running tests.
+You may also wish to set `AUTOCXX_ASAN=1` on Linux when running tests. To exercise all
+the code paths related to generating both C++ and Rust side shims, you can set
+`AUTOCXX_FORCE_WRAPPER_GENERATION=1`. The test suite doesn't do this by default because
+we also want to test the normal code paths. (In the future we might want to
+parameterize the test suite to do both.)
 
 ## Reporting bugs
 
@@ -99,6 +103,17 @@
   the C++ bindings as distilled by `bindgen`, and then the version which
   we've converted and moulded to be suitable for use by `cxx`.
 
+## Bugs related to linking problems
+
+Unfortunately, _linking_ C++ binaries is a complex area subject in itself, and
+we won't be able to debug your linking issues by means of an autocxx bug report.
+Assuming you're using autocxx's build.rs support, the actual C++ build and
+managed by the [`cc`](https://crates.io/crates/cc) crate. You can find
+many of its options on its [`Build` type](https://docs.rs/cc/latest/cc/struct.Build.html).
+If you need to bring in an external library, you may also need to emit certain
+[print statements from your `build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib)
+to instruct cargo to link against that library.
+
 ## How to contribute to this manual
 
 More examples in this manual are _very_ welcome!
diff --git a/third_party/autocxx/book/src/examples.md b/third_party/autocxx/book/src/examples.md
index d0246f5..e7f2afe 100644
--- a/third_party/autocxx/book/src/examples.md
+++ b/third_party/autocxx/book/src/examples.md
@@ -4,7 +4,7 @@
 * [S2 example](https://github.com/google/autocxx/tree/main/examples/s2) - example using S2 geometry library
 * [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini) - example using (something like) the Steam client library
 * [Subclass example](https://github.com/google/autocxx/tree/main/examples/subclass) - example using subclasses
-* [Integration tests](https://github.com/google/autocxx/blob/main/integration-tests/src/tests.rs)
+* [Integration tests](https://github.com/google/autocxx/blob/main/integration-tests/tests/integration_test.rs)
   - hundreds of small snippets
 
 Contributions of more examples to the `examples` directory are much appreciated!
diff --git a/third_party/autocxx/book/src/rust_calls.md b/third_party/autocxx/book/src/rust_calls.md
index ebd37c5..369c304 100644
--- a/third_party/autocxx/book/src/rust_calls.md
+++ b/third_party/autocxx/book/src/rust_calls.md
@@ -36,8 +36,7 @@
     }
 }
 ",
-"#include <memory>
-class GoatObserver {
+"class GoatObserver {
 public:
     virtual void goat_full() const = 0;
     virtual ~GoatObserver() {}
@@ -62,7 +61,7 @@
 
 use ffi::*;
 
-#[is_subclass(superclass("GoatObserver"))]
+#[subclass]
 #[derive(Default)]
 pub struct MyGoatObserver;
 
@@ -124,8 +123,6 @@
     virtual ~Dinosaur() {}
 };
 
-// Currently, autocxx requires at least one 'generate!' call.
-inline void do_a_thing() {};
 ",
 {
 use autocxx::prelude::*;
@@ -136,16 +133,15 @@
     safety!(unsafe_ffi)
     subclass!("Dinosaur", TRex)
     subclass!("Dinosaur", Diplodocus)
-    generate!("do_a_thing")
 }
 
 use ffi::*;
 
-#[is_subclass(superclass("Dinosaur"))]
+#[subclass]
 #[derive(Default)]
 pub struct TRex;
 
-#[is_subclass(superclass("Dinosaur"))]
+#[subclass]
 #[derive(Default)]
 pub struct Diplodocus;
 
diff --git a/third_party/autocxx/book/src/tutorial.md b/third_party/autocxx/book/src/tutorial.md
index 3e8e5b2..5582cd9 100644
--- a/third_party/autocxx/book/src/tutorial.md
+++ b/third_party/autocxx/book/src/tutorial.md
@@ -21,12 +21,12 @@
 
 ```toml
 [dependencies]
-autocxx = "0.22.3"
+autocxx = "0.26.0"
 cxx = "1.0"
 
 [build-dependencies]
-autocxx-build = "0.22.3"
-miette = { version="4.3", features=["fancy"] } # optional but gives nicer error messages!
+autocxx-build = "0.26.0"
+miette = { version = "5", features = ["fancy"] } # optional but gives nicer error messages!
 ```
 
 Now, add a `build.rs` next to your `Cargo.toml` (this is a standard `cargo` [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html)). This is where you need your include path:
diff --git a/third_party/autocxx/build.rs b/third_party/autocxx/build.rs
new file mode 100644
index 0000000..b2de2dc
--- /dev/null
+++ b/third_party/autocxx/build.rs
@@ -0,0 +1,27 @@
+// Copyright 2023 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.
+
+// It would be nice to use the rustversion crate here instead,
+// but that doesn't work with inner attributes.
+fn main() {
+    if let Some(ver) = rustc_version() {
+        if ver.contains("nightly") {
+            println!("cargo:rustc-cfg=nightly")
+        }
+    }
+}
+
+fn rustc_version() -> Option<String> {
+    let rustc = std::env::var_os("RUSTC")?;
+    let output = std::process::Command::new(rustc)
+        .arg("--version")
+        .output()
+        .ok()?;
+    let version = String::from_utf8(output.stdout).ok()?;
+    Some(version)
+}
diff --git a/third_party/autocxx/demo/Cargo.toml b/third_party/autocxx/demo/Cargo.toml
index e81cb80..7064bea 100644
--- a/third_party/autocxx/demo/Cargo.toml
+++ b/third_party/autocxx/demo/Cargo.toml
@@ -8,14 +8,14 @@
 
 [package]
 name = "autocxx-demo"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.68"
-autocxx = { path = "..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../gen/build", version="0.22.3" }
-miette = { version="4.3", features=["fancy"]}
+autocxx-build = { path = "../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/demo/build.rs b/third_party/autocxx/demo/build.rs
index 9a16a34..69c632c 100644
--- a/third_party/autocxx/demo/build.rs
+++ b/third_party/autocxx/demo/build.rs
@@ -8,7 +8,7 @@
 
 fn main() -> miette::Result<()> {
     let path = std::path::PathBuf::from("src");
-    let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).build()?;
+    let mut b = autocxx_build::Builder::new("src/main.rs", [&path]).build()?;
     b.flag_if_supported("-std=c++14").compile("autocxx-demo");
 
     println!("cargo:rerun-if-changed=src/main.rs");
diff --git a/third_party/autocxx/engine/BUILD b/third_party/autocxx/engine/BUILD
index fd4fc83..7f72ea0 100644
--- a/third_party/autocxx/engine/BUILD
+++ b/third_party/autocxx/engine/BUILD
@@ -17,9 +17,9 @@
     crate_root = "src/lib.rs",
     edition = "2021",
     proc_macro_deps = [
+        "@crate_index//:rustversion",
         "@crate_index//:indoc",
         "@crate_index//:aquamarine",
-        "@crate_index//:strum_macros",
     ],
     rustc_flags = [
         "--cap-lints=allow",
@@ -39,13 +39,13 @@
         "@crate_index//:log",
         "@crate_index//:miette",
         "@crate_index//:once_cell",
+        "@crate_index//:prettyplease",
         "@crate_index//:proc-macro2",
         "@crate_index//:quote",
         "@crate_index//:regex",
         "@crate_index//:serde_json",
-        "@crate_index//:syn",
+        "@crate_index//:syn-2.0.28",
         "@crate_index//:tempfile",
         "@crate_index//:thiserror",
-        "@crate_index//:version_check",
     ],
 )
diff --git a/third_party/autocxx/engine/Cargo.toml b/third_party/autocxx/engine/Cargo.toml
index 1400847..9736db9 100644
--- a/third_party/autocxx/engine/Cargo.toml
+++ b/third_party/autocxx/engine/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-engine"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
@@ -18,42 +18,43 @@
 categories = ["development-tools::ffi", "api-bindings"]
 
 [features]
-default = [ "reproduction_case" ]
+default = ["reproduction_case", "runtime"]
 build = ["cc"]
-nightly = [] # for doc generation purposes only; used by docs.rs
-reproduction_case = [ "serde_json", "autocxx-parser/reproduction_case" ]
-runtime = [ "autocxx-bindgen/runtime" ]
-static = [ "autocxx-bindgen/static" ]
+nightly = []                                                           # for doc generation purposes only; used by docs.rs
+reproduction_case = ["serde_json", "autocxx-parser/reproduction_case"]
+runtime = ["autocxx-bindgen/runtime"]
+static = ["autocxx-bindgen/static"]
 
 [dependencies]
 log = "0.4"
 proc-macro2 = "1.0.11"
 quote = "1.0"
 indoc = "1.0"
-autocxx-bindgen = "=0.59.17"
-#autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "pollute-fewer-typedefs" }
+autocxx-bindgen = { version = "=0.65.1", default-features = false, features = ["logging", "which-rustfmt"] }
+#autocxx-bindgen = { git = "https://github.com/maurer/rust-bindgen", branch = "update-0.65.1", default-features = false, features = ["logging", "which-rustfmt"] }
 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.68"
-autocxx-parser = { version = "=0.22.3", path="../parser" }
+cxx-gen = "0.7.78"
+autocxx-parser = { version = "=0.26.0", path = "../parser" }
 version_check = "0.9"
-aquamarine = "0.1" # docs
-tempfile = "3.1"
+aquamarine = "0.1"                                             # docs
+tempfile = "3.4"
 once_cell = "1.7"
-strum_macros = "0.24"
 serde_json = { version = "1.0", optional = true }
-miette = "4.3"
+miette = "5"
 thiserror = "1"
 regex = "1.5"
 indexmap = "1.8"
+prettyplease = { version = "0.2.6", features = ["verbatim"] }
+rustversion = "1.0"
 
 [dependencies.syn]
-version = "1.0.39"
-features = [ "full", "printing" ]
-#features = [ "full", "printing", "extra-traits" ]
+version = "2.0.1"
+features = ["full", "printing", "visit-mut"]
+#features = [ "full", "printing", "visit-mut", "extra-traits" ]
 
 [package.metadata.docs.rs]
 features = ["build", "nightly"]
diff --git a/third_party/autocxx/engine/src/ast_discoverer.rs b/third_party/autocxx/engine/src/ast_discoverer.rs
index 8840899..f555419 100644
--- a/third_party/autocxx/engine/src/ast_discoverer.rs
+++ b/third_party/autocxx/engine/src/ast_discoverer.rs
@@ -14,12 +14,13 @@
 };
 use itertools::Itertools;
 use proc_macro2::Ident;
+use syn::visit_mut::{visit_type_mut, VisitMut};
 use syn::{
-    parse_quote, punctuated::Punctuated, Attribute, Binding, Expr, ExprAssign, ExprAssignOp,
-    ExprAwait, ExprBinary, ExprBox, ExprBreak, ExprCast, ExprField, ExprGroup, ExprLet, ExprParen,
-    ExprReference, ExprTry, ExprType, ExprUnary, ImplItem, Item, ItemEnum, ItemStruct, Pat, PatBox,
-    PatReference, PatSlice, PatTuple, Path, Receiver, ReturnType, Signature, Stmt, TraitItem, Type,
-    TypeArray, TypeGroup, TypeParamBound, TypeParen, TypePtr, TypeReference, TypeSlice,
+    parse_quote, punctuated::Punctuated, AssocConst, AssocType, Attribute, Expr, ExprAssign,
+    ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCast, ExprConst, ExprField, ExprGroup,
+    ExprLet, ExprParen, ExprReference, ExprTry, ExprUnary, ImplItem, Item, ItemEnum, ItemStruct,
+    LocalInit, Pat, PatReference, PatSlice, PatTuple, Path, ReturnType, Signature, Stmt, TraitItem,
+    Type, TypeArray, TypeGroup, TypeParamBound, TypeParen, TypePtr, TypeReference, TypeSlice,
 };
 use thiserror::Error;
 
@@ -34,8 +35,6 @@
 pub enum DiscoveryErr {
     #[error("#[extern_rust_function] was attached to a method in an impl block that was too complex for autocxx. autocxx supports only \"impl X {{...}}\" where X is a single identifier, not a path or more complex type.")]
     FoundExternRustFunOnTypeWithoutClearReceiver,
-    #[error("#[extern_rust_function] was attached to a method taking no parameters.")]
-    NonReferenceReceiver,
     #[error("#[extern_rust_function] was attached to a method taking a receiver by value.")]
     NoParameterOnMethod,
     #[error("#[extern_rust_function] was in an impl block nested wihtin another block. This is only supported in the outermost mod of a file, alongside the include_cpp!.")]
@@ -103,7 +102,7 @@
                     self.discoveries.extern_rust_funs.push(RustFun {
                         path: self.deeper_path(&fun.sig.ident),
                         sig: fun.sig.clone(),
-                        receiver: None,
+                        has_receiver: false,
                     });
                 }
             }
@@ -178,7 +177,7 @@
     }
 
     fn search_trait_item(&mut self, itm: &TraitItem) -> Result<(), DiscoveryErr> {
-        if let TraitItem::Method(itm) = itm {
+        if let TraitItem::Fn(itm) = itm {
             if let Some(block) = &itm.default {
                 self.search_stmts(block.stmts.iter())?
             }
@@ -199,13 +198,14 @@
     fn search_stmt(&mut self, stmt: &Stmt) -> Result<(), DiscoveryErr> {
         match stmt {
             Stmt::Local(lcl) => {
-                if let Some((_, expr)) = &lcl.init {
+                if let Some(LocalInit { expr, .. }) = &lcl.init {
                     self.search_expr(expr)?
                 }
                 self.search_pat(&lcl.pat)
             }
             Stmt::Item(itm) => self.search_item(itm),
-            Stmt::Expr(exp) | Stmt::Semi(exp, _) => self.search_expr(exp),
+            Stmt::Expr(exp, _) => self.search_expr(exp),
+            Stmt::Macro(_) => Ok(()),
         }
     }
 
@@ -214,10 +214,9 @@
             Expr::Path(exp) => {
                 self.search_path(&exp.path)?;
             }
-            Expr::Macro(_) => {}
+            Expr::Macro(_) | Expr::Infer(_) => {}
             Expr::Array(array) => self.search_exprs(array.elems.iter())?,
             Expr::Assign(ExprAssign { left, right, .. })
-            | Expr::AssignOp(ExprAssignOp { left, right, .. })
             | Expr::Binary(ExprBinary { left, right, .. }) => {
                 self.search_expr(left)?;
                 self.search_expr(right)?;
@@ -226,9 +225,10 @@
             Expr::Await(ExprAwait { base, .. }) | Expr::Field(ExprField { base, .. }) => {
                 self.search_expr(base)?
             }
-            Expr::Block(blck) => self.search_stmts(blck.block.stmts.iter())?,
-            Expr::Box(ExprBox { expr, .. })
-            | Expr::Break(ExprBreak {
+            Expr::Block(ExprBlock { block, .. }) | Expr::Const(ExprConst { block, .. }) => {
+                self.search_stmts(block.stmts.iter())?
+            }
+            Expr::Break(ExprBreak {
                 expr: Some(expr), ..
             })
             | Expr::Cast(ExprCast { expr, .. })
@@ -236,7 +236,6 @@
             | Expr::Paren(ExprParen { expr, .. })
             | Expr::Reference(ExprReference { expr, .. })
             | Expr::Try(ExprTry { expr, .. })
-            | Expr::Type(ExprType { expr, .. })
             | Expr::Unary(ExprUnary { expr, .. }) => self.search_expr(expr)?,
             Expr::Call(exc) => {
                 self.search_expr(&exc.func)?;
@@ -281,8 +280,8 @@
                 self.search_exprs(mtc.args.iter())?;
             }
             Expr::Range(exr) => {
-                self.search_option_expr(&exr.from)?;
-                self.search_option_expr(&exr.to)?;
+                self.search_option_expr(&exr.start)?;
+                self.search_option_expr(&exr.end)?;
             }
             Expr::Repeat(exr) => {
                 self.search_expr(&exr.expr)?;
@@ -334,7 +333,7 @@
         impl_item: &ImplItem,
         receiver: Option<&RustPath>,
     ) -> Result<(), DiscoveryErr> {
-        if let ImplItem::Method(itm) = impl_item {
+        if let ImplItem::Fn(itm) = impl_item {
             if Self::has_attr(&itm.attrs, EXTERN_RUST_FUN) {
                 if self.mod_path.is_some() {
                     return Err(DiscoveryErr::FoundExternRustFunWithinMod);
@@ -347,7 +346,7 @@
                     self.discoveries.extern_rust_funs.push(RustFun {
                         path: self.deeper_path(&itm.sig.ident),
                         sig,
-                        receiver: Some(receiver.get_final_ident().clone()),
+                        has_receiver: true,
                     });
                     self.discoveries.extern_rust_types.push(receiver.clone())
                 } else {
@@ -363,10 +362,15 @@
 
     fn search_pat(&mut self, pat: &Pat) -> Result<(), DiscoveryErr> {
         match pat {
-            Pat::Box(PatBox { pat, .. }) | Pat::Reference(PatReference { pat, .. }) => {
-                self.search_pat(pat)
+            Pat::Const(const_) => {
+                for stmt in &const_.block.stmts {
+                    self.search_stmt(stmt)?
+                }
+                Ok(())
             }
+            Pat::Reference(PatReference { pat, .. }) => self.search_pat(pat),
             Pat::Ident(_) | Pat::Lit(_) | Pat::Macro(_) | Pat::Range(_) | Pat::Rest(_) => Ok(()),
+            Pat::Paren(paren) => self.search_pat(&paren.pat),
             Pat::Or(pator) => {
                 for case in &pator.cases {
                     self.search_pat(case)?;
@@ -389,7 +393,7 @@
             }
             Pat::TupleStruct(tps) => {
                 self.search_path(&tps.path)?;
-                for f in &tps.pat.elems {
+                for f in &tps.elems {
                     self.search_pat(f)?;
                 }
                 Ok(())
@@ -398,6 +402,7 @@
                 self.search_pat(&pt.pat)?;
                 self.search_type(&pt.ty)
             }
+            Pat::Verbatim(_) | Pat::Wild(_) => Ok(()),
             _ => Ok(()),
         }
     }
@@ -438,7 +443,7 @@
 
     fn search_type_param_bounds(
         &mut self,
-        bounds: &Punctuated<TypeParamBound, syn::token::Add>,
+        bounds: &Punctuated<TypeParamBound, syn::token::Plus>,
     ) -> Result<(), DiscoveryErr> {
         for b in bounds {
             if let syn::TypeParamBound::Trait(tpbt) = b {
@@ -467,13 +472,17 @@
                     match arg {
                         syn::GenericArgument::Lifetime(_) => {}
                         syn::GenericArgument::Type(ty)
-                        | syn::GenericArgument::Binding(Binding { ty, .. }) => {
+                        | syn::GenericArgument::AssocType(AssocType { ty, .. }) => {
                             self.search_type(ty)?
                         }
                         syn::GenericArgument::Constraint(c) => {
                             self.search_type_param_bounds(&c.bounds)?
                         }
-                        syn::GenericArgument::Const(c) => self.search_expr(c)?,
+                        syn::GenericArgument::Const(value)
+                        | syn::GenericArgument::AssocConst(AssocConst { value, .. }) => {
+                            self.search_expr(value)?
+                        }
+                        _ => {}
                     }
                 }
             }
@@ -489,7 +498,7 @@
 
     fn has_attr(attrs: &[Attribute], attr_name: &str) -> bool {
         attrs.iter().any(|attr| {
-            attr.path
+            attr.path()
                 .segments
                 .last()
                 .map(|seg| seg.ident == attr_name)
@@ -498,6 +507,24 @@
     }
 }
 
+struct SelfSubstituter<'a> {
+    self_ty: &'a Ident,
+}
+
+impl<'a> SelfSubstituter<'a> {
+    pub fn new(self_ty: &'a Ident) -> Self {
+        Self { self_ty }
+    }
+}
+
+impl<'a> VisitMut for SelfSubstituter<'a> {
+    fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) {
+        if i.qself.is_none() && i.path.is_ident("Self") {
+            i.path = Path::from(self.self_ty.clone());
+        }
+    }
+}
+
 /// Take a method signature that may be `fn a(&self)`
 /// and turn it into `fn a(self: &A)` which is what we will
 /// need to specify to cxx.
@@ -505,25 +532,19 @@
     let mut sig = sig.clone();
     match sig.inputs.iter_mut().next() {
         Some(first_arg) => match first_arg {
-            syn::FnArg::Receiver(Receiver {
-                reference: Some(_),
-                mutability: Some(_),
-                ..
-            }) => {
+            syn::FnArg::Receiver(rec_arg) => {
+                let mut substituted_type = rec_arg.ty.clone();
+                visit_type_mut(&mut SelfSubstituter::new(receiver), &mut substituted_type);
                 *first_arg = parse_quote! {
-                    self: &mut #receiver
+                        qelf: #substituted_type
+                };
+                if let syn::FnArg::Typed(ref mut pat_type) = *first_arg {
+                    if let syn::Pat::Ident(ref mut pat_ident) = *pat_type.pat {
+                        assert_eq!(pat_ident.ident.to_string(), "qelf");
+                        pat_ident.ident = Ident::new("self", pat_ident.ident.span());
+                    }
                 }
             }
-            syn::FnArg::Receiver(Receiver {
-                reference: Some(_),
-                mutability: None,
-                ..
-            }) => {
-                *first_arg = parse_quote! {
-                    self: &#receiver
-                }
-            }
-            syn::FnArg::Receiver(..) => return Err(DiscoveryErr::NonReferenceReceiver),
             syn::FnArg::Typed(_) => {}
         },
         None => return Err(DiscoveryErr::NoParameterOnMethod),
@@ -534,7 +555,7 @@
 #[cfg(test)]
 mod tests {
     use quote::{quote, ToTokens};
-    use syn::{parse_quote, ImplItemMethod};
+    use syn::{parse_quote, ImplItemFn};
 
     use crate::{ast_discoverer::add_receiver, types::make_ident};
 
@@ -690,7 +711,7 @@
 
     #[test]
     fn test_add_receiver() {
-        let meth: ImplItemMethod = parse_quote! {
+        let meth: ImplItemFn = parse_quote! {
             fn a(&self) {}
         };
         let a = make_ident("A");
@@ -702,7 +723,7 @@
             quote! { fn a(self: &A) }.to_string()
         );
 
-        let meth: ImplItemMethod = parse_quote! {
+        let meth: ImplItemFn = parse_quote! {
             fn a(&mut self, b: u32) -> Foo {}
         };
         assert_eq!(
@@ -713,12 +734,7 @@
             quote! { fn a(self: &mut A, b: u32) -> Foo }.to_string()
         );
 
-        let meth: ImplItemMethod = parse_quote! {
-            fn a(self) {}
-        };
-        assert!(add_receiver(&meth.sig, &a).is_err());
-
-        let meth: ImplItemMethod = parse_quote! {
+        let meth: ImplItemFn = parse_quote! {
             fn a() {}
         };
         assert!(add_receiver(&meth.sig, &a).is_err());
diff --git a/third_party/autocxx/engine/src/builder.rs b/third_party/autocxx/engine/src/builder.rs
index d0b5199..064056e 100644
--- a/third_party/autocxx/engine/src/builder.rs
+++ b/third_party/autocxx/engine/src/builder.rs
@@ -10,8 +10,8 @@
 use miette::Diagnostic;
 use thiserror::Error;
 
-use crate::generate_rs_single;
-use crate::{strip_system_headers, CppCodegenOptions, ParseError, RebuildDependencyRecorder};
+use crate::{generate_rs_single, CodegenOptions};
+use crate::{get_cxx_header_bytes, CppCodegenOptions, ParseError, RebuildDependencyRecorder};
 use std::ffi::OsStr;
 use std::ffi::OsString;
 use std::fs::File;
@@ -63,8 +63,16 @@
 
 /// An object to allow building of bindings from a `build.rs` file.
 ///
-/// It would be unusual to use this directly - see the `autocxx_build` or
+/// It would be unusual to create this directly - see the `autocxx_build` or
 /// `autocxx_gen` crates.
+///
+/// Once you've got one of these objects, you may set some configuration
+/// options but then you're likely to want to call the [`build`] method.
+///
+/// # Setting C++ version
+///
+/// Ensure you use [`extra_clang_args`] as well as giving an appropriate
+/// option to the [`cc::Build`] which you receive from the [`build`] function.
 #[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
 pub struct Builder<'a, BuilderContext> {
     rs_file: PathBuf,
@@ -73,7 +81,7 @@
     dependency_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
     custom_gendir: Option<PathBuf>,
     auto_allowlist: bool,
-    cpp_codegen_options: CppCodegenOptions<'a>,
+    codegen_options: CodegenOptions<'a>,
     // This member is to ensure that this type is parameterized
     // by a BuilderContext. The goal is to balance three needs:
     // (1) have most of the functionality over in autocxx_engine,
@@ -108,12 +116,14 @@
             dependency_recorder: CTX::get_dependency_recorder(),
             custom_gendir: None,
             auto_allowlist: false,
-            cpp_codegen_options: CppCodegenOptions::default(),
+            codegen_options: CodegenOptions::default(),
             ctx: PhantomData,
         }
     }
 
-    /// Specify extra arguments for clang.
+    /// Specify extra arguments for clang. These are used when parsing
+    /// C++ headers. For example, you might want to provide
+    /// `-std=c++17` to specify C++17.
     pub fn extra_clang_args(mut self, extra_clang_args: &[&str]) -> Self {
         self.extra_clang_args = extra_clang_args.iter().map(|s| s.to_string()).collect();
         self
@@ -130,7 +140,7 @@
     where
         F: FnOnce(&mut CppCodegenOptions),
     {
-        modifier(&mut self.cpp_codegen_options);
+        modifier(&mut self.codegen_options.cpp_codegen_options);
         self
     }
 
@@ -152,20 +162,33 @@
         self
     }
 
+    #[doc(hidden)]
+    /// Whether to force autocxx always to generate extra Rust and C++
+    /// side shims. This is only used by the integration test suite to
+    /// exercise more code paths - don't use it!
+    pub fn force_wrapper_generation(mut self, do_it: bool) -> Self {
+        self.codegen_options.force_wrapper_gen = do_it;
+        self
+    }
+
     /// Whether to suppress inclusion of system headers (`memory`, `string` etc.)
     /// from generated C++ bindings code. This should not normally be used,
     /// but can occasionally be useful if you're reducing a test case and you
     /// have a preprocessed header file which already contains absolutely everything
     /// that the bindings could ever need.
     pub fn suppress_system_headers(mut self, do_it: bool) -> Self {
-        self.cpp_codegen_options.suppress_system_headers = do_it;
+        self.codegen_options
+            .cpp_codegen_options
+            .suppress_system_headers = do_it;
         self
     }
 
     /// An annotation optionally to include on each C++ function.
     /// For example to export the symbol from a library.
     pub fn cxx_impl_annotations(mut self, cxx_impl_annotations: Option<String>) -> Self {
-        self.cpp_codegen_options.cxx_impl_annotations = cxx_impl_annotations;
+        self.codegen_options
+            .cpp_codegen_options
+            .cxx_impl_annotations = cxx_impl_annotations;
         self
     }
 
@@ -176,6 +199,17 @@
     /// so if you use the `miette` crate and its `fancy` feature, then simply
     /// return a `miette::Result` from your main function, you should get nicely
     /// printed diagnostics.
+    ///
+    /// As this is a [`cc::Build`] there are lots of options you can apply to
+    /// the resulting options, but please bear in mind that these only apply
+    /// to the build process for the generated code - such options will not
+    /// influence autocxx's process for parsing header files.
+    ///
+    /// For example, if you wish to set the C++ version to C++17, you might
+    /// be tempted to use [`cc::Build::flag_if_supported`] to add the
+    /// `-std=c++17` flag. However, this won't affect the header parsing which
+    /// autocxx does internally (by means of bindgen) so you _additionally_
+    /// should call [`extra_clang_args`] with that same option.
     pub fn build(self) -> Result<BuilderBuild, BuilderError> {
         self.build_listing_files().map(|r| r.0)
     }
@@ -208,7 +242,11 @@
         write_to_file(
             &incdir,
             "cxx.h",
-            &Self::get_cxx_header_bytes(self.cpp_codegen_options.suppress_system_headers),
+            &get_cxx_header_bytes(
+                self.codegen_options
+                    .cpp_codegen_options
+                    .suppress_system_headers,
+            ),
         )?;
 
         let autocxx_inc = build_autocxx_inc(self.autocxx_incs, &incdir);
@@ -221,7 +259,7 @@
                 autocxx_inc,
                 clang_args,
                 self.dependency_recorder,
-                &self.cpp_codegen_options,
+                &self.codegen_options,
             )
             .map_err(BuilderError::ParseError)?;
         let mut counter = 0;
@@ -235,10 +273,10 @@
         builder.includes(parsed_file.include_dirs());
         for include_cpp in parsed_file.get_cpp_buildables() {
             let generated_code = include_cpp
-                .generate_h_and_cxx(&self.cpp_codegen_options)
+                .generate_h_and_cxx(&self.codegen_options.cpp_codegen_options)
                 .map_err(BuilderError::InvalidCxx)?;
             for filepair in generated_code.0 {
-                let fname = format!("gen{}.cxx", counter);
+                let fname = format!("gen{counter}.cxx");
                 counter += 1;
                 if let Some(implementation) = &filepair.implementation {
                     let gen_cxx_path = write_to_file(&cxxdir, &fname, implementation)?;
@@ -260,10 +298,6 @@
             Ok(BuilderSuccess(builder, generated_rs, generated_cpp))
         }
     }
-
-    fn get_cxx_header_bytes(suppress_system_headers: bool) -> Vec<u8> {
-        strip_system_headers(crate::HEADER.as_bytes().to_vec(), suppress_system_headers)
-    }
 }
 
 fn ensure_created(dir: &Path) -> Result<(), BuilderError> {
@@ -285,6 +319,13 @@
 
 fn write_to_file(dir: &Path, filename: &str, content: &[u8]) -> Result<PathBuf, BuilderError> {
     let path = dir.join(filename);
+    if let Ok(existing_contents) = std::fs::read(&path) {
+        // Avoid altering timestamps on disk if the file already exists,
+        // to stop downstream build steps recurring.
+        if existing_contents == content {
+            return Ok(path);
+        }
+    }
     try_write_to_file(&path, content).map_err(|e| BuilderError::FileWriteFail(e, path.clone()))?;
     Ok(path)
 }
diff --git a/third_party/autocxx/engine/src/conversion/analysis/abstract_types.rs b/third_party/autocxx/engine/src/conversion/analysis/abstract_types.rs
index dac9684..884cbd0 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/abstract_types.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/abstract_types.rs
@@ -6,6 +6,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use indexmap::map::IndexMap as HashMap;
+use syn::{punctuated::Punctuated, token::Comma, FnArg};
+
 use super::{
     fun::{
         FnAnalysis, FnKind, FnPhase, FnPrePhase2, MethodKind, PodAndConstructorAnalysis,
@@ -13,106 +16,164 @@
     },
     pod::PodAnalysis,
 };
-use crate::conversion::{api::Api, apivec::ApiVec};
 use crate::conversion::{
-    api::TypeKind,
+    analysis::{depth_first::fields_and_bases_first, fun::ReceiverMutability},
+    api::{ApiName, TypeKind},
     error_reporter::{convert_apis, convert_item_apis},
-    ConvertError,
+    ConvertErrorFromCpp,
+};
+use crate::{
+    conversion::{api::Api, apivec::ApiVec},
+    types::QualifiedName,
 };
 use indexmap::set::IndexSet as HashSet;
 
+#[derive(Hash, PartialEq, Eq, Clone, Debug)]
+struct Signature {
+    name: String,
+    args: Vec<syn::Type>,
+    constness: ReceiverMutability,
+}
+
+impl Signature {
+    fn new(
+        name: &ApiName,
+        params: &Punctuated<FnArg, Comma>,
+        constness: ReceiverMutability,
+    ) -> Self {
+        Signature {
+            name: name.cpp_name(),
+            args: params
+                .iter()
+                .skip(1) // skip `this` implicit argument
+                .filter_map(|p| {
+                    if let FnArg::Typed(t) = p {
+                        Some((*t.ty).clone())
+                    } else {
+                        None
+                    }
+                })
+                .collect(),
+            constness,
+        }
+    }
+}
+
 /// Spot types with pure virtual functions and mark them abstract.
-pub(crate) fn mark_types_abstract(mut apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPrePhase2> {
-    let mut abstract_types: HashSet<_> = apis
-        .iter()
-        .filter_map(|api| match &api {
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind:
-                            FnKind::Method {
-                                impl_for: self_ty_name,
-                                method_kind: MethodKind::PureVirtual(_),
-                                ..
-                            },
-                        ..
-                    },
-                ..
-            } => Some(self_ty_name.clone()),
-            _ => None,
+pub(crate) fn mark_types_abstract(apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPrePhase2> {
+    #[derive(Default, Debug, Clone)]
+    struct ClassAbstractState {
+        undefined: HashSet<Signature>,
+        defined: HashSet<Signature>,
+    }
+    let mut class_states: HashMap<QualifiedName, ClassAbstractState> = HashMap::new();
+    let mut abstract_classes = HashSet::new();
+
+    for api in apis.iter() {
+        if let Api::Function {
+            name,
+            analysis:
+                FnAnalysis {
+                    kind:
+                        FnKind::Method {
+                            impl_for: self_ty_name,
+                            method_kind,
+                            ..
+                        },
+                    params,
+                    ..
+                },
+            ..
+        } = api
+        {
+            match method_kind {
+                MethodKind::PureVirtual(constness) => {
+                    class_states
+                        .entry(self_ty_name.clone())
+                        .or_default()
+                        .undefined
+                        .insert(Signature::new(name, params, *constness));
+                }
+                MethodKind::Virtual(constness) => {
+                    class_states
+                        .entry(self_ty_name.clone())
+                        .or_default()
+                        .defined
+                        .insert(Signature::new(name, params, *constness));
+                }
+                _ => {}
+            }
+        }
+    }
+
+    for api in fields_and_bases_first(apis.iter()) {
+        if let Api::Struct {
+            analysis:
+                PodAndConstructorAnalysis {
+                    pod:
+                        PodAnalysis {
+                            bases,
+                            kind: TypeKind::Pod | TypeKind::NonPod,
+                            ..
+                        },
+                    ..
+                },
+            name,
+            ..
+        } = api
+        {
+            // resolve virtuals for a class: start with new pure virtuals in this class
+            let mut self_cs = class_states.get(&name.name).cloned().unwrap_or_default();
+
+            // then add pure virtuals of bases
+            for base in bases.iter() {
+                if let Some(base_cs) = class_states.get(base) {
+                    self_cs.undefined.extend(base_cs.undefined.iter().cloned());
+                }
+            }
+
+            // then remove virtuals defined in this class
+            self_cs
+                .undefined
+                .retain(|und| !self_cs.defined.contains(und));
+
+            // if there are undefined functions, mark as virtual
+            if !self_cs.undefined.is_empty() {
+                abstract_classes.insert(name.name.clone());
+            }
+
+            // store it back so child classes can read it properly
+            *class_states.entry(name.name.clone()).or_default() = self_cs;
+        }
+    }
+
+    // mark abstract types as abstract
+    let mut apis: ApiVec<_> = apis
+        .into_iter()
+        .map(|mut api| {
+            if let Api::Struct { name, analysis, .. } = &mut api {
+                if abstract_classes.contains(&name.name) {
+                    analysis.pod.kind = TypeKind::Abstract;
+                }
+            }
+            api
         })
         .collect();
 
-    // Spot any derived classes (recursively). Also, any types which have a base
-    // class that's not on the allowlist are presumed to be abstract, because we
-    // have no way of knowing (as they're not on the allowlist, there will be
-    // no methods associated so we won't be able to spot pure virtual methods).
-    let mut iterate = true;
-    while iterate {
-        iterate = false;
-        apis = apis
-            .into_iter()
-            .map(|api| {
-                match api {
-                    Api::Struct {
-                        analysis:
-                            PodAndConstructorAnalysis {
-                                pod:
-                                    PodAnalysis {
-                                        bases,
-                                        kind: TypeKind::Pod | TypeKind::NonPod,
-                                        castable_bases,
-                                        field_deps,
-                                        field_info,
-                                        is_generic,
-                                        in_anonymous_namespace,
-                                    },
-                                constructors,
-                            },
-                        name,
-                        details,
-                    } if abstract_types.contains(&name.name)
-                        || !abstract_types.is_disjoint(&bases) =>
-                    {
-                        abstract_types.insert(name.name.clone());
-                        // Recurse in case there are further dependent types
-                        iterate = true;
-                        Api::Struct {
-                            analysis: PodAndConstructorAnalysis {
-                                pod: PodAnalysis {
-                                    bases,
-                                    kind: TypeKind::Abstract,
-                                    castable_bases,
-                                    field_deps,
-                                    field_info,
-                                    is_generic,
-                                    in_anonymous_namespace,
-                                },
-                                constructors,
-                            },
-                            name,
-                            details,
-                        }
-                    }
-                    _ => api,
-                }
-            })
-            .collect()
-    }
-
     // We also need to remove any constructors belonging to these
     // abstract types.
     apis.retain(|api| {
         !matches!(&api,
-        Api::Function {
-            analysis:
-                FnAnalysis {
-                    kind: FnKind::Method{impl_for: self_ty, method_kind: MethodKind::Constructor{..}, ..}
-                        | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..},
+            Api::Function {
+                analysis:
+                    FnAnalysis {
+                        kind: FnKind::Method{impl_for: self_ty, method_kind: MethodKind::Constructor{..}, ..}
+                            | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..},
+                        ..
+                    },
                     ..
-                },
-                ..
-        } if abstract_types.contains(self_ty))
+            } if abstract_classes.contains(self_ty)
+        )
     });
 
     // Finally, if there are any types which are nested inside other types,
@@ -143,10 +204,11 @@
             .map(|n| n.contains("::"))
             .unwrap_or_default() =>
         {
-            Err(ConvertError::AbstractNestedType)
+            Err(ConvertErrorFromCpp::AbstractNestedType)
         }
         _ => Ok(Box::new(std::iter::once(api))),
     });
+
     results
 }
 
diff --git a/third_party/autocxx/engine/src/conversion/analysis/allocators.rs b/third_party/autocxx/engine/src/conversion/analysis/allocators.rs
index 3695de0..da25a77 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/allocators.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/allocators.rs
@@ -12,9 +12,13 @@
 
 use crate::{
     conversion::{
-        api::{Api, ApiName, CppVisibility, FuncToConvert, Provenance, References, TraitSynthesis},
+        api::{
+            Api, ApiName, CppVisibility, DeletedOrDefaulted, FuncToConvert, Provenance, References,
+            TraitSynthesis,
+        },
         apivec::ApiVec,
     },
+    minisyn::minisynize_punctuated,
     types::{make_ident, QualifiedName},
 };
 
@@ -73,8 +77,8 @@
                 fun: Box::new(FuncToConvert {
                     ident,
                     doc_attrs: Vec::new(),
-                    inputs,
-                    output,
+                    inputs: minisynize_punctuated(inputs),
+                    output: output.into(),
                     vis: parse_quote! { pub },
                     virtualness: crate::conversion::api::Virtualness::None,
                     cpp_vis: CppVisibility::Public,
@@ -86,7 +90,7 @@
                     synthesized_this_type: None,
                     synthetic_cpp: Some((cpp_function_body, CppFunctionKind::Function)),
                     add_to_trait: Some(synthesis),
-                    is_deleted: false,
+                    is_deleted: DeletedOrDefaulted::Neither,
                     provenance: Provenance::SynthesizedOther,
                     variadic: false,
                 }),
diff --git a/third_party/autocxx/engine/src/conversion/analysis/casts.rs b/third_party/autocxx/engine/src/conversion/analysis/casts.rs
index 5f493f9..2426a50 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/casts.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/casts.rs
@@ -6,13 +6,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use crate::minisyn::FnArg;
 use itertools::Itertools;
 use quote::quote;
-use syn::{parse_quote, FnArg};
+use syn::parse_quote;
 
 use crate::{
     conversion::{
-        api::{Api, ApiName, CastMutability, Provenance, References, TraitSynthesis},
+        api::{
+            Api, ApiName, CastMutability, DeletedOrDefaulted, Provenance, References,
+            TraitSynthesis,
+        },
         apivec::ApiVec,
     },
     types::{make_ident, QualifiedName},
@@ -112,7 +116,7 @@
                 mutable,
             }),
             synthetic_cpp: Some((CppFunctionBody::Cast, CppFunctionKind::Function)),
-            is_deleted: false,
+            is_deleted: DeletedOrDefaulted::Neither,
             provenance: Provenance::SynthesizedOther,
             variadic: false,
         }),
diff --git a/third_party/autocxx/engine/src/conversion/analysis/ctypes.rs b/third_party/autocxx/engine/src/conversion/analysis/ctypes.rs
index b2ce840..bc6f096 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/ctypes.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/ctypes.rs
@@ -8,7 +8,7 @@
 
 use indexmap::map::IndexMap as HashMap;
 
-use syn::Ident;
+use crate::minisyn::Ident;
 
 use crate::conversion::api::ApiName;
 use crate::conversion::apivec::ApiVec;
diff --git a/third_party/autocxx/engine/src/conversion/analysis/deps.rs b/third_party/autocxx/engine/src/conversion/analysis/deps.rs
index 7aca96c..a301090 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/deps.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/deps.rs
@@ -6,8 +6,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use itertools::Itertools;
-
 use crate::{
     conversion::api::{Api, TypeKind},
     types::QualifiedName,
@@ -22,10 +20,6 @@
 pub(crate) trait HasDependencies {
     fn name(&self) -> &QualifiedName;
     fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>;
-
-    fn format_deps(&self) -> String {
-        self.deps().join(",")
-    }
 }
 
 impl HasDependencies for Api<FnPrePhase1> {
@@ -37,13 +31,9 @@
                 ..
             } => Box::new(old_tyname.iter().chain(deps.iter())),
             Api::Struct {
-                analysis:
-                    PodAnalysis {
-                        kind: TypeKind::Pod,
-                        bases,
-                        field_deps,
-                        ..
-                    },
+                analysis: PodAnalysis {
+                    bases, field_deps, ..
+                },
                 ..
             } => Box::new(field_deps.iter().chain(bases.iter())),
             Api::Function { analysis, .. } => Box::new(analysis.deps.iter()),
@@ -52,7 +42,7 @@
                 superclass,
             } => Box::new(std::iter::once(superclass)),
             Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()),
-            Api::RustFn { receiver, .. } => Box::new(receiver.iter()),
+            Api::RustFn { deps, .. } => Box::new(deps.iter()),
             _ => Box::new(std::iter::empty()),
         }
     }
@@ -105,7 +95,7 @@
                 superclass,
             } => Box::new(std::iter::once(superclass)),
             Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()),
-            Api::RustFn { receiver, .. } => Box::new(receiver.iter()),
+            Api::RustFn { deps, .. } => Box::new(deps.iter()),
             _ => Box::new(std::iter::empty()),
         }
     }
diff --git a/third_party/autocxx/engine/src/conversion/analysis/depth_first.rs b/third_party/autocxx/engine/src/conversion/analysis/depth_first.rs
index 02459e1..baadbd7 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/depth_first.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/depth_first.rs
@@ -14,10 +14,20 @@
 
 use crate::types::QualifiedName;
 
-use super::deps::HasDependencies;
+/// A little like `HasDependencies` but accounts for only direct fiele
+/// and bases.
+pub(crate) trait HasFieldsAndBases {
+    fn name(&self) -> &QualifiedName;
+    /// Return field and base class dependencies of this item.
+    /// This should only include those items where a definition is required,
+    /// not merely a declaration. So if the field type is
+    /// `std::unique_ptr<A>`, this should only return `std::unique_ptr`.
+    fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>;
+}
 
-/// Return APIs in a depth-first order, i.e. those with no dependencies first.
-pub(super) fn depth_first<'a, T: HasDependencies + Debug + 'a>(
+/// Iterate through APIs in an order such that later APIs have no fields or bases
+/// other than those whose types have already been processed.
+pub(super) fn fields_and_bases_first<'a, T: HasFieldsAndBases + Debug + 'a>(
     inputs: impl Iterator<Item = &'a T> + 'a,
 ) -> impl Iterator<Item = &'a T> {
     let queue: VecDeque<_> = inputs.collect();
@@ -25,18 +35,21 @@
     DepthFirstIter { queue, yet_to_do }
 }
 
-struct DepthFirstIter<'a, T: HasDependencies + Debug> {
+struct DepthFirstIter<'a, T: HasFieldsAndBases + Debug> {
     queue: VecDeque<&'a T>,
     yet_to_do: HashSet<&'a QualifiedName>,
 }
 
-impl<'a, T: HasDependencies + Debug> Iterator for DepthFirstIter<'a, T> {
+impl<'a, T: HasFieldsAndBases + Debug> Iterator for DepthFirstIter<'a, T> {
     type Item = &'a T;
 
     fn next(&mut self) -> Option<Self::Item> {
         let first_candidate = self.queue.get(0).map(|api| api.name());
         while let Some(candidate) = self.queue.pop_front() {
-            if !candidate.deps().any(|d| self.yet_to_do.contains(&d)) {
+            if !candidate
+                .field_and_base_deps()
+                .any(|d| self.yet_to_do.contains(&d))
+            {
                 self.yet_to_do.remove(candidate.name());
                 return Some(candidate);
             }
@@ -46,7 +59,11 @@
                     "Failed to find a candidate; there must be a circular dependency. Queue is {}",
                     self.queue
                         .iter()
-                        .map(|item| format!("{}: {}", item.name(), item.deps().join(",")))
+                        .map(|item| format!(
+                            "{}: {}",
+                            item.name(),
+                            item.field_and_base_deps().join(",")
+                        ))
                         .join("\n")
                 );
             }
@@ -59,17 +76,17 @@
 mod test {
     use crate::types::QualifiedName;
 
-    use super::{depth_first, HasDependencies};
+    use super::{fields_and_bases_first, HasFieldsAndBases};
 
     #[derive(Debug)]
     struct Thing(QualifiedName, Vec<QualifiedName>);
 
-    impl HasDependencies for Thing {
+    impl HasFieldsAndBases for Thing {
         fn name(&self) -> &QualifiedName {
             &self.0
         }
 
-        fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+        fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
             Box::new(self.1.iter())
         }
     }
@@ -89,7 +106,7 @@
             vec![QualifiedName::new_from_cpp_name("a")],
         );
         let api_list = vec![a, b, c];
-        let mut it = depth_first(api_list.iter());
+        let mut it = fields_and_bases_first(api_list.iter());
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("a"));
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("c"));
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("b"));
diff --git a/third_party/autocxx/engine/src/conversion/analysis/fun/bridge_name_tracker.rs b/third_party/autocxx/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
index 7e81c59..c98beaf 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
@@ -104,7 +104,7 @@
             *count += 1;
             prefix
         } else {
-            let r = format!("{}_autocxx{}", prefix, count);
+            let r = format!("{prefix}_autocxx{count}");
             *count += 1;
             r
         }
diff --git a/third_party/autocxx/engine/src/conversion/analysis/fun/function_wrapper.rs b/third_party/autocxx/engine/src/conversion/analysis/fun/function_wrapper.rs
index ab3b7d9..0495533 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/fun/function_wrapper.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/fun/function_wrapper.rs
@@ -6,12 +6,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use crate::minisyn::Ident;
 use crate::{
-    conversion::api::SubclassName,
+    conversion::{api::SubclassName, type_helpers::extract_pinned_mutable_reference_type},
     types::{Namespace, QualifiedName},
 };
 use quote::ToTokens;
-use syn::{parse_quote, Ident, Type, TypeReference};
+use syn::{parse_quote, Type, TypeReference};
 
 #[derive(Clone, Debug)]
 pub(crate) enum CppConversionType {
@@ -85,9 +86,9 @@
 /// variant params. That would remove the possibility of various runtime
 /// panics by enforcing (for example) that conversion from a pointer always
 /// has a Type::Ptr.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct TypeConversionPolicy {
-    unwrapped_type: Type,
+    unwrapped_type: crate::minisyn::Type,
     pub(crate) cpp_conversion: CppConversionType,
     pub(crate) rust_conversion: RustConversionType,
 }
@@ -103,7 +104,7 @@
         rust_conversion: RustConversionType,
     ) -> Self {
         Self {
-            unwrapped_type: ty,
+            unwrapped_type: ty.into(),
             cpp_conversion,
             rust_conversion,
         }
@@ -118,7 +119,14 @@
             Type::Reference(TypeReference {
                 elem, mutability, ..
             }) => (*elem, mutability.is_some()),
-            _ => panic!("Not a ptr: {}", ty.to_token_stream()),
+            Type::Path(ref tp) => {
+                if let Some(unwrapped_type) = extract_pinned_mutable_reference_type(tp) {
+                    (unwrapped_type.clone(), true)
+                } else {
+                    panic!("Path was not a mutable reference: {}", ty.to_token_stream())
+                }
+            }
+            _ => panic!("Not a reference: {}", ty.to_token_stream()),
         };
         TypeConversionPolicy {
             unwrapped_type: if is_mut {
@@ -133,7 +141,7 @@
 
     pub(crate) fn new_to_unique_ptr(ty: Type) -> Self {
         TypeConversionPolicy {
-            unwrapped_type: ty,
+            unwrapped_type: ty.into(),
             cpp_conversion: CppConversionType::FromValueToUniquePtr,
             rust_conversion: RustConversionType::None,
         }
@@ -141,7 +149,7 @@
 
     pub(crate) fn new_for_placement_return(ty: Type) -> Self {
         TypeConversionPolicy {
-            unwrapped_type: ty,
+            unwrapped_type: ty.into(),
             cpp_conversion: CppConversionType::FromReturnValueToPlacementPtr,
             // Rust conversion is marked as none here, since this policy
             // will be applied to the return value, and the Rust-side
@@ -157,7 +165,7 @@
     pub(crate) fn unconverted_rust_type(&self) -> Type {
         match self.cpp_conversion {
             CppConversionType::FromValueToUniquePtr => self.make_unique_ptr_type(),
-            _ => self.unwrapped_type.clone(),
+            _ => self.unwrapped_type.clone().into(),
         }
     }
 
@@ -170,7 +178,7 @@
                     *mut #innerty
                 }
             }
-            _ => self.unwrapped_type.clone(),
+            _ => self.unwrapped_type.clone().into(),
         }
     }
 
@@ -222,7 +230,7 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum CppFunctionBody {
     FunctionCall(Namespace, Ident),
     StaticMethodCall(Namespace, Ident, Ident),
@@ -234,7 +242,7 @@
     FreeUninitialized(QualifiedName),
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum CppFunctionKind {
     Function,
     Method,
@@ -243,10 +251,10 @@
     SynthesizedConstructor,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct CppFunction {
     pub(crate) payload: CppFunctionBody,
-    pub(crate) wrapper_function_name: Ident,
+    pub(crate) wrapper_function_name: crate::minisyn::Ident,
     pub(crate) original_cpp_name: String,
     pub(crate) return_conversion: Option<TypeConversionPolicy>,
     pub(crate) argument_conversion: Vec<TypeConversionPolicy>,
diff --git a/third_party/autocxx/engine/src/conversion/analysis/fun/implicit_constructors.rs b/third_party/autocxx/engine/src/conversion/analysis/fun/implicit_constructors.rs
index 469ed89..a1ebf27 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/fun/implicit_constructors.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/fun/implicit_constructors.rs
@@ -11,13 +11,16 @@
 
 use syn::{Type, TypeArray};
 
+use crate::conversion::api::DeletedOrDefaulted;
 use crate::{
     conversion::{
-        analysis::{depth_first::depth_first, pod::PodAnalysis, type_converter::TypeKind},
+        analysis::{
+            depth_first::fields_and_bases_first, pod::PodAnalysis, type_converter::TypeKind,
+        },
         api::{Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind},
         apivec::ApiVec,
         convert_error::ConvertErrorWithContext,
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     known_types::{known_types, KnownTypeConstructorDetails},
     types::QualifiedName,
@@ -177,6 +180,13 @@
     apis: &ApiVec<FnPrePhase1>,
 ) -> HashMap<QualifiedName, ItemsFound> {
     let (explicits, unknown_types) = find_explicit_items(apis);
+    let enums: HashSet<QualifiedName> = apis
+        .iter()
+        .filter_map(|api| match api {
+            Api::Enum { name, .. } => Some(name.name.clone()),
+            _ => None,
+        })
+        .collect();
 
     // These contain all the classes we've seen so far with the relevant properties on their
     // constructors of each kind. We iterate via [`depth_first`], so analyzing later classes
@@ -189,7 +199,7 @@
     // These analyses include all bases and members of each class.
     let mut all_items_found: HashMap<QualifiedName, ItemsFound> = HashMap::new();
 
-    for api in depth_first(apis.iter()) {
+    for api in fields_and_bases_first(apis.iter()) {
         if let Api::Struct {
             name,
             analysis:
@@ -211,7 +221,17 @@
                 })
             };
             let get_items_found = |qn: &QualifiedName| -> Option<ItemsFound> {
-                if let Some(constructor_details) = known_types().get_constructor_details(qn) {
+                if enums.contains(qn) {
+                    Some(ItemsFound {
+                        default_constructor: SpecialMemberFound::NotPresent,
+                        destructor: SpecialMemberFound::Implicit,
+                        const_copy_constructor: SpecialMemberFound::Implicit,
+                        non_const_copy_constructor: SpecialMemberFound::NotPresent,
+                        move_constructor: SpecialMemberFound::Implicit,
+                        name: Some(name.clone()),
+                    })
+                } else if let Some(constructor_details) = known_types().get_constructor_details(qn)
+                {
                     Some(known_type_items_found(constructor_details))
                 } else {
                     all_items_found.get(qn).cloned()
@@ -539,8 +559,7 @@
                 all_items_found
                     .insert(name.name.clone(), items_found)
                     .is_none(),
-                "Duplicate struct: {:?}",
-                name
+                "Duplicate struct: {name:?}"
             );
         }
     }
@@ -556,7 +575,7 @@
         .entry(ExplicitType { ty, kind })
     {
         Entry::Vacant(entry) => {
-            entry.insert(if fun.is_deleted {
+            entry.insert(if matches!(fun.is_deleted, DeletedOrDefaulted::Deleted) {
                 ExplicitFound::Deleted
             } else {
                 ExplicitFound::UserDefined(fun.cpp_vis)
@@ -575,7 +594,8 @@
                         kind: FnKind::Method { impl_for, .. },
                         param_details,
                         ignore_reason:
-                            Ok(()) | Err(ConvertErrorWithContext(ConvertError::AssignmentOperator, _)),
+                            Ok(())
+                            | Err(ConvertErrorWithContext(ConvertErrorFromCpp::AssignmentOperator, _)),
                         ..
                     },
                 fun,
diff --git a/third_party/autocxx/engine/src/conversion/analysis/fun/mod.rs b/third_party/autocxx/engine/src/conversion/analysis/fun/mod.rs
index 7194746..415e40a 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/fun/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/fun/mod.rs
@@ -19,9 +19,9 @@
             type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
         },
         api::{
-            ApiName, CastMutability, CppVisibility, FuncToConvert, NullPhase, Provenance,
-            References, SpecialMemberKind, SubclassName, TraitImplSignature, TraitSynthesis,
-            UnsafetyNeeded, Virtualness,
+            ApiName, CastMutability, CppVisibility, DeletedOrDefaulted, FuncToConvert, NullPhase,
+            Provenance, References, SpecialMemberKind, SubclassName, TraitImplSignature,
+            TraitSynthesis, UnsafetyNeeded, Virtualness,
         },
         apivec::ApiVec,
         convert_error::ErrorContext,
@@ -29,6 +29,7 @@
         error_reporter::{convert_apis, report_any_error},
     },
     known_types::known_types,
+    minisyn::minisynize_punctuated,
     types::validate_ident_ok_for_rust,
 };
 use indexmap::map::IndexMap as HashMap;
@@ -40,14 +41,14 @@
 use proc_macro2::Span;
 use quote::quote;
 use syn::{
-    parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, ReturnType, Type,
-    TypePath, TypePtr, TypeReference, Visibility,
+    parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, PatType, ReturnType,
+    Type, TypePath, TypePtr, TypeReference, Visibility,
 };
 
 use crate::{
     conversion::{
         api::{AnalysisPhase, Api, TypeKind},
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::{make_ident, validate_ident_ok_for_cxx, Namespace, QualifiedName},
 };
@@ -64,13 +65,14 @@
 };
 
 use super::{
+    depth_first::HasFieldsAndBases,
     doc_label::make_doc_attrs,
     pod::{PodAnalysis, PodPhase},
     tdef::TypedefAnalysis,
     type_converter::{Annotated, PointerTreatment},
 };
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 pub(crate) enum ReceiverMutability {
     Const,
     Mutable,
@@ -85,7 +87,7 @@
     PureVirtual(ReceiverMutability),
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum TraitMethodKind {
     CopyConstructor,
     MoveConstructor,
@@ -95,11 +97,11 @@
     Dealloc,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct TraitMethodDetails {
     pub(crate) trt: TraitImplSignature,
     pub(crate) avoid_self: bool,
-    pub(crate) method_name: Ident,
+    pub(crate) method_name: crate::minisyn::Ident,
     /// For traits, where we're trying to implement a specific existing
     /// interface, we may need to reorder the parameters to fit that
     /// interface.
@@ -109,7 +111,7 @@
     pub(crate) trait_call_is_unsafe: bool,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum FnKind {
     Function,
     Method {
@@ -129,31 +131,31 @@
 
 /// Strategy for ensuring that the final, callable, Rust name
 /// is what the user originally expected.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 
 pub(crate) enum RustRenameStrategy {
     /// cxx::bridge name matches user expectations
     None,
     /// Even the #[rust_name] attribute would cause conflicts, and we need
     /// to use a 'use XYZ as ABC'
-    RenameInOutputMod(Ident),
+    RenameInOutputMod(crate::minisyn::Ident),
     /// This function requires us to generate a Rust function to do
     /// parameter conversion.
     RenameUsingWrapperFunction,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct FnAnalysis {
     /// Each entry in the cxx::bridge needs to have a unique name, even if
     /// (from the perspective of Rust and C++) things are in different
     /// namespaces/mods.
-    pub(crate) cxxbridge_name: Ident,
+    pub(crate) cxxbridge_name: crate::minisyn::Ident,
     /// ... so record also the name under which we wish to expose it in Rust.
     pub(crate) rust_name: String,
     pub(crate) rust_rename_strategy: RustRenameStrategy,
     pub(crate) params: Punctuated<FnArg, Comma>,
     pub(crate) kind: FnKind,
-    pub(crate) ret_type: ReturnType,
+    pub(crate) ret_type: crate::minisyn::ReturnType,
     pub(crate) param_details: Vec<ArgumentAnalysis>,
     pub(crate) ret_conversion: Option<TypeConversionPolicy>,
     pub(crate) requires_unsafe: UnsafetyNeeded,
@@ -172,12 +174,13 @@
     pub(crate) rust_wrapper_needed: bool,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct ArgumentAnalysis {
     pub(crate) conversion: TypeConversionPolicy,
-    pub(crate) name: Pat,
+    pub(crate) name: crate::minisyn::Pat,
     pub(crate) self_type: Option<(QualifiedName, ReceiverMutability)>,
     pub(crate) has_lifetime: bool,
+    pub(crate) is_mutable_reference: bool,
     pub(crate) deps: HashSet<QualifiedName>,
     pub(crate) requires_unsafe: UnsafetyNeeded,
     pub(crate) is_placement_return_destination: bool,
@@ -187,6 +190,7 @@
     rt: ReturnType,
     conversion: Option<TypeConversionPolicy>,
     was_reference: bool,
+    was_mutable_reference: bool,
     deps: HashSet<QualifiedName>,
     placement_param_needed: Option<(FnArg, ArgumentAnalysis)>,
 }
@@ -197,12 +201,14 @@
             rt: parse_quote! {},
             conversion: None,
             was_reference: false,
+            was_mutable_reference: false,
             deps: Default::default(),
             placement_param_needed: None,
         }
     }
 }
 
+#[derive(std::fmt::Debug)]
 pub(crate) struct PodAndConstructorAnalysis {
     pub(crate) pod: PodAnalysis,
     pub(crate) constructors: PublicConstructors,
@@ -210,6 +216,7 @@
 
 /// An analysis phase where we've analyzed each function, but
 /// haven't yet determined which constructors/etc. belong to each type.
+#[derive(std::fmt::Debug)]
 pub(crate) struct FnPrePhase1;
 
 impl AnalysisPhase for FnPrePhase1 {
@@ -220,6 +227,7 @@
 
 /// An analysis phase where we've analyzed each function, and identified
 /// what implicit constructors/destructors are present in each type.
+#[derive(std::fmt::Debug)]
 pub(crate) struct FnPrePhase2;
 
 impl AnalysisPhase for FnPrePhase2 {
@@ -228,6 +236,7 @@
     type FunAnalysis = FnAnalysis;
 }
 
+#[derive(Debug)]
 pub(crate) struct PodAndDepAnalysis {
     pub(crate) pod: PodAnalysis,
     pub(crate) constructor_and_allocator_deps: Vec<QualifiedName>,
@@ -236,6 +245,7 @@
 
 /// Analysis phase after we've finished analyzing functions and determined
 /// which constructors etc. belong to them.
+#[derive(std::fmt::Debug)]
 pub(crate) struct FnPhase;
 
 /// Indicates which kinds of public constructors are known to exist for a type.
@@ -283,6 +293,7 @@
     generic_types: HashSet<QualifiedName>,
     types_in_anonymous_namespace: HashSet<QualifiedName>,
     existing_superclass_trait_api_names: HashSet<QualifiedName>,
+    force_wrapper_generation: bool,
 }
 
 impl<'a> FnAnalyzer<'a> {
@@ -290,6 +301,7 @@
         apis: ApiVec<PodPhase>,
         unsafe_policy: &'a UnsafePolicy,
         config: &'a IncludeCppConfig,
+        force_wrapper_generation: bool,
     ) -> ApiVec<FnPrePhase2> {
         let mut me = Self {
             unsafe_policy,
@@ -305,6 +317,7 @@
             generic_types: Self::build_generic_type_set(&apis),
             existing_superclass_trait_api_names: HashSet::new(),
             types_in_anonymous_namespace: Self::build_types_in_anonymous_namespace(&apis),
+            force_wrapper_generation,
         };
         let mut results = ApiVec::new();
         convert_apis(
@@ -427,7 +440,7 @@
         ty: Box<Type>,
         ns: &Namespace,
         pointer_treatment: PointerTreatment,
-    ) -> Result<Annotated<Box<Type>>, ConvertError> {
+    ) -> Result<Annotated<Box<Type>>, ConvertErrorFromCpp> {
         let ctx = TypeConversionContext::OuterType { pointer_treatment };
         let mut annotated = self.type_converter.convert_boxed_type(ty, ns, &ctx)?;
         self.extra_apis.append(&mut annotated.extra_apis);
@@ -513,7 +526,7 @@
                     **fun,
                     FuncToConvert {
                         special_member: Some(SpecialMemberKind::Destructor),
-                        is_deleted: false,
+                        is_deleted: DeletedOrDefaulted::Neither | DeletedOrDefaulted::Defaulted,
                         cpp_vis: CppVisibility::Public,
                         ..
                     }
@@ -741,6 +754,10 @@
                     sophistication,
                     false,
                 )
+                .map_err(|err| ConvertErrorFromCpp::Argument {
+                    arg: describe_arg(i),
+                    err: Box::new(err),
+                })
             })
             .partition(Result::is_ok);
         let (mut params, mut param_details): (Punctuated<_, Comma>, Vec<_>) =
@@ -775,7 +792,7 @@
                 if initial_rust_name.ends_with('_') {
                     initial_rust_name // case 2
                 } else if validate_ident_ok_for_rust(cpp_name).is_err() {
-                    format!("{}_", cpp_name) // case 5
+                    format!("{cpp_name}_") // case 5
                 } else {
                     cpp_name.to_string() // cases 3, 4, 6
                 }
@@ -829,7 +846,7 @@
                 let is_move =
                     matches!(fun.special_member, Some(SpecialMemberKind::MoveConstructor));
                 if let Some(constructor_suffix) = rust_name.strip_prefix(nested_type_ident) {
-                    rust_name = format!("new{}", constructor_suffix);
+                    rust_name = format!("new{constructor_suffix}");
                 }
                 rust_name = predetermined_rust_name
                     .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
@@ -868,7 +885,7 @@
                             impl_for: self_ty,
                             details: Box::new(TraitMethodDetails {
                                 trt: TraitImplSignature {
-                                    ty,
+                                    ty: ty.into(),
                                     trait_signature: parse_quote! {
                                         autocxx::moveit::new:: #trait_id
                                     },
@@ -904,7 +921,7 @@
                         impl_for: self_ty,
                         details: Box::new(TraitMethodDetails {
                             trt: TraitImplSignature {
-                                ty,
+                                ty: ty.into(),
                                 trait_signature: parse_quote! {
                                     Drop
                                 },
@@ -934,7 +951,7 @@
                     // fn make_unique(...args) -> UniquePtr<Type>
                     // If there are multiple constructors, bindgen generates
                     // new, new1, new2 etc. and we'll keep those suffixes.
-                    rust_name = format!("new{}", constructor_suffix);
+                    rust_name = format!("new{constructor_suffix}");
                     MethodKind::Constructor {
                         is_default: matches!(
                             fun.special_member,
@@ -1033,10 +1050,10 @@
                 ..
             } => {
                 if param_details.len() < 2 {
-                    set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+                    set_ignore_reason(ConvertErrorFromCpp::ConstructorWithOnlyOneParam);
                 }
                 if param_details.len() > 2 {
-                    set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
+                    set_ignore_reason(ConvertErrorFromCpp::ConstructorWithMultipleParams);
                 }
                 self.reanalyze_parameter(
                     0,
@@ -1058,10 +1075,10 @@
                 ..
             } => {
                 if param_details.len() < 2 {
-                    set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+                    set_ignore_reason(ConvertErrorFromCpp::ConstructorWithOnlyOneParam);
                 }
                 if param_details.len() > 2 {
-                    set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
+                    set_ignore_reason(ConvertErrorFromCpp::ConstructorWithMultipleParams);
                 }
                 self.reanalyze_parameter(
                     0,
@@ -1099,14 +1116,14 @@
         // or note whether the type is abstract.
         let externally_callable = match fun.cpp_vis {
             CppVisibility::Private => {
-                set_ignore_reason(ConvertError::PrivateMethod);
+                set_ignore_reason(ConvertErrorFromCpp::PrivateMethod);
                 false
             }
             CppVisibility::Protected => false,
             CppVisibility::Public => true,
         };
         if fun.variadic {
-            set_ignore_reason(ConvertError::Variadic);
+            set_ignore_reason(ConvertErrorFromCpp::Variadic);
         }
         if let Some(problem) = bads.into_iter().next() {
             match problem {
@@ -1116,7 +1133,7 @@
         } else if fun.unused_template_param {
             // This indicates that bindgen essentially flaked out because templates
             // were too complex.
-            set_ignore_reason(ConvertError::UnusedTemplateParam)
+            set_ignore_reason(ConvertErrorFromCpp::UnusedTemplateParam)
         } else if matches!(
             fun.special_member,
             Some(SpecialMemberKind::AssignmentOperator)
@@ -1124,11 +1141,11 @@
             // Be careful with the order of this if-else tree. Anything above here means we won't
             // treat it as an assignment operator, but anything below we still consider when
             // deciding which other C++ special member functions are implicitly defined.
-            set_ignore_reason(ConvertError::AssignmentOperator)
+            set_ignore_reason(ConvertErrorFromCpp::AssignmentOperator)
         } else if fun.references.rvalue_ref_return {
-            set_ignore_reason(ConvertError::RValueReturn)
-        } else if fun.is_deleted {
-            set_ignore_reason(ConvertError::Deleted)
+            set_ignore_reason(ConvertErrorFromCpp::RValueReturn)
+        } else if matches!(fun.is_deleted, DeletedOrDefaulted::Deleted) {
+            set_ignore_reason(ConvertErrorFromCpp::Deleted)
         } else {
             match kind {
                 FnKind::Method {
@@ -1140,21 +1157,21 @@
                         | MethodKind::Virtual(..),
                     ..
                 } if !known_types().is_cxx_acceptable_receiver(impl_for) => {
-                    set_ignore_reason(ConvertError::UnsupportedReceiver);
+                    set_ignore_reason(ConvertErrorFromCpp::UnsupportedReceiver);
                 }
                 FnKind::Method { ref impl_for, .. } if !self.is_on_allowlist(impl_for) => {
                     // Bindgen will output methods for types which have been encountered
                     // virally as arguments on other allowlisted types. But we don't want
                     // to generate methods unless the user has specifically asked us to.
                     // It may, for instance, be a private type.
-                    set_ignore_reason(ConvertError::MethodOfNonAllowlistedType);
+                    set_ignore_reason(ConvertErrorFromCpp::MethodOfNonAllowlistedType);
                 }
                 FnKind::Method { ref impl_for, .. } | FnKind::TraitMethod { ref impl_for, .. } => {
                     if self.is_generic_type(impl_for) {
-                        set_ignore_reason(ConvertError::MethodOfGenericType);
+                        set_ignore_reason(ConvertErrorFromCpp::MethodOfGenericType);
                     }
                     if self.types_in_anonymous_namespace.contains(impl_for) {
-                        set_ignore_reason(ConvertError::MethodInAnonymousNamespace);
+                        set_ignore_reason(ConvertErrorFromCpp::MethodInAnonymousNamespace);
                     }
                 }
                 _ => {}
@@ -1199,12 +1216,46 @@
 
         let requires_unsafe = self.should_be_unsafe(&param_details, &kind);
 
-        let num_input_references = param_details.iter().filter(|pd| pd.has_lifetime).count();
-        if num_input_references != 1 && return_analysis.was_reference {
+        // The following sections reject some types of function because of the arrangement
+        // of Rust references. We could lift these restrictions when/if we switch to using
+        // CppRef to represent C++ references.
+        if return_analysis.was_reference {
             // cxx only allows functions to return a reference if they take exactly
-            // one reference as a parameter. Let's see...
-            set_ignore_reason(ConvertError::NotOneInputReference(rust_name.clone()));
+            // one reference as a parameter. Let's see.
+            let num_input_references = param_details.iter().filter(|pd| pd.has_lifetime).count();
+            if num_input_references == 0 {
+                set_ignore_reason(ConvertErrorFromCpp::NoInputReference(rust_name.clone()));
+            }
+            if num_input_references > 1 {
+                set_ignore_reason(ConvertErrorFromCpp::MultipleInputReferences(
+                    rust_name.clone(),
+                ));
+            }
         }
+        if return_analysis.was_mutable_reference {
+            // This one's a bit more subtle. We can't have:
+            //    fn foo(thing: &Thing) -> &mut OtherThing
+            // because Rust doesn't allow it.
+            // We could probably allow:
+            //    fn foo(thing: &mut Thing, thing2: &mut OtherThing) -> &mut OtherThing
+            // but probably cxx doesn't allow that. (I haven't checked). Even if it did,
+            // there's ambiguity here so won't allow it.
+            let num_input_mutable_references = param_details
+                .iter()
+                .filter(|pd| pd.has_lifetime && pd.is_mutable_reference)
+                .count();
+            if num_input_mutable_references == 0 {
+                set_ignore_reason(ConvertErrorFromCpp::NoMutableInputReference(
+                    rust_name.clone(),
+                ));
+            }
+            if num_input_mutable_references > 1 {
+                set_ignore_reason(ConvertErrorFromCpp::MultipleMutableInputReferences(
+                    rust_name.clone(),
+                ));
+            }
+        }
+
         let mut ret_type = return_analysis.rt;
         let ret_type_conversion = return_analysis.conversion;
 
@@ -1248,19 +1299,23 @@
             _ if ret_type_conversion_needed => true,
             _ if cpp_name_incompatible_with_cxx => true,
             _ if fun.synthetic_cpp.is_some() => true,
+            _ if self.force_wrapper_generation => true,
             _ => false,
         };
 
         let cpp_wrapper = if wrapper_function_needed {
             // Generate a new layer of C++ code to wrap/unwrap parameters
             // and return values into/out of std::unique_ptrs.
-            let cpp_construction_ident = make_ident(&effective_cpp_name);
+            let cpp_construction_ident = make_ident(effective_cpp_name);
             let joiner = if cxxbridge_name.to_string().ends_with('_') {
                 ""
             } else {
                 "_"
             };
-            cxxbridge_name = make_ident(&format!("{}{}autocxx_wrapper", cxxbridge_name, joiner));
+            cxxbridge_name = make_ident(
+                self.config
+                    .uniquify_name_per_mod(&format!("{cxxbridge_name}{joiner}autocxx_wrapper")),
+            );
             let (payload, cpp_function_kind) = match fun.synthetic_cpp.as_ref().cloned() {
                 Some((payload, cpp_function_kind)) => (payload, cpp_function_kind),
                 None => match kind {
@@ -1321,10 +1376,10 @@
             params.clear();
             for pd in &param_details {
                 let type_name = pd.conversion.converted_rust_type();
-                let arg_name = if pd.self_type.is_some() {
+                let arg_name: syn::Pat = if pd.self_type.is_some() {
                     parse_quote!(autocxx_gen_this)
                 } else {
-                    pd.name.clone()
+                    pd.name.clone().into()
                 };
                 params.push(parse_quote!(
                     #arg_name: #type_name
@@ -1358,12 +1413,15 @@
             _ if any_param_needs_rust_conversion || return_needs_rust_conversion => true,
             FnKind::TraitMethod { .. } => true,
             FnKind::Method { .. } => cxxbridge_name != rust_name,
+            _ if self.force_wrapper_generation => true,
             _ => false,
         };
 
         // Naming, part two.
         // Work out our final naming strategy.
-        validate_ident_ok_for_cxx(&cxxbridge_name.to_string()).unwrap_or_else(set_ignore_reason);
+        validate_ident_ok_for_cxx(&cxxbridge_name.to_string())
+            .map_err(ConvertErrorFromCpp::InvalidIdent)
+            .unwrap_or_else(set_ignore_reason);
         let rust_name_ident = make_ident(&rust_name);
         let rust_rename_strategy = match kind {
             _ if rust_wrapper_needed => RustRenameStrategy::RenameUsingWrapperFunction,
@@ -1380,10 +1438,10 @@
             params,
             ret_conversion: ret_type_conversion,
             kind,
-            ret_type,
+            ret_type: ret_type.into(),
             param_details,
             requires_unsafe,
-            vis,
+            vis: vis.into(),
             cpp_wrapper,
             deps,
             ignore_reason,
@@ -1422,7 +1480,7 @@
         sophistication: TypeConversionSophistication,
         construct_into_self: bool,
         is_move_constructor: bool,
-    ) -> Result<(), ConvertError> {
+    ) -> Result<(), ConvertErrorFromCpp> {
         self.convert_fn_arg(
             fun.inputs.iter().nth(param_idx).unwrap(),
             ns,
@@ -1484,7 +1542,7 @@
                             AsRef < #to_type >
                         },
                         parse_quote! {
-                            &'a mut ::std::pin::Pin < &'a mut #from_type_path >
+                            &'a mut ::core::pin::Pin < &'a mut #from_type_path >
                         },
                         "as_ref",
                     ),
@@ -1493,7 +1551,7 @@
                             autocxx::PinMut < #to_type >
                         },
                         parse_quote! {
-                            ::std::pin::Pin < &'a mut #from_type_path >
+                            ::core::pin::Pin < &'a mut #from_type_path >
                         },
                         "pin_mut",
                     ),
@@ -1505,7 +1563,7 @@
                         impl_for: from_type.clone(),
                         details: Box::new(TraitMethodDetails {
                             trt: TraitImplSignature {
-                                ty,
+                                ty: ty.into(),
                                 trait_signature,
                                 unsafety: None,
                             },
@@ -1549,7 +1607,7 @@
                 impl_for: ty.clone(),
                 details: Box::new(TraitMethodDetails {
                     trt: TraitImplSignature {
-                        ty: Type::Path(typ),
+                        ty: Type::Path(typ).into(),
                         trait_signature: parse_quote! { autocxx::moveit::MakeCppStorage },
                         unsafety: Some(parse_quote! { unsafe }),
                     },
@@ -1590,7 +1648,7 @@
         force_rust_conversion: Option<RustConversionType>,
         sophistication: TypeConversionSophistication,
         construct_into_self: bool,
-    ) -> Result<(FnArg, ArgumentAnalysis), ConvertError> {
+    ) -> Result<(FnArg, ArgumentAnalysis), ConvertErrorFromCpp> {
         Ok(match arg {
             FnArg::Typed(pt) => {
                 let mut pt = pt.clone();
@@ -1627,12 +1685,11 @@
                                     };
                                     Ok((this_type, receiver_mutability))
                                 }
-                                _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
-                                    ns,
-                                    make_ident(fn_name),
-                                ))),
+                                _ => Err(ConvertErrorFromCpp::UnexpectedThisType(
+                                    QualifiedName::new(ns, make_ident(fn_name)),
+                                )),
                             },
-                            _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
+                            _ => Err(ConvertErrorFromCpp::UnexpectedThisType(QualifiedName::new(
                                 ns,
                                 make_ident(fn_name),
                             ))),
@@ -1646,8 +1703,9 @@
                         syn::Pat::Ident(pp)
                     }
                     syn::Pat::Ident(pp) => {
-                        validate_ident_ok_for_cxx(&pp.ident.to_string())?;
-                        pointer_treatment = references.param_treatment(&pp.ident);
+                        validate_ident_ok_for_cxx(&pp.ident.to_string())
+                            .map_err(ConvertErrorFromCpp::InvalidIdent)?;
+                        pointer_treatment = references.param_treatment(&pp.ident.clone().into());
                         syn::Pat::Ident(pp)
                     }
                     _ => old_pat,
@@ -1684,13 +1742,17 @@
                     FnArg::Typed(pt),
                     ArgumentAnalysis {
                         self_type,
-                        name: new_pat,
+                        name: new_pat.into(),
                         conversion,
                         has_lifetime: matches!(
                             annotated_type.kind,
                             type_converter::TypeKind::Reference
                                 | type_converter::TypeKind::MutableReference
                         ),
+                        is_mutable_reference: matches!(
+                            annotated_type.kind,
+                            type_converter::TypeKind::MutableReference
+                        ),
                         deps: annotated_type.types_encountered,
                         requires_unsafe,
                         is_placement_return_destination,
@@ -1859,7 +1921,7 @@
         ns: &Namespace,
         references: &References,
         sophistication: TypeConversionSophistication,
-    ) -> Result<ReturnTypeAnalysis, ConvertError> {
+    ) -> Result<ReturnTypeAnalysis, ConvertErrorFromCpp> {
         Ok(match rt {
             ReturnType::Default => ReturnTypeAnalysis::default(),
             ReturnType::Type(rarrow, boxed_type) => {
@@ -1902,9 +1964,9 @@
                                 conversion: Some(TypeConversionPolicy::new_for_placement_return(
                                     ty.clone(),
                                 )),
-                                was_reference: false,
                                 deps: annotated_type.types_encountered,
                                 placement_param_needed: Some((fnarg, analysis)),
+                                ..Default::default()
                             }
                         } else {
                             // There are some types which we can't currently represent within a moveit::new::New.
@@ -1917,14 +1979,18 @@
                             ReturnTypeAnalysis {
                                 rt: ReturnType::Type(*rarrow, boxed_type),
                                 conversion,
-                                was_reference: false,
                                 deps: annotated_type.types_encountered,
-                                placement_param_needed: None,
+                                ..Default::default()
                             }
                         }
                     }
                     _ => {
-                        let was_reference = references.ref_return;
+                        let was_mutable_reference = matches!(
+                            annotated_type.kind,
+                            type_converter::TypeKind::MutableReference
+                        );
+                        let was_reference = was_mutable_reference
+                            || matches!(annotated_type.kind, type_converter::TypeKind::Reference);
                         let conversion = Some(
                             if was_reference
                                 && matches!(
@@ -1941,6 +2007,7 @@
                             rt: ReturnType::Type(*rarrow, boxed_type),
                             conversion,
                             was_reference,
+                            was_mutable_reference,
                             deps: annotated_type.types_encountered,
                             placement_param_needed: None,
                         }
@@ -2091,9 +2158,12 @@
                     Box::new(FuncToConvert {
                         self_ty: Some(self_ty.clone()),
                         ident,
-                        doc_attrs: make_doc_attrs(format!("Synthesized {}.", special_member)),
-                        inputs,
-                        output: ReturnType::Default,
+                        doc_attrs: make_doc_attrs(format!("Synthesized {special_member}."))
+                            .into_iter()
+                            .map(Into::into)
+                            .collect(),
+                        inputs: minisynize_punctuated(inputs),
+                        output: ReturnType::Default.into(),
                         vis: parse_quote! { pub },
                         virtualness: Virtualness::None,
                         cpp_vis: CppVisibility::Public,
@@ -2102,7 +2172,7 @@
                         references,
                         original_name: None,
                         synthesized_this_type: None,
-                        is_deleted: false,
+                        is_deleted: DeletedOrDefaulted::Neither,
                         add_to_trait: None,
                         synthetic_cpp: None,
                         provenance: Provenance::SynthesizedOther,
@@ -2192,7 +2262,7 @@
         )
     }
 
-    pub(crate) fn cxxbridge_name(&self) -> Option<Ident> {
+    pub(crate) fn cxxbridge_name(&self) -> Option<crate::minisyn::Ident> {
         match self {
             Api::Function { ref analysis, .. } => Some(analysis.cxxbridge_name.clone()),
             Api::StringConstructor { .. }
@@ -2224,3 +2294,60 @@
         _ => panic!("did not find angle bracketed args"),
     }
 }
+
+impl HasFieldsAndBases for Api<FnPrePhase1> {
+    fn name(&self) -> &QualifiedName {
+        self.name()
+    }
+
+    fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+        match self {
+            Api::Struct {
+                analysis:
+                    PodAnalysis {
+                        field_definition_deps,
+                        bases,
+                        ..
+                    },
+                ..
+            } => Box::new(field_definition_deps.iter().chain(bases.iter())),
+            _ => Box::new(std::iter::empty()),
+        }
+    }
+}
+
+impl HasFieldsAndBases for Api<FnPrePhase2> {
+    fn name(&self) -> &QualifiedName {
+        self.name()
+    }
+
+    fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+        match self {
+            Api::Struct {
+                analysis:
+                    PodAndConstructorAnalysis {
+                        pod:
+                            PodAnalysis {
+                                field_definition_deps,
+                                bases,
+                                ..
+                            },
+                        ..
+                    },
+                ..
+            } => Box::new(field_definition_deps.iter().chain(bases.iter())),
+            _ => Box::new(std::iter::empty()),
+        }
+    }
+}
+
+/// Stringify a function argument for diagnostics
+fn describe_arg(arg: &FnArg) -> String {
+    match arg {
+        FnArg::Receiver(_) => "the function receiver (this/self paramter)".into(),
+        FnArg::Typed(PatType { pat, .. }) => match pat.as_ref() {
+            Pat::Ident(pti) => pti.ident.to_string(),
+            _ => "another argument we don't know how to describe".into(),
+        },
+    }
+}
diff --git a/third_party/autocxx/engine/src/conversion/analysis/fun/overload_tracker.rs b/third_party/autocxx/engine/src/conversion/analysis/fun/overload_tracker.rs
index 6fc532c..20165f5 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/fun/overload_tracker.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/fun/overload_tracker.rs
@@ -49,7 +49,7 @@
         if this_offset == 0 {
             cpp_method_name
         } else {
-            format!("{}{}", cpp_method_name, this_offset)
+            format!("{cpp_method_name}{this_offset}")
         }
     }
 }
diff --git a/third_party/autocxx/engine/src/conversion/analysis/fun/subclass.rs b/third_party/autocxx/engine/src/conversion/analysis/fun/subclass.rs
index 6383d2c..8698539 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/fun/subclass.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/fun/subclass.rs
@@ -6,6 +6,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::ops::DerefMut;
+
 use indexmap::map::IndexMap as HashMap;
 
 use syn::{parse_quote, FnArg, PatType, Type, TypePtr};
@@ -17,6 +19,7 @@
     SubclassName, SuperclassMethod, UnsafetyNeeded, Virtualness,
 };
 use crate::conversion::apivec::ApiVec;
+use crate::minisyn::minisynize_punctuated;
 use crate::{
     conversion::{
         analysis::fun::function_wrapper::{
@@ -85,6 +88,7 @@
         .param_details
         .iter()
         .map(|pd| pd.name.clone())
+        .map(Into::into)
         .collect();
     let requires_unsafe = if matches!(unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) {
         UnsafetyNeeded::Always
@@ -95,10 +99,10 @@
         name,
         details: SuperclassMethod {
             name: make_ident(&analysis.rust_name),
-            params: analysis.params.clone(),
+            params: minisynize_punctuated(analysis.params.clone()),
             ret_type: analysis.ret_type.clone(),
             param_names,
-            receiver_mutability: receiver_mutability.clone(),
+            receiver_mutability: *receiver_mutability,
             requires_unsafe,
             is_pure_virtual,
             receiver,
@@ -122,10 +126,10 @@
         sub.0.name.get_final_item(),
         name.name.get_final_item()
     ));
-    let params = std::iter::once(parse_quote! {
+    let params = std::iter::once(crate::minisyn::FnArg(parse_quote! {
         me: & #holder_name
-    })
-    .chain(analysis.params.iter().skip(1).cloned())
+    }))
+    .chain(analysis.params.iter().skip(1).cloned().map(Into::into))
     .collect();
     let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) {
         CppFunctionKind::Method
@@ -161,7 +165,7 @@
                 qualification: Some(cpp),
             },
             superclass: superclass.clone(),
-            receiver_mutability: receiver_mutability.clone(),
+            receiver_mutability: *receiver_mutability,
             dependencies,
             requires_unsafe,
             is_pure_virtual: matches!(
@@ -213,7 +217,9 @@
     let subclass_constructor_name =
         make_ident(format!("{}_{}", cpp.get_final_item(), cpp.get_final_item()));
     let mut existing_params = fun.inputs.clone();
-    if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() {
+    if let Some(FnArg::Typed(PatType { ty, .. })) =
+        existing_params.first_mut().map(DerefMut::deref_mut)
+    {
         if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty {
             *elem = Box::new(Type::Path(sub.cpp().to_type_path()));
         } else {
@@ -229,7 +235,7 @@
     };
     let inputs = self_param
         .into_iter()
-        .chain(std::iter::once(boxed_holder_param))
+        .chain(std::iter::once(boxed_holder_param.into()))
         .chain(existing_params)
         .collect();
     let maybe_wrap = Box::new(FuncToConvert {
diff --git a/third_party/autocxx/engine/src/conversion/analysis/name_check.rs b/third_party/autocxx/engine/src/conversion/analysis/name_check.rs
index 7547c7c..38b8db4 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/name_check.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/name_check.rs
@@ -8,14 +8,14 @@
 
 use indexmap::map::IndexMap as HashMap;
 
-use syn::Ident;
+use crate::minisyn::Ident;
 
 use crate::{
     conversion::{
         api::{Api, SubclassName},
         apivec::ApiVec,
         error_reporter::convert_item_apis,
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::{validate_ident_ok_for_cxx, QualifiedName},
 };
@@ -92,7 +92,7 @@
         if let Some(name) = my_name {
             let symbols_for_this_name = names_found.entry(name).or_default();
             if symbols_for_this_name.len() > 1usize {
-                Err(ConvertError::DuplicateCxxBridgeName(
+                Err(ConvertErrorFromCpp::DuplicateCxxBridgeName(
                     symbols_for_this_name.clone(),
                 ))
             } else {
@@ -107,9 +107,9 @@
 
 fn validate_all_segments_ok_for_cxx(
     items: impl Iterator<Item = String>,
-) -> Result<(), ConvertError> {
+) -> Result<(), ConvertErrorFromCpp> {
     for seg in items {
-        validate_ident_ok_for_cxx(&seg)?;
+        validate_ident_ok_for_cxx(&seg).map_err(ConvertErrorFromCpp::InvalidIdent)?;
     }
     Ok(())
 }
diff --git a/third_party/autocxx/engine/src/conversion/analysis/pod/byvalue_checker.rs b/third_party/autocxx/engine/src/conversion/analysis/pod/byvalue_checker.rs
index de72eec..6e2e9b9 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/pod/byvalue_checker.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/pod/byvalue_checker.rs
@@ -7,7 +7,7 @@
 // except according to those terms.
 
 use crate::conversion::apivec::ApiVec;
-use crate::{conversion::ConvertError, known_types::known_types};
+use crate::{conversion::ConvertErrorFromCpp, known_types::known_types};
 use crate::{
     conversion::{
         analysis::tdef::TypedefPhase,
@@ -48,6 +48,9 @@
 /// std::string contains a self-referential pointer.
 /// It is possible that this is duplicative of the information stored
 /// elsewhere in the `Api` list and could possibly be removed or simplified.
+/// In general this is one of the oldest parts of autocxx and
+/// the code here could quite possibly be simplified by reusing code
+/// elsewhere.
 pub struct ByValueChecker {
     // Mapping from type name to whether it is safe to be POD
     results: HashMap<QualifiedName, StructDetails>,
@@ -60,7 +63,7 @@
             let safety = if by_value_safe {
                 PodState::IsPod
             } else {
-                PodState::UnsafeToBePod(format!("type {} is not safe for POD", tn))
+                PodState::UnsafeToBePod(format!("type {tn} is not safe for POD"))
             };
             results.insert(tn.clone(), StructDetails::new(safety));
         }
@@ -72,7 +75,7 @@
     pub(crate) fn new_from_apis(
         apis: &ApiVec<TypedefPhase>,
         config: &IncludeCppConfig,
-    ) -> Result<ByValueChecker, ConvertError> {
+    ) -> Result<ByValueChecker, ConvertErrorFromCpp> {
         let mut byvalue_checker = ByValueChecker::new();
         for blocklisted in config.get_blocklist() {
             let tn = QualifiedName::new_from_cpp_name(blocklisted);
@@ -81,6 +84,11 @@
                 .results
                 .insert(tn, StructDetails::new(safety));
         }
+        // As we do this analysis, we need to be aware that structs
+        // may depend on other types. Ideally we'd use the depth first iterator
+        // but that's awkward given that our ApiPhase does not yet have a fixed
+        // list of field/base types. Instead, we'll iterate first over non-struct
+        // types and then over structs.
         for api in apis.iter() {
             match api {
                 Api::Typedef { analysis, .. } => {
@@ -94,7 +102,7 @@
                             _ => None,
                         },
                         TypedefKind::Use(_, ref ty) => match **ty {
-                            Type::Path(ref typ) => {
+                            crate::minisyn::Type(Type::Path(ref typ)) => {
                                 let target_tn = QualifiedName::from_type_path(typ);
                                 known_types().consider_substitution(&target_tn)
                             }
@@ -113,15 +121,7 @@
                         None => byvalue_checker.ingest_nonpod_type(name.clone()),
                     }
                 }
-                Api::Struct { details, .. } => {
-                    byvalue_checker.ingest_struct(&details.item, api.name().get_namespace())
-                }
-                Api::Enum { .. } => {
-                    byvalue_checker
-                        .results
-                        .insert(api.name().clone(), StructDetails::new(PodState::IsPod));
-                }
-                Api::ExternCppType { pod: true, .. } => {
+                Api::Enum { .. } | Api::ExternCppType { pod: true, .. } => {
                     byvalue_checker
                         .results
                         .insert(api.name().clone(), StructDetails::new(PodState::IsPod));
@@ -129,6 +129,11 @@
                 _ => {}
             }
         }
+        for api in apis.iter() {
+            if let Api::Struct { details, .. } = api {
+                byvalue_checker.ingest_struct(&details.item, api.name().get_namespace())
+            }
+        }
         let pod_requests = config
             .get_pod_requests()
             .iter()
@@ -136,27 +141,32 @@
             .collect();
         byvalue_checker
             .satisfy_requests(pod_requests)
-            .map_err(ConvertError::UnsafePodType)?;
+            .map_err(ConvertErrorFromCpp::UnsafePodType)?;
         Ok(byvalue_checker)
     }
 
     fn ingest_struct(&mut self, def: &ItemStruct, ns: &Namespace) {
         // For this struct, work out whether it _could_ be safe as a POD.
-        let tyname = QualifiedName::new(ns, def.ident.clone());
+        let tyname = QualifiedName::new(ns, def.ident.clone().into());
         let mut field_safety_problem = PodState::SafeToBePod;
         let fieldlist = Self::get_field_types(def);
         for ty_id in &fieldlist {
             match self.results.get(ty_id) {
+                None if ty_id.get_final_item() == "__BindgenUnionField" => {
+                    field_safety_problem = PodState::UnsafeToBePod(format!(
+                        "Type {tyname} could not be POD because it is a union"
+                    ));
+                    break;
+                }
                 None => {
                     field_safety_problem = PodState::UnsafeToBePod(format!(
-                        "Type {} could not be POD because its dependent type {} isn't known",
-                        tyname, ty_id
+                        "Type {tyname} could not be POD because its dependent type {ty_id} isn't known"
                     ));
                     break;
                 }
                 Some(deets) => {
                     if let PodState::UnsafeToBePod(reason) = &deets.state {
-                        let new_reason = format!("Type {} could not be POD because its dependent type {} isn't safe to be POD. Because: {}", tyname, ty_id, reason);
+                        let new_reason = format!("Type {tyname} could not be POD because its dependent type {ty_id} isn't safe to be POD. Because: {reason}");
                         field_safety_problem = PodState::UnsafeToBePod(new_reason);
                         break;
                     }
@@ -164,10 +174,8 @@
             }
         }
         if Self::has_vtable(def) {
-            let reason = format!(
-                "Type {} could not be POD because it has virtual functions.",
-                tyname
-            );
+            let reason =
+                format!("Type {tyname} could not be POD because it has virtual functions.");
             field_safety_problem = PodState::UnsafeToBePod(reason);
         }
         let mut my_details = StructDetails::new(field_safety_problem);
@@ -176,7 +184,7 @@
     }
 
     fn ingest_nonpod_type(&mut self, tyname: QualifiedName) {
-        let new_reason = format!("Type {} is a typedef to a complex type", tyname);
+        let new_reason = format!("Type {tyname} is a typedef to a complex type");
         self.results.insert(
             tyname,
             StructDetails::new(PodState::UnsafeToBePod(new_reason)),
@@ -191,8 +199,7 @@
             match deets {
                 None => {
                     return Err(format!(
-                        "Unable to make {} POD because we never saw a struct definition",
-                        ty_id
+                        "Unable to make {ty_id} POD because we never saw a struct definition"
                     ))
                 }
                 Some(deets) => match &deets.state {
@@ -236,6 +243,10 @@
         )
     }
 
+    /// This is a miniature version of the analysis in `super::get_struct_field_types`.
+    /// It would be nice to unify them. However, this version only cares about spotting
+    /// fields which may be non-POD, so can largely concern itself with just `Type::Path`
+    /// fields.
     fn get_field_types(def: &ItemStruct) -> Vec<QualifiedName> {
         let mut results = Vec::new();
         for f in &def.fields {
@@ -261,10 +272,11 @@
 #[cfg(test)]
 mod tests {
     use super::ByValueChecker;
+    use crate::minisyn::ItemStruct;
     use crate::types::{Namespace, QualifiedName};
-    use syn::{parse_quote, Ident, ItemStruct};
+    use syn::parse_quote;
 
-    fn ty_from_ident(id: &Ident) -> QualifiedName {
+    fn ty_from_ident(id: &syn::Ident) -> QualifiedName {
         QualifiedName::new_from_cpp_name(&id.to_string())
     }
 
diff --git a/third_party/autocxx/engine/src/conversion/analysis/pod/mod.rs b/third_party/autocxx/engine/src/conversion/analysis/pod/mod.rs
index eeb5051..2fafc95 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/pod/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/pod/mod.rs
@@ -13,7 +13,7 @@
 
 use autocxx_parser::IncludeCppConfig;
 use byvalue_checker::ByValueChecker;
-use syn::{ItemEnum, ItemStruct, Type, Visibility};
+use syn::{ItemStruct, Type, Visibility};
 
 use crate::{
     conversion::{
@@ -23,18 +23,21 @@
         convert_error::{ConvertErrorWithContext, ErrorContext},
         error_reporter::convert_apis,
         parse::BindgenSemanticAttributes,
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::{Namespace, QualifiedName},
 };
 
 use super::tdef::{TypedefAnalysis, TypedefPhase};
 
+#[derive(std::fmt::Debug)]
+
 pub(crate) struct FieldInfo {
     pub(crate) ty: Type,
     pub(crate) type_kind: type_converter::TypeKind,
 }
 
+#[derive(std::fmt::Debug)]
 pub(crate) struct PodAnalysis {
     pub(crate) kind: TypeKind,
     pub(crate) bases: HashSet<QualifiedName>,
@@ -43,12 +46,18 @@
     /// because otherwise we don't know whether they're
     /// abstract or not.
     pub(crate) castable_bases: HashSet<QualifiedName>,
+    /// All field types. e.g. for std::unique_ptr<A>, this would include
+    /// both std::unique_ptr and A
     pub(crate) field_deps: HashSet<QualifiedName>,
+    /// Types within fields where we need a definition, e.g. for
+    /// std::unique_ptr<A> it would just be std::unique_ptr.
+    pub(crate) field_definition_deps: HashSet<QualifiedName>,
     pub(crate) field_info: Vec<FieldInfo>,
     pub(crate) is_generic: bool,
     pub(crate) in_anonymous_namespace: bool,
 }
 
+#[derive(std::fmt::Debug)]
 pub(crate) struct PodPhase;
 
 impl AnalysisPhase for PodPhase {
@@ -65,7 +74,7 @@
 pub(crate) fn analyze_pod_apis(
     apis: ApiVec<TypedefPhase>,
     config: &IncludeCppConfig,
-) -> Result<ApiVec<PodPhase>, ConvertError> {
+) -> Result<ApiVec<PodPhase>, ConvertErrorFromCpp> {
     // This next line will return an error if any of the 'generate_pod'
     // directives from the user can't be met because, for instance,
     // a type contains a std::string or some other type which can't be
@@ -118,7 +127,7 @@
 
 fn analyze_enum(
     name: ApiName,
-    mut item: ItemEnum,
+    mut item: crate::minisyn::ItemEnum,
 ) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
     let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
     metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
@@ -138,12 +147,14 @@
     metadata.check_for_fatal_attrs(&id)?;
     let bases = get_bases(&details.item);
     let mut field_deps = HashSet::new();
+    let mut field_definition_deps = HashSet::new();
     let mut field_info = Vec::new();
     let field_conversion_errors = get_struct_field_types(
         type_converter,
         name.name.get_namespace(),
         &details.item,
         &mut field_deps,
+        &mut field_definition_deps,
         &mut field_info,
         extra_apis,
     );
@@ -152,7 +163,7 @@
         // Let's not allow anything to be POD if it's got rvalue reference fields.
         if details.has_rvalue_reference_fields {
             return Err(ConvertErrorWithContext(
-                ConvertError::RValueReferenceField,
+                ConvertErrorFromCpp::RValueReferenceField,
                 Some(ErrorContext::new_for_item(id)),
             ));
         }
@@ -186,6 +197,7 @@
             bases: bases.into_keys().collect(),
             castable_bases,
             field_deps,
+            field_definition_deps,
             field_info,
             is_generic,
             in_anonymous_namespace,
@@ -198,9 +210,10 @@
     ns: &Namespace,
     s: &ItemStruct,
     field_deps: &mut HashSet<QualifiedName>,
+    field_definition_deps: &mut HashSet<QualifiedName>,
     field_info: &mut Vec<FieldInfo>,
     extra_apis: &mut ApiVec<NullPhase>,
-) -> Vec<ConvertError> {
+) -> Vec<ConvertErrorFromCpp> {
     let mut convert_errors = Vec::new();
     let struct_type_params = s
         .generics
@@ -225,6 +238,14 @@
                     .unwrap_or(false)
                 {
                     field_deps.extend(r.types_encountered);
+                    if let Type::Path(typ) = &r.ty {
+                        // Later analyses need to know about the field
+                        // types where we need full definitions, as opposed
+                        // to just declarations. That means just the outermost
+                        // type path.
+                        // TODO: consider arrays.
+                        field_definition_deps.insert(QualifiedName::from_type_path(typ));
+                    }
                     field_info.push(FieldInfo {
                         ty: r.ty,
                         type_kind: r.kind,
diff --git a/third_party/autocxx/engine/src/conversion/analysis/remove_ignored.rs b/third_party/autocxx/engine/src/conversion/analysis/remove_ignored.rs
index bd11b13..4ed4b9f 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/remove_ignored.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/remove_ignored.rs
@@ -11,7 +11,7 @@
 use super::deps::HasDependencies;
 use super::fun::{FnAnalysis, FnKind, FnPhase};
 use crate::conversion::apivec::ApiVec;
-use crate::conversion::{convert_error::ErrorContext, ConvertError};
+use crate::conversion::{convert_error::ErrorContext, ConvertErrorFromCpp};
 use crate::{conversion::api::Api, known_types};
 
 /// Remove any APIs which depend on other items which have been ignored.
@@ -44,7 +44,10 @@
                 if !ignored_dependents.is_empty() {
                     iterate_again = true;
                     ignored_items.insert(api.name().clone());
-                    create_ignore_item(api, ConvertError::IgnoredDependent(ignored_dependents))
+                    create_ignore_item(
+                        api,
+                        ConvertErrorFromCpp::IgnoredDependent(ignored_dependents),
+                    )
                 } else {
                     let mut missing_deps = api.deps().filter(|dep| {
                         !valid_types.contains(*dep) && !known_types().is_known_type(dep)
@@ -52,7 +55,10 @@
                     let first = missing_deps.next();
                     std::mem::drop(missing_deps);
                     if let Some(missing_dep) = first.cloned() {
-                        create_ignore_item(api, ConvertError::UnknownDependentType(missing_dep))
+                        create_ignore_item(
+                            api,
+                            ConvertErrorFromCpp::UnknownDependentType(missing_dep),
+                        )
                     } else {
                         api
                     }
@@ -63,7 +69,7 @@
     apis
 }
 
-fn create_ignore_item(api: Api<FnPhase>, err: ConvertError) -> Api<FnPhase> {
+fn create_ignore_item(api: Api<FnPhase>, err: ConvertErrorFromCpp) -> Api<FnPhase> {
     let id = api.name().get_final_ident();
     log::info!("Marking as ignored: {} because {}", id.to_string(), err);
     Api::IgnoredItem {
diff --git a/third_party/autocxx/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs b/third_party/autocxx/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
index 8d5d033..918fb8f 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
@@ -15,7 +15,7 @@
         api::Api,
         apivec::ApiVec,
         convert_error::{ConvertErrorWithContext, ErrorContext},
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::QualifiedName,
 };
@@ -71,7 +71,7 @@
                 {
                     Api::IgnoredItem {
                         name: api.name_info().clone(),
-                        err: ConvertError::NestedOpaqueTypedef,
+                        err: ConvertErrorFromCpp::NestedOpaqueTypedef,
                         ctx: Some(ErrorContext::new_for_item(name_id)),
                     }
                 } else {
diff --git a/third_party/autocxx/engine/src/conversion/analysis/tdef.rs b/third_party/autocxx/engine/src/conversion/analysis/tdef.rs
index 5a635f8..1c414e8 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/tdef.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/tdef.rs
@@ -19,11 +19,12 @@
         convert_error::{ConvertErrorWithContext, ErrorContext},
         error_reporter::convert_apis,
         parse::BindgenSemanticAttributes,
-        ConvertError,
+        ConvertErrorFromCpp,
     },
     types::QualifiedName,
 };
 
+#[derive(std::fmt::Debug)]
 pub(crate) struct TypedefAnalysis {
     pub(crate) kind: TypedefKind,
     pub(crate) deps: HashSet<QualifiedName>,
@@ -31,6 +32,7 @@
 
 /// Analysis phase where typedef analysis has been performed but no other
 /// analyses just yet.
+#[derive(std::fmt::Debug)]
 pub(crate) struct TypedefPhase;
 
 impl AnalysisPhase for TypedefPhase {
@@ -57,7 +59,7 @@
             Ok(Box::new(std::iter::once(match item {
                 TypedefKind::Type(ity) => get_replacement_typedef(
                     name,
-                    ity,
+                    ity.into(),
                     old_tyname,
                     &mut type_converter,
                     &mut extra_apis,
@@ -87,7 +89,7 @@
 ) -> Result<Api<TypedefPhase>, ConvertErrorWithContext> {
     if !ity.generics.params.is_empty() {
         return Err(ConvertErrorWithContext(
-            ConvertError::TypedefTakesGenericParameters,
+            ConvertErrorFromCpp::TypedefTakesGenericParameters,
             Some(ErrorContext::new_for_item(name.name.get_final_ident())),
         ));
     }
@@ -108,7 +110,7 @@
             ty: syn::Type::Path(ref typ),
             ..
         }) if QualifiedName::from_type_path(typ) == name.name => Err(ConvertErrorWithContext(
-            ConvertError::InfinitelyRecursiveTypedef(name.name.clone()),
+            ConvertErrorFromCpp::InfinitelyRecursiveTypedef(name.name.clone()),
             Some(ErrorContext::new_for_item(name.name.get_final_ident())),
         )),
         Ok(mut final_type) => {
@@ -116,10 +118,10 @@
             extra_apis.append(&mut final_type.extra_apis);
             Ok(Api::Typedef {
                 name,
-                item: TypedefKind::Type(ity),
+                item: TypedefKind::Type(ity.into()),
                 old_tyname,
                 analysis: TypedefAnalysis {
-                    kind: TypedefKind::Type(converted_type),
+                    kind: TypedefKind::Type(converted_type.into()),
                     deps: final_type.types_encountered,
                 },
             })
diff --git a/third_party/autocxx/engine/src/conversion/analysis/type_converter.rs b/third_party/autocxx/engine/src/conversion/analysis/type_converter.rs
index 7e2d2bd..63f7ae9 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/type_converter.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/type_converter.rs
@@ -10,8 +10,8 @@
     conversion::{
         api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind, UnanalyzedApi},
         apivec::ApiVec,
-        codegen_cpp::type_to_cpp::type_to_cpp,
-        ConvertError,
+        codegen_cpp::type_to_cpp::CppNameMap,
+        ConvertErrorFromCpp,
     },
     known_types::{known_types, CxxGenericType},
     types::{make_ident, Namespace, QualifiedName},
@@ -34,7 +34,7 @@
 pub(crate) enum TypeKind {
     Regular,
     Pointer,
-    SubclassHolder(Ident),
+    SubclassHolder(crate::minisyn::Ident),
     Reference,
     RValueReference,
     MutableReference,
@@ -109,14 +109,9 @@
         matches!(self, Self::WithinReference)
     }
     fn allowed_generic_type(&self, ident: &Ident) -> bool {
-        match self {
+        !matches!(self,
             Self::WithinStructField { struct_type_params }
-                if struct_type_params.contains(ident) =>
-            {
-                false
-            }
-            _ => true,
-        }
+                if struct_type_params.contains(ident))
     }
 }
 
@@ -135,6 +130,7 @@
     forward_declarations: HashSet<QualifiedName>,
     ignored_types: HashSet<QualifiedName>,
     config: &'a IncludeCppConfig,
+    original_name_map: CppNameMap,
 }
 
 impl<'a> TypeConverter<'a> {
@@ -149,6 +145,7 @@
             forward_declarations: Self::find_incomplete_types(apis),
             ignored_types: Self::find_ignored_types(apis),
             config,
+            original_name_map: CppNameMap::new_from_apis(apis),
         }
     }
 
@@ -157,7 +154,7 @@
         ty: Box<Type>,
         ns: &Namespace,
         ctx: &TypeConversionContext,
-    ) -> Result<Annotated<Box<Type>>, ConvertError> {
+    ) -> Result<Annotated<Box<Type>>, ConvertErrorFromCpp> {
         Ok(self.convert_type(*ty, ns, ctx)?.map(Box::new))
     }
 
@@ -166,7 +163,7 @@
         ty: Type,
         ns: &Namespace,
         ctx: &TypeConversionContext,
-    ) -> Result<Annotated<Type>, ConvertError> {
+    ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
         let result = match ty {
             Type::Path(p) => {
                 let newp = self.convert_type_path(p, ns, ctx)?;
@@ -175,7 +172,7 @@
                     if !ctx.allow_instantiation_of_forward_declaration()
                         && self.forward_declarations.contains(&qn)
                     {
-                        return Err(ConvertError::TypeContainingForwardDeclaration(qn));
+                        return Err(ConvertErrorFromCpp::TypeContainingForwardDeclaration(qn));
                     }
                     // Special handling because rust_Str (as emitted by bindgen)
                     // doesn't simply get renamed to a different type _identifier_.
@@ -220,7 +217,11 @@
                 )
             }
             Type::Ptr(ptr) => self.convert_ptr(ptr, ns, ctx.pointer_treatment())?,
-            _ => return Err(ConvertError::UnknownType(ty.to_token_stream().to_string())),
+            _ => {
+                return Err(ConvertErrorFromCpp::UnknownType(
+                    ty.to_token_stream().to_string(),
+                ))
+            }
         };
         Ok(result)
     }
@@ -230,7 +231,7 @@
         mut typ: TypePath,
         ns: &Namespace,
         ctx: &TypeConversionContext,
-    ) -> Result<Annotated<Type>, ConvertError> {
+    ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
         // First, qualify any unqualified paths.
         if typ.path.segments.iter().next().unwrap().ident != "root" {
             let ty = QualifiedName::from_type_path(&typ);
@@ -241,7 +242,7 @@
             if !known_types().is_known_type(&ty) {
                 let num_segments = typ.path.segments.len();
                 if num_segments > 1 {
-                    return Err(ConvertError::UnsupportedBuiltInType(ty));
+                    return Err(ConvertErrorFromCpp::UnsupportedBuiltInType(ty));
                 }
                 if !self.types_found.contains(&ty) {
                     typ.path.segments = std::iter::once(&"root".to_string())
@@ -257,9 +258,11 @@
         }
 
         let original_tn = QualifiedName::from_type_path(&typ);
-        original_tn.validate_ok_for_cxx()?;
+        original_tn
+            .validate_ok_for_cxx()
+            .map_err(ConvertErrorFromCpp::InvalidIdent)?;
         if self.config.is_on_blocklist(&original_tn.to_cpp_name()) {
-            return Err(ConvertError::Blocked(original_tn));
+            return Err(ConvertErrorFromCpp::Blocked(original_tn));
         }
         let mut deps = HashSet::new();
 
@@ -331,7 +334,9 @@
                     )?;
                     deps.extend(innerty.types_encountered.drain(..));
                 } else {
-                    return Err(ConvertError::TemplatedTypeContainingNonPathArg(tn.clone()));
+                    return Err(ConvertErrorFromCpp::TemplatedTypeContainingNonPathArg(
+                        tn.clone(),
+                    ));
                 }
             } else {
                 // Oh poop. It's a generic type which cxx won't be able to handle.
@@ -347,7 +352,9 @@
                                     if typ.path.segments.len() == 1
                                         && !ctx.allowed_generic_type(&seg.ident)
                                     {
-                                        return Err(ConvertError::ReferringToGenericTypeParam);
+                                        return Err(
+                                            ConvertErrorFromCpp::ReferringToGenericTypeParam,
+                                        );
                                     }
                                 }
                             }
@@ -361,7 +368,7 @@
                 // this a bit.
                 let qn = QualifiedName::from_type_path(&typ); // ignores generic params
                 if self.ignored_types.contains(&qn) {
-                    return Err(ConvertError::ConcreteVersionOfIgnoredTemplate);
+                    return Err(ConvertErrorFromCpp::ConcreteVersionOfIgnoredTemplate);
                 }
                 let (new_tn, api) = self.get_templated_typename(&Type::Path(typ))?;
                 extra_apis.extend(api.into_iter());
@@ -385,7 +392,7 @@
         pun: Punctuated<GenericArgument, P>,
         ns: &Namespace,
         ctx: &TypeConversionContext,
-    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
+    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertErrorFromCpp>
     where
         P: Default,
     {
@@ -411,7 +418,10 @@
         ))
     }
 
-    fn resolve_typedef<'b>(&'b self, tn: &QualifiedName) -> Result<Option<&'b Type>, ConvertError> {
+    fn resolve_typedef<'b>(
+        &'b self,
+        tn: &QualifiedName,
+    ) -> Result<Option<&'b Type>, ConvertErrorFromCpp> {
         let mut encountered = HashSet::new();
         let mut tn = tn.clone();
         let mut previous_typ = None;
@@ -422,7 +432,7 @@
                     previous_typ = r;
                     let new_tn = QualifiedName::from_type_path(typ);
                     if encountered.contains(&new_tn) {
-                        return Err(ConvertError::InfinitelyRecursiveTypedef(tn.clone()));
+                        return Err(ConvertErrorFromCpp::InfinitelyRecursiveTypedef(tn.clone()));
                     }
                     if typ
                         .path
@@ -430,7 +440,7 @@
                         .iter()
                         .any(|seg| seg.ident.to_string().starts_with("_bindgen_mod"))
                     {
-                        return Err(ConvertError::TypedefToTypeInAnonymousNamespace);
+                        return Err(ConvertErrorFromCpp::TypedefToTypeInAnonymousNamespace);
                     }
                     encountered.insert(new_tn.clone());
                     tn = new_tn;
@@ -446,10 +456,10 @@
         mut ptr: TypePtr,
         ns: &Namespace,
         pointer_treatment: PointerTreatment,
-    ) -> Result<Annotated<Type>, ConvertError> {
+    ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
         match pointer_treatment {
             PointerTreatment::Pointer => {
-                crate::known_types::ensure_pointee_is_valid(&ptr)?;
+                Self::ensure_pointee_is_valid(&ptr)?;
                 let innerty =
                     self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?;
                 ptr.elem = innerty.ty;
@@ -470,7 +480,7 @@
                 // be a plain value. We should detect and abort.
                 let mut outer = elem.map(|elem| match mutability {
                     Some(_) => Type::Path(parse_quote! {
-                        ::std::pin::Pin < & #mutability #elem >
+                        ::core::pin::Pin < & #mutability #elem >
                     }),
                     None => Type::Reference(parse_quote! {
                         & #elem
@@ -484,7 +494,7 @@
                 Ok(outer)
             }
             PointerTreatment::RValueReference => {
-                crate::known_types::ensure_pointee_is_valid(&ptr)?;
+                Self::ensure_pointee_is_valid(&ptr)?;
                 let innerty =
                     self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?;
                 ptr.elem = innerty.ty;
@@ -498,15 +508,26 @@
         }
     }
 
+    fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertErrorFromCpp> {
+        match *ptr.elem {
+            Type::Path(..) => Ok(()),
+            Type::Array(..) => Err(ConvertErrorFromCpp::InvalidArrayPointee),
+            Type::Ptr(..) => Err(ConvertErrorFromCpp::InvalidPointerPointee),
+            _ => Err(ConvertErrorFromCpp::InvalidPointee(
+                ptr.elem.to_token_stream().to_string(),
+            )),
+        }
+    }
+
     fn get_templated_typename(
         &mut self,
         rs_definition: &Type,
-    ) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertError> {
+    ) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertErrorFromCpp> {
         let count = self.concrete_templates.len();
         // We just use this as a hash key, essentially.
         // TODO: Once we've completed the TypeConverter refactoring (see #220),
         // pass in an actual original_name_map here.
-        let cpp_definition = type_to_cpp(rs_definition, &HashMap::new())?;
+        let cpp_definition = self.original_name_map.type_to_cpp(rs_definition)?;
         let e = self.concrete_templates.get(&cpp_definition);
         match e {
             Some(tn) => Ok((tn.clone(), None)),
@@ -530,12 +551,12 @@
                     .find(|s| s == &synthetic_ident)
                 {
                     None => synthetic_ident,
-                    Some(_) => format!("AutocxxConcrete{}", count),
+                    Some(_) => format!("AutocxxConcrete{count}"),
                 };
                 let api = UnanalyzedApi::ConcreteType {
-                    name: ApiName::new_in_root_namespace(make_ident(&synthetic_ident)),
+                    name: ApiName::new_in_root_namespace(make_ident(synthetic_ident)),
                     cpp_definition: cpp_definition.clone(),
-                    rs_definition: Some(Box::new(rs_definition.clone())),
+                    rs_definition: Some(Box::new(rs_definition.clone().into())),
                 };
                 self.concrete_templates
                     .insert(cpp_definition, api.name().clone());
@@ -550,21 +571,25 @@
         desc: &QualifiedName,
         generic_behavior: CxxGenericType,
         forward_declarations_ok: bool,
-    ) -> Result<TypeKind, ConvertError> {
+    ) -> Result<TypeKind, ConvertErrorFromCpp> {
         for inner in path_args {
             match inner {
                 GenericArgument::Type(Type::Path(typ)) => {
                     let inner_qn = QualifiedName::from_type_path(typ);
                     if !forward_declarations_ok && self.forward_declarations.contains(&inner_qn) {
-                        return Err(ConvertError::TypeContainingForwardDeclaration(inner_qn));
+                        return Err(ConvertErrorFromCpp::TypeContainingForwardDeclaration(
+                            inner_qn,
+                        ));
                     }
                     match generic_behavior {
                         CxxGenericType::Rust => {
                             if !inner_qn.get_namespace().is_empty() {
-                                return Err(ConvertError::RustTypeWithAPath(inner_qn));
+                                return Err(ConvertErrorFromCpp::RustTypeWithAPath(inner_qn));
                             }
                             if !self.config.is_rust_type(&inner_qn.get_final_ident()) {
-                                return Err(ConvertError::BoxContainingNonRustType(inner_qn));
+                                return Err(ConvertErrorFromCpp::BoxContainingNonRustType(
+                                    inner_qn,
+                                ));
                             }
                             if self
                                 .config
@@ -577,12 +602,12 @@
                         }
                         CxxGenericType::CppPtr => {
                             if !known_types().permissible_within_unique_ptr(&inner_qn) {
-                                return Err(ConvertError::InvalidTypeForCppPtr(inner_qn));
+                                return Err(ConvertErrorFromCpp::InvalidTypeForCppPtr(inner_qn));
                             }
                         }
                         CxxGenericType::CppVector => {
                             if !known_types().permissible_within_vector(&inner_qn) {
-                                return Err(ConvertError::InvalidTypeForCppVector(inner_qn));
+                                return Err(ConvertErrorFromCpp::InvalidTypeForCppVector(inner_qn));
                             }
                             if matches!(
                                 typ.path.segments.last().map(|ps| &ps.arguments),
@@ -591,14 +616,14 @@
                                         | PathArguments::AngleBracketed(_)
                                 )
                             ) {
-                                return Err(ConvertError::GenericsWithinVector);
+                                return Err(ConvertErrorFromCpp::GenericsWithinVector);
                             }
                         }
                         _ => {}
                     }
                 }
                 _ => {
-                    return Err(ConvertError::TemplatedTypeContainingNonPathArg(
+                    return Err(ConvertErrorFromCpp::TemplatedTypeContainingNonPathArg(
                         desc.clone(),
                     ))
                 }
diff --git a/third_party/autocxx/engine/src/conversion/api.rs b/third_party/autocxx/engine/src/conversion/api.rs
index c5a1b60..d5da41c 100644
--- a/third_party/autocxx/engine/src/conversion/api.rs
+++ b/third_party/autocxx/engine/src/conversion/api.rs
@@ -9,17 +9,20 @@
 use indexmap::set::IndexSet as HashSet;
 use std::fmt::Display;
 
-use crate::types::{make_ident, Namespace, QualifiedName};
-use autocxx_parser::{ExternCppType, RustFun, RustPath};
-use itertools::Itertools;
-use quote::ToTokens;
 use syn::{
     parse::Parse,
     punctuated::Punctuated,
     token::{Comma, Unsafe},
+};
+
+use crate::minisyn::{
     Attribute, FnArg, Ident, ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, LitBool, LitInt,
     Pat, ReturnType, Type, Visibility,
 };
+use crate::types::{make_ident, Namespace, QualifiedName};
+use autocxx_parser::{ExternCppType, RustFun, RustPath};
+use itertools::Itertools;
+use quote::ToTokens;
 
 use super::{
     analysis::{
@@ -30,10 +33,10 @@
         PointerTreatment,
     },
     convert_error::{ConvertErrorWithContext, ErrorContext},
-    ConvertError,
+    ConvertErrorFromCpp,
 };
 
-#[derive(Copy, Clone, Eq, PartialEq)]
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
 pub(crate) enum TypeKind {
     Pod,    // trivial. Can be moved and copied in Rust.
     NonPod, // has destructor or non-trivial move constructors. Can only hold by UniquePtr
@@ -53,6 +56,7 @@
 }
 
 /// Details about a C++ struct.
+#[derive(Debug)]
 pub(crate) struct StructDetails {
     pub(crate) item: ItemStruct,
     pub(crate) layout: Option<Layout>,
@@ -60,7 +64,7 @@
 }
 
 /// Layout of a type, equivalent to the same type in ir/layout.rs in bindgen
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct Layout {
     /// The size (in bytes) of this layout.
     pub(crate) size: usize,
@@ -85,14 +89,14 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum Virtualness {
     None,
     Virtual,
     PureVirtual,
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
 pub(crate) enum CastMutability {
     ConstToConst,
     MutToConst,
@@ -101,7 +105,7 @@
 
 /// Indicates that this function (which is synthetic) should
 /// be a trait implementation rather than a method or free function.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum TraitSynthesis {
     Cast {
         to_type: QualifiedName,
@@ -113,7 +117,7 @@
 
 /// Details of a subclass constructor.
 /// TODO: zap this; replace with an extra API.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct SubclassConstructorDetails {
     pub(crate) subclass: SubclassName,
     pub(crate) is_trivial: bool,
@@ -124,7 +128,7 @@
 
 /// Contributions to traits representing C++ superclasses that
 /// we may implement as Rust subclasses.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct SuperclassMethod {
     pub(crate) name: Ident,
     pub(crate) receiver: QualifiedName,
@@ -139,7 +143,7 @@
 /// Information about references (as opposed to pointers) to be found
 /// within the function signature. This is derived from bindgen annotations
 /// which is why it's not within `FuncToConvert::inputs`
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Debug)]
 pub(crate) struct References {
     pub(crate) rvalue_ref_params: HashSet<Ident>,
     pub(crate) ref_params: HashSet<Ident>,
@@ -175,7 +179,7 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct TraitImplSignature {
     pub(crate) ty: Type,
     pub(crate) trait_signature: Type,
@@ -239,13 +243,21 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum Provenance {
     Bindgen,
     SynthesizedOther,
     SynthesizedSubclassConstructor(Box<SubclassConstructorDetails>),
 }
 
+/// Whether a function has =delete or =default
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum DeletedOrDefaulted {
+    Neither,
+    Deleted,
+    Defaulted,
+}
+
 /// A C++ function for which we need to generate bindings, but haven't
 /// yet analyzed in depth. This is little more than a `ForeignItemFn`
 /// broken down into its constituent parts, plus some metadata from the
@@ -256,7 +268,7 @@
 /// during normal bindgen parsing. If that happens, they'll create one
 /// of these structures, and typically fill in some of the
 /// `synthesized_*` members which are not filled in from bindgen.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) struct FuncToConvert {
     pub(crate) provenance: Provenance,
     pub(crate) ident: Ident,
@@ -283,18 +295,20 @@
     /// If Some, this function didn't really exist in the original
     /// C++ and instead we're synthesizing it.
     pub(crate) synthetic_cpp: Option<(CppFunctionBody, CppFunctionKind)>,
-    pub(crate) is_deleted: bool,
+    /// =delete
+    pub(crate) is_deleted: DeletedOrDefaulted,
 }
 
 /// Layers of analysis which may be applied to decorate each API.
 /// See description of the purpose of this trait within `Api`.
-pub(crate) trait AnalysisPhase {
-    type TypedefAnalysis;
-    type StructAnalysis;
-    type FunAnalysis;
+pub(crate) trait AnalysisPhase: std::fmt::Debug {
+    type TypedefAnalysis: std::fmt::Debug;
+    type StructAnalysis: std::fmt::Debug;
+    type FunAnalysis: std::fmt::Debug;
 }
 
 /// No analysis has been applied to this API.
+#[derive(std::fmt::Debug)]
 pub(crate) struct NullPhase;
 
 impl AnalysisPhase for NullPhase {
@@ -303,7 +317,7 @@
     type FunAnalysis = ();
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum TypedefKind {
     Use(ItemUse, Box<Type>),
     Type(ItemType),
@@ -365,7 +379,7 @@
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "{}", self.name)?;
         if let Some(cpp_name) = &self.cpp_name {
-            write!(f, " (cpp={})", cpp_name)?;
+            write!(f, " (cpp={cpp_name})")?;
         }
         Ok(())
     }
@@ -418,7 +432,7 @@
     }
     // TODO this and the following should probably include both class name and method name
     pub(crate) fn get_super_fn_name(superclass_namespace: &Namespace, id: &str) -> QualifiedName {
-        let id = make_ident(format!("{}_super", id));
+        let id = make_ident(format!("{id}_super"));
         QualifiedName::new(superclass_namespace, id)
     }
     pub(crate) fn get_methods_trait_name(superclass_name: &QualifiedName) -> QualifiedName {
@@ -434,7 +448,7 @@
     }
 }
 
-#[derive(strum_macros::Display)]
+#[derive(std::fmt::Debug)]
 /// Different types of API we might encounter.
 ///
 /// This type is parameterized over an `ApiAnalysis`. This is any additional
@@ -445,11 +459,9 @@
 /// because sometimes we pass on the `bindgen` output directly in the
 /// Rust codegen output.
 ///
-/// This derives from [strum_macros::Display] because we want to be
-/// able to debug-print the enum discriminant without worrying about
-/// the fact that their payloads may not be `Debug` or `Display`.
-/// (Specifically, allowing `syn` Types to be `Debug` requires
-/// enabling syn's `extra-traits` feature which increases compile time.)
+/// Any `syn` types represented in this `Api` type, or any of the types from
+/// which it is composed, should be wrapped in `crate::minisyn` equivalents
+/// to avoid excessively verbose `Debug` logging.
 pub(crate) enum Api<T: AnalysisPhase> {
     /// A forward declaration, which we mustn't store in a UniquePtr.
     ForwardDeclaration {
@@ -522,7 +534,7 @@
     /// dependent items.
     IgnoredItem {
         name: ApiName,
-        err: ConvertError,
+        err: ConvertErrorFromCpp,
         ctx: Option<ErrorContext>,
     },
     /// A Rust type which is not a C++ type.
@@ -531,7 +543,7 @@
     RustFn {
         name: ApiName,
         details: RustFun,
-        receiver: Option<QualifiedName>,
+        deps: Vec<QualifiedName>,
     },
     /// Some function for the extern "Rust" block.
     RustSubclassFn {
@@ -559,6 +571,7 @@
     },
 }
 
+#[derive(Debug)]
 pub(crate) struct RustSubclassFnDetails {
     pub(crate) params: Punctuated<FnArg, Comma>,
     pub(crate) ret: ReturnType,
@@ -646,12 +659,6 @@
     }
 }
 
-impl<T: AnalysisPhase> std::fmt::Debug for Api<T> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{:?} (kind={})", self.name_info(), self)
-    }
-}
-
 pub(crate) type UnanalyzedApi = Api<NullPhase>;
 
 impl<T: AnalysisPhase> Api<T> {
diff --git a/third_party/autocxx/engine/src/conversion/apivec.rs b/third_party/autocxx/engine/src/conversion/apivec.rs
index 1e440cc..24e70ed 100644
--- a/third_party/autocxx/engine/src/conversion/apivec.rs
+++ b/third_party/autocxx/engine/src/conversion/apivec.rs
@@ -15,7 +15,7 @@
 use indexmap::set::IndexSet as HashSet;
 
 use crate::{
-    conversion::{api::ApiName, convert_error::ErrorContext, ConvertError},
+    conversion::{api::ApiName, convert_error::ErrorContext, ConvertErrorFromCpp},
     types::QualifiedName,
 };
 
@@ -64,7 +64,7 @@
                 self.retain(|api| api.name() != name);
                 self.push(Api::IgnoredItem {
                     name: ApiName::new_from_qualified_name(name.clone()),
-                    err: ConvertError::DuplicateItemsFoundInParsing,
+                    err: ConvertErrorFromCpp::DuplicateItemsFoundInParsing,
                     ctx: Some(ErrorContext::new_for_item(name.get_final_ident())),
                 })
             }
diff --git a/third_party/autocxx/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs b/third_party/autocxx/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
index 5367626..661a5b3 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
@@ -11,16 +11,16 @@
 use crate::conversion::{
     analysis::fun::function_wrapper::{CppConversionType, TypeConversionPolicy},
     api::Pointerness,
-    ConvertError,
+    ConvertErrorFromCpp,
 };
 
-use super::type_to_cpp::{type_to_cpp, CppNameMap};
+use super::type_to_cpp::CppNameMap;
 
 impl TypeConversionPolicy {
     pub(super) fn unconverted_type(
         &self,
         cpp_name_map: &CppNameMap,
-    ) -> Result<String, ConvertError> {
+    ) -> Result<String, ConvertErrorFromCpp> {
         match self.cpp_conversion {
             CppConversionType::FromUniquePtrToValue => self.unique_ptr_wrapped_type(cpp_name_map),
             CppConversionType::FromPtrToValue => {
@@ -30,7 +30,10 @@
         }
     }
 
-    pub(super) fn converted_type(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
+    pub(super) fn converted_type(
+        &self,
+        cpp_name_map: &CppNameMap,
+    ) -> Result<String, ConvertErrorFromCpp> {
         match self.cpp_conversion {
             CppConversionType::FromValueToUniquePtr => self.unique_ptr_wrapped_type(cpp_name_map),
             CppConversionType::FromReferenceToPointer => {
@@ -46,15 +49,18 @@
                 Ok(format!(
                     "{}{}*",
                     const_string,
-                    type_to_cpp(ty, cpp_name_map)?
+                    cpp_name_map.type_to_cpp(ty)?
                 ))
             }
             _ => 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.cxxbridge_type(), cpp_name_map)
+    fn unwrapped_type_as_string(
+        &self,
+        cpp_name_map: &CppNameMap,
+    ) -> Result<String, ConvertErrorFromCpp> {
+        cpp_name_map.type_to_cpp(self.cxxbridge_type())
     }
 
     pub(crate) fn is_a_pointer(&self) -> Pointerness {
@@ -71,7 +77,7 @@
     fn unique_ptr_wrapped_type(
         &self,
         original_name_map: &CppNameMap,
-    ) -> Result<String, ConvertError> {
+    ) -> Result<String, ConvertErrorFromCpp> {
         Ok(format!(
             "std::unique_ptr<{}>",
             self.unwrapped_type_as_string(original_name_map)?
@@ -83,17 +89,17 @@
         var_name: &str,
         cpp_name_map: &CppNameMap,
         is_return: bool,
-    ) -> Result<Option<String>, ConvertError> {
+    ) -> Result<Option<String>, ConvertErrorFromCpp> {
         // If is_return we want to avoid unnecessary std::moves because they
         // make RVO less effective
         Ok(match self.cpp_conversion {
             CppConversionType::None | CppConversionType::FromReturnValueToPlacementPtr => {
                 Some(var_name.to_string())
             }
-            CppConversionType::FromPointerToReference { .. } => Some(format!("(*{})", var_name)),
-            CppConversionType::Move => Some(format!("std::move({})", var_name)),
+            CppConversionType::FromPointerToReference { .. } => Some(format!("(*{var_name})")),
+            CppConversionType::Move => Some(format!("std::move({var_name})")),
             CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToMove => {
-                Some(format!("std::move(*{})", var_name))
+                Some(format!("std::move(*{var_name})"))
             }
             CppConversionType::FromValueToUniquePtr => Some(format!(
                 "std::make_unique<{}>({})",
@@ -101,15 +107,15 @@
                 var_name
             )),
             CppConversionType::FromPtrToValue => {
-                let dereference = format!("*{}", var_name);
+                let dereference = format!("*{var_name}");
                 Some(if is_return {
                     dereference
                 } else {
-                    format!("std::move({})", dereference)
+                    format!("std::move({dereference})")
                 })
             }
             CppConversionType::IgnoredPlacementPtrParameter => None,
-            CppConversionType::FromReferenceToPointer { .. } => Some(format!("&{}", var_name)),
+            CppConversionType::FromReferenceToPointer { .. } => Some(format!("&{var_name}")),
         })
     }
 }
diff --git a/third_party/autocxx/engine/src/conversion/codegen_cpp/mod.rs b/third_party/autocxx/engine/src/conversion/codegen_cpp/mod.rs
index 0e6dcce..bb341a8 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_cpp/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_cpp/mod.rs
@@ -20,11 +20,7 @@
 use indexmap::set::IndexSet as HashSet;
 use itertools::Itertools;
 use std::borrow::Cow;
-use type_to_cpp::{original_name_map_from_apis, type_to_cpp, CppNameMap};
-
-use self::type_to_cpp::{
-    final_ident_using_original_name_map, namespaced_name_using_original_name_map,
-};
+use type_to_cpp::CppNameMap;
 
 use super::{
     analysis::{
@@ -36,7 +32,7 @@
     },
     api::{Api, Provenance, SubclassName, TypeKind},
     apivec::ApiVec,
-    ConvertError,
+    ConvertErrorFromCpp,
 };
 
 #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
@@ -55,17 +51,17 @@
     ) -> String {
         let blank = "".to_string();
         match self {
-            Self::System(name) => format!("#include <{}>", name),
+            Self::System(name) => format!("#include <{name}>"),
             Self::CxxH => {
                 let prefix = cpp_codegen_options.path_to_cxx_h.as_ref().unwrap_or(&blank);
-                format!("#include \"{}cxx.h\"", prefix)
+                format!("#include \"{prefix}cxx.h\"")
             }
             Self::CxxgenH => {
                 let prefix = cpp_codegen_options
                     .path_to_cxxgen_h
                     .as_ref()
                     .unwrap_or(&blank);
-                format!("#include \"{}{}\"", prefix, cxxgen_header_name)
+                format!("#include \"{prefix}{cxxgen_header_name}\"")
             }
             Header::NewDeletePrelude => new_and_delete_prelude::NEW_AND_DELETE_PRELUDE.to_string(),
         }
@@ -120,11 +116,11 @@
         config: &'a IncludeCppConfig,
         cpp_codegen_options: &CppCodegenOptions,
         cxxgen_header_name: &str,
-    ) -> Result<Option<CppFilePair>, ConvertError> {
+    ) -> Result<Option<CppFilePair>, ConvertErrorFromCpp> {
         let mut gen = CppCodeGenerator {
             additional_functions: Vec::new(),
             inclusions,
-            original_name_map: original_name_map_from_apis(apis),
+            original_name_map: CppNameMap::new_from_apis(apis),
             config,
             cpp_codegen_options,
             cxxgen_header_name,
@@ -139,7 +135,7 @@
     fn add_needs<'b>(
         &mut self,
         apis: impl Iterator<Item = &'a Api<FnPhase>>,
-    ) -> Result<(), ConvertError> {
+    ) -> Result<(), ConvertErrorFromCpp> {
         let mut constructors_by_subclass: HashMap<SubclassName, Vec<&CppFunction>> = HashMap::new();
         let mut methods_by_subclass: HashMap<SubclassName, Vec<SubclassFunction>> = HashMap::new();
         let mut deferred_apis = Vec::new();
@@ -172,7 +168,7 @@
                 } => {
                     let effective_cpp_definition = match rs_definition {
                         Some(rs_definition) => {
-                            Cow::Owned(type_to_cpp(rs_definition, &self.original_name_map)?)
+                            Cow::Owned(self.original_name_map.type_to_cpp(rs_definition)?)
                         }
                         None => Cow::Borrowed(cpp_definition),
                     };
@@ -248,10 +244,8 @@
                 .any(|x| x.definition.is_some())
             {
                 let definitions = self.concat_additional_items(|x| x.definition.as_ref());
-                let definitions = format!(
-                    "#include \"{}\"\n{}\n{}",
-                    header_name, cpp_headers, definitions
-                );
+                let definitions =
+                    format!("#include \"{header_name}\"\n{cpp_headers}\n{definitions}");
                 log::info!("Additional C++ defs:\n{}", definitions);
                 Some(definitions.into_bytes())
             } else {
@@ -302,7 +296,7 @@
         // can result in destructors for nested types being called multiple times
         // if we represent them as trivial types. So generate an extra
         // assertion to make sure.
-        let declaration = Some(format!("static_assert(::rust::IsRelocatable<{}>::value, \"type {} should be trivially move constructible and trivially destructible to be used with generate_pod! in autocxx\");", name, name));
+        let declaration = Some(format!("static_assert(::rust::IsRelocatable<{name}>::value, \"type {name} should be trivially move constructible and trivially destructible to be used with generate_pod! in autocxx\");"));
         self.additional_functions.push(ExtraCpp {
             declaration,
             headers: vec![Header::CxxH],
@@ -312,7 +306,7 @@
 
     fn generate_string_constructor(&mut self) {
         let makestring_name = self.config.get_makestring_name();
-        let declaration = Some(format!("inline std::unique_ptr<std::string> {}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}", makestring_name));
+        let declaration = Some(format!("inline std::unique_ptr<std::string> {makestring_name}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}"));
         self.additional_functions.push(ExtraCpp {
             declaration,
             headers: vec![
@@ -324,7 +318,7 @@
         })
     }
 
-    fn generate_cpp_function(&mut self, details: &CppFunction) -> Result<(), ConvertError> {
+    fn generate_cpp_function(&mut self, details: &CppFunction) -> Result<(), ConvertErrorFromCpp> {
         self.additional_functions
             .push(self.generate_cpp_function_inner(
                 details,
@@ -343,7 +337,7 @@
         conversion_direction: ConversionDirection,
         requires_rust_declarations: bool,
         force_name: Option<&str>,
-    ) -> Result<ExtraCpp, ConvertError> {
+    ) -> Result<ExtraCpp, ConvertErrorFromCpp> {
         // Even if the original function call is in a namespace,
         // we generate this wrapper in the global namespace.
         // We could easily do this the other way round, and when
@@ -373,7 +367,7 @@
                 // may be able to remove this.
                 "autocxx_gen_this".to_string()
             } else {
-                format!("arg{}", counter)
+                format!("arg{counter}")
             }
         };
         // If this returns a non-POD value, we may instead wish to emplace
@@ -425,16 +419,13 @@
             CppFunctionKind::ConstMethod => " const",
             _ => "",
         };
-        let declaration = format!("{} {}({}){}", ret_type, name, args, constness);
+        let declaration = format!("{ret_type} {name}({args}){constness}");
         let qualification = if let Some(qualification) = &details.qualification {
             format!("{}::", qualification.to_cpp_name())
         } else {
             "".to_string()
         };
-        let qualified_declaration = format!(
-            "{} {}{}({}){}",
-            ret_type, qualification, name, args, constness
-        );
+        let qualified_declaration = format!("{ret_type} {qualification}{name}({args}){constness}");
         // Whether there's a placement param in which to put the return value
         let placement_param = details
             .argument_conversion
@@ -491,13 +482,38 @@
                 )
             }
             CppFunctionBody::Destructor(ns, id) => {
-                let ty_id = QualifiedName::new(ns, id.clone());
-                let ty_id = final_ident_using_original_name_map(&ty_id, &self.original_name_map);
-                (format!("{}->~{}()", arg_list, ty_id), "".to_string(), false)
+                let full_name = QualifiedName::new(ns, id.clone());
+                let ty_id = self.original_name_map.get_final_item(&full_name);
+                let is_a_nested_struct = self.original_name_map.get(&full_name).is_some();
+                // This is all super duper fiddly.
+                // All we want to do is call a destructor. Constraints:
+                // * an unnamed struct, e.g. typedef struct { .. } A, does not
+                //   have any way of fully qualifying its destructor name.
+                //   We have to use a 'using' statement.
+                // * we don't get enough information from bindgen to distinguish
+                //   typedef struct { .. } A  // unnamed struct
+                //   from
+                //   struct A { .. }          // named struct
+                // * we can only do 'using A::B::C' if 'B' is a namespace,
+                //   as opposed to a type with an inner type.
+                // * we can always do 'using C = A::B::C' but then SOME C++
+                //   compilers complain that it's unused, iff it's a named struct.
+                let destructor_call = format!("{arg_list}->{ty_id}::~{ty_id}()");
+                let destructor_call = if ns.is_empty() {
+                    destructor_call
+                } else {
+                    let path = self.original_name_map.map(&full_name);
+                    if is_a_nested_struct {
+                        format!("{{ using {ty_id} = {path}; {destructor_call}; {ty_id}* pointless; (void)pointless; }}")
+                    } else {
+                        format!("{{ using {path}; {destructor_call}; }}")
+                    }
+                };
+                (destructor_call, "".to_string(), false)
             }
             CppFunctionBody::FunctionCall(ns, id) => match receiver {
                 Some(receiver) => (
-                    format!("{}.{}({})", receiver, id, arg_list),
+                    format!("{receiver}.{id}({arg_list})"),
                     "".to_string(),
                     false,
                 ),
@@ -508,7 +524,7 @@
                         .chain(std::iter::once(id.to_string()))
                         .join("::");
                     (
-                        format!("{}({})", underlying_function_call, arg_list),
+                        format!("{underlying_function_call}({arg_list})"),
                         "".to_string(),
                         false,
                     )
@@ -521,7 +537,7 @@
                     .chain([ty_id.to_string(), fn_id.to_string()].iter().cloned())
                     .join("::");
                 (
-                    format!("{}({})", underlying_function_call, arg_list),
+                    format!("{underlying_function_call}({arg_list})"),
                     "".to_string(),
                     false,
                 )
@@ -530,7 +546,7 @@
             CppFunctionBody::AllocUninitialized(ty) => {
                 let namespaced_ty = self.namespaced_name(ty);
                 (
-                    format!("new_appropriately<{}>();", namespaced_ty,),
+                    format!("new_appropriately<{namespaced_ty}>();",),
                     "".to_string(),
                     true,
                 )
@@ -559,39 +575,35 @@
 
             underlying_function_call = match placement_param {
                 Some(placement_param) => {
-                    let tyname = type_to_cpp(ret.cxxbridge_type(), &self.original_name_map)?;
-                    format!("new({}) {}({})", placement_param, tyname, call_itself)
+                    let tyname = self.original_name_map.type_to_cpp(ret.cxxbridge_type())?;
+                    format!("new({placement_param}) {tyname}({call_itself})")
                 }
-                None => format!("return {}", call_itself),
+                None => format!("return {call_itself}"),
             };
         };
         if !underlying_function_call.is_empty() {
-            underlying_function_call = format!("{};", underlying_function_call);
+            underlying_function_call = format!("{underlying_function_call};");
         }
         let field_assignments =
             if let CppFunctionBody::ConstructSuperclass(superclass_name) = &details.payload {
                 let superclass_assignments = if field_assignments.is_empty() {
                     "".to_string()
                 } else {
-                    format!("{}({}), ", superclass_name, field_assignments)
+                    format!("{superclass_name}({field_assignments}), ")
                 };
-                format!(": {}obs(std::move(arg0))", superclass_assignments)
+                format!(": {superclass_assignments}obs(std::move(arg0))")
             } else {
                 "".into()
             };
-        let definition_after_sig =
-            format!("{} {{ {} }}", field_assignments, underlying_function_call,);
+        let definition_after_sig = format!("{field_assignments} {{ {underlying_function_call} }}",);
         let (declaration, definition) = if requires_rust_declarations {
             (
-                Some(format!("{};", declaration)),
-                Some(format!(
-                    "{} {}",
-                    qualified_declaration, definition_after_sig
-                )),
+                Some(format!("{declaration};")),
+                Some(format!("{qualified_declaration} {definition_after_sig}")),
             )
         } else {
             (
-                Some(format!("inline {} {}", declaration, definition_after_sig)),
+                Some(format!("inline {declaration} {definition_after_sig}")),
                 None,
             )
         };
@@ -609,7 +621,7 @@
     }
 
     fn namespaced_name(&self, name: &QualifiedName) -> String {
-        namespaced_name_using_original_name_map(name, &self.original_name_map)
+        self.original_name_map.map(name)
     }
 
     fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
@@ -620,7 +632,7 @@
     fn generate_typedef(&mut self, tn: &QualifiedName, definition: &str) {
         let our_name = tn.get_final_item();
         self.additional_functions.push(ExtraCpp {
-            type_definition: Some(format!("typedef {} {};", definition, our_name)),
+            type_definition: Some(format!("typedef {definition} {our_name};")),
             ..Default::default()
         })
     }
@@ -631,10 +643,10 @@
         subclass: &SubclassName,
         constructors: Vec<&CppFunction>,
         methods: Vec<SubclassFunction>,
-    ) -> Result<(), ConvertError> {
+    ) -> Result<(), ConvertErrorFromCpp> {
         let holder = subclass.holder();
         self.additional_functions.push(ExtraCpp {
-            type_definition: Some(format!("struct {};", holder)),
+            type_definition: Some(format!("struct {holder};")),
             ..Default::default()
         });
         let mut method_decls = Vec::new();
@@ -677,12 +689,10 @@
         // In future, for each superclass..
         let super_name = superclass.get_final_item();
         method_decls.push(format!(
-            "const {}& As_{}() const {{ return *this; }}",
-            super_name, super_name,
+            "const {super_name}& As_{super_name}() const {{ return *this; }}",
         ));
         method_decls.push(format!(
-            "{}& As_{}_mut() {{ return *this; }}",
-            super_name, super_name
+            "{super_name}& As_{super_name}_mut() {{ return *this; }}"
         ));
         self.additional_functions.push(ExtraCpp {
             declaration: Some(format!(
diff --git a/third_party/autocxx/engine/src/conversion/codegen_cpp/type_to_cpp.rs b/third_party/autocxx/engine/src/conversion/codegen_cpp/type_to_cpp.rs
index 7febb7a..b2da44b 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_cpp/type_to_cpp.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_cpp/type_to_cpp.rs
@@ -7,7 +7,7 @@
 // except according to those terms.
 
 use crate::{
-    conversion::{apivec::ApiVec, AnalysisPhase, ConvertError},
+    conversion::{apivec::ApiVec, AnalysisPhase, ConvertErrorFromCpp},
     types::QualifiedName,
 };
 use indexmap::map::IndexMap as HashMap;
@@ -19,116 +19,129 @@
 /// Map from QualifiedName to original C++ name. Original C++ name does not
 /// include the namespace; this can be assumed to be the same as the namespace
 /// in the QualifiedName.
-pub(crate) type CppNameMap = HashMap<QualifiedName, String>;
+/// The "original C++ name" is mostly relevant in the case of nested types,
+/// where the typename might be A::B within a namespace C::D.
+pub(crate) struct CppNameMap(HashMap<QualifiedName, String>);
 
-pub(crate) fn original_name_map_from_apis<T: AnalysisPhase>(apis: &ApiVec<T>) -> CppNameMap {
-    apis.iter()
-        .filter_map(|api| {
-            api.cpp_name()
-                .as_ref()
-                .map(|cpp_name| (api.name().clone(), cpp_name.clone()))
-        })
-        .collect()
-}
-
-pub(crate) fn namespaced_name_using_original_name_map(
-    qual_name: &QualifiedName,
-    original_name_map: &CppNameMap,
-) -> String {
-    if let Some(cpp_name) = original_name_map.get(qual_name) {
-        qual_name
-            .get_namespace()
-            .iter()
-            .chain(once(cpp_name))
-            .join("::")
-    } else {
-        qual_name.to_cpp_name()
-    }
-}
-
-pub(crate) fn final_ident_using_original_name_map(
-    qual_name: &QualifiedName,
-    original_name_map: &CppNameMap,
-) -> String {
-    match original_name_map.get(qual_name) {
-        Some(original_name) => {
-            // If we have an original name, this may be a nested struct
-            // (e.g. A::B). The final ident here is just 'B' so...
-            original_name
-                .rsplit_once("::")
-                .map_or(original_name.clone(), |(_, original_name)| {
-                    original_name.to_string()
+impl CppNameMap {
+    /// Look through the APIs we've found to assemble the original name
+    /// map.
+    pub(crate) fn new_from_apis<T: AnalysisPhase>(apis: &ApiVec<T>) -> Self {
+        Self(
+            apis.iter()
+                .filter_map(|api| {
+                    api.cpp_name()
+                        .as_ref()
+                        .map(|cpp_name| (api.name().clone(), cpp_name.clone()))
                 })
-        }
-        None => qual_name.get_final_cpp_item(),
+                .collect(),
+        )
     }
-}
 
-pub(crate) fn type_to_cpp(ty: &Type, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
-    match ty {
-        Type::Path(typ) => {
-            // If this is a std::unique_ptr we do need to pass
-            // its argument through.
-            let qual_name = QualifiedName::from_type_path(typ);
-            let root = namespaced_name_using_original_name_map(&qual_name, cpp_name_map);
-            if root == "Pin" {
-                // Strip all Pins from type names when describing them in C++.
-                let inner_type = &typ.path.segments.last().unwrap().arguments;
-                if let syn::PathArguments::AngleBracketed(ab) = inner_type {
-                    let inner_type = ab.args.iter().next().unwrap();
-                    if let syn::GenericArgument::Type(gat) = inner_type {
-                        return type_to_cpp(gat, cpp_name_map);
-                    }
-                }
-                panic!("Pin<...> didn't contain the inner types we expected");
-            }
-            let suffix = match &typ.path.segments.last().unwrap().arguments {
-                syn::PathArguments::AngleBracketed(ab) => {
-                    let results: Result<Vec<_>, _> = ab
-                        .args
-                        .iter()
-                        .map(|x| match x {
-                            syn::GenericArgument::Type(gat) => type_to_cpp(gat, cpp_name_map),
-                            _ => Ok("".to_string()),
-                        })
-                        .collect();
-                    Some(results?.join(", "))
-                }
-                syn::PathArguments::None | syn::PathArguments::Parenthesized(_) => None,
-            };
-            match suffix {
-                None => Ok(root),
-                Some(suffix) => Ok(format!("{}<{}>", root, suffix)),
-            }
+    /// Imagine a nested struct in namespace::outer::inner
+    /// This function converts from the bindgen name, namespace::outer_inner,
+    /// to namespace::outer::inner.
+    pub(crate) fn map(&self, qual_name: &QualifiedName) -> String {
+        if let Some(cpp_name) = self.0.get(qual_name) {
+            qual_name
+                .get_namespace()
+                .iter()
+                .chain(once(cpp_name))
+                .join("::")
+        } else {
+            qual_name.to_cpp_name()
         }
-        Type::Reference(typr) => match &*typr.elem {
-            Type::Path(typ) if typ.path.is_ident("str") => Ok("rust::Str".into()),
-            _ => Ok(format!(
-                "{}{}&",
-                get_mut_string(&typr.mutability),
-                type_to_cpp(typr.elem.as_ref(), cpp_name_map)?
+    }
+
+    /// Get a stringified version of the last ident in the name.
+    /// e.g. for namespace::outer_inner this will return inner.
+    /// This is useful for doing things such as calling constructors
+    /// such as inner() or destructors such as ~inner()
+    pub(crate) fn get_final_item<'b>(&'b self, qual_name: &'b QualifiedName) -> &'b str {
+        match self.get(qual_name) {
+            Some(n) => match n.rsplit_once("::") {
+                Some((_, suffix)) => suffix,
+                None => qual_name.get_final_item(),
+            },
+            None => qual_name.get_final_item(),
+        }
+    }
+
+    /// Convert a type to its C++ spelling.
+    pub(crate) fn type_to_cpp(&self, ty: &Type) -> Result<String, ConvertErrorFromCpp> {
+        match ty {
+            Type::Path(typ) => {
+                // If this is a std::unique_ptr we do need to pass
+                // its argument through.
+                let qual_name = QualifiedName::from_type_path(typ);
+                let root = self.map(&qual_name);
+                if root == "Pin" {
+                    // Strip all Pins from type names when describing them in C++.
+                    let inner_type = &typ.path.segments.last().unwrap().arguments;
+                    if let syn::PathArguments::AngleBracketed(ab) = inner_type {
+                        let inner_type = ab.args.iter().next().unwrap();
+                        if let syn::GenericArgument::Type(gat) = inner_type {
+                            return self.type_to_cpp(gat);
+                        }
+                    }
+                    panic!("Pin<...> didn't contain the inner types we expected");
+                }
+                let suffix = match &typ.path.segments.last().unwrap().arguments {
+                    syn::PathArguments::AngleBracketed(ab) => {
+                        let results: Result<Vec<_>, _> = ab
+                            .args
+                            .iter()
+                            .map(|x| match x {
+                                syn::GenericArgument::Type(gat) => self.type_to_cpp(gat),
+                                _ => Ok("".to_string()),
+                            })
+                            .collect();
+                        Some(results?.join(", "))
+                    }
+                    syn::PathArguments::None | syn::PathArguments::Parenthesized(_) => None,
+                };
+                match suffix {
+                    None => Ok(root),
+                    Some(suffix) => Ok(format!("{root}<{suffix}>")),
+                }
+            }
+            Type::Reference(typr) => match &*typr.elem {
+                Type::Path(typ) if typ.path.is_ident("str") => Ok("rust::Str".into()),
+                _ => Ok(format!(
+                    "{}{}&",
+                    get_mut_string(&typr.mutability),
+                    self.type_to_cpp(typr.elem.as_ref())?
+                )),
+            },
+            Type::Ptr(typp) => Ok(format!(
+                "{}{}*",
+                get_mut_string(&typp.mutability),
+                self.type_to_cpp(typp.elem.as_ref())?
             )),
-        },
-        Type::Ptr(typp) => Ok(format!(
-            "{}{}*",
-            get_mut_string(&typp.mutability),
-            type_to_cpp(typp.elem.as_ref(), cpp_name_map)?
-        )),
-        Type::Array(_)
-        | Type::BareFn(_)
-        | Type::Group(_)
-        | Type::ImplTrait(_)
-        | Type::Infer(_)
-        | Type::Macro(_)
-        | Type::Never(_)
-        | Type::Paren(_)
-        | Type::Slice(_)
-        | Type::TraitObject(_)
-        | Type::Tuple(_)
-        | Type::Verbatim(_) => Err(ConvertError::UnsupportedType(
-            ty.to_token_stream().to_string(),
-        )),
-        _ => Err(ConvertError::UnknownType(ty.to_token_stream().to_string())),
+            Type::Array(_)
+            | Type::BareFn(_)
+            | Type::Group(_)
+            | Type::ImplTrait(_)
+            | Type::Infer(_)
+            | Type::Macro(_)
+            | Type::Never(_)
+            | Type::Paren(_)
+            | Type::Slice(_)
+            | Type::TraitObject(_)
+            | Type::Tuple(_)
+            | Type::Verbatim(_) => Err(ConvertErrorFromCpp::UnsupportedType(
+                ty.to_token_stream().to_string(),
+            )),
+            _ => Err(ConvertErrorFromCpp::UnknownType(
+                ty.to_token_stream().to_string(),
+            )),
+        }
+    }
+
+    /// Check an individual item in the name map. Returns a thing if
+    /// it's an inner type, otherwise returns none.
+    pub(crate) fn get(&self, name: &QualifiedName) -> Option<&String> {
+        self.0.get(name)
     }
 }
 
diff --git a/third_party/autocxx/engine/src/conversion/codegen_rs/fun_codegen.rs b/third_party/autocxx/engine/src/conversion/codegen_rs/fun_codegen.rs
index db222a6..1b68e8c 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_rs/fun_codegen.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_rs/fun_codegen.rs
@@ -34,6 +34,7 @@
         },
         api::{Pointerness, UnsafetyNeeded},
     },
+    minisyn::minisynize_vec,
     types::{Namespace, QualifiedName},
 };
 use crate::{
@@ -104,7 +105,7 @@
     let params = analysis.params;
     let vis = analysis.vis;
     let kind = analysis.kind;
-    let doc_attrs = fun.doc_attrs;
+    let doc_attrs = minisynize_vec(fun.doc_attrs);
 
     let mut cpp_name_attr = Vec::new();
     let mut impl_entry = None;
@@ -132,7 +133,6 @@
         params,
         Cow::Borrowed(&ret_type),
         non_pod_types,
-        true,
     );
 
     if analysis.rust_wrapper_needed {
@@ -170,10 +170,10 @@
         FnKind::Method { .. } | FnKind::TraitMethod { .. } => None,
         FnKind::Function => match analysis.rust_rename_strategy {
             _ if analysis.rust_wrapper_needed => {
-                Some(Use::SpecificNameFromBindgen(make_ident(rust_name)))
+                Some(Use::SpecificNameFromBindgen(make_ident(rust_name).into()))
             }
             RustRenameStrategy::RenameInOutputMod(ref alias) => {
-                Some(Use::UsedFromCxxBridgeWithAlias(alias.clone()))
+                Some(Use::UsedFromCxxBridgeWithAlias(alias.clone().into()))
             }
             _ => Some(Use::UsedFromCxxBridge),
         },
@@ -259,14 +259,14 @@
         let mut ptr_arg_name = None;
         let mut ret_type: Cow<'a, _> = ret_type
             .map(Cow::Owned)
-            .unwrap_or(Cow::Borrowed(self.ret_type));
+            .unwrap_or_else(|| 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 {
+            let wrapper_arg_name: syn::Pat = if pd.self_type.is_some() && !avoid_self {
                 parse_quote!(self)
             } else {
-                pd.name.clone()
+                pd.name.clone().into()
             };
             let rust_for_param = pd
                 .conversion
@@ -308,7 +308,6 @@
             wrapper_params,
             ret_type,
             self.non_pod_types,
-            false,
         );
 
         let cxxbridge_name = self.cxxbridge_name;
@@ -322,6 +321,17 @@
             || 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() => {
+                // There's a potential lurking bug below. If the return type conversion requires
+                // unsafe, then we'll end up doing something like
+                //   unsafe { do_return_conversion( unsafe { call_body() })}
+                // and the generated code will get warnings about nested unsafe blocks.
+                // That's because we convert the call body to tokens in the following
+                // line without considering the fact it's embedded in another expression.
+                // At the moment this is OK because no return type conversions require
+                // unsafe, but if this happens in future, we should do:
+                //   let temp_ret_val = unsafe { call_body() };
+                //   do_return_conversion(temp_ret_val)
+                // by returning a vector of MaybeUnsafes within call_body.
                 let expr = maybe_unsafes_to_tokens(vec![call_body], context_is_unsafe);
                 let conv =
                     ret_conversion.rust_conversion(parse_quote! { #expr }, &mut variable_counter);
@@ -391,33 +401,26 @@
             .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 }),
+        let ty = match receiver_pointerness {
+            Pointerness::MutPtr if self.reference_wrappers => ImplBlockKey {
+                ty: parse_quote! {
+                    #ty
                 },
-                Pointerness::ConstPtr => ImplBlockKey {
-                    ty: parse_quote! {
-                        CppRef< 'a, #ty>
-                    },
-                    lifetime: Some(parse_quote! { 'a }),
+                lifetime: Some(parse_quote! { 'a }),
+            },
+            Pointerness::ConstPtr if self.reference_wrappers => ImplBlockKey {
+                ty: parse_quote! {
+                    #ty
                 },
-                Pointerness::Not => ImplBlockKey {
-                    ty: parse_quote! { # ty },
-                    lifetime: None,
-                },
-            }
-        } else {
-            ImplBlockKey {
+                lifetime: Some(parse_quote! { 'a }),
+            },
+            _ => ImplBlockKey {
                 ty: parse_quote! { # ty },
                 lifetime: None,
-            }
+            },
         };
         Box::new(ImplBlockDetails {
-            item: ImplItem::Method(parse_quote! {
+            item: ImplItem::Fn(parse_quote! {
                 #(#doc_attrs)*
                 pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                     #call_body
@@ -453,7 +456,7 @@
         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, Some(ret_type));
-        let rust_name = make_ident(&self.rust_name);
+        let rust_name = make_ident(self.rust_name);
         let doc_attrs = self.doc_attrs;
         let unsafety = self.unsafety.wrapper_token();
         let ty = impl_block_type_name.get_final_ident();
@@ -465,7 +468,7 @@
                 }
         };
         Box::new(ImplBlockDetails {
-            item: ImplItem::Method(parse_quote! { #stuff }),
+            item: ImplItem::Fn(parse_quote! { #stuff }),
             ty: ImplBlockKey { ty, lifetime: None },
         })
     }
diff --git a/third_party/autocxx/engine/src/conversion/codegen_rs/function_wrapper_rs.rs b/third_party/autocxx/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
index 708d41c..4afe3c7 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
@@ -63,11 +63,11 @@
             }
             RustConversionType::FromPinMaybeUninitToPtr => {
                 let ty = match self.cxxbridge_type() {
-                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    Type::Ptr(TypePtr { elem, .. }) => elem,
                     _ => panic!("Not a ptr"),
                 };
                 let ty = parse_quote! {
-                    ::std::pin::Pin<&mut ::std::mem::MaybeUninit< #ty >>
+                    ::core::pin::Pin<&mut ::core::mem::MaybeUninit< #ty >>
                 };
                 RustParamConversion::Param {
                     ty,
@@ -80,17 +80,17 @@
             }
             RustConversionType::FromPinMoveRefToPtr => {
                 let ty = match self.cxxbridge_type() {
-                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    Type::Ptr(TypePtr { elem, .. }) => elem,
                     _ => panic!("Not a ptr"),
                 };
                 let ty = parse_quote! {
-                    ::std::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >>
+                    ::core::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >>
                 };
                 RustParamConversion::Param {
                     ty,
                     local_variables: Vec::new(),
                     conversion: quote! {
-                        { let r: &mut _ = ::std::pin::Pin::into_inner_unchecked(#var.as_mut());
+                        { let r: &mut _ = ::core::pin::Pin::into_inner_unchecked(#var.as_mut());
                             r
                         }
                     },
@@ -99,7 +99,7 @@
             }
             RustConversionType::FromTypeToPtr => {
                 let ty = match self.cxxbridge_type() {
-                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    Type::Ptr(TypePtr { elem, .. }) => elem,
                     _ => panic!("Not a ptr"),
                 };
                 let ty = parse_quote! { &mut #ty };
@@ -124,7 +124,7 @@
                 let param_trait = make_ident(param_trait);
                 let var_counter = *counter;
                 *counter += 1;
-                let space_var_name = format!("space{}", var_counter);
+                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> };
@@ -138,10 +138,10 @@
                         ),
                         MaybeUnsafeStmt::binary(
                             quote! { let mut #space_var_name =
-                                unsafe { ::std::pin::Pin::new_unchecked(&mut #space_var_name) };
+                                unsafe { ::core::pin::Pin::new_unchecked(&mut #space_var_name) };
                             },
                             quote! { let mut #space_var_name =
-                                ::std::pin::Pin::new_unchecked(&mut #space_var_name);
+                                ::core::pin::Pin::new_unchecked(&mut #space_var_name);
                             },
                         ),
                         MaybeUnsafeStmt::needs_unsafe(
@@ -172,16 +172,16 @@
                     _ => panic!("Not a pointer"),
                 };
                 let (ty, wrapper_name) = if is_mut {
-                    (parse_quote! { CppMutRef<'a, #ty> }, "CppMutRef")
+                    (parse_quote! { autocxx::CppMutRef<'a, #ty> }, "CppMutRef")
                 } else {
-                    (parse_quote! { CppRef<'a, #ty> }, "CppRef")
+                    (parse_quote! { autocxx::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)
+                        autocxx::#wrapper_name::from_ptr (#var)
                     },
                     conversion_requires_unsafe: false,
                 }
@@ -194,15 +194,21 @@
                     _ => panic!("Not a pointer"),
                 };
                 let ty = if is_mut {
-                    parse_quote! { &mut CppMutRef<'a, #ty> }
+                    parse_quote! { &mut autocxx::CppMutRef<'a, #ty> }
                 } else {
-                    parse_quote! { &CppRef<'a, #ty> }
+                    parse_quote! { &autocxx::CppRef<'a, #ty> }
                 };
                 RustParamConversion::Param {
                     ty,
                     local_variables: Vec::new(),
-                    conversion: quote! {
-                        #var .0
+                    conversion: if is_mut {
+                        quote! {
+                            #var .as_mut_ptr()
+                        }
+                    } else {
+                        quote! {
+                            #var .as_ptr()
+                        }
                     },
                     conversion_requires_unsafe: false,
                 }
diff --git a/third_party/autocxx/engine/src/conversion/codegen_rs/lifetime.rs b/third_party/autocxx/engine/src/conversion/codegen_rs/lifetime.rs
index ea2c782..5560158 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_rs/lifetime.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_rs/lifetime.rs
@@ -36,7 +36,6 @@
     mut params: Punctuated<FnArg, Comma>,
     ret_type: Cow<'r, ReturnType>,
     non_pod_types: &HashSet<QualifiedName>,
-    assert_all_parameters_are_references: bool,
 ) -> (
     Option<TokenStream>,
     Punctuated<FnArg, Comma>,
@@ -92,25 +91,16 @@
     match new_return_type {
         None => (None, params, ret_type),
         Some(new_return_type) => {
-            for mut param in params.iter_mut() {
-                match &mut param {
-                    FnArg::Typed(PatType { ty, .. }) => match ty.as_mut() {
-                        Type::Path(TypePath {
-                            path: Path { segments, .. },
-                            ..
-                        }) => add_lifetime_to_pinned_reference(segments).unwrap_or_else(|e| {
-                            if assert_all_parameters_are_references {
-                                panic!("Expected a pinned reference: {:?}", e)
-                            }
-                        }),
-                        Type::Reference(tyr) => add_lifetime_to_reference(tyr),
-                        Type::ImplTrait(tyit) => add_lifetime_to_impl_trait(tyit),
-                        _ if assert_all_parameters_are_references => {
-                            panic!("Expected Pin<&mut T> or &T")
-                        }
-                        _ => {}
-                    },
-                    _ if assert_all_parameters_are_references => panic!("Unexpected fnarg"),
+            for FnArg::Typed(PatType { ty, .. }) | FnArg::Receiver(syn::Receiver { ty, .. }) in
+                params.iter_mut()
+            {
+                match ty.as_mut() {
+                    Type::Path(TypePath {
+                        path: Path { segments, .. },
+                        ..
+                    }) => add_lifetime_to_pinned_reference(segments).unwrap_or(()),
+                    Type::Reference(tyr) => add_lifetime_to_reference(tyr),
+                    Type::ImplTrait(tyit) => add_lifetime_to_impl_trait(tyit),
                     _ => {}
                 }
             }
@@ -168,16 +158,19 @@
 }
 
 fn add_lifetime_to_pinned_reference(
-    segments: &mut Punctuated<PathSegment, syn::token::Colon2>,
+    segments: &mut Punctuated<PathSegment, syn::token::PathSep>,
 ) -> Result<(), AddLifetimeError> {
-    static EXPECTED_SEGMENTS: &[(&str, bool)] = &[
-        ("std", false),
-        ("pin", false),
-        ("Pin", true), // true = act on the arguments of this segment
+    static EXPECTED_SEGMENTS: &[(&[&str], bool)] = &[
+        (&["std", "core"], false),
+        (&["pin"], false),
+        (&["Pin"], true), // true = act on the arguments of this segment
     ];
 
     for (seg, (expected_name, act)) in segments.iter_mut().zip(EXPECTED_SEGMENTS.iter()) {
-        if seg.ident != expected_name {
+        if !expected_name
+            .iter()
+            .any(|expected_name| seg.ident == expected_name)
+        {
             return Err(AddLifetimeError::WasNotPin);
         }
         if *act {
diff --git a/third_party/autocxx/engine/src/conversion/codegen_rs/mod.rs b/third_party/autocxx/engine/src/conversion/codegen_rs/mod.rs
index d1219d2..abab612 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_rs/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_rs/mod.rs
@@ -28,13 +28,11 @@
 };
 
 use crate::{
-    conversion::{
-        codegen_rs::{
-            non_pod_struct::{make_non_pod, new_non_pod_struct},
-            unqualify::{unqualify_params, unqualify_ret_type},
-        },
-        doc_attr::get_doc_attrs,
+    conversion::codegen_rs::{
+        non_pod_struct::{make_non_pod, new_non_pod_struct},
+        unqualify::{unqualify_params, unqualify_ret_type},
     },
+    minisyn::minisynize_punctuated,
     types::{make_ident, Namespace, QualifiedName},
 };
 use impl_item_creator::create_impl_items;
@@ -51,15 +49,14 @@
     },
     api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind},
     convert_error::ErrorContextType,
+    doc_attr::get_doc_attrs,
 };
 use super::{
     api::{Layout, Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature},
     apivec::ApiVec,
-    codegen_cpp::type_to_cpp::{
-        namespaced_name_using_original_name_map, original_name_map_from_apis, CppNameMap,
-    },
+    codegen_cpp::type_to_cpp::CppNameMap,
 };
-use super::{convert_error::ErrorContext, ConvertError};
+use super::{convert_error::ErrorContext, ConvertErrorFromCpp};
 use quote::quote;
 
 #[derive(Clone, Hash, PartialEq, Eq)]
@@ -137,91 +134,6 @@
     .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.
@@ -248,7 +160,7 @@
             unsafe_policy,
             include_list,
             bindgen_mod,
-            original_name_map: original_name_map_from_apis(&all_apis),
+            original_name_map: CppNameMap::new_from_apis(&all_apis),
             config,
             header_name,
         };
@@ -314,9 +226,6 @@
         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));
@@ -430,7 +339,7 @@
                     Use::UsedFromCxxBridge => Self::generate_cxx_use_stmt(name, None),
                     Use::UsedFromBindgen => Self::generate_bindgen_use_stmt(name),
                     Use::SpecificNameFromBindgen(id) => {
-                        let name = QualifiedName::new(name.get_namespace(), id.clone());
+                        let name = QualifiedName::new(name.get_namespace(), id.clone().into());
                         Self::generate_bindgen_use_stmt(&name)
                     }
                     Use::Custom(item) => *item.clone(),
@@ -459,9 +368,6 @@
         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);
@@ -576,9 +482,9 @@
                         fn #make_string_name(str_: &str) -> UniquePtr<CxxString>;
                     ))],
                     global_items: get_string_items(),
-                    materializations: vec![Use::UsedFromCxxBridgeWithAlias(make_ident(
-                        "make_string",
-                    ))],
+                    materializations: vec![Use::UsedFromCxxBridgeWithAlias(
+                        make_ident("make_string").into(),
+                    )],
                     ..Default::default()
                 }
             }
@@ -591,14 +497,14 @@
                 self.config,
             ),
             Api::Const { const_item, .. } => RsCodegenResult {
-                bindgen_mod_items: vec![Item::Const(const_item)],
+                bindgen_mod_items: vec![Item::Const(const_item.into())],
                 materializations: vec![Use::UsedFromBindgen],
                 ..Default::default()
             },
             Api::Typedef { analysis, .. } => RsCodegenResult {
                 bindgen_mod_items: vec![match analysis.kind {
-                    TypedefKind::Type(type_item) => Item::Type(type_item),
-                    TypedefKind::Use(use_item, _) => Item::Use(use_item),
+                    TypedefKind::Type(type_item) => Item::Type(type_item.into()),
+                    TypedefKind::Use(use_item, _) => Item::Use(use_item.into()),
                 }],
                 materializations: vec![Use::UsedFromBindgen],
                 ..Default::default()
@@ -624,7 +530,7 @@
                     kind,
                     constructors.move_constructor,
                     constructors.destructor,
-                    || Some((Item::Struct(details.item), doc_attrs)),
+                    || Some((Item::Struct(details.item.into()), doc_attrs)),
                     associated_methods,
                     layout,
                     is_generic,
@@ -638,15 +544,13 @@
                     TypeKind::Pod,
                     true,
                     true,
-                    || Some((Item::Enum(item), doc_attrs)),
+                    || Some((Item::Enum(item.into()), doc_attrs)),
                     associated_methods,
                     None,
                     false,
                 )
             }
-            Api::ForwardDeclaration { .. }
-            | Api::ConcreteType { .. }
-            | Api::OpaqueTypedef { .. } => self.generate_type(
+            Api::ConcreteType { .. } => self.generate_type(
                 &name,
                 id,
                 TypeKind::Abstract,
@@ -657,56 +561,68 @@
                 None,
                 false,
             ),
+            Api::ForwardDeclaration { .. } | Api::OpaqueTypedef { .. } => self.generate_type(
+                &name,
+                id,
+                TypeKind::Abstract,
+                false, // these types can't be kept in a Vector
+                false, // these types can't be put in a smart pointer
+                || None,
+                associated_methods,
+                None,
+                false,
+            ),
             Api::CType { .. } => RsCodegenResult {
                 extern_c_mod_items: vec![ForeignItem::Verbatim(quote! {
                     type #id = autocxx::#id;
                 })],
                 ..Default::default()
             },
-            Api::RustType { path, .. } => RsCodegenResult {
-                global_items: vec![parse_quote! {
-                    use super::#path;
-                }],
-                extern_rust_mod_items: vec![parse_quote! {
-                    type #id;
-                }],
-                ..Default::default()
-            },
+            Api::RustType { path, .. } => {
+                let id = path.get_final_ident();
+                RsCodegenResult {
+                    global_items: vec![parse_quote! {
+                        use super::#path;
+                    }],
+                    extern_rust_mod_items: vec![parse_quote! {
+                        type #id;
+                    }],
+                    bindgen_mod_items: vec![parse_quote! {
+                        #[allow(unused_imports)]
+                        use super::super::#id;
+                    }],
+                    ..Default::default()
+                }
+            }
             Api::RustFn {
                 details:
                     RustFun {
                         path,
-                        sig,
-                        receiver: None,
+                        mut sig,
+                        has_receiver,
                         ..
                     },
                 ..
-            } => RsCodegenResult {
-                global_items: vec![parse_quote! {
-                    use super::#path;
-                }],
-                extern_rust_mod_items: vec![parse_quote! {
-                    #sig;
-                }],
-                ..Default::default()
-            },
-            Api::RustFn {
-                details:
-                    RustFun {
-                        sig,
-                        receiver: Some(_),
-                        ..
+            } => {
+                sig.inputs = unqualify_params(sig.inputs);
+                sig.output = unqualify_ret_type(sig.output);
+                RsCodegenResult {
+                    global_items: if !has_receiver {
+                        vec![parse_quote! {
+                            use super::#path;
+                        }]
+                    } else {
+                        Vec::new()
                     },
-                ..
-            } => RsCodegenResult {
-                extern_rust_mod_items: vec![parse_quote! {
-                    #sig;
-                }],
-                ..Default::default()
-            },
+                    extern_rust_mod_items: vec![parse_quote! {
+                        #sig;
+                    }],
+                    ..Default::default()
+                }
+            }
             Api::RustSubclassFn {
                 details, subclass, ..
-            } => Self::generate_subclass_fn(id, *details, subclass),
+            } => Self::generate_subclass_fn(id.into(), *details, subclass),
             Api::Subclass {
                 name, superclass, ..
             } => {
@@ -812,7 +728,7 @@
             bindgen_mod_items.push(parse_quote! {
                 impl autocxx::subclass::CppPeerConstructor<#cpp_id> for super::super::super::#id {
                     fn make_peer(&mut self, peer_holder: autocxx::subclass::CppSubclassRustPeerHolder<Self>) -> cxx::UniquePtr<#cpp_path> {
-                        use autocxx::moveit::EmplaceUnpinned;
+                        use autocxx::moveit::Emplace;
                         cxx::UniquePtr::emplace(#cpp_id :: new(peer_holder))
                     }
                 }
@@ -820,15 +736,15 @@
         };
 
         // Once for each superclass, in future...
-        let as_id = make_ident(format!("As_{}", super_name));
+        let as_id = make_ident(format!("As_{super_name}"));
         extern_c_mod_items.push(parse_quote! {
             fn #as_id(self: &#cpp_id) -> &#super_cxxxbridge_id;
         });
-        let as_mut_id = make_ident(format!("As_{}_mut", super_name));
+        let as_mut_id = make_ident(format!("As_{super_name}_mut"));
         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));
+        let as_unique_ptr_id = make_ident(format!("{cpp_id}_As_{super_name}_UniquePtr"));
         extern_c_mod_items.push(parse_quote! {
             fn #as_unique_ptr_id(u: UniquePtr<#cpp_id>) -> UniquePtr<#super_cxxxbridge_id>;
         });
@@ -843,13 +759,13 @@
         // TODO it would be nice to impl AsMut here but pin prevents us
         bindgen_mod_items.push(parse_quote! {
             impl super::super::super::#id {
-                pub fn pin_mut(&mut self) -> ::std::pin::Pin<&mut cxxbridge::#super_cxxxbridge_id> {
+                pub fn pin_mut(&mut self) -> ::core::pin::Pin<&mut cxxbridge::#super_cxxxbridge_id> {
                     use autocxx::subclass::CppSubclass;
                     self.peer_mut().#as_mut_id()
                 }
             }
         });
-        let rs_as_unique_ptr_id = make_ident(format!("as_{}_unique_ptr", super_name));
+        let rs_as_unique_ptr_id = make_ident(format!("as_{super_name}_unique_ptr"));
         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> {
@@ -896,8 +812,8 @@
         let ret = details.ret;
         let unsafe_token = details.requires_unsafe.wrapper_token();
         let global_def = quote! { #unsafe_token fn #api_name(#params) #ret };
-        let params = unqualify_params(params);
-        let ret = unqualify_ret_type(ret);
+        let params = unqualify_params(minisynize_punctuated(params));
+        let ret = unqualify_ret_type(ret.into());
         let method_name = details.method_name;
         let cxxbridge_decl: ForeignItemFn =
             parse_quote! { #unsafe_token fn #api_name(#params) #ret; };
@@ -930,7 +846,7 @@
                         .as_ref()
                         .#borrow()
                         .expect(#reentrancy_panic_msg);
-                    let r = ::std::ops::#deref_ty::#deref_call(& #mut_token b);
+                    let r = ::core::ops::#deref_ty::#deref_call(& #mut_token b);
                     #methods_trait :: #method_name
                         (r,
                         #args)
@@ -955,7 +871,7 @@
     fn generate_type<F>(
         &self,
         name: &QualifiedName,
-        id: Ident,
+        id: crate::minisyn::Ident,
         type_kind: TypeKind,
         movable: bool,
         destroyable: bool,
@@ -1004,7 +920,7 @@
                         make_non_pod(s, layout);
                     } else {
                         // enum
-                        item = Item::Struct(new_non_pod_struct(id.clone()));
+                        item = Item::Struct(new_non_pod_struct(id.clone().into()));
                     }
                 }
                 bindgen_mod_items.push(item);
@@ -1066,8 +982,9 @@
                     let super_id =
                         SubclassName::get_super_fn_name(&Namespace::new(), &id.to_string())
                             .get_final_ident();
+                    let params = minisynize_punctuated(method.params.clone());
                     let param_names: Punctuated<Expr, Comma> =
-                        Self::args_from_sig(&method.params).collect();
+                        Self::args_from_sig(&params).collect();
                     let mut params = method.params.clone();
                     *(params.iter_mut().next().unwrap()) = match method.receiver_mutability {
                         ReceiverMutability::Const => parse_quote!(&self),
@@ -1111,7 +1028,7 @@
                         #(#mains)*
                     }
                 });
-                materializations.push(Use::SpecificNameFromBindgen(supers_name));
+                materializations.push(Use::SpecificNameFromBindgen(supers_name.into()));
             } else {
                 bindgen_mod_items.push(parse_quote! {
                     #[allow(non_snake_case)]
@@ -1120,7 +1037,7 @@
                     }
                 });
             }
-            materializations.push(Use::SpecificNameFromBindgen(methods_name));
+            materializations.push(Use::SpecificNameFromBindgen(methods_name.into()));
         }
     }
 
@@ -1150,8 +1067,8 @@
     /// Generates something in the output mod that will carry a docstring
     /// explaining why a given type or function couldn't have bindings
     /// generated.
-    fn generate_error_entry(err: ConvertError, ctx: ErrorContext) -> RsCodegenResult {
-        let err = format!("autocxx bindings couldn't be generated: {}", err);
+    fn generate_error_entry(err: ConvertErrorFromCpp, ctx: ErrorContext) -> RsCodegenResult {
+        let err = format!("autocxx bindings couldn't be generated: {err}");
         let (impl_entry, bindgen_mod_item, materialization) = match ctx.into_type() {
             ErrorContextType::Item(id) => (
                 // Populate within bindgen mod because impl blocks may attach.
@@ -1160,7 +1077,7 @@
                     #[doc = #err]
                     pub struct #id;
                 }),
-                Some(Use::SpecificNameFromBindgen(id)),
+                Some(Use::SpecificNameFromBindgen(id.into())),
             ),
             ErrorContextType::SanitizedItem(id) => (
                 // Guaranteed to be no impl blocks - populate directly in output mod.
@@ -1218,7 +1135,7 @@
     }
 
     fn generate_extern_type_impl(&self, type_kind: TypeKind, tyname: &QualifiedName) -> Vec<Item> {
-        let tynamestring = namespaced_name_using_original_name_map(tyname, &self.original_name_map);
+        let tynamestring = self.original_name_map.map(tyname);
         let fulltypath = tyname.get_bindgen_path_idents();
         let kind_item = match type_kind {
             TypeKind::Pod => "Trivial",
@@ -1292,7 +1209,7 @@
         ForeignItem::Verbatim(for_extern_c_ts)
     }
 
-    fn find_output_mod_root(ns: &Namespace) -> impl Iterator<Item = Ident> {
+    fn find_output_mod_root(ns: &Namespace) -> impl Iterator<Item = crate::minisyn::Ident> {
         std::iter::repeat(make_ident("super")).take(ns.depth())
     }
 }
diff --git a/third_party/autocxx/engine/src/conversion/codegen_rs/non_pod_struct.rs b/third_party/autocxx/engine/src/conversion/codegen_rs/non_pod_struct.rs
index e4bec2f..d9a75c8 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_rs/non_pod_struct.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_rs/non_pod_struct.rs
@@ -55,7 +55,7 @@
     let doc_attr = s
         .attrs
         .iter()
-        .filter(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
+        .filter(|a| a.path().get_ident().iter().any(|p| *p == "doc"))
         .cloned();
     let repr_attr = if let Some(layout) = &layout {
         let align = make_lit_int(layout.align);
@@ -85,9 +85,9 @@
         .filter_map(|(counter, gp)| match gp {
             GenericParam::Type(gpt) => {
                 let id = &gpt.ident;
-                let field_name = make_ident(&format!("_phantom_{}", counter));
+                let field_name = make_ident(format!("_phantom_{counter}"));
                 let toks = quote! {
-                    #field_name: ::std::marker::PhantomData<::std::cell::UnsafeCell< #id >>
+                    #field_name: ::core::marker::PhantomData<::core::cell::UnsafeCell< #id >>
                 };
                 Some(Field::parse_named.parse2(toks).unwrap())
             }
diff --git a/third_party/autocxx/engine/src/conversion/conversion_tests.rs b/third_party/autocxx/engine/src/conversion/conversion_tests.rs
index b0474d6..e74888e 100644
--- a/third_party/autocxx/engine/src/conversion/conversion_tests.rs
+++ b/third_party/autocxx/engine/src/conversion/conversion_tests.rs
@@ -11,7 +11,7 @@
 use syn::parse_quote;
 use syn::ItemMod;
 
-use crate::CppCodegenOptions;
+use crate::CodegenOptions;
 
 use super::BridgeConverter;
 
@@ -33,7 +33,8 @@
         input,
         UnsafePolicy::AllFunctionsSafe,
         inclusions,
-        &CppCodegenOptions::default(),
+        &CodegenOptions::default(),
+        "",
     )
     .unwrap();
 }
diff --git a/third_party/autocxx/engine/src/conversion/convert_error.rs b/third_party/autocxx/engine/src/conversion/convert_error.rs
index ba8344d..855b235 100644
--- a/third_party/autocxx/engine/src/conversion/convert_error.rs
+++ b/third_party/autocxx/engine/src/conversion/convert_error.rs
@@ -8,19 +8,35 @@
 
 use indexmap::set::IndexSet as HashSet;
 
+use crate::minisyn::Ident;
 use itertools::Itertools;
-use syn::Ident;
+use miette::{Diagnostic, SourceSpan};
+use proc_macro2::Span;
 use thiserror::Error;
 
 use crate::{
-    known_types,
-    types::{make_ident, Namespace, QualifiedName},
+    known_types, proc_macro_span_to_miette_span,
+    types::{make_ident, InvalidIdentError, Namespace, QualifiedName},
 };
 
-#[derive(Debug, Clone, Error)]
+/// Errors which can occur during conversion
+#[derive(Debug, Clone, Error, Diagnostic)]
 pub enum ConvertError {
     #[error("The initial run of 'bindgen' did not generate any content. This might be because none of the requested items for generation could be converted.")]
     NoContent,
+    #[error(transparent)]
+    Cpp(ConvertErrorFromCpp),
+    #[error(transparent)]
+    #[diagnostic(transparent)]
+    Rust(LocatedConvertErrorFromRust),
+}
+
+/// Errors that can occur during conversion which are detected from some C++
+/// source code. Currently, we do not gain span information from bindgen
+/// so these errors are presented without useful source code snippets.
+/// We hope to change this in future.
+#[derive(Debug, Clone, Error)]
+pub enum ConvertErrorFromCpp {
     #[error("An item was requested using 'generate_pod' which was not safe to hold by value in Rust. {0}")]
     UnsafePodType(String),
     #[error("Bindgen generated some unexpected code in a foreign mod section. You may have specified something in a 'generate' directive which is not currently compatible with autocxx.")]
@@ -39,8 +55,14 @@
     ConflictingTemplatedArgsWithTypedef(QualifiedName),
     #[error("Function {0} has a parameter or return type which is either on the blocklist or a forward declaration")]
     UnacceptableParam(String),
-    #[error("Function {0} has a return reference parameter, but 0 or >1 input reference parameters, so the lifetime of the output reference cannot be deduced.")]
-    NotOneInputReference(String),
+    #[error("Function {0} has a reference return value, but no reference parameters, so the lifetime of the output reference cannot be deduced.")]
+    NoInputReference(String),
+    #[error("Function {0} has a reference return value, but >1 input reference parameters, so the lifetime of the output reference cannot be deduced.")]
+    MultipleInputReferences(String),
+    #[error("Function {0} has a mutable reference return value, but no mutable reference parameters, so the lifetime of the output reference cannot be deduced.")]
+    NoMutableInputReference(String),
+    #[error("Function {0} has a mutable reference return value, but >1 input mutable reference parameters, so the lifetime of the output reference cannot be deduced.")]
+    MultipleMutableInputReferences(String),
     #[error("Encountered type not yet supported by autocxx: {0}")]
     UnsupportedType(String),
     #[error("Encountered type not yet known by autocxx: {0}")]
@@ -53,8 +75,12 @@
     UnexpectedUseStatement(Option<String>),
     #[error("Type {} was parameterized over something complex which we don't yet support", .0.to_cpp_name())]
     TemplatedTypeContainingNonPathArg(QualifiedName),
-    #[error("Pointer pointed to something unsupported")]
-    InvalidPointee,
+    #[error("Pointer pointed to an array, which is not yet supported")]
+    InvalidArrayPointee,
+    #[error("Pointer pointed to another pointer, which is not yet supported")]
+    InvalidPointerPointee,
+    #[error("Pointer pointed to something unsupported (autocxx only supports pointers to named types): {0}")]
+    InvalidPointee(String),
     #[error("The 'generate' or 'generate_pod' directive for '{0}' did not result in any code being generated. Perhaps this was mis-spelled or you didn't qualify the name with any namespaces? Otherwise please report a bug.")]
     DidNotGenerateAnything(String),
     #[error("Found an attempt at using a forward declaration ({}) inside a templated cxx type such as UniquePtr or CxxVector. If the forward declaration is a typedef, perhaps autocxx wasn't sure whether or not it involved a forward declaration. If you're sure it didn't, then you may be able to solve this by using instantiable!.", .0.to_cpp_name())]
@@ -63,14 +89,12 @@
     Blocked(QualifiedName),
     #[error("This function or method uses a type where one of the template parameters was incomprehensible to bindgen/autocxx - probably because it uses template specialization.")]
     UnusedTemplateParam,
-    #[error("Names containing __ are reserved by C++ so not acceptable to cxx")]
-    TooManyUnderscores,
     #[error("This item relies on a type not known to autocxx ({})", .0.to_cpp_name())]
     UnknownDependentType(QualifiedName),
     #[error("This item depends on some other type(s) which autocxx could not generate, some of them are: {}", .0.iter().join(", "))]
     IgnoredDependent(HashSet<QualifiedName>),
-    #[error("The item name '{0}' is a reserved word in Rust.")]
-    ReservedName(String),
+    #[error(transparent)]
+    InvalidIdent(InvalidIdentError),
     #[error("This item name is used in multiple namespaces. At present, autocxx and cxx allow only one type of a given name. This limitation will be fixed in future. (Items found with this name: {})", .0.iter().join(", "))]
     DuplicateCxxBridgeName(Vec<String>),
     #[error("This is a method on a type which can't be used as the receiver in Rust (i.e. self/this). This is probably because some type involves template specialization.")]
@@ -123,29 +147,71 @@
     MethodInAnonymousNamespace,
     #[error("We're unable to make a concrete version of this template, because we found an error handling the template.")]
     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,
+    #[error("Problem handling function argument {arg}: {err}")]
+    Argument {
+        arg: String,
+        #[source]
+        err: Box<ConvertErrorFromCpp>,
+    },
+}
+
+/// Error types derived from Rust code. This is separate from [`ConvertError`] because these
+/// may have spans attached for better diagnostics.
+#[derive(Debug, Clone, Error)]
+pub enum ConvertErrorFromRust {
+    #[error("extern_rust_function only supports limited parameter and return types. This is not such a supported type")]
+    UnsupportedTypeForExternFun,
+    #[error("extern_rust_function requires a fully qualified receiver, that is: fn a(self: &SomeType) as opposed to fn a(&self)")]
+    ExternRustFunRequiresFullyQualifiedReceiver,
+    #[error("extern_rust_function cannot support &mut T references; instead use Pin<&mut T> (see cxx documentation for more details")]
+    PinnedReferencesRequiredForExternFun,
+    #[error("extern_rust_function cannot currently support qualified type paths (that is, foo::bar::Baz). All type paths must be within the current module, imported using 'use'. This restriction may be lifted in future.")]
+    NamespacesNotSupportedForExternFun,
+    #[error("extern_rust_function signatures must never reference Self: instead, spell out the type explicitly.")]
+    ExplicitSelf,
+}
+
+/// A [`ConvertErrorFromRust`] which also implements [`miette::Diagnostic`] so can be pretty-printed
+/// to show the affected span of code.
+#[derive(Error, Debug, Diagnostic, Clone)]
+#[error("{err}")]
+pub struct LocatedConvertErrorFromRust {
+    err: ConvertErrorFromRust,
+    #[source_code]
+    file: String,
+    #[label("error here")]
+    span: SourceSpan,
+}
+
+impl LocatedConvertErrorFromRust {
+    pub(crate) fn new(err: ConvertErrorFromRust, span: &Span, file: &str) -> Self {
+        Self {
+            err,
+            span: proc_macro_span_to_miette_span(span),
+            file: file.to_string(),
+        }
+    }
 }
 
 /// Ensures that error contexts are always created using the constructors in this
 /// mod, therefore undergoing identifier sanitation.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 struct PhantomSanitized;
 
 /// The context of an error, e.g. whether it applies to a function or a method.
 /// This is used to generate suitable rustdoc in the output codegen so that
 /// the errors can be revealed in rust-analyzer-based IDEs, etc.
-#[derive(Clone)]
-pub(crate) struct ErrorContext(ErrorContextType, PhantomSanitized);
+#[derive(Clone, Debug)]
+pub(crate) struct ErrorContext(Box<ErrorContextType>, PhantomSanitized);
 
 /// All idents in this structure are guaranteed to be something we can safely codegen for.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum ErrorContextType {
     Item(Ident),
     SanitizedItem(Ident),
@@ -155,8 +221,11 @@
 impl ErrorContext {
     pub(crate) fn new_for_item(id: Ident) -> Self {
         match Self::sanitize_error_ident(&id) {
-            None => Self(ErrorContextType::Item(id), PhantomSanitized),
-            Some(sanitized) => Self(ErrorContextType::SanitizedItem(sanitized), PhantomSanitized),
+            None => Self(Box::new(ErrorContextType::Item(id)), PhantomSanitized),
+            Some(sanitized) => Self(
+                Box::new(ErrorContextType::SanitizedItem(sanitized)),
+                PhantomSanitized,
+            ),
         }
     }
 
@@ -166,14 +235,16 @@
         // an impl block.
         match Self::sanitize_error_ident(&self_ty) {
             None => Self(
-                ErrorContextType::Method {
+                Box::new(ErrorContextType::Method {
                     self_ty,
                     method: Self::sanitize_error_ident(&method).unwrap_or(method),
-                },
+                }),
                 PhantomSanitized,
             ),
             Some(_) => Self(
-                ErrorContextType::SanitizedItem(make_ident(format!("{}_{}", self_ty, method))),
+                Box::new(ErrorContextType::SanitizedItem(make_ident(format!(
+                    "{self_ty}_{method}"
+                )))),
                 PhantomSanitized,
             ),
         }
@@ -195,21 +266,24 @@
     }
 
     pub(crate) fn into_type(self) -> ErrorContextType {
-        self.0
+        *self.0
     }
 }
 
 impl std::fmt::Display for ErrorContext {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match &self.0 {
-            ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{}", id),
-            ErrorContextType::Method { self_ty, method } => write!(f, "{}::{}", self_ty, method),
+        match &*self.0 {
+            ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{id}"),
+            ErrorContextType::Method { self_ty, method } => write!(f, "{self_ty}::{method}"),
         }
     }
 }
 
 #[derive(Clone)]
-pub(crate) struct ConvertErrorWithContext(pub(crate) ConvertError, pub(crate) Option<ErrorContext>);
+pub(crate) struct ConvertErrorWithContext(
+    pub(crate) ConvertErrorFromCpp,
+    pub(crate) Option<ErrorContext>,
+);
 
 impl std::fmt::Debug for ConvertErrorWithContext {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
diff --git a/third_party/autocxx/engine/src/conversion/doc_attr.rs b/third_party/autocxx/engine/src/conversion/doc_attr.rs
index 8fe9d0b..7a44f9b 100644
--- a/third_party/autocxx/engine/src/conversion/doc_attr.rs
+++ b/third_party/autocxx/engine/src/conversion/doc_attr.rs
@@ -12,7 +12,7 @@
 pub(super) fn get_doc_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
     attrs
         .iter()
-        .filter(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
+        .filter(|a| a.path().get_ident().iter().any(|p| *p == "doc"))
         .cloned()
         .collect()
 }
diff --git a/third_party/autocxx/engine/src/conversion/error_reporter.rs b/third_party/autocxx/engine/src/conversion/error_reporter.rs
index 7942a32..adea153 100644
--- a/third_party/autocxx/engine/src/conversion/error_reporter.rs
+++ b/third_party/autocxx/engine/src/conversion/error_reporter.rs
@@ -6,13 +6,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syn::ItemEnum;
+use crate::minisyn::ItemEnum;
 
 use super::{
     api::{AnalysisPhase, Api, ApiName, FuncToConvert, StructDetails, TypedefKind},
     apivec::ApiVec,
     convert_error::{ConvertErrorWithContext, ErrorContext},
-    ConvertError,
+    ConvertErrorFromCpp,
 };
 use crate::{
     conversion::convert_error::ErrorContextType,
@@ -33,7 +33,7 @@
     match fun() {
         Ok(result) => Some(result),
         Err(ConvertErrorWithContext(err, None)) => {
-            eprintln!("Ignored item: {}", err);
+            eprintln!("Ignored item: {err}");
             None
         }
         Err(ConvertErrorWithContext(err, Some(ctx))) => {
@@ -127,11 +127,11 @@
             Api::RustFn {
                 name,
                 details,
-                receiver,
+                deps,
             } => Ok(Box::new(std::iter::once(Api::RustFn {
                 name,
                 details,
-                receiver,
+                deps,
             }))),
             Api::RustSubclassFn {
                 name,
@@ -210,7 +210,7 @@
     out_apis: &mut ApiVec<B>,
     mut fun: F,
 ) where
-    F: FnMut(Api<A>) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertError>,
+    F: FnMut(Api<A>) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertErrorFromCpp>,
     A: AnalysisPhase,
     B: AnalysisPhase,
 {
diff --git a/third_party/autocxx/engine/src/conversion/mod.rs b/third_party/autocxx/engine/src/conversion/mod.rs
index aa639a2..61d7d6d 100644
--- a/third_party/autocxx/engine/src/conversion/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/mod.rs
@@ -17,18 +17,18 @@
 mod doc_attr;
 mod error_reporter;
 mod parse;
+mod type_helpers;
 mod utilities;
 
 use analysis::fun::FnAnalyzer;
 use autocxx_parser::IncludeCppConfig;
 pub(crate) use codegen_cpp::CppCodeGenerator;
 pub(crate) use convert_error::ConvertError;
+use convert_error::ConvertErrorFromCpp;
 use itertools::Itertools;
 use syn::{Item, ItemMod};
 
-use crate::{
-    conversion::analysis::deps::HasDependencies, CppCodegenOptions, CppFilePair, UnsafePolicy,
-};
+use crate::{CodegenOptions, CppFilePair, UnsafePolicy};
 
 use self::{
     analysis::{
@@ -37,7 +37,6 @@
         casts::add_casts,
         check_names,
         constructor_deps::decorate_types_with_constructor_deps,
-        fun::FnPhase,
         gc::filter_apis_by_following_edges_from_allowlist,
         pod::analyze_pod_apis,
         remove_ignored::filter_apis_by_ignored_dependents,
@@ -87,23 +86,10 @@
     fn dump_apis<T: AnalysisPhase>(label: &str, apis: &ApiVec<T>) {
         if LOG_APIS {
             log::info!(
-                "APIs after {}:\n{}",
+                "##### APIs after {}:\n{}",
                 label,
                 apis.iter()
-                    .map(|api| { format!("  {:?}", api) })
-                    .sorted()
-                    .join("\n")
-            )
-        }
-    }
-
-    fn dump_apis_with_deps(label: &str, apis: &ApiVec<FnPhase>) {
-        if LOG_APIS {
-            log::info!(
-                "APIs after {}:\n{}",
-                label,
-                apis.iter()
-                    .map(|api| { format!("  {:?}, deps={}", api, api.format_deps()) })
+                    .map(|api| { format!("  {api:?}") })
                     .sorted()
                     .join("\n")
             )
@@ -121,7 +107,8 @@
         mut bindgen_mod: ItemMod,
         unsafe_policy: UnsafePolicy,
         inclusions: String,
-        cpp_codegen_options: &CppCodegenOptions,
+        codegen_options: &CodegenOptions,
+        source_file_contents: &str,
     ) -> Result<CodegenResults, ConvertError> {
         match &mut bindgen_mod.content {
             None => Err(ConvertError::NoContent),
@@ -129,7 +116,7 @@
                 // Parse the bindgen mod.
                 let items_to_process = items.drain(..).collect();
                 let parser = ParseBindgen::new(self.config);
-                let apis = parser.parse_items(items_to_process)?;
+                let apis = parser.parse_items(items_to_process, source_file_contents)?;
                 Self::dump_apis("parsing", &apis);
                 // Inside parse_results, we now have a list of APIs.
                 // We now enter various analysis phases.
@@ -143,9 +130,9 @@
                 // Specifically, let's confirm that the items requested by the user to be
                 // POD really are POD, and duly mark any dependent types.
                 // This returns a new list of `Api`s, which will be parameterized with
-                // the analysis results. It also returns an object which can be used
-                // by subsequent phases to work out which objects are POD.
-                let analyzed_apis = analyze_pod_apis(apis, self.config)?;
+                // the analysis results.
+                let analyzed_apis =
+                    analyze_pod_apis(apis, self.config).map_err(ConvertError::Cpp)?;
                 Self::dump_apis("pod analysis", &analyzed_apis);
                 let analyzed_apis = replace_hopeless_typedef_targets(self.config, analyzed_apis);
                 let analyzed_apis = add_casts(analyzed_apis);
@@ -156,8 +143,12 @@
                 // part of `autocxx`. Again, this returns a new set of `Api`s, but
                 // 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);
+                let analyzed_apis = FnAnalyzer::analyze_functions(
+                    analyzed_apis,
+                    &unsafe_policy,
+                    self.config,
+                    codegen_options.force_wrapper_gen,
+                );
                 // 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.
@@ -167,9 +158,9 @@
                 // Annotate structs with a note of any copy/move constructors which
                 // we may want to retain to avoid garbage collecting them later.
                 let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
-                Self::dump_apis_with_deps("adding constructor deps", &analyzed_apis);
+                Self::dump_apis("adding constructor deps", &analyzed_apis);
                 let analyzed_apis = discard_ignored_functions(analyzed_apis);
-                Self::dump_apis_with_deps("ignoring ignorable fns", &analyzed_apis);
+                Self::dump_apis("ignoring ignorable fns", &analyzed_apis);
                 // Remove any APIs whose names are not compatible with cxx.
                 let analyzed_apis = check_names(analyzed_apis);
                 // During parsing or subsequent processing we might have encountered
@@ -177,24 +168,28 @@
                 // There might be other items depending on such things. Let's remove them
                 // too.
                 let analyzed_apis = filter_apis_by_ignored_dependents(analyzed_apis);
-                Self::dump_apis_with_deps("removing ignored dependents", &analyzed_apis);
+                Self::dump_apis("removing ignored dependents", &analyzed_apis);
 
                 // We now garbage collect the ones we don't need...
                 let mut analyzed_apis =
                     filter_apis_by_following_edges_from_allowlist(analyzed_apis, self.config);
                 // Determine what variably-sized C types (e.g. int) we need to include
                 analysis::ctypes::append_ctype_information(&mut analyzed_apis);
-                Self::dump_apis_with_deps("GC", &analyzed_apis);
+                Self::dump_apis("GC", &analyzed_apis);
                 // And finally pass them to the code gen phases, which outputs
                 // code suitable for cxx to consume.
-                let cxxgen_header_name = cpp_codegen_options.cxxgen_header_namer.name_header();
+                let cxxgen_header_name = codegen_options
+                    .cpp_codegen_options
+                    .cxxgen_header_namer
+                    .name_header();
                 let cpp = CppCodeGenerator::generate_cpp_code(
                     inclusions,
                     &analyzed_apis,
                     self.config,
-                    cpp_codegen_options,
+                    &codegen_options.cpp_codegen_options,
                     &cxxgen_header_name,
-                )?;
+                )
+                .map_err(ConvertError::Cpp)?;
                 let rs = RsCodeGenerator::generate_rs_code(
                     analyzed_apis,
                     &unsafe_policy,
diff --git a/third_party/autocxx/engine/src/conversion/parse/bindgen_semantic_attributes.rs b/third_party/autocxx/engine/src/conversion/parse/bindgen_semantic_attributes.rs
index a8de9ce..f2dd7d7 100644
--- a/third_party/autocxx/engine/src/conversion/parse/bindgen_semantic_attributes.rs
+++ b/third_party/autocxx/engine/src/conversion/parse/bindgen_semantic_attributes.rs
@@ -14,9 +14,9 @@
 };
 
 use crate::conversion::{
-    api::{CppVisibility, Layout, References, SpecialMemberKind, Virtualness},
+    api::{CppVisibility, DeletedOrDefaulted, Layout, References, SpecialMemberKind, Virtualness},
     convert_error::{ConvertErrorWithContext, ErrorContext},
-    ConvertError,
+    ConvertErrorFromCpp,
 };
 
 /// The set of all annotations that autocxx_bindgen has added
@@ -31,7 +31,7 @@
     // item can't be processed.
     pub(crate) fn new_retaining_others(attrs: &mut Vec<Attribute>) -> Self {
         let metadata = Self::new(attrs);
-        attrs.retain(|a| a.path.segments.last().unwrap().ident != "cpp_semantics");
+        attrs.retain(|a| a.path().segments.last().unwrap().ident != "cpp_semantics");
         metadata
     }
 
@@ -40,7 +40,7 @@
             attrs
                 .iter()
                 .filter_map(|attr| {
-                    if attr.path.segments.last().unwrap().ident == "cpp_semantics" {
+                    if attr.path().segments.last().unwrap().ident == "cpp_semantics" {
                         let r: Result<BindgenSemanticAttribute, syn::Error> = attr.parse_args();
                         r.ok()
                     } else {
@@ -58,13 +58,13 @@
     ) -> Result<(), ConvertErrorWithContext> {
         if self.has_attr("unused_template_param") {
             Err(ConvertErrorWithContext(
-                ConvertError::UnusedTemplateParam,
-                Some(ErrorContext::new_for_item(id_for_context.clone())),
+                ConvertErrorFromCpp::UnusedTemplateParam,
+                Some(ErrorContext::new_for_item(id_for_context.clone().into())),
             ))
         } else if self.get_cpp_visibility() != CppVisibility::Public {
             Err(ConvertErrorWithContext(
-                ConvertError::NonPublicNestedType,
-                Some(ErrorContext::new_for_item(id_for_context.clone())),
+                ConvertErrorFromCpp::NonPublicNestedType,
+                Some(ErrorContext::new_for_item(id_for_context.clone().into())),
             ))
         } else {
             Ok(())
@@ -98,6 +98,16 @@
         }
     }
 
+    pub(super) fn get_deleted_or_defaulted(&self) -> DeletedOrDefaulted {
+        if self.has_attr("deleted") {
+            DeletedOrDefaulted::Deleted
+        } else if self.has_attr("defaulted") {
+            DeletedOrDefaulted::Defaulted
+        } else {
+            DeletedOrDefaulted::Neither
+        }
+    }
+
     fn parse_if_present<T: Parse>(&self, annotation: &str) -> Option<T> {
         self.0
             .iter()
@@ -144,12 +154,12 @@
             } else if a.is_ident("arg_type_reference") {
                 let r: Result<Ident, syn::Error> = a.parse_args();
                 if let Ok(ls) = r {
-                    results.ref_params.insert(ls);
+                    results.ref_params.insert(ls.into());
                 }
             } else if a.is_ident("arg_type_rvalue_reference") {
                 let r: Result<Ident, syn::Error> = a.parse_args();
                 if let Ok(ls) = r {
-                    results.rvalue_ref_params.insert(ls);
+                    results.rvalue_ref_params.insert(ls.into());
                 }
             }
         }
diff --git a/third_party/autocxx/engine/src/conversion/parse/extern_fun_signatures.rs b/third_party/autocxx/engine/src/conversion/parse/extern_fun_signatures.rs
new file mode 100644
index 0000000..cb002b5
--- /dev/null
+++ b/third_party/autocxx/engine/src/conversion/parse/extern_fun_signatures.rs
@@ -0,0 +1,253 @@
+// 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.
+
+use indexmap::IndexSet as HashSet;
+
+use syn::{
+    spanned::Spanned, AngleBracketedGenericArguments, GenericArgument, PatType, PathArguments,
+    PathSegment, ReturnType, Signature, Type, TypePath, TypeReference,
+};
+
+use crate::{
+    conversion::convert_error::{ConvertErrorFromRust, LocatedConvertErrorFromRust},
+    types::QualifiedName,
+};
+
+pub(super) fn assemble_extern_fun_deps(
+    sig: &Signature,
+    file: &str,
+) -> Result<Vec<QualifiedName>, LocatedConvertErrorFromRust> {
+    let mut deps = HashSet::new();
+    // It's possible that this will need to be implemented using TypeConverter
+    // and the encountered_types field on its annotated results.
+    // But the design of that code is intended to convert from C++ types
+    // (via bindgen) to cxx types, and instead here we're starting with pure
+    // Rust types as written by a Rustacean human. It may therefore not
+    // be quite right to go via TypeConverter.
+    // Also, by doing it ourselves here, we're in a better place to emit
+    // meaningful errors about types which can't be supported within
+    // extern_rust_fun.
+    if let ReturnType::Type(_, ty) = &sig.output {
+        add_type_to_deps(ty, &mut deps, file)?;
+    }
+    for input in &sig.inputs {
+        match input {
+            syn::FnArg::Receiver(_) => {
+                return Err(LocatedConvertErrorFromRust::new(
+                    ConvertErrorFromRust::ExternRustFunRequiresFullyQualifiedReceiver,
+                    &input.span(),
+                    file,
+                ))
+            }
+            syn::FnArg::Typed(PatType { ty, .. }) => add_type_to_deps(ty, &mut deps, file)?,
+        }
+    }
+    Ok(deps.into_iter().collect())
+}
+
+/// For all types within an extern_rust_function signature, add them to the deps
+/// hash, or raise an appropriate error.
+fn add_type_to_deps(
+    ty: &Type,
+    deps: &mut HashSet<QualifiedName>,
+    file: &str,
+) -> Result<(), LocatedConvertErrorFromRust> {
+    match ty {
+        Type::Reference(TypeReference {
+            mutability: Some(_),
+            ..
+        }) => {
+            return Err(LocatedConvertErrorFromRust::new(
+                ConvertErrorFromRust::PinnedReferencesRequiredForExternFun,
+                &ty.span(),
+                file,
+            ))
+        }
+        Type::Reference(TypeReference { elem, .. }) => match &**elem {
+            Type::Path(tp) => add_path_to_deps(tp, deps, file)?,
+            _ => {
+                return Err(LocatedConvertErrorFromRust::new(
+                    ConvertErrorFromRust::UnsupportedTypeForExternFun,
+                    &ty.span(),
+                    file,
+                ))
+            }
+        },
+        Type::Path(tp) => {
+            if tp.path.segments.len() != 1 {
+                return Err(LocatedConvertErrorFromRust::new(
+                    ConvertErrorFromRust::NamespacesNotSupportedForExternFun,
+                    &tp.span(),
+                    file,
+                ));
+            }
+            if let Some(PathSegment {
+                ident,
+                arguments:
+                    PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }),
+            }) = tp.path.segments.last()
+            {
+                if ident == "Pin" {
+                    if args.len() != 1 {
+                        return Err(LocatedConvertErrorFromRust::new(
+                            ConvertErrorFromRust::UnsupportedTypeForExternFun,
+                            &tp.span(),
+                            file,
+                        ));
+                    }
+
+                    if let Some(GenericArgument::Type(Type::Reference(TypeReference {
+                        mutability: Some(_),
+                        elem,
+                        ..
+                    }))) = args.first()
+                    {
+                        if let Type::Path(tp) = &**elem {
+                            add_path_to_deps(tp, deps, file)?
+                        } else {
+                            return Err(LocatedConvertErrorFromRust::new(
+                                ConvertErrorFromRust::UnsupportedTypeForExternFun,
+                                &elem.span(),
+                                file,
+                            ));
+                        }
+                    } else {
+                        return Err(LocatedConvertErrorFromRust::new(
+                            ConvertErrorFromRust::UnsupportedTypeForExternFun,
+                            &ty.span(),
+                            file,
+                        ));
+                    }
+                } else if ident == "Box" || ident == "Vec" {
+                    if args.len() != 1 {
+                        return Err(LocatedConvertErrorFromRust::new(
+                            ConvertErrorFromRust::UnsupportedTypeForExternFun,
+                            &tp.span(),
+                            file,
+                        ));
+                    }
+                    if let Some(GenericArgument::Type(Type::Path(tp))) = args.first() {
+                        add_path_to_deps(tp, deps, file)?
+                    } else {
+                        return Err(LocatedConvertErrorFromRust::new(
+                            ConvertErrorFromRust::UnsupportedTypeForExternFun,
+                            &ty.span(),
+                            file,
+                        ));
+                    }
+                } else {
+                    return Err(LocatedConvertErrorFromRust::new(
+                        ConvertErrorFromRust::UnsupportedTypeForExternFun,
+                        &ident.span(),
+                        file,
+                    ));
+                }
+            } else {
+                add_path_to_deps(tp, deps, file)?
+            }
+        }
+        _ => {
+            return Err(LocatedConvertErrorFromRust::new(
+                ConvertErrorFromRust::UnsupportedTypeForExternFun,
+                &ty.span(),
+                file,
+            ))
+        }
+    };
+    Ok(())
+}
+
+fn add_path_to_deps(
+    type_path: &TypePath,
+    deps: &mut HashSet<QualifiedName>,
+    file: &str,
+) -> Result<(), LocatedConvertErrorFromRust> {
+    if let Some(PathSegment {
+        arguments: PathArguments::AngleBracketed(..) | PathArguments::Parenthesized(..),
+        ..
+    }) = type_path.path.segments.last()
+    {
+        return Err(LocatedConvertErrorFromRust::new(
+            ConvertErrorFromRust::UnsupportedTypeForExternFun,
+            &type_path.span(),
+            file,
+        ));
+    }
+    let qn = QualifiedName::from_type_path(type_path);
+    if !qn.get_namespace().is_empty() {
+        return Err(LocatedConvertErrorFromRust::new(
+            ConvertErrorFromRust::NamespacesNotSupportedForExternFun,
+            &type_path.span(),
+            file,
+        ));
+    }
+    if qn.get_final_item() == "Self" {
+        return Err(LocatedConvertErrorFromRust::new(
+            ConvertErrorFromRust::ExplicitSelf,
+            &type_path.span(),
+            file,
+        ));
+    }
+    deps.insert(qn);
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use syn::parse_quote;
+
+    use super::*;
+
+    fn run_test_expect_ok(sig: Signature, expected_deps: &[&str]) {
+        let expected_as_set: HashSet<QualifiedName> = expected_deps
+            .iter()
+            .cloned()
+            .map(QualifiedName::new_from_cpp_name)
+            .collect();
+        let result = assemble_extern_fun_deps(&sig, "").unwrap();
+        let actual_as_set: HashSet<QualifiedName> = result.into_iter().collect();
+        assert_eq!(expected_as_set, actual_as_set);
+    }
+
+    fn run_test_expect_fail(sig: Signature) {
+        assert!(assemble_extern_fun_deps(&sig, "").is_err())
+    }
+
+    #[test]
+    fn test_assemble_extern_fun_deps() {
+        run_test_expect_fail(parse_quote! { fn function(self: A::B)});
+        run_test_expect_fail(parse_quote! { fn function(self: Self)});
+        run_test_expect_fail(parse_quote! { fn function(self: Self)});
+        run_test_expect_fail(parse_quote! { fn function(self)});
+        run_test_expect_fail(parse_quote! { fn function(&self)});
+        run_test_expect_fail(parse_quote! { fn function(&mut self)});
+        run_test_expect_fail(parse_quote! { fn function(self: Pin<&mut Self>)});
+        run_test_expect_fail(parse_quote! { fn function(self: Pin<&mut A::B>)});
+        run_test_expect_fail(parse_quote! { fn function(a: Pin<A>)});
+        run_test_expect_fail(parse_quote! { fn function(a: Pin<A::B>)});
+        run_test_expect_fail(parse_quote! { fn function(a: A::B)});
+        run_test_expect_fail(parse_quote! { fn function(a: &mut A)});
+        run_test_expect_fail(parse_quote! { fn function() -> A::B});
+        run_test_expect_fail(parse_quote! { fn function() -> &A::B});
+        run_test_expect_fail(parse_quote! { fn function(a: ())});
+        run_test_expect_fail(parse_quote! { fn function(a: &[A])});
+        run_test_expect_fail(parse_quote! { fn function(a: Bob<A>)});
+        run_test_expect_fail(parse_quote! { fn function(a: Box<A, B>)});
+        run_test_expect_fail(parse_quote! { fn function(a: a::Pin<&mut A>)});
+        run_test_expect_fail(parse_quote! { fn function(a: Pin<&A>)});
+        run_test_expect_ok(parse_quote! { fn function(a: A, b: B)}, &["A", "B"]);
+        run_test_expect_ok(parse_quote! { fn function(a: Box<A>)}, &["A"]);
+        run_test_expect_ok(parse_quote! { fn function(a: Vec<A>)}, &["A"]);
+        run_test_expect_ok(parse_quote! { fn function(a: &A)}, &["A"]);
+        run_test_expect_ok(parse_quote! { fn function(a: Pin<&mut A>)}, &["A"]);
+        run_test_expect_ok(
+            parse_quote! { fn function(a: A, b: B) -> Box<C>},
+            &["A", "B", "C"],
+        );
+    }
+}
diff --git a/third_party/autocxx/engine/src/conversion/parse/mod.rs b/third_party/autocxx/engine/src/conversion/parse/mod.rs
index 3f42ce4..b4f2e07 100644
--- a/third_party/autocxx/engine/src/conversion/parse/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/parse/mod.rs
@@ -7,6 +7,7 @@
 // except according to those terms.
 
 mod bindgen_semantic_attributes;
+mod extern_fun_signatures;
 mod parse_bindgen;
 mod parse_foreign_mod;
 
diff --git a/third_party/autocxx/engine/src/conversion/parse/parse_bindgen.rs b/third_party/autocxx/engine/src/conversion/parse/parse_bindgen.rs
index 2d4e3de..f499145 100644
--- a/third_party/autocxx/engine/src/conversion/parse/parse_bindgen.rs
+++ b/third_party/autocxx/engine/src/conversion/parse/parse_bindgen.rs
@@ -13,7 +13,8 @@
     conversion::{
         api::{Api, ApiName, NullPhase, StructDetails, SubclassName, TypedefKind, UnanalyzedApi},
         apivec::ApiVec,
-        ConvertError,
+        convert_error::LocatedConvertErrorFromRust,
+        ConvertError, ConvertErrorFromCpp,
     },
     types::Namespace,
     types::QualifiedName,
@@ -41,7 +42,7 @@
 }
 
 fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName {
-    ApiName::new_with_cpp_name(ns, id, attrs.get_original_name())
+    ApiName::new_with_cpp_name(ns, id.into(), attrs.get_original_name())
 }
 
 pub(crate) fn api_name_qualified(
@@ -51,8 +52,11 @@
 ) -> Result<ApiName, ConvertErrorWithContext> {
     match validate_ident_ok_for_cxx(&id.to_string()) {
         Err(e) => {
-            let ctx = ErrorContext::new_for_item(id);
-            Err(ConvertErrorWithContext(e, Some(ctx)))
+            let ctx = ErrorContext::new_for_item(id.into());
+            Err(ConvertErrorWithContext(
+                ConvertErrorFromCpp::InvalidIdent(e),
+                Some(ctx),
+            ))
         }
         Ok(..) => Ok(api_name(ns, id, attrs)),
     }
@@ -71,43 +75,49 @@
     pub(crate) fn parse_items(
         mut self,
         items: Vec<Item>,
+        source_file_contents: &str,
     ) -> Result<ApiVec<NullPhase>, ConvertError> {
-        let items = Self::find_items_in_root(items)?;
+        let items = Self::find_items_in_root(items).map_err(ConvertError::Cpp)?;
         if !self.config.exclude_utilities() {
             generate_utilities(&mut self.apis, self.config);
         }
-        self.add_apis_from_config();
+        self.add_apis_from_config(source_file_contents)
+            .map_err(ConvertError::Rust)?;
         let root_ns = Namespace::new();
         self.parse_mod_items(items, root_ns);
-        self.confirm_all_generate_directives_obeyed()?;
+        self.confirm_all_generate_directives_obeyed()
+            .map_err(ConvertError::Cpp)?;
         self.replace_extern_cpp_types();
         Ok(self.apis)
     }
 
     /// Some API items are not populated from bindgen output, but instead
     /// directly from items in the config.
-    fn add_apis_from_config(&mut self) {
+    fn add_apis_from_config(
+        &mut self,
+        source_file_contents: &str,
+    ) -> Result<(), LocatedConvertErrorFromRust> {
         self.apis
             .extend(self.config.subclasses.iter().map(|sc| Api::Subclass {
-                name: SubclassName::new(sc.subclass.clone()),
+                name: SubclassName::new(sc.subclass.clone().into()),
                 superclass: QualifiedName::new_from_cpp_name(&sc.superclass),
             }));
-        self.apis
-            .extend(self.config.extern_rust_funs.iter().map(|fun| {
-                let id = fun.sig.ident.clone();
-                Api::RustFn {
-                    name: ApiName::new_in_root_namespace(id),
-                    details: fun.clone(),
-                    receiver: fun.receiver.as_ref().map(|receiver_id| {
-                        QualifiedName::new(&Namespace::new(), receiver_id.clone())
-                    }),
-                }
-            }));
+        for fun in &self.config.extern_rust_funs {
+            let id = fun.sig.ident.clone();
+            self.apis.push(Api::RustFn {
+                name: ApiName::new_in_root_namespace(id.into()),
+                details: fun.clone(),
+                deps: super::extern_fun_signatures::assemble_extern_fun_deps(
+                    &fun.sig,
+                    source_file_contents,
+                )?,
+            })
+        }
         let unique_rust_types: HashSet<&RustPath> = self.config.rust_types.iter().collect();
         self.apis.extend(unique_rust_types.into_iter().map(|path| {
             let id = path.get_final_ident();
             Api::RustType {
-                name: ApiName::new_in_root_namespace(id.clone()),
+                name: ApiName::new_in_root_namespace(id.clone().into()),
                 path: path.clone(),
             }
         }));
@@ -117,7 +127,7 @@
                 .0
                 .iter()
                 .map(|(cpp_definition, rust_id)| {
-                    let name = ApiName::new_in_root_namespace(rust_id.clone());
+                    let name = ApiName::new_in_root_namespace(rust_id.clone().into());
                     Api::ConcreteType {
                         name,
                         cpp_definition: cpp_definition.clone(),
@@ -125,6 +135,7 @@
                     }
                 }),
         );
+        Ok(())
     }
 
     /// We do this last, _after_ we've parsed all the APIs, because we might want to actually
@@ -154,7 +165,7 @@
         self.apis.extend(replacements.into_iter().map(|(_, v)| v));
     }
 
-    fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
+    fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertErrorFromCpp> {
         for item in items {
             match item {
                 Item::Mod(root_mod) => {
@@ -166,7 +177,7 @@
                         return Ok(items);
                     }
                 }
-                _ => return Err(ConvertError::UnexpectedOuterItem),
+                _ => return Err(ConvertErrorFromCpp::UnexpectedOuterItem),
             }
         }
         Ok(Vec::new())
@@ -224,8 +235,8 @@
                     // forward declarations.
                     if err.is_none() && name.cpp_name().contains("::") {
                         err = Some(ConvertErrorWithContext(
-                            ConvertError::ForwardDeclaredNestedType,
-                            Some(ErrorContext::new_for_item(s.ident)),
+                            ConvertErrorFromCpp::ForwardDeclaredNestedType,
+                            Some(ErrorContext::new_for_item(s.ident.into())),
                         ));
                     }
                     Some(UnanalyzedApi::ForwardDeclaration { name, err })
@@ -237,7 +248,7 @@
                         name,
                         details: Box::new(StructDetails {
                             layout: annotations.get_layout(),
-                            item: s,
+                            item: s.into(),
                             has_rvalue_reference_fields,
                         }),
                         analysis: (),
@@ -254,7 +265,7 @@
                 let annotations = BindgenSemanticAttributes::new(&e.attrs);
                 let api = UnanalyzedApi::Enum {
                     name: api_name_qualified(ns, e.ident.clone(), &annotations)?,
-                    item: e,
+                    item: e.into(),
                 };
                 if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
                     self.apis.push(api);
@@ -293,7 +304,7 @@
                         UseTree::Rename(urn) => {
                             let old_id = &urn.ident;
                             let new_id = &urn.rename;
-                            let new_tyname = QualifiedName::new(ns, new_id.clone());
+                            let new_tyname = QualifiedName::new(ns, new_id.clone().into());
                             assert!(segs.remove(0) == "self", "Path didn't start with self");
                             assert!(
                                 segs.remove(0) == "super",
@@ -309,8 +320,8 @@
                             let old_tyname = QualifiedName::from_type_path(&old_path);
                             if new_tyname == old_tyname {
                                 return Err(ConvertErrorWithContext(
-                                    ConvertError::InfinitelyRecursiveTypedef(new_tyname),
-                                    Some(ErrorContext::new_for_item(new_id.clone())),
+                                    ConvertErrorFromCpp::InfinitelyRecursiveTypedef(new_tyname),
+                                    Some(ErrorContext::new_for_item(new_id.clone().into())),
                                 ));
                             }
                             let annotations = BindgenSemanticAttributes::new(&use_item.attrs);
@@ -320,7 +331,7 @@
                                     parse_quote! {
                                         pub use #old_path as #new_id;
                                     },
-                                    Box::new(Type::Path(old_path)),
+                                    Box::new(Type::Path(old_path).into()),
                                 ),
                                 old_tyname: Some(old_tyname),
                                 analysis: (),
@@ -329,7 +340,7 @@
                         }
                         _ => {
                             return Err(ConvertErrorWithContext(
-                                ConvertError::UnexpectedUseStatement(
+                                ConvertErrorFromCpp::UnexpectedUseStatement(
                                     segs.into_iter().last().map(|i| i.to_string()),
                                 ),
                                 None,
@@ -341,10 +352,23 @@
             }
             Item::Const(const_item) => {
                 let annotations = BindgenSemanticAttributes::new(&const_item.attrs);
-                self.apis.push(UnanalyzedApi::Const {
-                    name: api_name(ns, const_item.ident.clone(), &annotations),
-                    const_item,
-                });
+                // Bindgen generates const expressions for nested unnamed enums,
+                // but autcxx will refuse to expand those enums, making these consts
+                // invalid.
+                let mut enum_type_name_valid = true;
+                if let Type::Path(p) = &*const_item.ty {
+                    if let Some(p) = &p.path.segments.last() {
+                        if validate_ident_ok_for_cxx(&p.ident.to_string()).is_err() {
+                            enum_type_name_valid = false;
+                        }
+                    }
+                }
+                if enum_type_name_valid {
+                    self.apis.push(UnanalyzedApi::Const {
+                        name: api_name(ns, const_item.ident.clone(), &annotations),
+                        const_item: const_item.into(),
+                    });
+                }
                 Ok(())
             }
             Item::Type(ity) => {
@@ -353,14 +377,14 @@
                 // same name - see test_issue_264.
                 self.apis.push(UnanalyzedApi::Typedef {
                     name: api_name(ns, ity.ident.clone(), &annotations),
-                    item: TypedefKind::Type(ity),
+                    item: TypedefKind::Type(ity.into()),
                     old_tyname: None,
                     analysis: (),
                 });
                 Ok(())
             }
             _ => Err(ConvertErrorWithContext(
-                ConvertError::UnexpectedItemInMod,
+                ConvertErrorFromCpp::UnexpectedItemInMod,
                 None,
             )),
         }
@@ -380,7 +404,7 @@
             .any(|id| id == desired_id)
     }
 
-    fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertError> {
+    fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertErrorFromCpp> {
         let api_names: HashSet<_> = self
             .apis
             .iter()
@@ -388,7 +412,9 @@
             .collect();
         for generate_directive in self.config.must_generate_list() {
             if !api_names.contains(&generate_directive) {
-                return Err(ConvertError::DidNotGenerateAnything(generate_directive));
+                return Err(ConvertErrorFromCpp::DidNotGenerateAnything(
+                    generate_directive,
+                ));
             }
         }
         Ok(())
diff --git a/third_party/autocxx/engine/src/conversion/parse/parse_foreign_mod.rs b/third_party/autocxx/engine/src/conversion/parse/parse_foreign_mod.rs
index 08c1fd8..ea82ff8 100644
--- a/third_party/autocxx/engine/src/conversion/parse/parse_foreign_mod.rs
+++ b/third_party/autocxx/engine/src/conversion/parse/parse_foreign_mod.rs
@@ -15,8 +15,9 @@
     convert_error::ConvertErrorWithContext,
     convert_error::ErrorContext,
 };
+use crate::minisyn::{minisynize_punctuated, minisynize_vec};
 use crate::{
-    conversion::ConvertError,
+    conversion::ConvertErrorFromCpp,
     types::{Namespace, QualifiedName},
 };
 use std::collections::HashMap;
@@ -72,11 +73,11 @@
                 self.funcs_to_convert.push(FuncToConvert {
                     provenance: Provenance::Bindgen,
                     self_ty: None,
-                    ident: item.sig.ident,
-                    doc_attrs,
-                    inputs: item.sig.inputs,
-                    output: item.sig.output,
-                    vis: item.vis,
+                    ident: item.sig.ident.into(),
+                    doc_attrs: minisynize_vec(doc_attrs),
+                    inputs: minisynize_punctuated(item.sig.inputs),
+                    output: item.sig.output.into(),
+                    vis: item.vis.into(),
                     virtualness: annotations.get_virtualness(),
                     cpp_vis: annotations.get_cpp_visibility(),
                     special_member: annotations.special_member_kind(),
@@ -86,18 +87,18 @@
                     original_name: annotations.get_original_name(),
                     synthesized_this_type: None,
                     add_to_trait: None,
-                    is_deleted: annotations.has_attr("deleted"),
+                    is_deleted: annotations.get_deleted_or_defaulted(),
                     synthetic_cpp: None,
                     variadic: item.sig.variadic.is_some(),
                 });
                 Ok(())
             }
             ForeignItem::Static(item) => Err(ConvertErrorWithContext(
-                ConvertError::StaticData(item.ident.to_string()),
-                Some(ErrorContext::new_for_item(item.ident)),
+                ConvertErrorFromCpp::StaticData(item.ident.to_string()),
+                Some(ErrorContext::new_for_item(item.ident.into())),
             )),
             _ => Err(ConvertErrorWithContext(
-                ConvertError::UnexpectedForeignItem,
+                ConvertErrorFromCpp::UnexpectedForeignItem,
                 None,
             )),
         }
@@ -111,14 +112,14 @@
             _ => return,
         };
         for i in imp.items {
-            if let ImplItem::Method(itm) = i {
+            if let ImplItem::Fn(itm) = i {
                 let effective_fun_name = match get_called_function(&itm.block) {
                     Some(id) => id.clone(),
                     None => itm.sig.ident,
                 };
                 self.method_receivers.insert(
                     effective_fun_name,
-                    QualifiedName::new(&self.ns, ty_id.clone()),
+                    QualifiedName::new(&self.ns, ty_id.clone().into()),
                 );
             }
         }
@@ -151,7 +152,7 @@
 /// name of the actual function call inside the block's body.
 fn get_called_function(block: &Block) -> Option<&Ident> {
     match block.stmts.first() {
-        Some(Stmt::Expr(Expr::Call(ExprCall { func, .. }))) => match **func {
+        Some(Stmt::Expr(Expr::Call(ExprCall { func, .. }), _)) => match **func {
             Expr::Path(ref exp) => exp.path.segments.first().map(|ps| &ps.ident),
             _ => None,
         },
diff --git a/third_party/autocxx/engine/src/conversion/type_helpers.rs b/third_party/autocxx/engine/src/conversion/type_helpers.rs
new file mode 100644
index 0000000..8eb3a3a
--- /dev/null
+++ b/third_party/autocxx/engine/src/conversion/type_helpers.rs
@@ -0,0 +1,58 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use syn::{
+    AngleBracketedGenericArguments, GenericArgument, PathArguments, PathSegment, Type, TypePath,
+    TypeReference,
+};
+
+/// Looks in a `core::pin::Pin<&mut Something>` and returns the `Something`
+/// if it's found.
+/// This code could _almost_ be used from various other places around autocxx
+/// but they each have slightly different requirements. Over time we should
+/// try to migrate other instances to use this, though.
+pub(crate) fn extract_pinned_mutable_reference_type(tp: &TypePath) -> Option<&Type> {
+    if !is_pin(tp) {
+        return None;
+    }
+    if let Some(PathSegment {
+        arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }),
+        ..
+    }) = tp.path.segments.last()
+    {
+        if args.len() == 1 {
+            if let Some(GenericArgument::Type(Type::Reference(TypeReference {
+                mutability: Some(_),
+                elem,
+                ..
+            }))) = args.first()
+            {
+                return Some(elem);
+            }
+        }
+    }
+    None
+}
+
+/// Whether this type path is a `Pin`
+fn is_pin(tp: &TypePath) -> bool {
+    if tp.path.segments.len() != 3 {
+        return false;
+    }
+    static EXPECTED_SEGMENTS: &[&[&str]] = &[&["std", "core"], &["pin"], &["Pin"]];
+
+    for (seg, expected_name) in tp.path.segments.iter().zip(EXPECTED_SEGMENTS.iter()) {
+        if !expected_name
+            .iter()
+            .any(|expected_name| seg.ident == expected_name)
+        {
+            return false;
+        }
+    }
+    true
+}
diff --git a/third_party/autocxx/engine/src/known_types.rs b/third_party/autocxx/engine/src/known_types.rs
index 377101a..10199fb 100644
--- a/third_party/autocxx/engine/src/known_types.rs
+++ b/third_party/autocxx/engine/src/known_types.rs
@@ -6,14 +6,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use crate::{
-    conversion::ConvertError,
-    types::{make_ident, QualifiedName},
-};
+use crate::types::{make_ident, QualifiedName};
 use indexmap::map::IndexMap as HashMap;
 use indoc::indoc;
 use once_cell::sync::OnceCell;
-use syn::{parse_quote, Type, TypePath, TypePtr};
+use syn::{parse_quote, TypePath};
 
 //// The behavior of the type.
 #[derive(Debug)]
@@ -103,12 +100,12 @@
         let mut segs = self.rs_name.split("::").peekable();
         if segs.peek().map(|seg| seg.is_empty()).unwrap_or_default() {
             segs.next();
-            let segs = segs.into_iter().map(make_ident);
+            let segs = segs.map(make_ident);
             parse_quote! {
                 ::#(#segs)::*
             }
         } else {
-            let segs = segs.into_iter().map(make_ident);
+            let segs = segs.map(make_ident);
             parse_quote! {
                 #(#segs)::*
             }
@@ -143,7 +140,7 @@
 }
 
 /// The type of payload that a cxx generic can contain.
-#[derive(PartialEq, Clone, Copy)]
+#[derive(PartialEq, Eq, Clone, Copy)]
 pub enum CxxGenericType {
     /// Not a generic at all
     Not,
@@ -247,7 +244,9 @@
             .map(|td| {
                 matches!(
                     td.behavior,
-                    Behavior::CxxContainerPtr | Behavior::CxxContainerVector
+                    Behavior::CxxContainerPtr
+                        | Behavior::CxxContainerVector
+                        | Behavior::RustContainerByValueSafe
                 )
             })
             .unwrap_or(false)
@@ -447,8 +446,8 @@
     ));
     for (cpp_type, rust_type) in (4..7).map(|x| 2i32.pow(x)).flat_map(|x| {
         vec![
-            (format!("uint{}_t", x), format!("u{}", x)),
-            (format!("int{}_t", x), format!("i{}", x)),
+            (format!("uint{x}_t"), format!("u{x}")),
+            (format!("int{x}_t"), format!("i{x}")),
         ]
     }) {
         db.insert(TypeDetails::new(
@@ -470,10 +469,10 @@
     ));
 
     db.insert(TypeDetails::new(
-        "std::pin::Pin",
+        "core::pin::Pin",
         "Pin",
         Behavior::RustByValue, // because this is actually Pin<&something>
-        None,
+        Some("std::pin::Pin".to_string()),
         true,
         false,
     ));
@@ -481,18 +480,18 @@
     let mut insert_ctype = |cname: &str| {
         let concatenated_name = cname.replace(' ', "");
         db.insert(TypeDetails::new(
-            format!("autocxx::c_{}", concatenated_name),
+            format!("autocxx::c_{concatenated_name}"),
             cname,
             Behavior::CVariableLengthByValue,
-            Some(format!("std::os::raw::c_{}", concatenated_name)),
+            Some(format!("std::os::raw::c_{concatenated_name}")),
             true,
             true,
         ));
         db.insert(TypeDetails::new(
-            format!("autocxx::c_u{}", concatenated_name),
-            format!("unsigned {}", cname),
+            format!("autocxx::c_u{concatenated_name}"),
+            format!("unsigned {cname}"),
             Behavior::CVariableLengthByValue,
-            Some(format!("std::os::raw::c_u{}", concatenated_name)),
+            Some(format!("std::os::raw::c_u{concatenated_name}")),
             true,
             true,
         ));
@@ -553,10 +552,3 @@
     ));
     db
 }
-
-pub(crate) fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertError> {
-    match *ptr.elem {
-        Type::Path(..) => Ok(()),
-        _ => Err(ConvertError::InvalidPointee),
-    }
-}
diff --git a/third_party/autocxx/engine/src/lib.rs b/third_party/autocxx/engine/src/lib.rs
index 86a31ea..3988ab2 100644
--- a/third_party/autocxx/engine/src/lib.rs
+++ b/third_party/autocxx/engine/src/lib.rs
@@ -10,15 +10,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// This feature=nightly could be set by build.rs, but since we only care
-// about it for docs, we ask docs.rs to set it in the Cargo.toml.
-#![cfg_attr(feature = "nightly", feature(doc_cfg))]
 #![forbid(unsafe_code)]
+#![cfg_attr(feature = "nightly", feature(doc_cfg))]
 
 mod ast_discoverer;
 mod conversion;
 mod cxxbridge;
 mod known_types;
+mod minisyn;
 mod output_generators;
 mod parse_callbacks;
 mod parse_file;
@@ -28,6 +27,7 @@
 #[cfg(any(test, feature = "build"))]
 mod builder;
 
+use autocxx_bindgen::BindgenError;
 use autocxx_parser::{IncludeCppConfig, UnsafePolicy};
 use conversion::BridgeConverter;
 use miette::{SourceOffset, SourceSpan};
@@ -35,7 +35,9 @@
 use parse_file::CppBuildable;
 use proc_macro2::TokenStream as TokenStream2;
 use regex::Regex;
+use std::cell::RefCell;
 use std::path::PathBuf;
+use std::rc::Rc;
 use std::{
     fs::File,
     io::prelude::*,
@@ -113,7 +115,7 @@
 #[derive(Debug, Error, Diagnostic)]
 pub enum Error {
     #[error("Bindgen was unable to generate the initial .rs bindings for this file. This may indicate a parsing problem with the C++ headers.")]
-    Bindgen(()),
+    Bindgen(BindgenError),
     #[error(transparent)]
     #[diagnostic(transparent)]
     MacroParsing(LocatedSynError),
@@ -123,7 +125,10 @@
     #[error("no C++ include directory was provided.")]
     NoAutoCxxInc,
     #[error(transparent)]
+    #[diagnostic(transparent)]
     Conversion(conversion::ConvertError),
+    #[error("Using `unsafe_references_wrapped` requires the Rust nightly `arbitrary_self_types` feature")]
+    WrappedReferencesButNoArbitrarySelfTypes,
 }
 
 /// Result type.
@@ -142,6 +147,16 @@
     Generated(Box<GenerationResults>),
 }
 
+/// Code generation options.
+#[derive(Default)]
+pub struct CodegenOptions<'a> {
+    // An option used by the test suite to force a more convoluted
+    // route through our code, to uncover bugs.
+    pub force_wrapper_gen: bool,
+    /// Options about the C++ code generation.
+    pub cpp_codegen_options: CppCodegenOptions<'a>,
+}
+
 const AUTOCXX_CLANG_ARGS: &[&str; 4] = &["-x", "c++", "-std=c++14", "-DBINDGEN"];
 
 /// Implement to learn of header files which get included
@@ -254,6 +269,7 @@
 pub struct IncludeCppEngine {
     config: IncludeCppConfig,
     state: State,
+    source_code: Option<Rc<String>>, // so we can create diagnostics
 }
 
 impl Parse for IncludeCppEngine {
@@ -264,14 +280,31 @@
         } else {
             State::NotGenerated
         };
-        Ok(Self { config, state })
+        Ok(Self {
+            config,
+            state,
+            source_code: None,
+        })
     }
 }
 
 impl IncludeCppEngine {
-    pub fn new_from_syn(mac: Macro, file_contents: &str) -> Result<Self> {
-        mac.parse_body::<IncludeCppEngine>()
-            .map_err(|e| Error::MacroParsing(LocatedSynError::new(e, file_contents)))
+    pub fn new_from_syn(mac: Macro, file_contents: Rc<String>) -> Result<Self> {
+        let mut this = mac
+            .parse_body::<IncludeCppEngine>()
+            .map_err(|e| Error::MacroParsing(LocatedSynError::new(e, &file_contents)))?;
+        this.source_code = Some(file_contents);
+        Ok(this)
+    }
+
+    /// Used if we find that we're asked to auto-discover extern_rust_type and similar
+    /// but didn't have any include_cpp macro at all.
+    pub fn new_for_autodiscover() -> Self {
+        Self {
+            config: IncludeCppConfig::default(),
+            state: State::NotGenerated,
+            source_code: None,
+        }
     }
 
     pub fn config_mut(&mut self) -> &mut IncludeCppConfig {
@@ -287,7 +320,7 @@
             self.config
                 .inclusions
                 .iter()
-                .map(|path| format!("#include \"{}\"\n", path)),
+                .map(|path| format!("#include \"{path}\"\n")),
             "",
         )
     }
@@ -304,7 +337,11 @@
             .default_enum_style(bindgen::EnumVariation::Rust {
                 non_exhaustive: false,
             })
-            .rustfmt_bindings(log::log_enabled!(log::Level::Info))
+            .formatter(if log::log_enabled!(log::Level::Info) {
+                bindgen::Formatter::Rustfmt
+            } else {
+                bindgen::Formatter::None
+            })
             .size_t_is_usize(true)
             .enable_cxx_namespaces()
             .generate_inline_functions(true)
@@ -335,7 +372,7 @@
             builder
                 .command_line_flags()
                 .into_iter()
-                .map(|f| format!("\"{}\"", f))
+                .map(|f| format!("\"{f}\""))
                 .join(" ")
         );
         builder
@@ -370,7 +407,7 @@
         let bindings = bindings.to_string();
         // Manually add the mod ffi {} so that we can ask syn to parse
         // into a single construct.
-        let bindings = format!("mod bindgen {{ {} }}", bindings);
+        let bindings = format!("mod bindgen {{ {bindings} }}");
         info!("Bindings: {}", bindings);
         syn::parse_str::<ItemMod>(&bindings)
             .map_err(|e| Error::BindingsParsing(LocatedSynError::new(e, &bindings)))
@@ -386,7 +423,7 @@
         inc_dirs: Vec<PathBuf>,
         extra_clang_args: &[&str],
         dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
-        cpp_codegen_options: &CppCodegenOptions,
+        codegen_options: &CodegenOptions,
     ) -> Result<()> {
         // If we are in parse only mode, do nothing. This is used for
         // doc tests to ensure the parsing is valid, but we can't expect
@@ -397,6 +434,14 @@
             State::Generated(_) => panic!("Only call generate once"),
         }
 
+        if matches!(
+            self.config.unsafe_policy,
+            UnsafePolicy::ReferencesWrappedAllFunctionsSafe
+        ) && !rustversion::cfg!(nightly)
+        {
+            return Err(Error::WrappedReferencesButNoArbitrarySelfTypes);
+        }
+
         let mod_name = self.config.get_mod_name();
         let mut builder = self.make_bindgen_builder(&inc_dirs, extra_clang_args);
         if let Some(dep_recorder) = dep_recorder {
@@ -411,6 +456,14 @@
         let bindings = builder.generate().map_err(Error::Bindgen)?;
         let bindings = self.parse_bindings(bindings)?;
 
+        // Source code contents just used for diagnostics - if we don't have it,
+        // use a blank string and miette will not attempt to annotate it nicely.
+        let source_file_contents = self
+            .source_code
+            .as_ref()
+            .cloned()
+            .unwrap_or_else(|| Rc::new("".to_string()));
+
         let converter = BridgeConverter::new(&self.config.inclusions, &self.config);
 
         let conversion = converter
@@ -418,7 +471,8 @@
                 bindings,
                 self.config.unsafe_policy.clone(),
                 header_contents,
-                cpp_codegen_options,
+                codegen_options,
+                &source_file_contents,
             )
             .map_err(Error::Conversion)?;
         let mut items = conversion.rs;
@@ -433,7 +487,7 @@
         new_bindings.content.as_mut().unwrap().1.append(&mut items);
         info!(
             "New bindings:\n{}",
-            rust_pretty_printer::pretty_print(&new_bindings.to_token_stream())
+            rust_pretty_printer::pretty_print(&new_bindings)
         );
         self.state = State::Generated(Box::new(GenerationResults {
             item_mod: new_bindings,
@@ -484,7 +538,7 @@
                 "header": header,
                 "config": config
             });
-            let f = File::create(&output_path).unwrap();
+            let f = File::create(output_path).unwrap();
             serde_json::to_writer(f, &json).unwrap();
         }
     }
@@ -505,12 +559,12 @@
         // to refer to local headers on the reduction machine too.
         let suffix = ALL_KNOWN_SYSTEM_HEADERS
             .iter()
-            .map(|hdr| format!("#include <{}>\n", hdr))
+            .map(|hdr| format!("#include <{hdr}>\n"))
             .join("\n");
         let input = format!("/*\nautocxx config:\n\n{:?}\n\nend autocxx config.\nautocxx preprocessed input:\n*/\n\n{}\n\n/* autocxx: extra headers added below for completeness. */\n\n{}\n{}\n",
             self.config, header, suffix, cxx_gen::HEADER);
         let mut tf = NamedTempFile::new().unwrap();
-        write!(tf, "{}", input).unwrap();
+        write!(tf, "{input}").unwrap();
         let tp = tf.into_temp_path();
         preprocess(&tp, &PathBuf::from(output_path), inc_dirs, extra_clang_args).unwrap();
     }
@@ -565,7 +619,11 @@
     })
 }
 
-pub(crate) fn strip_system_headers(input: Vec<u8>, suppress_system_headers: bool) -> Vec<u8> {
+pub fn get_cxx_header_bytes(suppress_system_headers: bool) -> Vec<u8> {
+    strip_system_headers(cxx_gen::HEADER.as_bytes().to_vec(), suppress_system_headers)
+}
+
+fn strip_system_headers(input: Vec<u8>, suppress_system_headers: bool) -> Vec<u8> {
     if suppress_system_headers {
         std::str::from_utf8(&input)
             .unwrap()
@@ -660,7 +718,7 @@
 
 impl Default for AutocxxgenHeaderNamer<'static> {
     fn default() -> Self {
-        Self(Box::new(|mod_name| format!("autocxxgen_{}.h", mod_name)))
+        Self(Box::new(|mod_name| format!("autocxxgen_{mod_name}.h")))
     }
 }
 
@@ -677,7 +735,27 @@
 
 impl Default for CxxgenHeaderNamer<'static> {
     fn default() -> Self {
-        Self(Box::new(|| "cxxgen.h".into()))
+        // The default implementation here is to name these headers
+        // cxxgen.h, cxxgen1.h, cxxgen2.h etc.
+        // These names are not especially predictable by callers and this
+        // behavior is not tested anywhere - so this is considered semi-
+        // supported, at best. This only comes into play in the rare case
+        // that you're generating bindings to multiple include_cpp!
+        // or a mix of include_cpp! and #[cxx::bridge] bindings.
+        let header_counter = Rc::new(RefCell::new(0));
+        Self(Box::new(move || {
+            let header_counter = header_counter.clone();
+            let header_counter_cell = header_counter.as_ref();
+            let mut header_counter = header_counter_cell.borrow_mut();
+            if *header_counter == 0 {
+                *header_counter += 1;
+                "cxxgen.h".into()
+            } else {
+                let count = *header_counter;
+                *header_counter += 1;
+                format!("cxxgen{count}.h")
+            }
+        }))
     }
 }
 
@@ -694,10 +772,10 @@
     /// You may wish to do this to make a hermetic test case with no
     /// external dependencies.
     pub suppress_system_headers: bool,
-    /// Optionally, a prefix to go at `#include "<here>cxx.h". This is a header file from the `cxx`
+    /// Optionally, a prefix to go at `#include "*here*cxx.h". This is a header file from the `cxx`
     /// crate.
     pub path_to_cxx_h: Option<String>,
-    /// Optionally, a prefix to go at `#include "<here>cxxgen.h". This is a header file which we
+    /// Optionally, a prefix to go at `#include "*here*cxxgen.h". This is a header file which we
     /// generate.
     pub path_to_cxxgen_h: Option<String>,
     /// Optionally, a function called to determine the name that will be used
@@ -720,7 +798,7 @@
     // miette.
     struct Err;
     let r: Result<(usize, usize), Err> = (|| {
-        let span_desc = format!("{:?}", span);
+        let span_desc = format!("{span:?}");
         let re = Regex::new(r"(\d+)..(\d+)").unwrap();
         let captures = re.captures(&span_desc).ok_or(Err)?;
         let start = captures.get(1).ok_or(Err)?;
diff --git a/third_party/autocxx/engine/src/minisyn.rs b/third_party/autocxx/engine/src/minisyn.rs
new file mode 100644
index 0000000..e675baa
--- /dev/null
+++ b/third_party/autocxx/engine/src/minisyn.rs
@@ -0,0 +1,187 @@
+// Copyright 2023 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.
+
+//! Newtype wrappers for `syn` types implementing a different
+//! `Debug` implementation that results in more concise output.
+
+use std::fmt::Display;
+
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::punctuated::{Pair, Punctuated};
+
+macro_rules! minisyn_no_parse {
+    ($syntype:ident) => {
+        /// Equivalent to the identically-named `syn` type except
+        /// that its `Debug` implementation is more concise.
+        #[derive(Clone, Hash, Eq, PartialEq)]
+        pub struct $syntype(pub ::syn::$syntype);
+        impl std::fmt::Debug for $syntype {
+            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+                write!(f, "{}", self.0.to_token_stream().to_string())
+            }
+        }
+        impl ToTokens for $syntype
+        where
+            ::syn::$syntype: ToTokens,
+        {
+            fn to_tokens(&self, tokens: &mut TokenStream) {
+                self.0.to_tokens(tokens)
+            }
+
+            fn to_token_stream(&self) -> TokenStream {
+                self.0.to_token_stream()
+            }
+            fn into_token_stream(self) -> TokenStream
+            where
+                Self: Sized,
+            {
+                self.0.into_token_stream()
+            }
+        }
+        impl std::ops::Deref for $syntype {
+            type Target = ::syn::$syntype;
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
+        impl std::ops::DerefMut for $syntype {
+            fn deref_mut(&mut self) -> &mut Self::Target {
+                &mut self.0
+            }
+        }
+        impl std::convert::From<::syn::$syntype> for $syntype {
+            fn from(inner: ::syn::$syntype) -> Self {
+                Self(inner)
+            }
+        }
+        impl std::convert::From<$syntype> for syn::$syntype {
+            fn from(inner: $syntype) -> Self {
+                inner.0
+            }
+        }
+    };
+}
+
+macro_rules! minisyn {
+    ($syntype:ident) => {
+        minisyn_no_parse!($syntype);
+
+        impl syn::parse::Parse for $syntype {
+            fn parse(input: syn::parse::ParseStream<'_>) -> syn::parse::Result<Self> {
+                syn::parse::Parse::parse(input).map(Self)
+            }
+        }
+    };
+}
+
+minisyn!(ItemMod);
+minisyn_no_parse!(Attribute);
+minisyn_no_parse!(AssocConst);
+minisyn_no_parse!(AssocType);
+minisyn!(Expr);
+minisyn!(ExprAssign);
+minisyn!(ExprAwait);
+minisyn!(ExprBinary);
+minisyn!(ExprBlock);
+minisyn!(ExprBreak);
+minisyn!(ExprConst);
+minisyn!(ExprCast);
+minisyn!(ExprField);
+minisyn_no_parse!(ExprGroup);
+minisyn!(ExprLet);
+minisyn!(ExprParen);
+minisyn!(ExprReference);
+minisyn!(ExprTry);
+minisyn!(ExprUnary);
+minisyn_no_parse!(Field);
+minisyn_no_parse!(Fields);
+minisyn!(ForeignItem);
+minisyn!(FnArg);
+minisyn!(GenericArgument);
+minisyn!(GenericParam);
+minisyn!(Ident);
+minisyn!(ImplItem);
+minisyn!(Item);
+minisyn!(ItemConst);
+minisyn!(ItemEnum);
+minisyn!(ItemForeignMod);
+minisyn!(ItemStruct);
+minisyn!(ItemType);
+minisyn!(ItemUse);
+minisyn!(LitBool);
+minisyn!(LitInt);
+minisyn!(Macro);
+minisyn_no_parse!(Pat);
+minisyn_no_parse!(PatType);
+minisyn_no_parse!(PatReference);
+minisyn_no_parse!(PatSlice);
+minisyn_no_parse!(PatTuple);
+minisyn!(Path);
+minisyn_no_parse!(PathArguments);
+minisyn!(PathSegment);
+minisyn!(Receiver);
+minisyn!(ReturnType);
+minisyn!(Signature);
+minisyn!(Stmt);
+minisyn!(TraitItem);
+minisyn!(Type);
+minisyn!(TypeArray);
+minisyn!(TypeGroup);
+minisyn!(TypeParamBound);
+minisyn!(TypeParen);
+minisyn!(TypePath);
+minisyn!(TypePtr);
+minisyn!(TypeReference);
+minisyn!(TypeSlice);
+minisyn!(Visibility);
+
+/// Converts a `syn::Punctuated` from being full of `syn` types to being
+/// full of `minisyn` types or vice-versa.
+pub(crate) fn minisynize_punctuated<T1, T2, S>(input: Punctuated<T1, S>) -> Punctuated<T2, S>
+where
+    T1: Into<T2>,
+{
+    input
+        .into_pairs()
+        .map(|p| match p {
+            Pair::Punctuated(t, p) => Pair::Punctuated(t.into(), p),
+            Pair::End(t) => Pair::End(t.into()),
+        })
+        .collect()
+}
+
+/// Converts a `Vec` from being full of `syn` types to being
+/// full of `minisyn` types or vice-versa.
+pub(crate) fn minisynize_vec<T1, T2>(input: Vec<T1>) -> Vec<T2>
+where
+    T1: Into<T2>,
+{
+    input.into_iter().map(Into::into).collect()
+}
+
+impl Ident {
+    pub(crate) fn new(string: &str, span: proc_macro2::Span) -> Self {
+        Self(syn::Ident::new(string, span))
+    }
+}
+
+impl Display for Ident {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str(&self.0.to_string())
+    }
+}
+
+impl<T> PartialEq<T> for Ident
+where
+    T: AsRef<str> + ?Sized,
+{
+    fn eq(&self, rhs: &T) -> bool {
+        self.0.eq(rhs)
+    }
+}
diff --git a/third_party/autocxx/engine/src/output_generators.rs b/third_party/autocxx/engine/src/output_generators.rs
index a862558..09c143a 100644
--- a/third_party/autocxx/engine/src/output_generators.rs
+++ b/third_party/autocxx/engine/src/output_generators.rs
@@ -19,7 +19,7 @@
 
 /// Creates an on-disk archive (actually a JSON file) of the Rust side of the bindings
 /// for multiple `include_cpp` macros. If you use this, you will want to tell
-/// `autocxx_macro` how to find this file using the `AUTOCXX_RS_ARCHIVE`
+/// `autocxx_macro` how to find this file using the `AUTOCXX_RS_JSON_ARCHIVE`
 /// environment variable.
 pub fn generate_rs_archive<'a>(rs_outputs: impl Iterator<Item = RsOutput<'a>>) -> String {
     let mut multi_bindings = MultiBindings::default();
diff --git a/third_party/autocxx/engine/src/parse_file.rs b/third_party/autocxx/engine/src/parse_file.rs
index 3571d19..16ea625 100644
--- a/third_party/autocxx/engine/src/parse_file.rs
+++ b/third_party/autocxx/engine/src/parse_file.rs
@@ -12,14 +12,15 @@
     cxxbridge::CxxBridge, Error as EngineError, GeneratedCpp, IncludeCppEngine,
     RebuildDependencyRecorder,
 };
-use crate::{CppCodegenOptions, LocatedSynError};
+use crate::{proc_macro_span_to_miette_span, CodegenOptions, CppCodegenOptions, LocatedSynError};
 use autocxx_parser::directive_names::SUBCLASS;
 use autocxx_parser::{AllowlistEntry, RustPath, Subclass, SubclassAttrs};
 use indexmap::set::IndexSet as HashSet;
-use miette::Diagnostic;
+use miette::{Diagnostic, SourceSpan};
 use quote::ToTokens;
 use std::{io::Read, path::PathBuf};
 use std::{panic::UnwindSafe, path::Path, rc::Rc};
+use syn::spanned::Spanned;
 use syn::{token::Brace, Item, ItemMod};
 use thiserror::Error;
 
@@ -27,7 +28,7 @@
 /// and interpret include_cxx macros.
 #[derive(Error, Diagnostic, Debug)]
 pub enum ParseError {
-    #[error("unable to open the source file: {0}")]
+    #[error("unable to open the source file containing your autocxx bindings. (This filename is usually specified within your build.rs file.): {0}")]
     FileOpen(std::io::Error),
     #[error("the .rs file couldn't be read: {0}")]
     FileRead(std::io::Error),
@@ -39,6 +40,8 @@
     #[error("the subclass attribute couldn't be parsed: {0}")]
     #[diagnostic(transparent)]
     SubclassSyntax(LocatedSynError),
+    #[error("the subclass attribute macro with a superclass attribute requires the Builder::auto_allowlist option to be specified (probably in your build script). This is not recommended - instead you can specify subclass! within your include_cpp!.")]
+    SubclassSuperclassWithoutAutoAllowlist(#[source_code] String, #[label("here")] SourceSpan),
     /// The include CPP macro could not be expanded into
     /// Rust bindings to C++, because of some problem during the conversion
     /// process. This could be anything from a C++ parsing error to some
@@ -52,8 +55,6 @@
     /// mod name.
     #[error("there are two or more include_cpp! mods with the same mod name")]
     ConflictingModNames,
-    #[error("dynamic discovery was enabled but no mod was found")]
-    ZeroModsForDynamicDiscovery,
     #[error("dynamic discovery was enabled but multiple mods were found")]
     MultipleModsForDynamicDiscovery,
     #[error("a problem occurred while discovering C++ APIs used within the Rust: {0}")]
@@ -87,12 +88,13 @@
         extra_superclasses: Vec<Subclass>,
         discoveries: Discoveries,
     }
+    let file_contents = Rc::new(file_contents.to_string());
     impl State {
         fn parse_item(
             &mut self,
             item: Item,
             mod_path: Option<RustPath>,
-            file_contents: &str,
+            file_contents: Rc<String>,
         ) -> Result<(), ParseError> {
             let result = match item {
                 Item::Macro(mac)
@@ -110,10 +112,9 @@
                     )
                 }
                 Item::Mod(itm)
-                    if itm
-                        .attrs
-                        .iter()
-                        .any(|attr| attr.path.to_token_stream().to_string() == "cxx :: bridge") =>
+                    if itm.attrs.iter().any(|attr| {
+                        attr.path().to_token_stream().to_string() == "cxx :: bridge"
+                    }) =>
                 {
                     Segment::Cxx(CxxBridge::from(itm))
                 }
@@ -128,7 +129,11 @@
                             Some(mod_path) => mod_path.append(itm.ident.clone()),
                         };
                         for item in items {
-                            mod_state.parse_item(item, Some(mod_path.clone()), file_contents)?
+                            mod_state.parse_item(
+                                item,
+                                Some(mod_path.clone()),
+                                file_contents.clone(),
+                            )?
                         }
                         self.extra_superclasses.extend(mod_state.extra_superclasses);
                         self.discoveries.extend(mod_state.discoveries);
@@ -146,26 +151,34 @@
                         Segment::Other(Item::Mod(itm))
                     }
                 }
-                Item::Struct(ref its) if self.auto_allowlist => {
+                Item::Struct(ref its) => {
                     let attrs = &its.attrs;
                     let is_superclass_attr = attrs.iter().find(|attr| {
-                        attr.path
+                        attr.path()
                             .segments
                             .last()
                             .map(|seg| seg.ident == "is_subclass" || seg.ident == SUBCLASS)
                             .unwrap_or(false)
                     });
                     if let Some(is_superclass_attr) = is_superclass_attr {
-                        if !is_superclass_attr.tokens.is_empty() {
+                        if is_superclass_attr.meta.require_path_only().is_err() {
                             let subclass = its.ident.clone();
                             let args: SubclassAttrs =
                                 is_superclass_attr.parse_args().map_err(|e| {
                                     ParseError::SubclassSyntax(LocatedSynError::new(
                                         e,
-                                        file_contents,
+                                        &file_contents,
                                     ))
                                 })?;
                             if let Some(superclass) = args.superclass {
+                                if !self.auto_allowlist {
+                                    return Err(
+                                        ParseError::SubclassSuperclassWithoutAutoAllowlist(
+                                            file_contents.to_string(),
+                                            proc_macro_span_to_miette_span(&its.span()),
+                                        ),
+                                    );
+                                }
                                 self.extra_superclasses.push(Subclass {
                                     superclass,
                                     subclass,
@@ -194,7 +207,7 @@
         ..Default::default()
     };
     for item in source.items {
-        state.parse_item(item, None, file_contents)?
+        state.parse_item(item, None, file_contents.clone())?
     }
     let State {
         auto_allowlist,
@@ -210,13 +223,18 @@
     // We do not want to enter this 'if' block unless the above conditions are true,
     // since we may emit errors.
     if must_handle_discovered_things {
+        // If we have to handle discovered things but there was no include_cpp! macro,
+        // fake one.
+        if !results.iter().any(|seg| matches!(seg, Segment::Autocxx(_))) {
+            results.push(Segment::Autocxx(IncludeCppEngine::new_for_autodiscover()));
+        }
         let mut autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
             Segment::Autocxx(engine) => Some(engine),
             _ => None,
         });
         let our_seg = autocxx_seg_iterator.next();
         match our_seg {
-            None => return Err(ParseError::ZeroModsForDynamicDiscovery),
+            None => panic!("We should have just added a fake mod but apparently didn't"),
             Some(engine) => {
                 engine
                     .config_mut()
@@ -364,7 +382,7 @@
         autocxx_inc: Vec<PathBuf>,
         extra_clang_args: &[&str],
         dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
-        cpp_codegen_options: &CppCodegenOptions,
+        codegen_options: &CodegenOptions,
     ) -> Result<(), ParseError> {
         let mut mods_found = HashSet::new();
         let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
@@ -386,7 +404,7 @@
                     autocxx_inc.clone(),
                     extra_clang_args,
                     dep_recorder,
-                    cpp_codegen_options,
+                    codegen_options,
                 )
                 .map_err(ParseError::AutocxxCodegenError)?
         }
diff --git a/third_party/autocxx/engine/src/rust_pretty_printer.rs b/third_party/autocxx/engine/src/rust_pretty_printer.rs
index 9c23cbc..7c9d36c 100644
--- a/third_party/autocxx/engine/src/rust_pretty_printer.rs
+++ b/third_party/autocxx/engine/src/rust_pretty_printer.rs
@@ -6,35 +6,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use proc_macro2::TokenStream;
-use std::io::Write;
-use std::process::{Command, Stdio};
+use syn::{Item, ItemMod};
 
-enum Error {
-    Run(std::io::Error),
-    Write(std::io::Error),
-    Utf8(std::string::FromUtf8Error),
-    Wait(std::io::Error),
-}
-
-pub(crate) fn pretty_print(ts: &TokenStream) -> String {
-    reformat_or_else(ts.to_string())
-}
-
-fn reformat_or_else(text: impl std::fmt::Display) -> String {
-    match reformat(&text) {
-        Ok(s) => s,
-        Err(_) => text.to_string(),
-    }
-}
-
-fn reformat(text: impl std::fmt::Display) -> Result<String, Error> {
-    let mut rustfmt = Command::new("rustfmt")
-        .stdin(Stdio::piped())
-        .stdout(Stdio::piped())
-        .spawn()
-        .map_err(Error::Run)?;
-    write!(rustfmt.stdin.take().unwrap(), "{}", text).map_err(Error::Write)?;
-    let output = rustfmt.wait_with_output().map_err(Error::Wait)?;
-    String::from_utf8(output.stdout).map_err(Error::Utf8)
+pub(crate) fn pretty_print(itm: &ItemMod) -> String {
+    prettyplease::unparse(&syn::File {
+        shebang: None,
+        attrs: Vec::new(),
+        items: vec![Item::Mod(itm.clone())],
+    })
 }
diff --git a/third_party/autocxx/engine/src/types.rs b/third_party/autocxx/engine/src/types.rs
index 337da14..f7eaae0 100644
--- a/third_party/autocxx/engine/src/types.rs
+++ b/third_party/autocxx/engine/src/types.rs
@@ -6,14 +6,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use crate::minisyn::Ident;
 use itertools::Itertools;
 use proc_macro2::Span;
 use quote::ToTokens;
 use std::iter::Peekable;
 use std::{fmt::Display, sync::Arc};
-use syn::{parse_quote, Ident, PathSegment, TypePath};
+use syn::{parse_quote, PathSegment, TypePath};
+use thiserror::Error;
 
-use crate::{conversion::ConvertError, known_types::known_types};
+use crate::known_types::known_types;
 
 pub(crate) fn make_ident<S: AsRef<str>>(id: S) -> Ident {
     Ident::new(id.as_ref(), Span::call_site())
@@ -52,11 +54,15 @@
     pub(crate) fn depth(&self) -> usize {
         self.0.len()
     }
+
+    pub(crate) fn to_cpp_path(&self) -> String {
+        self.0.join("::")
+    }
 }
 
 impl Display for Namespace {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_str(&self.0.join("::"))
+        f.write_str(&self.to_cpp_path())
     }
 }
 
@@ -80,7 +86,7 @@
 /// either. It doesn't directly have functionality to convert
 /// from one to the other; `replace_type_path_without_arguments`
 /// does that.
-#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
+#[derive(PartialEq, PartialOrd, Eq, Hash, Clone)]
 pub struct QualifiedName(Namespace, String);
 
 impl QualifiedName {
@@ -140,7 +146,7 @@
 
     /// cxx doesn't accept names containing double underscores,
     /// but these are OK elsewhere in our output mod.
-    pub(crate) fn validate_ok_for_cxx(&self) -> Result<(), ConvertError> {
+    pub(crate) fn validate_ok_for_cxx(&self) -> Result<(), InvalidIdentError> {
         validate_ident_ok_for_cxx(self.get_final_item())
     }
 
@@ -172,14 +178,6 @@
         }
     }
 
-    pub(crate) fn get_final_cpp_item(&self) -> String {
-        let special_cpp_name = known_types().special_cpp_name(self);
-        match special_cpp_name {
-            Some(name) => name,
-            None => self.1.to_string(),
-        }
-    }
-
     pub(crate) fn to_type_path(&self) -> TypePath {
         if let Some(known_type_path) = known_types().known_type_type_path(self) {
             known_type_path
@@ -228,26 +226,44 @@
     }
 }
 
+impl std::fmt::Debug for QualifiedName {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        Display::fmt(self, f)
+    }
+}
+
+/// Problems representing C++ identifiers in a way which is compatible with
+/// cxx.
+#[derive(Error, Clone, Debug)]
+pub enum InvalidIdentError {
+    #[error("Names containing __ are reserved by C++ so not acceptable to cxx")]
+    TooManyUnderscores,
+    #[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("The item name '{0}' is a reserved word in Rust.")]
+    ReservedName(String),
+}
+
 /// cxx doesn't allow identifiers containing __. These are OK elsewhere
 /// in our output mod. It would be nice in future to think of a way we
 /// can enforce this using the Rust type system, e.g. a newtype
 /// wrapper for a CxxCompatibleIdent which is used in any context
 /// where code will be output as part of the `#[cxx::bridge]` mod.
-pub fn validate_ident_ok_for_cxx(id: &str) -> Result<(), ConvertError> {
+pub fn validate_ident_ok_for_cxx(id: &str) -> Result<(), InvalidIdentError> {
     validate_ident_ok_for_rust(id)?;
     if id.contains("__") {
-        Err(ConvertError::TooManyUnderscores)
+        Err(InvalidIdentError::TooManyUnderscores)
     } else if id.starts_with("_bindgen_ty_") {
-        Err(ConvertError::BindgenTy)
+        Err(InvalidIdentError::BindgenTy)
     } else {
         Ok(())
     }
 }
 
-pub fn validate_ident_ok_for_rust(label: &str) -> Result<(), ConvertError> {
+pub fn validate_ident_ok_for_rust(label: &str) -> Result<(), InvalidIdentError> {
     let id = make_ident(label);
     syn::parse2::<syn::Ident>(id.into_token_stream())
-        .map_err(|_| ConvertError::ReservedName(label.to_string()))
+        .map_err(|_| InvalidIdentError::ReservedName(label.to_string()))
         .map(|_| ())
 }
 
diff --git a/third_party/autocxx/examples/chromium-fake-render-frame-host/Cargo.toml b/third_party/autocxx/examples/chromium-fake-render-frame-host/Cargo.toml
index 3862f2c..9cc05cd 100644
--- a/third_party/autocxx/examples/chromium-fake-render-frame-host/Cargo.toml
+++ b/third_party/autocxx/examples/chromium-fake-render-frame-host/Cargo.toml
@@ -13,9 +13,9 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/examples/chromium-fake-render-frame-host/src/render_frame_host.rs b/third_party/autocxx/examples/chromium-fake-render-frame-host/src/render_frame_host.rs
index 9c2bf51..761c8a8 100644
--- a/third_party/autocxx/examples/chromium-fake-render-frame-host/src/render_frame_host.rs
+++ b/third_party/autocxx/examples/chromium-fake-render-frame-host/src/render_frame_host.rs
@@ -209,7 +209,7 @@
     }
 }
 
-#[is_subclass(superclass("content::WebContentsObserver"))]
+#[subclass(superclass("content::WebContentsObserver"))]
 #[doc(hidden)]
 pub struct RenderFrameHostForWebContents {
     rfh: *mut ffi::content::RenderFrameHost,
diff --git a/third_party/autocxx/examples/cpp_calling_rust/Cargo.toml b/third_party/autocxx/examples/cpp_calling_rust/Cargo.toml
new file mode 100644
index 0000000..2814194
--- /dev/null
+++ b/third_party/autocxx/examples/cpp_calling_rust/Cargo.toml
@@ -0,0 +1,26 @@
+# 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 = "cpp_calling_rust"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+cxx = "1.0.78"
+autocxx = { path = "../..", version="0.26.0" }
+uwuify = "0.2.2"
+textwrap = "0.14"
+fastrand = "1.5.0"
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.26.0" }
+regex = "1.5.4"
+miette = { version="5", features = [ "fancy" ] }
diff --git a/third_party/autocxx/examples/cpp_calling_rust/build.rs b/third_party/autocxx/examples/cpp_calling_rust/build.rs
new file mode 100644
index 0000000..60cc460
--- /dev/null
+++ b/third_party/autocxx/examples/cpp_calling_rust/build.rs
@@ -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.
+
+fn main() -> miette::Result<()> {
+    let path = std::path::PathBuf::from("src");
+    let mut b = autocxx_build::Builder::new("src/main.rs", &[&path])
+        .auto_allowlist(true)
+        .build()?;
+    b.flag_if_supported("-std=c++17")
+        .file("src/input.cc")
+        .compile("autocxx-cpp-calling-rust-example");
+    println!("cargo:rerun-if-changed=src/main.rs");
+    println!("cargo:rerun-if-changed=src/input.cc");
+    println!("cargo:rerun-if-changed=src/input.h");
+    Ok(())
+}
diff --git a/third_party/autocxx/examples/cpp_calling_rust/src/input.cc b/third_party/autocxx/examples/cpp_calling_rust/src/input.cc
new file mode 100644
index 0000000..64c446a
--- /dev/null
+++ b/third_party/autocxx/examples/cpp_calling_rust/src/input.cc
@@ -0,0 +1,19 @@
+// 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 "cxxgen.h"
+#include "input.h"
+
+void jurassic() {
+    rust::Box<Dinosaur> prey = new_dinosaur(false);
+    rust::Box<Dinosaur> predator = new_dinosaur(true);
+    prey->roar();
+    predator->roar();
+    predator->eat(std::move(prey));
+    go_extinct();
+}
\ No newline at end of file
diff --git a/third_party/autocxx/examples/cpp_calling_rust/src/input.h b/third_party/autocxx/examples/cpp_calling_rust/src/input.h
new file mode 100644
index 0000000..206adc6
--- /dev/null
+++ b/third_party/autocxx/examples/cpp_calling_rust/src/input.h
@@ -0,0 +1,16 @@
+// 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>
+
+void jurassic();
\ No newline at end of file
diff --git a/third_party/autocxx/examples/cpp_calling_rust/src/main.rs b/third_party/autocxx/examples/cpp_calling_rust/src/main.rs
new file mode 100644
index 0000000..3fe6404
--- /dev/null
+++ b/third_party/autocxx/examples/cpp_calling_rust/src/main.rs
@@ -0,0 +1,58 @@
+// 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 shows calls from C++ back into Rust. That's really not
+// the main purpose of autocxx, and this support is immature. If you're
+// primarily doing this sort of thing, look into other tools such as
+// cbindgen or cxx.
+
+use autocxx::prelude::*;
+use std::pin::Pin;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("jurassic")
+}
+
+fn main() {
+    ffi::jurassic();
+}
+
+#[autocxx::extern_rust::extern_rust_type]
+pub struct Dinosaur {
+    carnivore: bool,
+}
+
+#[autocxx::extern_rust::extern_rust_function]
+pub fn new_dinosaur(carnivore: bool) -> Box<Dinosaur> {
+    Box::new(Dinosaur { carnivore })
+}
+
+impl Dinosaur {
+    #[autocxx::extern_rust::extern_rust_function]
+    fn roar(&self) {
+        println!("Roar");
+    }
+
+    #[autocxx::extern_rust::extern_rust_function]
+    fn eat(self: Pin<&mut Dinosaur>, other_dinosaur: Box<Dinosaur>) {
+        assert!(self.carnivore);
+        other_dinosaur.get_eaten();
+        println!("Nom nom");
+    }
+
+    fn get_eaten(&self) {
+        println!("Uh-oh");
+    }
+}
+
+#[autocxx::extern_rust::extern_rust_function]
+pub fn go_extinct() {
+    println!("Boom")
+}
diff --git a/third_party/autocxx/examples/llvm/Cargo.toml b/third_party/autocxx/examples/llvm/Cargo.toml
index 4c03db7..3c274ac 100644
--- a/third_party/autocxx/examples/llvm/Cargo.toml
+++ b/third_party/autocxx/examples/llvm/Cargo.toml
@@ -13,9 +13,9 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.17.2" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.17.2" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/examples/non-trivial-type-on-stack/Cargo.toml b/third_party/autocxx/examples/non-trivial-type-on-stack/Cargo.toml
index 110bd1b..7af139e 100644
--- a/third_party/autocxx/examples/non-trivial-type-on-stack/Cargo.toml
+++ b/third_party/autocxx/examples/non-trivial-type-on-stack/Cargo.toml
@@ -13,9 +13,9 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/examples/pod/Cargo.toml b/third_party/autocxx/examples/pod/Cargo.toml
index 9f4ee5a..e05d261 100644
--- a/third_party/autocxx/examples/pod/Cargo.toml
+++ b/third_party/autocxx/examples/pod/Cargo.toml
@@ -13,9 +13,9 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/examples/reference-wrappers/Cargo.toml b/third_party/autocxx/examples/reference-wrappers/Cargo.toml
index cb85e80..93c68ba 100644
--- a/third_party/autocxx/examples/reference-wrappers/Cargo.toml
+++ b/third_party/autocxx/examples/reference-wrappers/Cargo.toml
@@ -13,9 +13,9 @@
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features=["fancy"]}
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/examples/reference-wrappers/build.rs b/third_party/autocxx/examples/reference-wrappers/build.rs
index 64c573d..8e15dd0 100644
--- a/third_party/autocxx/examples/reference-wrappers/build.rs
+++ b/third_party/autocxx/examples/reference-wrappers/build.rs
@@ -10,9 +10,11 @@
     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");
+        .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/third_party/autocxx/examples/reference-wrappers/src/input.h b/third_party/autocxx/examples/reference-wrappers/src/input.h
index 5e3c6e9..dd37dfa 100644
--- a/third_party/autocxx/examples/reference-wrappers/src/input.h
+++ b/third_party/autocxx/examples/reference-wrappers/src/input.h
@@ -18,6 +18,7 @@
     Goat() : horns(0) {}
     void add_a_horn();
     std::string describe() const;
+    uint32_t& get_horns() { return horns; }
 private:
     uint32_t horns;
 };
diff --git a/third_party/autocxx/examples/reference-wrappers/src/main.rs b/third_party/autocxx/examples/reference-wrappers/src/main.rs
index 6bea2ff..ca3e0cd 100644
--- a/third_party/autocxx/examples/reference-wrappers/src/main.rs
+++ b/third_party/autocxx/examples/reference-wrappers/src/main.rs
@@ -23,16 +23,52 @@
 // especially in the absence of the Rust "arbitrary self types"
 // feature.
 
+// Necessary to be able to call methods on reference wrappers.
+// For that reason, this example only builds on nightly Rust.
+#![feature(arbitrary_self_types)]
+
 use autocxx::prelude::*;
+use std::pin::Pin;
 
 include_cpp! {
     #include "input.h"
     // This next line enables C++ reference wrappers
+    // This is what requires the 'arbitrary_self_types' feature.
     safety!(unsafe_references_wrapped)
     generate!("Goat")
     generate!("Field")
 }
 
+impl ffi::Goat {
+    // Methods can be called on a CppRef<T> or &CppRef<T>
+    fn bleat(self: CppRef<Self>) {
+        println!("Bleat");
+    }
+}
+
+trait FarmProduce {
+    // Traits can be defined on a CppRef<T> so long as Self: Sized
+    fn sell(self: &CppRef<Self>)
+    where
+        Self: Sized;
+}
+
+impl FarmProduce for ffi::Goat {
+    fn sell(self: &CppRef<Self>) {
+        println!("Selling goat");
+    }
+}
+
+trait FarmArea {
+    fn maintain(self: CppRef<Self>);
+}
+
+impl FarmArea for ffi::Field {
+    fn maintain(self: CppRef<Self>) {
+        println!("Maintaining");
+    }
+}
+
 fn main() {
     // Create a cxx::UniquePtr as normal for a Field object.
     let field = ffi::Field::new().within_unique_ptr();
@@ -47,21 +83,36 @@
     // 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);
+    let field = CppUniquePtrPin::new(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...
+    // to call methods in C++. Quite often those methods will
+    // return other references, like this.
     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.
+
+    // We can still use these C++ references to call Rust methods,
+    // so long as those methods have a "self" type of a
+    // C++ reference not a Rust reference.
+    another_goat.clone().bleat();
+    another_goat.sell();
+
+    // But most commonly, C++ references are simply used as the 'this'
+    // type when calling other C++ methods, like this.
     assert_eq!(
         another_goat
             .describe() // returns a UniquePtr<CxxString>, there
-                // are no Rust or C++ references involved at this point.
+            // are no Rust or C++ references involved at this point.
             .as_ref()
             .unwrap()
             .to_string_lossy(),
         "This goat has 0 horns."
     );
+
+    // We can even use trait objects, though it's a bit of a fiddle.
+    let farm_area: Pin<Box<dyn FarmArea>> = ffi::Field::new().within_box();
+    let farm_area = CppPin::from_pinned_box(farm_area);
+    farm_area.as_cpp_ref().maintain();
 }
diff --git a/third_party/autocxx/examples/s2/Cargo.toml b/third_party/autocxx/examples/s2/Cargo.toml
index 04524b1..c93706b 100644
--- a/third_party/autocxx/examples/s2/Cargo.toml
+++ b/third_party/autocxx/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.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/examples/steam-mini/Cargo.toml b/third_party/autocxx/examples/steam-mini/Cargo.toml
index ceb81ff..5162f96 100644
--- a/third_party/autocxx/examples/steam-mini/Cargo.toml
+++ b/third_party/autocxx/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.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/examples/subclass/Cargo.toml b/third_party/autocxx/examples/subclass/Cargo.toml
index dc93f53..a19ea27 100644
--- a/third_party/autocxx/examples/subclass/Cargo.toml
+++ b/third_party/autocxx/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.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 uwuify = "0.2.2"
-textwrap = "0.14"
+textwrap = "0.15"
 fastrand = "1.5.0"
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
 regex = "1.5.4"
-miette = { version="4.3", features = [ "fancy" ] }
+miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/examples/subclass/src/main.rs b/third_party/autocxx/examples/subclass/src/main.rs
index 2841598..095abd4 100644
--- a/third_party/autocxx/examples/subclass/src/main.rs
+++ b/third_party/autocxx/examples/subclass/src/main.rs
@@ -34,7 +34,7 @@
 // See the main function at the bottom for how this subclass
 // is instantiated.
 
-#[is_subclass(superclass("MessageDisplayer"))]
+#[subclass(superclass("MessageDisplayer"))]
 #[derive(Default)]
 pub struct UwuDisplayer {}
 
@@ -60,7 +60,7 @@
 // for now, at least, we can't hold non-trivial C++ objects on the Rust stack.)
 // All the boxing and unboxing is done automatically by autocxx layers.
 
-#[is_subclass(superclass("MessageProducer"))]
+#[subclass(superclass("MessageProducer"))]
 #[derive(Default)]
 pub struct QuoteProducer;
 
@@ -93,7 +93,7 @@
 // doing stuff. In C++ you'd probably need a const_cast. Here we use
 // interior mutability.
 
-#[is_subclass(superclass("MessageDisplayer"))]
+#[subclass(superclass("MessageDisplayer"))]
 pub struct BoxDisplayer {
     message_count: RefCell<usize>,
 }
@@ -103,7 +103,7 @@
         Self::new_rust_owned(Self {
             // As we're allocating this class ourselves instead of using [`Default`]
             // we need to initialize the `cpp_peer` member ourselves. This member is
-            // inserted by the `#[is_subclass]` annotation. autocxx will
+            // inserted by the `#[subclass]` annotation. autocxx will
             // later use this to store a pointer back to the C++ peer.
             cpp_peer: Default::default(),
             message_count: RefCell::new(1usize),
diff --git a/third_party/autocxx/examples/subclass/src/messages.h b/third_party/autocxx/examples/subclass/src/messages.h
index 02ff248..bbf6118 100644
--- a/third_party/autocxx/examples/subclass/src/messages.h
+++ b/third_party/autocxx/examples/subclass/src/messages.h
@@ -12,7 +12,6 @@
 #pragma once
 
 #include <string>
-#include <memory>
 
 class MessageProducer {
 public:
diff --git a/third_party/autocxx/gen/build/Cargo.toml b/third_party/autocxx/gen/build/Cargo.toml
index 678a844..d101f91 100644
--- a/third_party/autocxx/gen/build/Cargo.toml
+++ b/third_party/autocxx/gen/build/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-build"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2021"
 license = "MIT OR Apache-2.0"
@@ -18,14 +18,16 @@
 categories = ["development-tools::ffi", "api-bindings"]
 
 [features]
-runtime = [ "autocxx-engine/runtime" ]
-static = [ "autocxx-engine/static" ]
+runtime = ["autocxx-engine/runtime"]
+static = ["autocxx-engine/static"]
 
 [dependencies]
-autocxx-engine = { version="=0.22.3", path="../../engine", features = ["build"] }
+autocxx-engine = { version = "=0.26.0", path = "../../engine", features = [
+    "build",
+] }
 env_logger = "0.9.0"
 indexmap = "1.8"
 
 [dependencies.syn]
-version = "1.0"
-features = [ "full" ]
+version = "2.0"
+features = ["full"]
diff --git a/third_party/autocxx/gen/build/src/lib.rs b/third_party/autocxx/gen/build/src/lib.rs
index c1df580..ccef184 100644
--- a/third_party/autocxx/gen/build/src/lib.rs
+++ b/third_party/autocxx/gen/build/src/lib.rs
@@ -45,7 +45,7 @@
     fn record_header_file_dependency(&self, filename: &str) {
         let mut already = self.printed_already.lock().unwrap();
         if already.insert(filename.into()) {
-            println!("cargo:rerun-if-changed={}", filename);
+            println!("cargo:rerun-if-changed={filename}");
         }
     }
 }
diff --git a/third_party/autocxx/gen/cmd/Cargo.toml b/third_party/autocxx/gen/cmd/Cargo.toml
index 51eb56c..7a78ba3 100644
--- a/third_party/autocxx/gen/cmd/Cargo.toml
+++ b/third_party/autocxx/gen/cmd/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-gen"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2021"
 license = "MIT OR Apache-2.0"
@@ -18,24 +18,24 @@
 categories = ["development-tools::ffi", "api-bindings"]
 
 [features]
-runtime = [ "autocxx-engine/runtime" ]
-static = [ "autocxx-engine/static" ]
+runtime = ["autocxx-engine/runtime"]
+static = ["autocxx-engine/static"]
 
 [dependencies]
-autocxx-engine = { version="=0.22.3", path="../../engine" }
+autocxx-engine = { version = "=0.26.0", path = "../../engine" }
 clap = { version = "3.1.2", features = ["cargo"] }
 proc-macro2 = "1.0"
 env_logger = "0.9.0"
-miette = { version="4.3", features=["fancy"]}
+miette = { version = "5", features = ["fancy"] }
 pathdiff = "0.2.1"
 indexmap = "1.8"
 
 [dev-dependencies]
-assert_cmd = "1.0.3"
+assert_cmd = "2"
 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.68"
-itertools = "0.10.3"
\ No newline at end of file
+autocxx = { path = "../.." }
+autocxx-integration-tests = { path = "../../integration-tests", version = "=0.26.0" }
+cxx = "1.0.78"
+itertools = "0.10.3"
diff --git a/third_party/autocxx/gen/cmd/src/depfile.rs b/third_party/autocxx/gen/cmd/src/depfile.rs
index d8d3626..315ff89 100644
--- a/third_party/autocxx/gen/cmd/src/depfile.rs
+++ b/third_party/autocxx/gen/cmd/src/depfile.rs
@@ -44,7 +44,7 @@
         let dependency_list = self.dependencies.join(" \\\n  ");
         for output in &self.outputs {
             self.file
-                .write_all(format!("{}: {}\n\n", output, dependency_list).as_bytes())?
+                .write_all(format!("{output}: {dependency_list}\n\n").as_bytes())?
         }
         Ok(())
     }
diff --git a/third_party/autocxx/gen/cmd/src/main.rs b/third_party/autocxx/gen/cmd/src/main.rs
index f8db241..4d533ff 100644
--- a/third_party/autocxx/gen/cmd/src/main.rs
+++ b/third_party/autocxx/gen/cmd/src/main.rs
@@ -11,8 +11,8 @@
 mod depfile;
 
 use autocxx_engine::{
-    generate_rs_archive, generate_rs_single, parse_file, AutocxxgenHeaderNamer, CxxgenHeaderNamer,
-    RebuildDependencyRecorder,
+    generate_rs_archive, generate_rs_single, get_cxx_header_bytes, parse_file,
+    AutocxxgenHeaderNamer, CxxgenHeaderNamer, RebuildDependencyRecorder,
 };
 use clap::{crate_authors, crate_version, Arg, ArgGroup, Command};
 use depfile::Depfile;
@@ -132,7 +132,7 @@
         .arg(
             Arg::new("gen-rs-include")
                 .long("gen-rs-include")
-                .help("whether to generate Rust files for inclusion using autocxx_macro (suffix will be .include.rs)")
+                .help("whether to generate Rust files for inclusion using autocxx_macro")
         )
         .arg(
             Arg::new("gen-rs-archive")
@@ -156,7 +156,7 @@
         .arg(
             Arg::new("fix-rs-include-name")
                 .long("fix-rs-include-name")
-                .help("Make the name of the .rs file predictable. You must set AUTOCXX_RS_FILE during Rust build time to educate autocxx_macro about your choice.")
+                .help("Make the name of the .rs file predictable (suffix will be .include.rs). You must set AUTOCXX_RS_FILE during Rust build time to educate autocxx_macro about your choice.")
                 .requires("gen-rs-include")
         )
         .arg(
@@ -177,6 +177,11 @@
                 .takes_value(true),
         )
         .arg(
+            Arg::new("generate-cxx-h")
+                .long("generate-cxx-h")
+                .help("whether to generate cxx.h header file. If you already knew where to find cxx.h, consider using --cxx-h-path")
+        )
+        .arg(
             Arg::new("cxx-h-path")
                 .long("cxx-h-path")
                 .value_name("PREFIX")
@@ -247,6 +252,10 @@
         autocxxgen_header_namer,
         cxxgen_header_namer,
     };
+    let codegen_options = autocxx_engine::CodegenOptions {
+        cpp_codegen_options,
+        ..Default::default()
+    };
     let depfile = match matches.value_of("depfile") {
         None => None,
         Some(depfile_path) => {
@@ -277,12 +286,20 @@
             incs.clone(),
             &extra_clang_args,
             dep_recorder,
-            &cpp_codegen_options,
+            &codegen_options,
         )?;
     }
 
     // Finally start to write the C++ and Rust out.
     let outdir: PathBuf = matches.value_of_os("outdir").unwrap().into();
+
+    if !outdir.exists() {
+        use miette::WrapErr as _;
+        std::fs::create_dir_all(&outdir)
+            .into_diagnostic()
+            .wrap_err_with(|| format!("Failed to create `outdir` '{}'", outdir.display()))?;
+    }
+
     let mut writer = FileWriter {
         depfile: &depfile,
         outdir: &outdir,
@@ -290,14 +307,14 @@
     };
     if matches.is_present("gen-cpp") {
         let cpp = matches.value_of("cpp-extension").unwrap();
-        let name_cc_file = |counter| format!("gen{}.{}", counter, cpp);
+        let name_cc_file = |counter| format!("gen{counter}.{cpp}");
         let mut counter = 0usize;
         for include_cxx in parsed_files
             .iter()
             .flat_map(|file| file.get_cpp_buildables())
         {
             let generations = include_cxx
-                .generate_h_and_cxx(&cpp_codegen_options)
+                .generate_h_and_cxx(&codegen_options.cpp_codegen_options)
                 .expect("Unable to generate header and C++ code");
             for pair in generations.0 {
                 let cppname = name_cc_file(counter);
@@ -306,7 +323,7 @@
                 counter += 1;
             }
         }
-        drop(cpp_codegen_options);
+        drop(codegen_options);
         // Write placeholders to ensure we always make exactly 'n' of each file type.
         writer.write_placeholders(counter, desired_number, name_cc_file)?;
         writer.write_placeholders(
@@ -320,6 +337,14 @@
             name_autocxxgen_h,
         )?;
     }
+
+    if matches.is_present("generate-cxx-h") {
+        writer.write_to_file(
+            "cxx.h".to_string(),
+            &get_cxx_header_bytes(suppress_system_headers),
+        )?;
+    }
+
     if matches.is_present("gen-rs-include") {
         if !matches.is_present("fix-rs-include-name") && desired_number.is_some() {
             return Err(miette::Report::msg(
@@ -356,15 +381,15 @@
 }
 
 fn name_autocxxgen_h(counter: usize) -> String {
-    format!("autocxxgen{}.h", counter)
+    format!("autocxxgen{counter}.h")
 }
 
 fn name_cxxgen_h(counter: usize) -> String {
-    format!("gen{}.h", counter)
+    format!("gen{counter}.h")
 }
 
 fn name_include_rs(counter: usize) -> String {
-    format!("gen{}.include.rs", counter)
+    format!("gen{counter}.include.rs")
 }
 
 fn get_dependency_recorder(depfile: Rc<RefCell<Depfile>>) -> Box<dyn RebuildDependencyRecorder> {
@@ -391,7 +416,7 @@
     ) -> miette::Result<()> {
         if let Some(desired_number) = desired_number {
             if counter > desired_number {
-                return Err(miette::Report::msg("More files were generated than expected. Increase the value passed to --generate-exact or reduce the number of include_cpp! sections."));
+                return Err(miette::Report::msg(format!("{counter} files were generated. Increase the value passed to --generate-exact or reduce the number of include_cpp! sections.")));
             }
             while counter < desired_number {
                 let fname = filename(counter);
@@ -420,7 +445,7 @@
         let mut f = File::create(&path).into_diagnostic()?;
         f.write_all(content).into_diagnostic()?;
         if self.written.contains(&filename) {
-            return Err(miette::Report::msg(format!("autocxx_gen would write two files entitled '{}' which would have conflicting contents. Consider using --generate-exact.", filename)));
+            return Err(miette::Report::msg(format!("autocxx_gen would write two files entitled '{filename}' which would have conflicting contents. Consider using --generate-exact.")));
         }
         self.written.insert(filename);
         Ok(())
diff --git a/third_party/autocxx/gen/cmd/tests/cmd_test.rs b/third_party/autocxx/gen/cmd/tests/cmd_test.rs
index 6fd3382..4ec3942 100644
--- a/third_party/autocxx/gen/cmd/tests/cmd_test.rs
+++ b/third_party/autocxx/gen/cmd/tests/cmd_test.rs
@@ -88,7 +88,8 @@
         .arg(demo_code_dir.to_str().unwrap())
         .arg("--outdir")
         .arg(tmp_dir.path().to_str().unwrap())
-        .arg("--gen-cpp");
+        .arg("--gen-cpp")
+        .arg("--generate-cxx-h");
     cmd.arg(match rs_gen_mode {
         RsGenMode::Single => "--gen-rs-include",
         RsGenMode::Archive => "--gen-rs-archive",
@@ -109,8 +110,6 @@
 fn test_gen() -> Result<(), Box<dyn std::error::Error>> {
     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()))?;
     std::env::set_var("OUT_DIR", tmp_dir.path().to_str().unwrap());
     let r = build_from_folder(
         tmp_dir.path(),
@@ -130,8 +129,6 @@
 fn test_gen_archive() -> Result<(), Box<dyn std::error::Error>> {
     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()))?;
     let r = build_from_folder(
         tmp_dir.path(),
         &tmp_dir.path().join("demo/main.rs"),
@@ -150,8 +147,6 @@
 fn test_gen_archive_first_entry() -> Result<(), Box<dyn std::error::Error>> {
     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()))?;
     let r = build_from_folder(
         tmp_dir.path(),
         &tmp_dir.path().join("demo/main.rs"),
@@ -176,8 +171,6 @@
 fn test_gen_archive_second_entry() -> Result<(), Box<dyn std::error::Error>> {
     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()))?;
     let r = build_from_folder(
         tmp_dir.path(),
         &tmp_dir.path().join("demo/main.rs"),
@@ -217,10 +210,8 @@
         files,
         vec!["directive1.rs", "directive2.rs"],
     )?;
-    File::create(tmp_dir.path().join("cxx.h"))
-        .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
     // We've asked to create 8 C++ files, mostly blank. Build 'em all.
-    let cpp_files = (0..7).map(|id| format!("gen{}.cc", id)).collect_vec();
+    let cpp_files = (0..7).map(|id| format!("gen{id}.cc")).collect_vec();
     let cpp_files = cpp_files.iter().map(|s| s.as_str()).collect_vec();
     let r = build_from_folder(
         tmp_dir.path(),
@@ -273,8 +264,6 @@
     assert_not_contentful(&tmp_dir, "autocxxgen1.h");
     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"),
@@ -319,7 +308,7 @@
 
 fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
     let path = dir.join(filename);
-    let mut f = File::create(&path).expect("Unable to create file");
+    let mut f = File::create(path).expect("Unable to create file");
     f.write_all(content).expect("Unable to write file");
 }
 
@@ -330,8 +319,7 @@
     }
     assert!(
         p.metadata().unwrap().len() > BLANK.len().try_into().unwrap(),
-        "File {} is empty",
-        fname
+        "File {fname} is empty"
     );
 }
 
@@ -350,7 +338,7 @@
 
 fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) {
     let p = outdir.path().join(fname);
-    let content = std::fs::read_to_string(&p).expect(fname);
-    eprintln!("content = {}", content);
+    let content = std::fs::read_to_string(p).expect(fname);
+    eprintln!("content = {content}");
     assert!(content.contains(pattern));
 }
diff --git a/third_party/autocxx/integration-tests/Cargo.toml b/third_party/autocxx/integration-tests/Cargo.toml
index 7791ea5..78384ce 100644
--- a/third_party/autocxx/integration-tests/Cargo.toml
+++ b/third_party/autocxx/integration-tests/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-integration-tests"
-version = "0.22.3"
+version = "0.26.0"
 autotests = false
 edition = "2021"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
@@ -27,20 +27,25 @@
 cc = "1.0"
 quote = "1.0"
 once_cell = "1.7"
-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.3" }
+autocxx = { path = "..", version = "=0.26.0" }
+autocxx-engine = { version = "=0.26.0", path = "../engine", features = [
+    "build",
+] }
+moveit = { version = "0.6", features = [ "cxx" ] }
 link-cplusplus = "1.0"
-tempfile = "3.1"
+tempfile = "3.4"
 indoc = "1.0"
 log = "0.4"
-cxx = "1.0.68"
+cxx = "1.0.78"
 itertools = "0.10"
+rustversion = "1.0"
+static_assertions = "1.1.0"
 
 [dependencies.syn]
 version = "1.0.39"
-features = [ "full" ]
+features = ["full"]
 #features = [ "full", "extra-traits" ]
 
 [[test]]
diff --git a/third_party/autocxx/integration-tests/src/lib.rs b/third_party/autocxx/integration-tests/src/lib.rs
index f4667d5..2335ee5 100644
--- a/third_party/autocxx/integration-tests/src/lib.rs
+++ b/third_party/autocxx/integration-tests/src/lib.rs
@@ -162,7 +162,7 @@
             );
         }
         let temp_path = self.temp_dir.path().to_str().unwrap();
-        let mut rustflags = format!("-L {}", temp_path);
+        let mut rustflags = format!("-L {temp_path}");
         if std::env::var_os("AUTOCXX_ASAN").is_some() {
             rustflags.push_str(" -Z sanitizer=address -Clinker=clang++ -Clink-arg=-fuse-ld=lld");
         }
@@ -194,6 +194,7 @@
 }
 
 /// A positive test, we expect to pass.
+#[track_caller]
 pub fn run_test(
     cxx_code: &str,
     header_code: &str,
@@ -210,6 +211,7 @@
         None,
         None,
         "unsafe_ffi",
+        None,
     )
     .unwrap()
 }
@@ -265,6 +267,7 @@
         code_checker,
         extra_rust,
         "unsafe_ffi",
+        None,
     )
     .unwrap()
 }
@@ -297,6 +300,7 @@
         None,
         None,
         "unsafe_ffi",
+        None,
     )
     .expect_err("Unexpected success");
 }
@@ -319,6 +323,7 @@
         code_checker,
         extra_rust,
         "unsafe_ffi",
+        None,
     )
     .expect_err("Unexpected success");
 }
@@ -369,10 +374,13 @@
     rust_code_checker: Option<CodeChecker>,
     extra_rust: Option<TokenStream>,
     safety_policy: &str,
+    module_attributes: Option<TokenStream>,
 ) -> Result<(), TestError> {
     let hexathorpe = Token![#](Span::call_site());
     let safety_policy = format_ident!("{}", safety_policy);
     let unexpanded_rust = quote! {
+            #module_attributes
+
             use autocxx::prelude::*;
 
             include_cpp!(
@@ -413,14 +421,12 @@
     builder_modifier: Option<BuilderModifier>,
     rust_code_checker: Option<CodeChecker>,
 ) -> Result<(), TestError> {
+    let builder_modifier = consider_forcing_wrapper_generation(builder_modifier);
+
     const HEADER_NAME: &str = "input.h";
     // Step 2: Write the C++ header snippet to a temp file
     let tdir = tempdir().unwrap();
-    write_to_file(
-        &tdir,
-        HEADER_NAME,
-        &format!("#pragma once\n{}", header_code),
-    );
+    write_to_file(&tdir, HEADER_NAME, &format!("#pragma once\n{header_code}"));
     write_to_file(&tdir, "cxx.h", HEADER);
 
     rust_code.append_all(quote! {
@@ -431,7 +437,7 @@
 
     let write_rust_to_file = |ts: &TokenStream| -> PathBuf {
         // Step 3: Write the Rust code to a temp file
-        let rs_code = format!("{}", ts);
+        let rs_code = format!("{ts}");
         write_to_file(&tdir, "input.rs", &rs_code)
     };
 
@@ -441,7 +447,7 @@
     let rs_path = write_rust_to_file(&rust_code);
 
     info!("Path is {:?}", tdir.path());
-    let builder = Builder::<TestBuilderContext>::new(&rs_path, &[tdir.path()])
+    let builder = Builder::<TestBuilderContext>::new(&rs_path, [tdir.path()])
         .custom_gendir(target_dir.clone());
     let builder = if let Some(builder_modifier) = &builder_modifier {
         builder_modifier.modify_autocxx_builder(builder)
@@ -470,7 +476,7 @@
     if !cxx_code.is_empty() {
         // Step 4: Write the C++ code snippet to a .cc file, along with a #include
         //         of the header emitted in step 5.
-        let cxx_code = format!("#include \"input.h\"\n#include \"cxxgen.h\"\n{}", cxx_code);
+        let cxx_code = format!("#include \"input.h\"\n#include \"cxxgen.h\"\n{cxx_code}");
         let cxx_path = write_to_file(&tdir, "input.cxx", &cxx_code);
         b.file(cxx_path);
     }
@@ -485,7 +491,7 @@
         .try_compile("autocxx-demo")
         .map_err(TestError::CppBuild)?;
     if KEEP_TEMPDIRS {
-        println!("Generated .rs files: {:?}", generated_rs_files);
+        println!("Generated .rs files: {generated_rs_files:?}");
     }
     // Step 8: use the trybuild crate to build the Rust file.
     let r = get_builder().lock().unwrap().build(
@@ -506,3 +512,38 @@
     }
     Ok(())
 }
+
+/// If AUTOCXX_FORCE_WRAPPER_GENERATION is set, always force both C++
+/// and Rust side shims, for extra testing of obscure code paths.
+fn consider_forcing_wrapper_generation(
+    existing_builder_modifier: Option<BuilderModifier>,
+) -> Option<BuilderModifier> {
+    if std::env::var("AUTOCXX_FORCE_WRAPPER_GENERATION").is_err() {
+        existing_builder_modifier
+    } else {
+        Some(Box::new(ForceWrapperGeneration(existing_builder_modifier)))
+    }
+}
+
+struct ForceWrapperGeneration(Option<BuilderModifier>);
+
+impl BuilderModifierFns for ForceWrapperGeneration {
+    fn modify_autocxx_builder<'a>(
+        &self,
+        builder: Builder<'a, TestBuilderContext>,
+    ) -> Builder<'a, TestBuilderContext> {
+        let builder = builder.force_wrapper_generation(true);
+        if let Some(modifier) = &self.0 {
+            modifier.modify_autocxx_builder(builder)
+        } else {
+            builder
+        }
+    }
+    fn modify_cc_builder<'a>(&self, builder: &'a mut cc::Build) -> &'a mut cc::Build {
+        if let Some(modifier) = &self.0 {
+            modifier.modify_cc_builder(builder)
+        } else {
+            builder
+        }
+    }
+}
diff --git a/third_party/autocxx/integration-tests/tests/builder_modifiers.rs b/third_party/autocxx/integration-tests/tests/builder_modifiers.rs
index 0cf763e..5fcc458 100644
--- a/third_party/autocxx/integration-tests/tests/builder_modifiers.rs
+++ b/third_party/autocxx/integration-tests/tests/builder_modifiers.rs
@@ -14,11 +14,19 @@
     make_clang_arg_adder(&["-std=c++17"])
 }
 
-struct ClangArgAdder(Vec<String>);
+struct ClangArgAdder(Vec<String>, Vec<String>);
 
 pub(crate) fn make_clang_arg_adder(args: &[&str]) -> Option<BuilderModifier> {
+    make_clang_optional_arg_adder(args, &[])
+}
+
+pub(crate) fn make_clang_optional_arg_adder(
+    args: &[&str],
+    optional_args: &[&str],
+) -> Option<BuilderModifier> {
     let args: Vec<_> = args.iter().map(|a| a.to_string()).collect();
-    Some(Box::new(ClangArgAdder(args)))
+    let optional_args: Vec<_> = optional_args.iter().map(|a| a.to_string()).collect();
+    Some(Box::new(ClangArgAdder(args, optional_args)))
 }
 
 impl BuilderModifierFns for ClangArgAdder {
@@ -34,6 +42,9 @@
         for f in &self.0 {
             builder = builder.flag(f);
         }
+        for f in &self.1 {
+            builder = builder.flag_if_supported(f);
+        }
         builder
     }
 }
diff --git a/third_party/autocxx/integration-tests/tests/code_checkers.rs b/third_party/autocxx/integration-tests/tests/code_checkers.rs
index 619c79c..ae12e3c 100644
--- a/third_party/autocxx/integration-tests/tests/code_checkers.rs
+++ b/third_party/autocxx/integration-tests/tests/code_checkers.rs
@@ -87,8 +87,7 @@
         for msg in &self.0 {
             if !toks.contains(msg) {
                 return Err(TestError::RsCodeExaminationFail(format!(
-                    "Couldn't find string '{}'",
-                    msg
+                    "Couldn't find string '{msg}'"
                 )));
             };
         }
@@ -110,8 +109,7 @@
             let needle = msg.to_string();
             if !haystack.contains(&needle) {
                 return Err(TestError::RsCodeExaminationFail(format!(
-                    "Couldn't find tokens '{}'",
-                    needle
+                    "Couldn't find tokens '{needle}'"
                 )));
             };
         }
@@ -146,7 +144,7 @@
         for filename in cpp {
             let file = File::open(filename).unwrap();
             let lines = BufReader::new(file).lines();
-            for l in lines.filter_map(|l| l.ok()) {
+            for l in lines.map_while(Result::ok) {
                 if self.negative_matches.iter().any(|neg| l.contains(neg)) {
                     return Err(TestError::CppCodeExaminationFail);
                 }
diff --git a/third_party/autocxx/integration-tests/tests/cpprefs_test.rs b/third_party/autocxx/integration-tests/tests/cpprefs_test.rs
index 4241dec..9cc6d39 100644
--- a/third_party/autocxx/integration-tests/tests/cpprefs_test.rs
+++ b/third_party/autocxx/integration-tests/tests/cpprefs_test.rs
@@ -13,6 +13,10 @@
 use proc_macro2::TokenStream;
 use quote::quote;
 
+const fn arbitrary_self_types_supported() -> bool {
+    rustversion::cfg!(nightly)
+}
+
 /// A positive test, we expect to pass.
 fn run_cpprefs_test(
     cxx_code: &str,
@@ -21,6 +25,10 @@
     generate: &[&str],
     generate_pods: &[&str],
 ) {
+    if !arbitrary_self_types_supported() {
+        // "unsafe_references_wrapped" requires arbitrary_self_types, which requires nightly.
+        return;
+    }
     do_run_test(
         cxx_code,
         header_code,
@@ -30,6 +38,9 @@
         None,
         None,
         "unsafe_references_wrapped",
+        Some(quote! {
+            #![feature(arbitrary_self_types)]
+        }),
     )
     .unwrap()
 }
@@ -54,7 +65,7 @@
     "},
         quote! {
             let goat = ffi::Goat::new().within_unique_ptr();
-            let mut goat = ffi::CppUniquePtrPin::new(goat);
+            let mut goat = autocxx::CppUniquePtrPin::new(goat);
             goat.as_cpp_mut_ref().add_a_horn();
         },
         &["Goat"],
@@ -87,7 +98,7 @@
     "},
         quote! {
             let goat = ffi::Goat::new().within_unique_ptr();
-            let goat = ffi::cpp_pin_uniqueptr(goat);
+            let goat = autocxx::CppUniquePtrPin::new(goat);
             goat.as_cpp_ref().describe();
         },
         &["Goat"],
diff --git a/third_party/autocxx/integration-tests/tests/integration_test.rs b/third_party/autocxx/integration-tests/tests/integration_test.rs
index 3ccc2d0..ec2b1e7 100644
--- a/third_party/autocxx/integration-tests/tests/integration_test.rs
+++ b/third_party/autocxx/integration-tests/tests/integration_test.rs
@@ -8,7 +8,8 @@
 
 use crate::{
     builder_modifiers::{
-        make_clang_arg_adder, make_cpp17_adder, EnableAutodiscover, SetSuppressSystemHeaders,
+        make_clang_arg_adder, make_clang_optional_arg_adder, make_cpp17_adder, EnableAutodiscover,
+        SetSuppressSystemHeaders,
     },
     code_checkers::{
         make_error_finder, make_rust_code_finder, make_string_finder, CppMatcher,
@@ -17,7 +18,7 @@
 };
 use autocxx_integration_tests::{
     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,
+    run_test_ex, run_test_expect_fail, run_test_expect_fail_ex, BuilderModifier, TestError,
 };
 use indoc::indoc;
 use itertools::Itertools;
@@ -501,6 +502,7 @@
     run_test_expect_fail(cxx, hdr, rs, &["take_bob"], &["Bob"]);
 }
 
+#[ignore] // https://github.com/google/autocxx/issues/1252
 #[test]
 fn test_take_as_pod_with_is_relocatable() {
     let cxx = indoc! {"
@@ -840,8 +842,7 @@
     run_test("", hdr, rs, &["A", "Bob", "C"], &[]);
 }
 
-#[test]
-fn test_take_char_by_ptr_in_wrapped_method() {
+fn run_char_test(builder_modifier: Option<BuilderModifier>) {
     let hdr = indoc! {"
         #include <cstdint>
         #include <memory>
@@ -871,7 +872,25 @@
         assert_eq!(unsafe { ch.as_ref()}.unwrap(), &104i8);
         assert_eq!(unsafe { a.as_ref().unwrap().take_char(ch, c2) }, 104);
     };
-    run_test("", hdr, rs, &["A", "C"], &[]);
+    run_test_ex(
+        "",
+        hdr,
+        rs,
+        directives_from_lists(&["A", "C"], &[], None),
+        builder_modifier,
+        None,
+        None,
+    );
+}
+
+#[test]
+fn test_take_char_by_ptr_in_wrapped_method() {
+    run_char_test(None)
+}
+
+#[test]
+fn test_take_char_by_ptr_in_wrapped_method_with_unsigned_chars() {
+    run_char_test(make_clang_arg_adder(&["-funsigned-char"]))
 }
 
 #[test]
@@ -1281,7 +1300,7 @@
         #define BOB \"foo\"
     "};
     let rs = quote! {
-        assert_eq!(std::str::from_utf8(ffi::BOB).unwrap().trim_end_matches(char::from(0)), "foo");
+        assert_eq!(core::str::from_utf8(ffi::BOB).unwrap().trim_end_matches(char::from(0)), "foo");
     };
     run_test(cxx, hdr, rs, &["BOB"], &[]);
 }
@@ -1428,7 +1447,7 @@
 #[test]
 fn test_asan_working_as_expected_for_rust_allocations() {
     perform_asan_doom_test(
-        quote! { Box::into_raw(std::pin::Pin::into_inner_unchecked(a)) },
+        quote! { Box::into_raw(core::pin::Pin::into_inner_unchecked(a)) },
         quote! { Box },
     )
 }
@@ -3042,7 +3061,7 @@
         const char* STRING = \"Foo\";
     "};
     let rs = quote! {
-        let a = std::str::from_utf8(ffi::STRING).unwrap().trim_end_matches(char::from(0));
+        let a = core::str::from_utf8(ffi::STRING).unwrap().trim_end_matches(char::from(0));
         assert_eq!(a, "Foo");
     };
     run_test("", hdr, rs, &["STRING"], &[]);
@@ -4221,6 +4240,29 @@
 }
 
 #[test]
+fn test_nested_unnamed_enum() {
+    let hdr = indoc! {"
+        namespace N {
+            struct A {
+                enum {
+                    LOW_VAL = 1,
+                    HIGH_VAL = 1000,
+                };
+            };
+        }
+    "};
+    run_test_ex(
+        "",
+        hdr,
+        quote! {},
+        quote! { generate_ns!("N")},
+        None,
+        None,
+        None,
+    );
+}
+
+#[test]
 fn test_nested_type_constructor() {
     let hdr = indoc! {"
         #include <string>
@@ -4420,6 +4462,31 @@
 }
 
 #[test]
+fn test_cycle_up_of_vec() {
+    let hdr = indoc! {"
+        #include <cstdint>
+        #include <vector>
+        #include <memory>
+        struct A {
+            uint32_t a;
+        };
+        inline std::unique_ptr<std::vector<A>> take_vec(std::unique_ptr<std::vector<A>> a) {
+            return a;
+        }
+        inline std::unique_ptr<std::vector<A>> get_vec() {
+            std::unique_ptr<std::vector<A>> items = std::make_unique<std::vector<A>>();
+            items->push_back(A { 3 });
+            items->push_back(A { 4 });
+            return items;
+        }
+    "};
+    let rs = quote! {
+        ffi::take_vec(ffi::get_vec());
+    };
+    run_test("", hdr, rs, &["take_vec", "get_vec"], &[]);
+}
+
+#[test]
 fn test_typedef_to_std() {
     let hdr = indoc! {"
         #include <string>
@@ -4955,6 +5022,78 @@
 }
 
 #[test]
+fn test_union_nonpod() {
+    let hdr = indoc! {"
+    #include <cstdint>
+    union A {
+        uint32_t a;
+        float b;
+    };
+    "};
+    let rs = quote! {};
+    run_test("", hdr, rs, &["A"], &[]);
+}
+
+#[test]
+fn test_union_pod() {
+    let hdr = indoc! {"
+    #include <cstdint>
+    union A {
+        uint32_t a;
+        float b;
+    };
+    "};
+    let rs = quote! {};
+    run_test_expect_fail("", hdr, rs, &[], &["A"]);
+}
+
+#[test]
+fn test_type_aliased_anonymous_union_ignored() {
+    let hdr = indoc! {"
+    #include <cstdint>
+    namespace test {
+        typedef union {
+        int a;
+        } Union;
+        };
+    "};
+    let rs = quote! {};
+    run_test("", hdr, rs, &["test::Union"], &[]);
+}
+
+#[test]
+fn test_type_aliased_anonymous_struct_ignored() {
+    let hdr = indoc! {"
+    #include <cstdint>
+    namespace test {
+        typedef struct {
+            int a;
+        } Struct;
+    };
+    "};
+    let rs = quote! {};
+    run_test("", hdr, rs, &["test::Struct"], &[]);
+}
+
+#[test]
+fn test_type_aliased_anonymous_nested_struct_ignored() {
+    let hdr = indoc! {"
+    #include <cstdint>
+    namespace test {
+        struct Outer {
+            typedef struct {
+                int a;
+            } Struct;
+            int b;
+        };
+    };
+    "};
+    let rs = quote! {};
+    run_test("", hdr, rs, &["test::Outer_Struct"], &[]);
+}
+
+#[ignore] // https://github.com/google/autocxx/issues/1251
+#[test]
 fn test_double_underscores_ignored() {
     let hdr = indoc! {"
     #include <cstdint>
@@ -5756,7 +5895,17 @@
         } // namespace spanner
     "};
     let rs = quote! {};
-    run_test("", hdr, rs, &["spanner::Database", "spanner::Row"], &[]);
+    run_test_ex(
+        "",
+        hdr,
+        rs,
+        directives_from_lists(&["spanner::Database", "spanner::Row"], &[], None),
+        // This is normally a valid warning for generating bindings for this code, but we're doing
+        // it on purpose as a regression test on minimized code so we'll just ignore it.
+        make_clang_optional_arg_adder(&[], &["-Wno-delete-abstract-non-virtual-dtor"]),
+        None,
+        None,
+    );
 }
 
 #[test]
@@ -5921,6 +6070,7 @@
         None,
         None,
         "unsafe_ffi",
+        None,
     ) {
         Err(TestError::CppBuild(_)) => {} // be sure this fails due to a static_assert
         // rather than some runtime problem
@@ -7025,6 +7175,35 @@
 }
 
 #[test]
+fn test_extern_rust_fn_callback() {
+    let hdr = indoc! {"
+        struct a {};
+    "};
+    let hexathorpe = Token![#](Span::call_site());
+    let rs = quote! {
+        autocxx::include_cpp! {
+            #hexathorpe include "input.h"
+            safety!(unsafe_ffi)
+            generate!("a")
+        }
+
+        use ffi::a;
+        use std::pin::Pin;
+
+        #[autocxx::extern_rust::extern_rust_function]
+        pub fn called_from_cpp(_a: Pin<&mut a>) {}
+
+        fn main() {}
+    };
+    do_run_test_manual("", hdr, rs, None, None).unwrap();
+}
+
+// TODO: there are various other tests for extern_rust_fn we should add:
+// 1) taking mutable and immutable references
+// 2) ensuring that types on which the signature depends as receiver,
+//    parameters and return are not garbage collected
+
+#[test]
 fn test_rust_reference_no_autodiscover() {
     let hdr = indoc! {"
     #include <cstdint>
@@ -7054,6 +7233,55 @@
 }
 
 #[test]
+fn test_rust_box() {
+    let hdr = indoc! {"
+    #include <cstdint>
+    #include <cxx.h>
+
+    struct RustType;
+    inline uint32_t take_rust_box(rust::Box<RustType>) {
+        return 4;
+    }
+    "};
+    let rs = quote! {
+        let foo = Box::new(RustType(3));
+        let result = ffi::take_rust_box(foo);
+        assert_eq!(result, 4);
+    };
+    run_test_ex(
+        "",
+        hdr,
+        rs,
+        directives_from_lists(&["take_rust_box"], &[], None),
+        None,
+        None,
+        Some(quote! {
+            #[autocxx::extern_rust::extern_rust_type]
+            pub struct RustType(i32);
+        }),
+    );
+}
+
+#[test]
+fn test_rust_reference_no_autodiscover_no_usage() {
+    let rs = quote! {
+        let _ = RustType(3);
+    };
+    run_test_ex(
+        "",
+        "",
+        rs,
+        directives_from_lists(&[], &[], None),
+        None,
+        None,
+        Some(quote! {
+            #[autocxx::extern_rust::extern_rust_type]
+            pub struct RustType(i32);
+        }),
+    );
+}
+
+#[test]
 #[cfg_attr(skip_windows_msvc_failing_tests, ignore)]
 // TODO - replace make_clang_arg_adder with something that knows how to add an MSVC-suitable
 // directive for the cc build.
@@ -7078,7 +7306,7 @@
 }
 
 #[test]
-fn test_box() {
+fn test_box_extern_rust_type() {
     let hdr = indoc! {"
         #include <cxx.h>
         struct Foo;
@@ -7106,6 +7334,47 @@
 }
 
 #[test]
+fn test_box_return_placement_new() {
+    let hdr = indoc! {"
+        #include <cxx.h>
+        struct Foo;
+        struct Foo2;
+        struct Ret {};
+        inline Ret take_box(rust::Box<Foo>, rust::Box<Foo2>) {
+            return Ret{};
+        }
+    "};
+    run_test_ex(
+        "",
+        hdr,
+        quote! {
+            let _ = ffi::take_box(
+                Box::new(Foo { a: "Hello".into() }),
+                Box::new(bar::Foo2 { a: "Goodbye".into() })
+            );
+        },
+        quote! {
+            generate!("take_box")
+            extern_rust_type!(Foo)
+            generate!("Ret")
+        },
+        None,
+        None,
+        Some(quote! {
+            pub struct Foo {
+                a: String,
+            }
+            mod bar {
+                #[autocxx::extern_rust::extern_rust_type]
+                pub struct Foo2 {
+                    pub a: String,
+                }
+            }
+        }),
+    );
+}
+
+#[test]
 fn test_box_via_extern_rust() {
     let hdr = indoc! {"
         #include <cxx.h>
@@ -7132,6 +7401,32 @@
 }
 
 #[test]
+fn test_box_via_extern_rust_no_include_cpp() {
+    let hdr = indoc! {"
+        #include <cxx.h>
+        struct Foo;
+        inline void take_box(rust::Box<Foo>) {
+        }
+    "};
+    do_run_test_manual(
+        "",
+        hdr,
+        quote! {
+            #[autocxx::extern_rust::extern_rust_type]
+            pub struct Foo {
+                a: String,
+            }
+
+            fn main() {
+            }
+        },
+        Some(Box::new(EnableAutodiscover)),
+        None,
+    )
+    .unwrap();
+}
+
+#[test]
 fn test_box_via_extern_rust_in_mod() {
     let hdr = indoc! {"
         #include <cxx.h>
@@ -7220,14 +7515,24 @@
     let hdr = indoc! {"
         #include <cstdint>
         inline void take_int(int&) {}
-        inline void take_uin16(uint16_t&) {}
-        inline void take_char16(char16_t &) {}
+        inline void take_uint16(uint16_t) {}
+        inline void take_us(unsigned short) {}
+        inline void take_char16(char16_t) {}
+        inline void take_uint16_ref(uint16_t&) {}
+        inline void take_char16_ref(char16_t &) {}
     "};
     run_test(
         "",
         hdr,
         quote! {},
-        &["take_int", "take_uin16", "take_char16"],
+        &[
+            "take_int",
+            "take_uint16",
+            "take_char16",
+            "take_uint16_ref",
+            "take_char16_ref",
+            "take_us",
+        ],
         &[],
     );
 }
@@ -7950,10 +8255,10 @@
             assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
             let obs_superclass = obs.as_ref().unwrap(); // &subclass
-            let obs_superclass = unsafe { std::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
+            let obs_superclass = unsafe { core::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
             ffi::TriggerTestObserverA(obs_superclass);
             assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
-            std::mem::drop(obs);
+            core::mem::drop(obs);
             Lazy::force(&STATUS).lock().unwrap().a_called = false;
             assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
@@ -7970,7 +8275,7 @@
             ffi::TriggerTestObserverA(obs.as_ref().borrow().as_ref());
             assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
             Lazy::force(&STATUS).lock().unwrap().a_called = false;
-            std::mem::drop(obs);
+            core::mem::drop(obs);
             assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
@@ -8085,11 +8390,11 @@
             assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
             let obs_superclass = obs.as_ref().unwrap(); // &subclass
-            let obs_superclass = unsafe { std::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
+            let obs_superclass = unsafe { core::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
 
             ffi::TriggerTestObserverA(obs_superclass);
             assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
-            std::mem::drop(obs);
+            core::mem::drop(obs);
             Lazy::force(&STATUS).lock().unwrap().a_called = false;
             assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
@@ -8106,7 +8411,7 @@
 
             assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
             Lazy::force(&STATUS).lock().unwrap().a_called = false;
-            std::mem::drop(obs);
+            core::mem::drop(obs);
             assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
@@ -8118,7 +8423,7 @@
             let obs_superclass_ptr: *const ffi::TestObserver = obs.as_ref().borrow().as_ref();
             // Retain just a pointer on the Rust side, so there is no Rust-side
             // ownership.
-            std::mem::drop(obs);
+            core::mem::drop(obs);
             assert!(Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
             assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
             assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
@@ -9182,7 +9487,7 @@
     };
     "};
     let rs = quote! {
-        use autocxx::moveit::EmplaceUnpinned;
+        use autocxx::moveit::Emplace;
         let mut up_obj = cxx::UniquePtr::emplace(ffi::A::new());
         up_obj.as_mut().unwrap().set(42);
         assert_eq!(up_obj.get(), 42);
@@ -9206,7 +9511,6 @@
     };
     "};
     let rs = quote! {
-        use autocxx::moveit::EmplaceUnpinned;
         use autocxx::moveit::Emplace;
         let mut up_obj = cxx::UniquePtr::emplace(ffi::A::new());
         up_obj.pin_mut().set(666);
@@ -9270,7 +9574,7 @@
         assert!(ffi::was_delete_called());
         ffi::reset_flags();
         {
-            use autocxx::moveit::EmplaceUnpinned;
+            use autocxx::moveit::Emplace;
             let _ = cxx::UniquePtr::emplace(ffi::A::new());
         }
         assert!(ffi::was_delete_called());
@@ -9638,8 +9942,7 @@
         ("F", "struct F { uint32_t a; uint8_t b; };"),
     ];
     let type_definitions = TYPES.iter().map(|(_, def)| *def).join("\n");
-    let function_definitions = TYPES.iter().map(|(name, _)| format!("inline size_t get_sizeof_{}() {{ return sizeof({}); }}\ninline size_t get_alignof_{}() {{ return alignof({}); }}\n",
-    name, name, name, name)).join("\n");
+    let function_definitions = TYPES.iter().map(|(name, _)| format!("inline size_t get_sizeof_{name}() {{ return sizeof({name}); }}\ninline size_t get_alignof_{name}() {{ return alignof({name}); }}\n")).join("\n");
     let hdr = format!(
         indoc! {"
         #include <cstdint>
@@ -9653,12 +9956,9 @@
     let allowlist_fns: Vec<String> = TYPES
         .iter()
         .flat_map(|(name, _)| {
-            [
-                format!("get_sizeof_{}", name),
-                format!("get_alignof_{}", name),
-            ]
-            .to_vec()
-            .into_iter()
+            [format!("get_sizeof_{name}"), format!("get_alignof_{name}")]
+                .to_vec()
+                .into_iter()
         })
         .collect_vec();
     let allowlist_types: Vec<String> = TYPES.iter().map(|(name, _)| name.to_string()).collect_vec();
@@ -9672,15 +9972,15 @@
     let allowlist_both: Vec<&str> = allowlist_both.iter().map(AsRef::as_ref).collect_vec();
     let rs = TYPES.iter().fold(quote! {}, |mut accumulator, (name, _)| {
         let get_align_symbol =
-            proc_macro2::Ident::new(&format!("get_alignof_{}", name), Span::call_site());
+            proc_macro2::Ident::new(&format!("get_alignof_{name}"), Span::call_site());
         let get_size_symbol =
-            proc_macro2::Ident::new(&format!("get_sizeof_{}", name), Span::call_site());
+            proc_macro2::Ident::new(&format!("get_sizeof_{name}"), Span::call_site());
         let type_symbol = proc_macro2::Ident::new(name, Span::call_site());
         accumulator.extend(quote! {
             let c_size = ffi::#get_size_symbol();
             let c_align = ffi::#get_align_symbol();
-            assert_eq!(std::mem::size_of::<ffi::#type_symbol>(), c_size);
-            assert_eq!(std::mem::align_of::<ffi::#type_symbol>(), c_align);
+            assert_eq!(core::mem::size_of::<ffi::#type_symbol>(), c_size);
+            assert_eq!(core::mem::align_of::<ffi::#type_symbol>(), c_align);
         });
         accumulator
     });
@@ -9775,6 +10075,24 @@
 }
 
 #[test]
+fn test_issue_1238() {
+    let hdr = indoc! {"
+    class b;
+    class c;
+    class f {
+        b d();
+    };
+    class S2E {
+    public:
+        f e;
+        b &d(c *) const;
+    };
+    "};
+    let rs = quote! {};
+    run_test("", hdr, rs, &["S2E"], &[]);
+}
+
+#[test]
 fn test_issue486_multi_types() {
     let hdr = indoc! {"
         namespace a {
@@ -9849,6 +10167,13 @@
             void a() const {}
         };
 
+        struct AllExplicitlyDefaulted {
+            AllExplicitlyDefaulted() = default;
+            AllExplicitlyDefaulted(const AllExplicitlyDefaulted&) = default;
+            AllExplicitlyDefaulted(AllExplicitlyDefaulted&&) = default;
+            void a() const {};
+        };
+
         struct PublicDeleted {
             PublicDeleted() = delete;
             PublicDeleted(const PublicDeleted&) = delete;
@@ -10390,6 +10715,12 @@
         test_movable![ffi::AllImplicitlyDefaulted];
         test_call_a![ffi::AllImplicitlyDefaulted];
 
+        test_constructible![ffi::AllExplicitlyDefaulted];
+        test_make_unique![ffi::AllExplicitlyDefaulted];
+        test_copyable![ffi::AllExplicitlyDefaulted];
+        test_movable![ffi::AllExplicitlyDefaulted];
+        test_call_a![ffi::AllExplicitlyDefaulted];
+
         test_call_a![ffi::PublicDeleted];
 
         test_copyable![ffi::PublicDeletedDefault];
@@ -10763,6 +11094,7 @@
         rs,
         &[
             "AllImplicitlyDefaulted",
+            "AllExplicitlyDefaulted",
             "PublicDeleted",
             "PublicDeletedDefault",
             "PublicDeletedCopy",
@@ -11097,7 +11429,7 @@
         "Function",
     ]
     .into_iter()
-    .flat_map(|l| [format!("{} line A", l), format!("{} line B", l)])
+    .flat_map(|l| [format!("{l} line A"), format!("{l} line B")])
     .collect_vec();
 
     run_test_ex(
@@ -11171,6 +11503,43 @@
 }
 
 #[test]
+fn test_enum_in_ns() {
+    let hdr = indoc! {"
+        namespace a {
+        enum b {};
+        } // namespace
+    "};
+    run_test("", hdr, quote! {}, &["a::b"], &[]);
+}
+
+#[test]
+fn test_recursive_field() {
+    let hdr = indoc! {"
+        #include <memory>
+        struct A {
+            std::unique_ptr<A> a;
+        };
+    "};
+    run_test("", hdr, quote! {}, &["A"], &[]);
+}
+
+#[test]
+fn test_recursive_field_indirect() {
+    let hdr = indoc! {"
+        #include <memory>
+        struct B;
+        struct A {
+            std::unique_ptr<B> a;
+        };
+        struct B {
+            std::unique_ptr<A> a1;
+            A a2;
+        };
+    "};
+    run_test("", hdr, quote! {}, &["A", "B"], &[]);
+}
+
+#[test]
 fn test_typedef_unsupported_type_pub() {
     let hdr = indoc! {"
         #include <set>
@@ -11572,6 +11941,306 @@
     );
 }
 
+#[test]
+fn test_issue_1143() {
+    let hdr = indoc! {
+        "namespace mapnik {
+            class Map {
+            public:
+              int &a(long);
+            };
+        }"
+    };
+
+    run_test("", hdr, quote! {}, &["mapnik::Map"], &[]);
+}
+
+#[test]
+fn test_issue_1170() {
+    let hdr = indoc! {
+        "#include <vector>
+        struct a {
+            enum b {} c;
+        } Loc;
+        struct Arch {
+            std::vector<a> d();
+        } DeterministicRNG;"
+    };
+    run_test("", hdr, quote! {}, &["Arch"], &[]);
+}
+
+// https://github.com/google/autocxx/issues/774
+#[test]
+fn test_virtual_methods() {
+    let hdr = indoc! {"
+        #include <cstdint>
+        #include <memory>
+        class Base {
+        public:
+            Base() {}
+            virtual ~Base() {}
+
+            virtual int a() = 0;
+
+            virtual void b(int) = 0;
+            virtual void b(bool) = 0;
+
+            virtual int c() const = 0;
+            virtual int c() = 0;
+        };
+        class FullyDefined : public Base {
+        public:
+            int a() { return 0; }
+
+            void b(int) { }
+            void b(bool) { }
+
+            int c() const { return 1; }
+            int c() { return 2; }
+        };
+        class Partial1 : public Base {
+        public:
+            int a() { return 0; }
+
+            void b(bool) {}
+        };
+
+        class Partial2 : public Base {
+        public:
+            int a() { return 0; }
+
+            void b(int) { }
+            void b(bool) { }
+
+            int c() const { return 1; }
+        };
+
+        class Partial3 : public Base {
+        public:
+            int a() { return 0; }
+
+            void b(int) { }
+
+            int c() const { return 1; }
+            int c() { return 2; }
+        };
+
+        class Partial4 : public Base {
+        public:
+            int a() { return 0; }
+
+            void b(int) { }
+            void b(bool) = 0;
+
+            int c() const { return 1; }
+            int c() { return 2; }
+        };
+
+        // TODO: currently this class cannot be detected as virtual as there
+        // is no metadata captured to show that this destructor is virtual
+        // uncommenting this (as well as corresponding sections below) gives a 
+        // 'instantiation of abstract class' error.
+        // class Partial5 : public Base {
+        // public:
+        //     ~Partial5() = 0;
+
+        //     int a() { return 0; }
+
+        //     void b(int) { }
+        //     void b(bool) { }
+
+        //     int c() const { return 1; }
+        //     int c() { return 2; }
+        // };
+
+    "};
+    let rs = quote! {
+        static_assertions::assert_impl_all!(ffi::FullyDefined: moveit::CopyNew);
+        static_assertions::assert_not_impl_any!(ffi::Partial1: moveit::CopyNew);
+        static_assertions::assert_not_impl_any!(ffi::Partial2: moveit::CopyNew);
+        static_assertions::assert_not_impl_any!(ffi::Partial3: moveit::CopyNew);
+        static_assertions::assert_not_impl_any!(ffi::Partial4: moveit::CopyNew);
+        // static_assertions::assert_not_impl_any!(ffi::Partial5: moveit::CopyNew);
+        let _c1 = ffi::FullyDefined::new().within_unique_ptr();
+    };
+    run_test(
+        "",
+        hdr,
+        rs,
+        &[
+            "FullyDefined",
+            "Partial1",
+            "Partial2",
+            "Partial3",
+            "Partial4",
+            // "Partial5"
+        ],
+        &[],
+    );
+}
+
+#[test]
+fn test_issue_1192() {
+    let hdr = indoc! {
+        "#include <vector>
+        #include <cstdint>
+        template <typename B>
+        struct A {
+            B a;
+        };
+        struct VecThingy {
+            A<uint32_t> contents[2];
+        };
+        struct MyStruct {
+            VecThingy vec;
+        };"
+    };
+    run_test_ex(
+        "",
+        hdr,
+        quote! {},
+        quote! {
+
+            extern_cpp_type!("VecThingy", crate::VecThingy)
+            pod!("VecThingy")
+
+            generate_pod!("MyStruct")
+        },
+        None,
+        None,
+        Some(quote! {
+            // VecThingy isn't necessarily 128 bits long.
+            // This test doesn't actually allocate one.
+            #[repr(transparent)]
+            pub struct VecThingy(pub u128);
+
+            unsafe impl cxx::ExternType for VecThingy {
+                type Id = cxx::type_id!("VecThingy");
+                type Kind = cxx::kind::Trivial;
+            }
+        }),
+    );
+}
+
+#[test]
+fn test_issue_1214() {
+    let hdr = indoc! {"
+        #include <cstdint>
+        enum class C: uint16_t {
+            A,
+            B,
+        };
+    "};
+    run_test("", hdr, quote! {}, &["C"], &[]);
+}
+
+#[test]
+fn test_issue_1229() {
+    let hdr = indoc! {"
+    struct Thing {
+        float id;
+    
+        Thing(float id) : id(id) {}
+    };
+
+    struct Item {
+        float id;
+    
+        Item(float id) : id(id) {}
+    };
+    "};
+    let hexathorpe = Token![#](Span::call_site());
+    let rs = quote! {
+        use autocxx::WithinUniquePtr;
+
+        autocxx::include_cpp! {
+            #hexathorpe include "input.h"
+            name!(thing)
+            safety!(unsafe)
+            generate!("Thing")
+        }
+        autocxx::include_cpp! {
+            #hexathorpe include "input.h"
+            name!(item)
+            safety!(unsafe)
+            generate!("Item")
+        }
+
+        fn main() {
+            let _thing = thing::Thing::new(15.).within_unique_ptr();
+            let _item = item::Item::new(15.).within_unique_ptr();
+        }
+    };
+
+    do_run_test_manual("", hdr, rs, None, None).unwrap();
+}
+
+#[test]
+#[ignore] // https://github.com/google/autocxx/issues/1265
+fn test_issue_1265() {
+    let hdr = indoc! {"
+        #include <string>
+
+        class Test
+        {
+        public:
+          explicit Test(std::string string)
+            : string(std::move(string))
+          {
+          }
+
+          Test() = delete;
+
+          [[nodiscard]] auto get_string() const -> std::string const& { return this->string; }
+
+        private:
+          std::string string;
+        };
+    "};
+    run_test_ex(
+        "",
+        hdr,
+        quote! {
+            run();
+        },
+        directives_from_lists(&["Test"], &[], None),
+        None,
+        None,
+        Some(quote! {
+            fn run() {
+                let str0 = "string";
+                let str1 = "another string";
+                let ptr0 = UniquePtr::emplace(ffi::Test::new(str0));
+                let ptr1 = UniquePtr::emplace(ffi::Test::new(str1));
+                println!("0: {}", ptr0.get_string());
+                println!("1: {}", ptr1.get_string());
+                moveit!(let mut ref0 = &move *ptr0);
+                moveit!(let mut ref1 = &move *ptr1);
+                println!("0: {}", ref0.get_string());
+                println!("1: {}", ref1.get_string());
+                println!("swap");
+                core::mem::swap(&mut *ref0, &mut *ref1);
+                println!("0: {}", ref0.get_string());
+                println!("1: {}", ref1.get_string());
+            }
+        }),
+    )
+}
+
+#[test]
+fn test_ignore_va_list() {
+    let hdr = indoc! {"
+        #include <stdarg.h>
+        class A {
+        public:
+            A() {}
+            void fn(va_list) {}
+        };
+    "};
+    let rs = quote! {};
+    run_test("", hdr, rs, &["A"], &[]);
+}
+
 // Yet to test:
 // - Ifdef
 // - Out param pointers
diff --git a/third_party/autocxx/macro/BUILD b/third_party/autocxx/macro/BUILD
index 4cc658e..a91156e 100644
--- a/third_party/autocxx/macro/BUILD
+++ b/third_party/autocxx/macro/BUILD
@@ -25,6 +25,6 @@
         "@crate_index//:proc-macro-error",
         "@crate_index//:proc-macro2",
         "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:syn-2.0.28",
     ],
 )
diff --git a/third_party/autocxx/macro/Cargo.toml b/third_party/autocxx/macro/Cargo.toml
index 1f81ab1..00f7e28 100644
--- a/third_party/autocxx/macro/Cargo.toml
+++ b/third_party/autocxx/macro/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-macro"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
@@ -21,11 +21,11 @@
 proc-macro = true
 
 [dependencies]
-autocxx-parser = { path="../parser", version="=0.22.3" }
+autocxx-parser = { path = "../parser", version = "=0.26.0" }
 proc-macro-error = "1.0"
 proc-macro2 = "1.0.11"
 quote = "1.0"
 
 [dependencies.syn]
-version = "1.0"
-features = [ "full" ]
+version = "2"
+features = ["full"]
diff --git a/third_party/autocxx/macro/src/lib.rs b/third_party/autocxx/macro/src/lib.rs
index 01ccd69..6cdb74a 100644
--- a/third_party/autocxx/macro/src/lib.rs
+++ b/third_party/autocxx/macro/src/lib.rs
@@ -37,7 +37,7 @@
         abort!(s.vis.span(), "Rust subclasses of C++ types must by public");
     }
     let id = &s.ident;
-    let cpp_ident = Ident::new(&format!("{}Cpp", id), Span::call_site());
+    let cpp_ident = Ident::new(&format!("{id}Cpp"), Span::call_site());
     let input = quote! {
         cpp_peer: autocxx::subclass::CppSubclassCppPeerHolder<ffi:: #cpp_ident>
     };
diff --git a/third_party/autocxx/parser/BUILD b/third_party/autocxx/parser/BUILD
index 1e1cd3e..8ad9480 100644
--- a/third_party/autocxx/parser/BUILD
+++ b/third_party/autocxx/parser/BUILD
@@ -32,7 +32,7 @@
         "@crate_index//:quote",
         "@crate_index//:serde",
         "@crate_index//:serde_json",
-        "@crate_index//:syn",
+        "@crate_index//:syn-2.0.28",
         "@crate_index//:thiserror",
     ],
 )
diff --git a/third_party/autocxx/parser/Cargo.toml b/third_party/autocxx/parser/Cargo.toml
index 3668e65..07a1f1e 100644
--- a/third_party/autocxx/parser/Cargo.toml
+++ b/third_party/autocxx/parser/Cargo.toml
@@ -8,7 +8,7 @@
 
 [package]
 name = "autocxx-parser"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
@@ -21,16 +21,16 @@
 log = "0.4"
 proc-macro2 = "1.0"
 quote = "1.0"
-serde = { version = "1.0", features = [ "derive" ]}
+serde = { version = "1.0", features = ["derive"] }
 thiserror = "1.0"
 once_cell = "1.10"
 itertools = "0.10.3"
-indexmap = { version="1.8", features = ["serde"]}
+indexmap = { version = "1.8", features = ["serde"] }
 serde_json = "1.0"
 
 [dependencies.syn]
-version = "1.0.39"
-features = [ "full", "extra-traits" ]
+version = "2"
+features = ["full", "extra-traits"]
 
 [features]
 reproduction_case = []
diff --git a/third_party/autocxx/parser/src/config.rs b/third_party/autocxx/parser/src/config.rs
index 8d30249..3634295 100644
--- a/third_party/autocxx/parser/src/config.rs
+++ b/third_party/autocxx/parser/src/config.rs
@@ -29,7 +29,7 @@
 
 use quote::quote;
 
-#[derive(PartialEq, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Clone, Debug, Hash)]
 pub enum UnsafePolicy {
     AllFunctionsSafe,
     AllFunctionsUnsafe,
@@ -101,7 +101,7 @@
     fn to_bindgen_item(&self) -> String {
         match self {
             AllowlistEntry::Item(i) => i.clone(),
-            AllowlistEntry::Namespace(ns) => format!("{}::.*", ns),
+            AllowlistEntry::Namespace(ns) => format!("{ns}::.*"),
         }
     }
 }
@@ -165,7 +165,7 @@
 pub struct RustFun {
     pub path: RustPath,
     pub sig: Signature,
-    pub receiver: Option<Ident>,
+    pub has_receiver: bool,
 }
 
 impl std::fmt::Debug for RustFun {
@@ -240,7 +240,7 @@
             let (possible_directives, to_parse, parse_completely) = if has_hexathorpe {
                 (&get_directives().need_hexathorpe, input, false)
             } else {
-                input.parse::<Option<syn::token::Bang>>()?;
+                input.parse::<Option<syn::token::Not>>()?;
                 syn::parenthesized!(args in input);
                 (&get_directives().need_exclamation, &args, true)
             };
@@ -250,7 +250,7 @@
                 None => {
                     return Err(syn::Error::new(
                         ident.span(),
-                        format!("expected {}", all_possible),
+                        format!("expected {all_possible}"),
                     ));
                 }
                 Some(directive) => directive.parse(to_parse, &mut config, &ident.span())?,
@@ -258,7 +258,7 @@
             if parse_completely && !to_parse.is_empty() {
                 return Err(syn::Error::new(
                     ident.span(),
-                    format!("found unexpected input within the directive {}", ident_str),
+                    format!("found unexpected input within the directive {ident_str}"),
                 ));
             }
             if input.is_empty() {
@@ -362,6 +362,7 @@
             || self.is_subclass_holder(cpp_name)
             || self.is_subclass_cpp(cpp_name)
             || self.is_rust_fun(cpp_name)
+            || self.is_rust_type_name(cpp_name)
             || self.is_concrete_type(cpp_name)
             || match &self.allowlist {
                 Allowlist::Unspecified(_) => panic!("Eek no allowlist yet"),
@@ -408,10 +409,14 @@
     }
 
     pub fn is_rust_type(&self, id: &Ident) -> bool {
+        let id_string = id.to_string();
+        self.is_rust_type_name(&id_string) || self.is_subclass_holder(&id_string)
+    }
+
+    fn is_rust_type_name(&self, possible_ty: &str) -> bool {
         self.rust_types
             .iter()
-            .any(|rt| rt.get_final_ident() == &id.to_string())
-            || self.is_subclass_holder(&id.to_string())
+            .any(|rt| rt.get_final_ident() == possible_ty)
     }
 
     fn is_rust_fun(&self, possible_fun: &str) -> bool {
diff --git a/third_party/autocxx/parser/src/directives.rs b/third_party/autocxx/parser/src/directives.rs
index 70c88fc..1cb35fb 100644
--- a/third_party/autocxx/parser/src/directives.rs
+++ b/third_party/autocxx/parser/src/directives.rs
@@ -275,7 +275,7 @@
 }
 
 fn allowlist_err_to_syn_err(err: AllowlistErr, span: &Span) -> syn::Error {
-    syn::Error::new(*span, format!("{}", err))
+    syn::Error::new(*span, format!("{err}"))
 }
 
 struct StringList<SET, GET>(SET, GET)
@@ -472,7 +472,7 @@
         config.extern_rust_funs.push(RustFun {
             path,
             sig,
-            receiver: None,
+            has_receiver: false,
         });
         Ok(())
     }
diff --git a/third_party/autocxx/parser/src/file_locations.rs b/third_party/autocxx/parser/src/file_locations.rs
index 6b3c58a..29acb48 100644
--- a/third_party/autocxx/parser/src/file_locations.rs
+++ b/third_party/autocxx/parser/src/file_locations.rs
@@ -77,7 +77,7 @@
             FileLocationStrategy::Custom(_) => panic!("Should never happen in the macro"),
             FileLocationStrategy::UnknownMaybeFromOutdir | FileLocationStrategy::FromOutDir(_) => {
                 let fname = config.get_rs_filename();
-                let fname = format!("/{}/{}/{}", BUILD_DIR_NAME, RS_DIR_NAME, fname);
+                let fname = format!("/{BUILD_DIR_NAME}/{RS_DIR_NAME}/{fname}");
                 // rust-analyzer works better if we ask Rust to do the path
                 // concatenation rather than doing it in proc-macro code.
                 // proc-macro code does not itself have access to the value of
diff --git a/third_party/autocxx/parser/src/path.rs b/third_party/autocxx/parser/src/path.rs
index 415b2ef..e3a0f9f 100644
--- a/third_party/autocxx/parser/src/path.rs
+++ b/third_party/autocxx/parser/src/path.rs
@@ -58,7 +58,7 @@
     fn parse(input: ParseStream) -> ParseResult<Self> {
         let id: Ident = input.parse()?;
         let mut p = RustPath::new_from_ident(id);
-        while input.parse::<Option<syn::token::Colon2>>()?.is_some() {
+        while input.parse::<Option<syn::token::PathSep>>()?.is_some() {
             let id: Ident = input.parse()?;
             p = p.append(id);
         }
diff --git a/third_party/autocxx/src/lib.rs b/third_party/autocxx/src/lib.rs
index 4b9f26b..fe235a9 100644
--- a/third_party/autocxx/src/lib.rs
+++ b/third_party/autocxx/src/lib.rs
@@ -1,4 +1,6 @@
 #![doc = include_str!("../README.md")]
+#![cfg_attr(nightly, feature(unsize))]
+#![cfg_attr(nightly, feature(dispatch_from_dyn))]
 
 // Copyright 2020 Google LLC
 //
@@ -19,7 +21,7 @@
 pub mod subclass;
 mod value_param;
 
-pub use reference_wrapper::{CppMutRef, CppPin, CppRef};
+pub use reference_wrapper::{AsCppMutRef, AsCppRef, CppMutRef, CppPin, CppRef, CppUniquePtrPin};
 
 #[cfg_attr(doc, aquamarine::aquamarine)]
 /// Include some C++ headers in your Rust project.
@@ -82,7 +84,7 @@
 ///
 /// * *Recommended*: provide various [`generate`] directives in the
 ///   [`include_cpp`] macro. This can specify functions or types.
-/// * *Not recommended*: in your `build.rs`, call [`Builder::auto_allowlist`].
+/// * *Not recommended*: in your `build.rs`, call `Builder::auto_allowlist`.
 ///   This will attempt to spot _uses_ of FFI bindings anywhere in your Rust code
 ///   and build the allowlist that way. This is experimental and has known limitations.
 /// * *Strongly not recommended*: use [`generate_all`]. This will attempt to
@@ -216,7 +218,7 @@
 
 /// A concrete type to make, for example
 /// `concrete!("Container<Contents>")`.
-/// All types msut already be on the allowlist by having used
+/// All types must already be on the allowlist by having used
 /// `generate!` or similar.
 ///
 /// A directive to be included inside
@@ -265,9 +267,12 @@
 /// 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`].
+/// them to be wrapped in a `CppRef` type: see [`CppRef`].
+/// This only works on nightly Rust because it
+/// depends upon an unstable feature
+/// (`arbitrary_self_types`). However, it should
+/// eliminate all undefined behavior related to Rust's
+/// stricter aliasing rules than C++.
 #[macro_export]
 macro_rules! safety {
     ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
@@ -286,12 +291,43 @@
 
 /// Indicates that a C++ type is not to be generated by autocxx in this case,
 /// but instead should refer to some pre-existing Rust type.
-/// Note that the size and alignment of this type *must* be correct.
-/// If you wish for the type to be POD, you can use a `pod!` directive too.
+///
+/// If you wish for the type to be POD, you can use a `pod!` directive too
+/// (but see the "requirements" section below).
 ///
 /// The syntax is:
 /// `extern_cpp_type!("CppNameGoesHere", path::to::rust::type)`
 ///
+/// Generally speaking, this should be used only to refer to types
+/// generated elsewhere by `autocxx` or `cxx` to ensure that they meet
+/// all the right requirements. It's possible - but fragile - to
+/// define such types yourself.
+///
+/// # Requirements for externally defined Rust types
+///
+/// It's generally expected that you would make such a type
+/// in Rust using a separate `include_cpp!` macro, or
+/// a manual `#[cxx::bridge]` directive somehwere. That is, this
+/// directive is intended mainly for use in cross-linking different
+/// sets of bindings in different mods, rather than truly to point to novel
+/// external types.
+///
+/// But with that in mind, here are the requirements you must stick to.
+///
+/// For non-POD external types:
+/// * The size and alignment of this type *must* be correct.
+///
+/// For POD external types:
+/// * As above
+/// * Your type must correspond to the requirements of
+///   [`cxx::kind::Trivial`]. In general, that means, no move constructor
+///   and no destructor. If you generate this type using `cxx` itself
+///   (or `autocxx`) this will be enforced using `static_assert`s
+///   within the generated C++ code. Without using those tools, you're
+///   on your own for determining this... and it's hard because the presence
+///   of particular fields or base classes may well result in your type
+///   violating those rules.
+///
 /// A directive to be included inside
 /// [include_cpp] - see [include_cpp] for general information.
 #[macro_export]
@@ -495,6 +531,25 @@
     /// for instance. This will contribute to an `extern "Rust"` section of the
     /// generated `cxx` bindings, and this type will appear in the C++ header
     /// generated for use in C++.
+    ///
+    /// # Finding these bindings from C++
+    ///
+    /// You will likely need to forward-declare this type within your C++ headers
+    /// before you can use it in such function signatures. autocxx can't generate
+    /// headers (with this type definition) until it's parsed your header files;
+    /// logically therefore if your header files mention one of these types
+    /// it's impossible for them to see the definition of the type.
+    ///
+    /// If you're using multiple sets of `include_cpp!` directives, or
+    /// a mixture of `include_cpp!` and `#[cxx::bridge]` bindings, then you
+    /// may be able to `#include "cxxgen.h"` to refer to the generated C++
+    /// function prototypes. In this particular circumstance, you'll want to know
+    /// how exactly the `cxxgen.h` header is named, because one will be
+    /// generated for each of the sets of bindings encountered. The pattern
+    /// can be set manually using `autocxxgen`'s command-line options. If you're
+    /// using `autocxx`'s `build.rs` support, those headers will be named
+    /// `cxxgen.h`, `cxxgen1.h`, `cxxgen2.h` according to the order in which
+    /// the `include_cpp` or `cxx::bridge` bindings are encountered.
     pub use autocxx_macro::extern_rust_type;
 
     /// Declare that a given function is a Rust function which is to be exported
@@ -504,6 +559,9 @@
     /// #[extern_rust_function]
     /// pub fn call_me_from_cpp() { }
     /// ```
+    ///
+    /// See [`extern_rust_type`] for details of how to find the generated
+    /// declarations from C++.
     pub use autocxx_macro::extern_rust_function;
 }
 
@@ -519,6 +577,7 @@
 /// and implemented by any (autocxx-related) [`moveit::New`].
 pub trait WithinUniquePtr {
     type Inner: UniquePtrTarget + MakeCppStorage;
+    /// Create this item within a [`cxx::UniquePtr`].
     fn within_unique_ptr(self) -> cxx::UniquePtr<Self::Inner>;
 }
 
@@ -527,13 +586,23 @@
 /// and implemented by any (autocxx-related) [`moveit::New`].
 pub trait WithinBox {
     type Inner;
+    /// Create this item inside a pinned box. This is a good option if you
+    /// want to own this object within Rust, and want to create Rust references
+    /// to it.
     fn within_box(self) -> Pin<Box<Self::Inner>>;
+    /// Create this item inside a [`CppPin`]. This is a good option if you
+    /// want to own this option within Rust, but you want to create [`CppRef`]
+    /// C++ references to it. You'd only want to choose that option if you have
+    /// enabled the C++ reference wrapper support by using the
+    /// `safety!(unsafe_references_wrapped`) directive. If you haven't done
+    /// that, ignore this function.
+    fn within_cpp_pin(self) -> CppPin<Self::Inner>;
 }
 
 use cxx::kind::Trivial;
 use cxx::ExternType;
+use moveit::Emplace;
 use moveit::MakeCppStorage;
-use moveit::{Emplace, EmplaceUnpinned};
 
 impl<N, T> WithinUniquePtr for N
 where
@@ -554,6 +623,9 @@
     fn within_box(self) -> Pin<Box<T>> {
         Box::emplace(self)
     }
+    fn within_cpp_pin(self) -> CppPin<Self::Inner> {
+        CppPin::from_pinned_box(Box::emplace(self))
+    }
 }
 
 /// Emulates the [`WithinUniquePtr`] trait, but for trivial (plain old data) types.
@@ -624,9 +696,12 @@
     pub use crate::c_void;
     pub use crate::cpp_semantics;
     pub use crate::include_cpp;
+    pub use crate::AsCppMutRef;
+    pub use crate::AsCppRef;
     pub use crate::CppMutRef;
     pub use crate::CppPin;
     pub use crate::CppRef;
+    pub use crate::CppUniquePtrPin;
     pub use crate::PinMut;
     pub use crate::RValueParam;
     pub use crate::ValueParam;
@@ -638,7 +713,6 @@
     pub use moveit::moveit;
     pub use moveit::new::New;
     pub use moveit::Emplace;
-    pub use moveit::EmplaceUnpinned;
 }
 
 /// Re-export moveit for ease of consumers.
diff --git a/third_party/autocxx/src/reference_wrapper.rs b/third_party/autocxx/src/reference_wrapper.rs
index ed943d3..041bd72 100644
--- a/third_party/autocxx/src/reference_wrapper.rs
+++ b/third_party/autocxx/src/reference_wrapper.rs
@@ -6,15 +6,124 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use core::{marker::PhantomData, ops::Deref, pin::Pin};
+
+#[cfg(nightly)]
+use std::{marker::Unsize, ops::DispatchFromDyn};
+
+use cxx::{memory::UniquePtrTarget, UniquePtr};
+
 /// A C++ const reference. These are different from Rust's `&T` in that
-/// these may exist even while the object is mutated elsewhere.
+/// these may exist even while the object is mutated elsewhere. See also
+/// [`CppMutRef`] for the mutable equivalent.
 ///
-/// 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> {
+/// The key rule is: we *never* dereference these in Rust. Therefore, any
+/// UB here cannot manifest within Rust, but only across in C++, and therefore
+/// they are equivalently safe to using C++ references in pure-C++ codebases.
+///
+/// *Important*: you might be wondering why you've never encountered this type.
+/// These exist in autocxx-generated bindings only if the `unsafe_references_wrapped`
+/// safety policy is given. This may become the default in future.
+///
+/// # Usage
+///
+/// These types of references are pretty useless in Rust. You can't do
+/// field access. But, you can pass them back into C++! And specifically,
+/// you can call methods on them (i.e. use this type as a `this`). So
+/// the common case here is when C++ gives you a reference to some type,
+/// then you want to call methods on that reference.
+///
+/// # Calling methods
+///
+/// As noted, one of the main reasons for this type is to call methods.
+/// Unfortunately, that depends on unstable Rust features. If you can't
+/// call methods on one of these references, check you're using nightly
+/// and add `#![feature(arbitrary_self_types)]` to your crate.
+///
+/// # Lifetimes and cloneability
+///
+/// Although these references implement C++ aliasing semantics, they
+/// do attempt to give you Rust lifetime tracking. This means if a C++ object
+/// gives you a reference, you won't be able to use that reference after the
+/// C++ object is no longer around.
+///
+/// This is usually what you need, since a C++ object will typically give
+/// you a reference to part of _itself_ or something that it owns. But,
+/// if you know that the returned reference lasts longer than its vendor,
+/// you can use `lifetime_cast` to get a long-lived version.
+///
+/// On the other hand, these references do not give you Rust's exclusivity
+/// guarantees. These references can be freely cloned, and using [`CppRef::const_cast`]
+/// you can even make a mutable reference from an immutable reference.
+///
+/// # Field access
+///
+/// Field access would be achieved by adding C++ `get` and/or `set` methods.
+/// It's possible that a future version of `autocxx` could generate such
+/// getters and setters automatically, but they would need to be `unsafe`
+/// because there is no guarantee that the referent of a `CppRef` is actually
+/// what it's supposed to be, or alive. `CppRef`s may flow from C++ to Rust
+/// via arbitrary means, and with sufficient uses of `get` and `set` it would
+/// even be possible to create a use-after-free in pure Rust code (for instance,
+/// store a [`CppPin`] in a struct field, get a `CppRef` to its referent, then
+/// use a setter to reset that field of the struct.)
+///
+/// # Deref
+///
+/// This type implements [`Deref`] because that's the mechanism that the
+/// unstable Rust `arbitrary_self_types` features uses to determine callable
+/// methods. However, actually calling `Deref::deref` is not permitted and will
+/// result in a compilation failure. If you wish to create a Rust reference
+/// from the C++ reference, see [`CppRef::as_ref`].
+///
+/// # Nullness
+///
+/// Creation of a null C++ reference is undefined behavior (because such
+/// a reference can only be created by dereferencing a null pointer.)
+/// However, in practice, they exist, and we need to be compatible with
+/// pre-existing C++ APIs even if they do naughty things like this.
+/// Therefore this `CppRef` type does allow null values. This is a bit
+/// unfortunate because it means `Option<CppRef<T>>`
+/// occupies more space than `CppRef<T>`.
+///
+/// # Dynamic dispatch
+///
+/// You might wonder if you can do this:
+/// ```ignore
+/// let CppRef<dyn Trait> = ...; // obtain some CppRef<concrete type>
+/// ```
+/// Dynamic dispatch works so long as you're using nightly (we require another
+/// unstable feature, `dispatch_from_dyn`). But we need somewhere to store
+/// the trait object, and `CppRef` isn't it -- a `CppRef` can only store a
+/// simple pointer to something else. So, you need to store the trait object
+/// in a `Box` or similar:
+/// ```ignore
+/// trait SomeTrait {
+///    fn some_method(self: CppRef<Self>)
+/// }
+/// impl SomeTrait for ffi::Concrete {
+///   fn some_method(self: CppRef<Self>) {}
+/// }
+/// let obj: Pin<Box<dyn SomeTrait>> = ffi::Concrete::new().within_box();
+/// let obj = CppPin::from_pinned_box(obj);
+/// farm_area.as_cpp_ref().some_method();
+/// ```
+///
+/// # Implementation notes
+///
+/// Internally, this is represented as a raw pointer in Rust. See the note above
+/// about Nullness for why we don't use [`core::ptr::NonNull`].
+#[repr(transparent)]
+pub struct CppRef<'a, T: ?Sized> {
+    ptr: *const T,
+    phantom: PhantomData<&'a T>,
+}
+
+impl<'a, T: ?Sized> CppRef<'a, T> {
     /// Retrieve the underlying C++ pointer.
-    fn as_ptr(&self) -> *const T;
+    pub fn as_ptr(&self) -> *const T {
+        self.ptr
+    }
 
     /// Get a regular Rust reference out of this C++ reference.
     ///
@@ -24,21 +133,111 @@
     /// 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 {
+    ///
+    /// Callers must also be sure that the C++ reference is properly
+    /// aligned, not null, pointing to valid data, etc.
+    pub unsafe fn as_ref(&self) -> &T {
         &*self.as_ptr()
     }
+
+    /// Create a C++ reference from a raw pointer.
+    pub fn from_ptr(ptr: *const T) -> Self {
+        Self {
+            ptr,
+            phantom: PhantomData,
+        }
+    }
+
+    /// Create a mutable version of this reference, roughly equivalent
+    /// to C++ `const_cast`.
+    ///
+    /// The opposite is to use [`AsCppRef::as_cpp_ref`] on a [`CppMutRef`]
+    /// to obtain a [`CppRef`].
+    ///
+    /// # Safety
+    ///
+    /// Because we never dereference a `CppRef` in Rust, this cannot create
+    /// undefined behavior _within Rust_ and is therefore not unsafe. It is
+    /// however generally unwise, just as it is in C++. Use sparingly.
+    pub fn const_cast(&self) -> CppMutRef<'a, T> {
+        CppMutRef {
+            ptr: self.ptr as *mut T,
+            phantom: self.phantom,
+        }
+    }
+
+    /// Extend the lifetime of the returned reference beyond normal Rust
+    /// borrow checker rules.
+    ///
+    /// Normally, a reference can't be used beyond the lifetime of the object
+    /// which gave it to you, but sometimes C++ APIs can return references
+    /// to global or other longer-lived objects. In such a case you should
+    /// use this method to get a longer-lived reference.
+    ///
+    /// # Usage
+    ///
+    /// When you're given a C++ reference and you know its referent is valid
+    /// for a long time, use this method. Store the resulting `PhantomReferent`
+    /// somewhere in Rust with an equivalent lifetime.
+    /// That object can then vend longer-lived `CppRef`s using
+    /// [`AsCppRef::as_cpp_ref`].
+    ///
+    /// # Safety
+    ///
+    /// Because `CppRef`s are never dereferenced in Rust, misuse of this API
+    /// cannot lead to undefined behavior _in Rust_ and is therefore not
+    /// unsafe. Nevertheless this can lead to UB in C++, so use carefully.
+    pub fn lifetime_cast(&self) -> PhantomReferent<T> {
+        PhantomReferent(self.ptr)
+    }
 }
 
+impl<'a, T: ?Sized> Deref for CppRef<'a, T> {
+    type Target = *const T;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        // With `inline_const` we can simplify this to:
+        // const { panic!("you shouldn't deref CppRef!") }
+        struct C<T: ?Sized>(T);
+        impl<T: ?Sized> C<T> {
+            const V: core::convert::Infallible = panic!(
+                "You cannot directly obtain a Rust reference from a CppRef. Use CppRef::as_ref."
+            );
+        }
+        match C::<T>::V {}
+    }
+}
+
+impl<'a, T: ?Sized> Clone for CppRef<'a, T> {
+    fn clone(&self) -> Self {
+        Self {
+            ptr: self.ptr,
+            phantom: self.phantom,
+        }
+    }
+}
+
+#[cfg(nightly)]
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppRef<'_, U>> for CppRef<'_, T> {}
+
 /// 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.
+/// and that's not permitted for regular Rust references.
 ///
-/// 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> {
+/// See [`CppRef`] for details on safety, usage models and implementation.
+///
+/// You can convert this to a [`CppRef`] using the [`std::convert::Into`] trait.
+#[repr(transparent)]
+pub struct CppMutRef<'a, T: ?Sized> {
+    ptr: *mut T,
+    phantom: PhantomData<&'a T>,
+}
+
+impl<'a, T: ?Sized> CppMutRef<'a, T> {
     /// Retrieve the underlying C++ pointer.
-    fn as_mut_ptr(&self) -> *mut T;
+    pub fn as_mut_ptr(&self) -> *mut T {
+        self.ptr
+    }
 
     /// Get a regular Rust mutable reference out of this C++ reference.
     ///
@@ -48,16 +247,98 @@
     /// 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 {
+    ///
+    /// Callers must also be sure that the C++ reference is properly
+    /// aligned, not null, pointing to valid data, etc.
+    pub unsafe fn as_mut(&mut self) -> &mut T {
         &mut *self.as_mut_ptr()
     }
+
+    /// Create a C++ reference from a raw pointer.
+    pub fn from_ptr(ptr: *mut T) -> Self {
+        Self {
+            ptr,
+            phantom: PhantomData,
+        }
+    }
+
+    /// Extend the lifetime of the returned reference beyond normal Rust
+    /// borrow checker rules. See [`CppRef::lifetime_cast`].
+    pub fn lifetime_cast(&mut self) -> PhantomReferentMut<T> {
+        PhantomReferentMut(self.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.
+impl<'a, T: ?Sized> Deref for CppMutRef<'a, T> {
+    type Target = *const T;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        // With `inline_const` we can simplify this to:
+        // const { panic!("you shouldn't deref CppRef!") }
+        struct C<T: ?Sized>(T);
+        impl<T: ?Sized> C<T> {
+            const V: core::convert::Infallible = panic!("You cannot directly obtain a Rust reference from a CppMutRef. Use CppMutRef::as_mut.");
+        }
+        match C::<T>::V {}
+    }
+}
+
+impl<'a, T: ?Sized> Clone for CppMutRef<'a, T> {
+    fn clone(&self) -> Self {
+        Self {
+            ptr: self.ptr,
+            phantom: self.phantom,
+        }
+    }
+}
+
+impl<'a, T> From<CppMutRef<'a, T>> for CppRef<'a, T> {
+    fn from(mutable: CppMutRef<'a, T>) -> Self {
+        Self {
+            ptr: mutable.ptr,
+            phantom: mutable.phantom,
+        }
+    }
+}
+
+#[cfg(nightly)]
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppMutRef<'_, U>> for CppMutRef<'_, T> {}
+
+/// Any type which can return a C++ reference to its contents.
+pub trait AsCppRef<T: ?Sized> {
+    /// Returns a reference which obeys C++ reference semantics
+    fn as_cpp_ref(&self) -> CppRef<T>;
+}
+
+/// Any type which can return a C++ reference to its contents.
+pub trait AsCppMutRef<T: ?Sized>: AsCppRef<T> {
+    /// Returns a mutable reference which obeys C++ reference semantics
+    fn as_cpp_mut_ref(&mut self) -> CppMutRef<T>;
+}
+
+impl<'a, T: ?Sized> AsCppRef<T> for CppMutRef<'a, T> {
+    fn as_cpp_ref(&self) -> CppRef<T> {
+        CppRef::from_ptr(self.ptr)
+    }
+}
+
+/// Workaround for the inability to use std::ptr::addr_of! on the contents
+/// of a box.
+#[repr(transparent)]
+struct CppPinContents<T: ?Sized>(T);
+
+impl<T: ?Sized> CppPinContents<T> {
+    fn addr_of(&self) -> *const T {
+        std::ptr::addr_of!(self.0)
+    }
+    fn addr_of_mut(&mut self) -> *mut T {
+        std::ptr::addr_of_mut!(self.0)
+    }
+}
+
+/// A newtype wrapper which causes the contained object to obey C++ reference
+/// semantics rather than Rust reference semantics. That is, multiple aliasing
+/// mutable C++ references may exist to the contents.
 ///
 /// C++ references are permitted to alias one another, and commonly do.
 /// Rust references must alias according only to the narrow rules of the
@@ -65,27 +346,98 @@
 ///
 /// 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>;
+/// If you need the object back for use in the Rust domain, use [`CppPin::extract`],
+/// but be aware of the safety invariants that you - as a human - will need
+/// to guarantee.
+///
+/// # Usage models
+///
+/// From fairly safe to fairly unsafe:
+///
+/// * *Configure a thing in Rust then give it to C++*. Take your Rust object,
+///   set it up freely using Rust references, methods and data, then imprison
+///   it in a `CppPin` and keep it around while you work with it in C++.
+///   There is no possibility of _aliasing_ UB in this usage model, but you
+///   still need to be careful of use-after-free bugs, just as if you were
+///   to create a reference to any data in C++. The Rust borrow checker will
+///   help you a little by ensuring that your `CppRef` objects don't outlive
+///   the `CppPin`, but once those references pass into C++, it can't help.
+/// * *Pass a thing to C++, have it operate on it synchronously, then take
+///   it back*. To do this, you'd imprison your Rust object in a `CppPin`,
+///   then pass mutable C++ references (using [`AsCppMutRef::as_cpp_mut_ref`])
+///   into a C++ function. C++ would duly operate on the object, and thereafter
+///   you could reclaim the object with `extract()`. At this point, you (as
+///   a human) will need to give a guarantee that no references remain in the
+///   C++ domain. If your object was just locally used by a single C++ function,
+///   which has now returned, this type of local analysis may well be practical.
+/// * *Share a thing between Rust and C++*. This object can vend both C++
+///   references and Rust references (via `as_ref` etc.) It may be possible
+///   for you to guarantee that C++ does not mutate the object while any Rust
+///   reference exists. If you choose this model, you'll need to carefully
+///   track exactly what happens to references and pointers on both sides,
+///   and document your evidence for why you are sure this is safe.
+///   Failure here is bad: Rust makes all sorts of optimization decisions based
+///   upon its borrow checker guarantees, so mistakes can lead to undebuggable
+///   action-at-a-distance crashes.
+///
+/// # See also
+///
+/// See also [`CppUniquePtrPin`], which is equivalent for data which is in
+/// a [`cxx::UniquePtr`].
+pub struct CppPin<T: ?Sized>(Box<CppPinContents<T>>);
 
-    /// The type of C++ mutable reference created to the contained object..
-    type CppMutRef: CppMutRef<'a, T>;
+impl<T: ?Sized> CppPin<T> {
+    /// Imprison the Rust data within a `CppPin`. This eliminates any remaining
+    /// Rust references (since we take the item by value) and this object
+    /// subsequently only vends C++ style references, not Rust references,
+    /// until or unless `extract` is called.
+    pub fn new(item: T) -> Self
+    where
+        T: Sized,
+    {
+        Self(Box::new(CppPinContents(item)))
+    }
+
+    /// Imprison the boxed Rust data within a `CppPin`. This eliminates any remaining
+    /// Rust references (since we take the item by value) and this object
+    /// subsequently only vends C++ style references, not Rust references,
+    /// until or unless `extract` is called.
+    ///
+    /// If the item is already in a `Box`, this is slightly more efficient than
+    /// `new` because it will avoid moving/reallocating it.
+    pub fn from_box(item: Box<T>) -> Self {
+        // Safety: CppPinContents<T> is #[repr(transparent)] so
+        // this transmute from
+        //   Box<T>
+        // to
+        //   Box<CppPinContents<T>>
+        // is safe.
+        let contents = unsafe { std::mem::transmute(item) };
+        Self(contents)
+    }
+
+    // Imprison the boxed Rust data within a `CppPin`.  This eliminates any remaining
+    /// Rust references (since we take the item by value) and this object
+    /// subsequently only vends C++ style references, not Rust references,
+    /// until or unless `extract` is called.
+    ///
+    /// If the item is already in a `Box`, this is slightly more efficient than
+    /// `new` because it will avoid moving/reallocating it.
+    pub fn from_pinned_box(item: Pin<Box<T>>) -> Self {
+        // Safety: it's OK to un-pin the Box because we'll be putting it
+        // into a CppPin which upholds the same pinned-ness contract.
+        Self::from_box(unsafe { Pin::into_inner_unchecked(item) })
+    }
 
     /// Get an immutable pointer to the underlying object.
-    fn as_ptr(&self) -> *const T;
+    pub fn as_ptr(&self) -> *const T {
+        self.0.addr_of()
+    }
 
     /// 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;
+    pub fn as_mut_ptr(&mut self) -> *mut T {
+        self.0.addr_of_mut()
+    }
 
     /// Get a normal Rust reference to the underlying object. This is unsafe.
     ///
@@ -93,7 +445,7 @@
     ///
     /// You must guarantee that C++ will not mutate the object while the
     /// reference exists.
-    unsafe fn as_ref(&self) -> &T {
+    pub unsafe fn as_ref(&self) -> &T {
         &*self.as_ptr()
     }
 
@@ -103,7 +455,183 @@
     ///
     /// You must guarantee that C++ will not mutate the object while the
     /// reference exists.
-    unsafe fn as_mut(&mut self) -> &mut T {
+    pub unsafe fn as_mut(&mut self) -> &mut T {
         &mut *self.as_mut_ptr()
     }
+
+    /// Extract the object from within its prison, for re-use again within
+    /// the domain of normal Rust references.
+    ///
+    /// This returns a `Box<T>`: if you want the underlying `T` you can extract
+    /// it using `*`.
+    ///
+    /// # Safety
+    ///
+    /// Callers promise that no remaining C++ references exist either
+    /// in the form of Rust [`CppRef`]/[`CppMutRef`] or any remaining pointers/
+    /// references within C++.
+    pub unsafe fn extract(self) -> Box<T> {
+        // Safety: CppPinContents<T> is #[repr(transparent)] so
+        // this transmute from
+        //   Box<CppPinContents<T>>
+        // to
+        //   Box<T>
+        // is safe.
+        std::mem::transmute(self.0)
+    }
+}
+
+impl<T: ?Sized> AsCppRef<T> for CppPin<T> {
+    fn as_cpp_ref(&self) -> CppRef<T> {
+        CppRef::from_ptr(self.as_ptr())
+    }
+}
+
+impl<T: ?Sized> AsCppMutRef<T> for CppPin<T> {
+    fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
+        CppMutRef::from_ptr(self.as_mut_ptr())
+    }
+}
+
+/// Any newtype wrapper which causes the contained [`UniquePtr`] target to obey C++ reference
+/// semantics rather than Rust reference semantics. That is, multiple aliasing
+/// mutable C++ references may exist to the contents.
+///
+/// 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.
+pub struct CppUniquePtrPin<T: UniquePtrTarget>(UniquePtr<T>);
+
+impl<T: UniquePtrTarget> CppUniquePtrPin<T> {
+    /// Imprison the type within a `CppPin`. This eliminates any remaining
+    /// Rust references (since we take the item by value) and this object
+    /// subsequently only vends C++ style references, not Rust references.
+    pub fn new(item: UniquePtr<T>) -> Self {
+        Self(item)
+    }
+
+    /// Get an immutable pointer to the underlying object.
+    pub fn as_ptr(&self) -> *const T {
+        // TODO - avoid brief reference here
+        self.0
+            .as_ref()
+            .expect("UniquePtr was null; we can't make a C++ reference")
+    }
+}
+
+impl<T: UniquePtrTarget> AsCppRef<T> for CppUniquePtrPin<T> {
+    fn as_cpp_ref(&self) -> CppRef<T> {
+        CppRef::from_ptr(self.as_ptr())
+    }
+}
+
+impl<T: UniquePtrTarget> AsCppMutRef<T> for CppUniquePtrPin<T> {
+    fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
+        let pinnned_ref: Pin<&mut T> = self
+            .0
+            .as_mut()
+            .expect("UniquePtr was null; we can't make a C++ reference");
+        let ptr = unsafe { Pin::into_inner_unchecked(pinnned_ref) };
+        CppMutRef::from_ptr(ptr)
+    }
+}
+
+/// A structure used to extend the lifetime of a returned C++ reference,
+/// to indicate to Rust that it's beyond the normal Rust lifetime rules.
+/// See [`CppRef::lifetime_cast`].
+#[repr(transparent)]
+pub struct PhantomReferent<T: ?Sized>(*const T);
+
+impl<T: ?Sized> AsCppRef<T> for PhantomReferent<T> {
+    fn as_cpp_ref(&self) -> CppRef<T> {
+        CppRef::from_ptr(self.0)
+    }
+}
+
+/// A structure used to extend the lifetime of a returned C++ mutable reference,
+/// to indicate to Rust that it's beyond the normal Rust lifetime rules.
+/// See [`CppRef::lifetime_cast`].
+#[repr(transparent)]
+pub struct PhantomReferentMut<T: ?Sized>(*mut T);
+
+impl<T: ?Sized> AsCppRef<T> for PhantomReferentMut<T> {
+    fn as_cpp_ref(&self) -> CppRef<T> {
+        CppRef::from_ptr(self.0)
+    }
+}
+
+impl<T: ?Sized> AsCppMutRef<T> for PhantomReferentMut<T> {
+    fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
+        CppMutRef::from_ptr(self.0)
+    }
+}
+
+#[cfg(all(feature = "arbitrary_self_types", test))]
+mod tests {
+    use super::*;
+
+    struct CppOuter {
+        _a: u32,
+        inner: CppInner,
+        global: *const CppInner,
+    }
+
+    impl CppOuter {
+        fn get_inner_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
+            // Safety: emulating C++ code for test purposes. This is safe
+            // because we know the data isn't modified during the lifetime of
+            // the returned reference.
+            let self_rust_ref = unsafe { self.as_ref() };
+            CppRef::from_ptr(std::ptr::addr_of!(self_rust_ref.inner))
+        }
+        fn get_global_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
+            // Safety: emulating C++ code for test purposes. This is safe
+            // because we know the data isn't modified during the lifetime of
+            // the returned reference.
+            let self_rust_ref = unsafe { self.as_ref() };
+            CppRef::from_ptr(self_rust_ref.global)
+        }
+    }
+
+    struct CppInner {
+        b: u32,
+    }
+
+    impl CppInner {
+        fn value_is(self: &CppRef<Self>) -> u32 {
+            // Safety: emulating C++ code for test purposes. This is safe
+            // because we know the data isn't modified during the lifetime of
+            // the returned reference.
+            let self_rust_ref = unsafe { self.as_ref() };
+            self_rust_ref.b
+        }
+    }
+
+    #[test]
+    fn cpp_objects() {
+        let mut global = CppInner { b: 7 };
+        let global_ref_lifetime_phantom;
+        {
+            let outer = CppOuter {
+                _a: 12,
+                inner: CppInner { b: 3 },
+                global: &mut global,
+            };
+            let outer = CppPin::new(outer);
+            let inner_ref = outer.as_cpp_ref().get_inner_ref();
+            assert_eq!(inner_ref.value_is(), 3);
+            global_ref_lifetime_phantom = Some(outer.as_cpp_ref().get_global_ref().lifetime_cast());
+        }
+        let global_ref = global_ref_lifetime_phantom.unwrap();
+        let global_ref = global_ref.as_cpp_ref();
+        assert_eq!(global_ref.value_is(), 7);
+    }
+
+    #[test]
+    fn cpp_pin() {
+        let a = RustThing { _a: 4 };
+        let a = CppPin::new(a);
+        let _ = a.as_cpp_ref();
+        let _ = a.as_cpp_ref();
+    }
 }
diff --git a/third_party/autocxx/src/rvalue_param.rs b/third_party/autocxx/src/rvalue_param.rs
index 4acc52e..e380c8a 100644
--- a/third_party/autocxx/src/rvalue_param.rs
+++ b/third_party/autocxx/src/rvalue_param.rs
@@ -78,6 +78,13 @@
 /// need to pass a value parameter into C++, and will take responsibility
 /// for extracting that value parameter from the [`RValueParam`] and doing
 /// any later cleanup.
+///
+/// Because C++ move constructors may modify the original object, we consume
+/// the object and store it, pinned, until the call completes. This avoids any
+/// risk that C++ will mutate objects elsewhere in Rust-land, which could cause
+/// problems in the case of re-entrancy as references might exist to those
+/// other objects and Rust assumes there are no unexpected mutations of objects
+/// where references exist.
 #[doc(hidden)]
 pub struct RValueParamHandler<T, RVP>
 where
diff --git a/third_party/autocxx/src/subclass.rs b/third_party/autocxx/src/subclass.rs
index 6c6ee31..bf905d9 100644
--- a/third_party/autocxx/src/subclass.rs
+++ b/third_party/autocxx/src/subclass.rs
@@ -31,7 +31,7 @@
 ///   #[subclass(superclass("MyCppSuperclass"))]
 ///   struct Bar {};
 ///   ```
-/// * as a directive within the [include_cpp] macro, in which case you
+/// * as a directive within the [crate::include_cpp] macro, in which case you
 ///   must provide two arguments of the superclass and then the
 ///   subclass:
 ///   ```
@@ -102,18 +102,14 @@
 /// A type showing how the Rust side of a Rust/C++ subclass pair refers to
 /// the C++ side.
 #[doc(hidden)]
+#[derive(Default)]
 pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> {
+    #[default]
     Empty,
     Owned(Box<UniquePtr<CppPeer>>),
     Unowned(*mut CppPeer),
 }
 
-impl<CppPeer: CppSubclassCppPeer> Default for CppSubclassCppPeerHolder<CppPeer> {
-    fn default() -> Self {
-        CppSubclassCppPeerHolder::Empty
-    }
-}
-
 impl<CppPeer: CppSubclassCppPeer> CppSubclassCppPeerHolder<CppPeer> {
     fn pin_mut(&mut self) -> Pin<&mut CppPeer> {
         match self {
diff --git a/third_party/autocxx/src/value_param.rs b/third_party/autocxx/src/value_param.rs
index 8e564a1..7386d19 100644
--- a/third_party/autocxx/src/value_param.rs
+++ b/third_party/autocxx/src/value_param.rs
@@ -7,7 +7,7 @@
 // except according to those terms.
 
 use cxx::{memory::UniquePtrTarget, UniquePtr};
-use moveit::{CopyNew, DerefMove, MoveNew, New};
+use moveit::{AsMove, CopyNew, MoveNew, New};
 use std::{marker::PhantomPinned, mem::MaybeUninit, ops::Deref, pin::Pin};
 
 /// A trait representing a parameter to a C++ function which is received
@@ -192,22 +192,21 @@
 
 /// Explicitly force a value parameter to be taken using any type of [`crate::moveit::new::New`],
 /// i.e. a constructor.
-pub fn as_new<N: New<Output = T>, T>(constructor: N) -> impl ValueParam<T> {
+pub fn as_new<N: New>(constructor: N) -> impl ValueParam<N::Output> {
     ByNew(constructor)
 }
 
 /// Explicitly force a value parameter to be taken by copy.
-pub fn as_copy<P: Deref<Target = T>, T>(ptr: P) -> impl ValueParam<T>
+pub fn as_copy<P: Deref>(ptr: P) -> impl ValueParam<P::Target>
 where
-    T: CopyNew,
+    P::Target: CopyNew,
 {
     ByNew(crate::moveit::new::copy(ptr))
 }
 
-/// Explicitly force a value parameter to be taken usign C++ move semantics.
-pub fn as_mov<P: DerefMove + Deref<Target = T>, T>(ptr: P) -> impl ValueParam<T>
+/// Explicitly force a value parameter to be taken using C++ move semantics.
+pub fn as_mov<P: AsMove>(ptr: P) -> impl ValueParam<P::Target>
 where
-    P: DerefMove,
     P::Target: MoveNew,
 {
     ByNew(crate::moveit::new::mov(ptr))
@@ -216,11 +215,8 @@
 #[doc(hidden)]
 pub struct ByNew<N: New>(N);
 
-unsafe impl<N, T> ValueParam<T> for ByNew<N>
-where
-    N: New<Output = T>,
-{
-    type StackStorage = MaybeUninit<T>;
+unsafe impl<N: New> ValueParam<N::Output> for ByNew<N> {
+    type StackStorage = MaybeUninit<N::Output>;
 
     unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
         // Safety: we won't move/swap things within the pin.
@@ -228,11 +224,11 @@
         *slot = Some(MaybeUninit::uninit());
         self.0.new(Pin::new_unchecked(slot.as_mut().unwrap()))
     }
-    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
-        // Safety: it's OK to (briefly) create a reference to the T because we
+    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut N::Output {
+        // Safety: it's OK to (briefly) create a reference to the N::Output because we
         // populated it within `populate_stack_space`. It's OK to unpack the pin
         // because we're not going to move the contents.
-        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T }
+        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut N::Output }
     }
 
     fn do_drop(stack: Pin<&mut Self::StackStorage>) {
diff --git a/third_party/autocxx/tools/mdbook-preprocessor/Cargo.toml b/third_party/autocxx/tools/mdbook-preprocessor/Cargo.toml
index f88e833..b6fb48c 100644
--- a/third_party/autocxx/tools/mdbook-preprocessor/Cargo.toml
+++ b/third_party/autocxx/tools/mdbook-preprocessor/Cargo.toml
@@ -8,28 +8,28 @@
 
 [package]
 name = "autocxx-mdbook-preprocessor"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["adetaylor <adetaylor@chromium.org>"]
 edition = "2021"
 
 [dependencies]
-clap = { version = "3", features = [ "cargo" ] }
+clap = { version = "3", features = ["cargo"] }
 serde_json = "1"
 itertools = "0.10"
 anyhow = "1"
 regex = "1"
-autocxx-integration-tests = { path = "../../integration-tests", version="=0.22.3"}
+autocxx-integration-tests = { path = "../../integration-tests", version = "=0.26.0" }
 rayon = "1.5"
 gag = "1.0"
 env_logger = "0.9.0"
 
 [dependencies.syn]
 version = "1.0.39"
-features = [ "parsing" ]
+features = ["parsing"]
 
 [dependencies.proc-macro2]
 version = "1.0.11"
-features = [ "span-locations" ]
+features = ["span-locations"]
 
 [dependencies.mdbook]
 version = "0.4"
diff --git a/third_party/autocxx/tools/mdbook-preprocessor/src/main.rs b/third_party/autocxx/tools/mdbook-preprocessor/src/main.rs
index 1bb4e31..7123ec1 100644
--- a/third_party/autocxx/tools/mdbook-preprocessor/src/main.rs
+++ b/third_party/autocxx/tools/mdbook-preprocessor/src/main.rs
@@ -9,6 +9,7 @@
 use std::{
     borrow::Cow,
     collections::HashSet,
+    ffi::OsString,
     fmt::Display,
     io::{self, Read},
     path::PathBuf,
@@ -135,11 +136,11 @@
                     &case.cpp,
                     &case.hdr,
                     case.rs,
-                    args.value_of_os("manifest_dir").unwrap(),
+                    &OsString::from(args.value_of("manifest_dir").unwrap()),
                 );
                 let desc = match err {
                     Ok(_) => "passed".to_string(),
-                    Err(ref err) => format!("failed: {:?}", err),
+                    Err(ref err) => format!("failed: {err:?}"),
                 };
                 eprintln!(
                     "Doctest {}/{} at {} {}.",
@@ -164,7 +165,7 @@
             .read_to_string(&mut stdout_str)
             .unwrap();
         if !stdout_str.is_empty() {
-            eprintln!("Stdout from tests:\n{}", stdout_str);
+            eprintln!("Stdout from tests:\n{stdout_str}");
         }
         if !fails.is_empty() {
             panic!(
@@ -304,7 +305,7 @@
 ) -> impl Iterator<Item = String> {
     let input_str = lines.join("\n");
     let fn_call = syn::parse_str::<syn::Expr>(&input_str)
-        .unwrap_or_else(|_| panic!("Unable to parse outer function at {}", location));
+        .unwrap_or_else(|_| panic!("Unable to parse outer function at {location}"));
     let fn_call = match fn_call {
         Expr::Call(expr) => expr,
         _ => panic!("Parsing unexpected"),
@@ -338,7 +339,7 @@
             cpp,
             hdr,
             rs: syn::parse_file(&rs)
-                .unwrap_or_else(|_| panic!("Unable to parse code at {}", location))
+                .unwrap_or_else(|_| panic!("Unable to parse code at {location}"))
                 .to_token_stream(),
             location,
         });
diff --git a/third_party/autocxx/tools/publish-all.sh b/third_party/autocxx/tools/publish-all.sh
index b43b95b..7252b5e 100755
--- a/third_party/autocxx/tools/publish-all.sh
+++ b/third_party/autocxx/tools/publish-all.sh
@@ -11,6 +11,4 @@
   echo "Publish: $CRATE"
   cargo publish
   popd
-  sleep 30 # sometimes crates.io takes a moment, and our
-           # crates are interdependent.
 done
diff --git a/third_party/autocxx/tools/reduce/Cargo.toml b/third_party/autocxx/tools/reduce/Cargo.toml
index 83e3916..c60d2d0 100644
--- a/third_party/autocxx/tools/reduce/Cargo.toml
+++ b/third_party/autocxx/tools/reduce/Cargo.toml
@@ -8,30 +8,32 @@
 
 [package]
 name = "autocxx-reduce"
-version = "0.22.3"
+version = "0.26.0"
 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.3", path="../../engine" }
-autocxx-parser = { version = "=0.22.3", path="../../parser", features = [ "reproduction_case" ] }
+autocxx-engine = { version = "=0.26.0", path = "../../engine" }
+autocxx-parser = { version = "=0.26.0", path = "../../parser", features = [
+    "reproduction_case",
+] }
 clap = { version = "3.1.2", features = ["cargo"] }
-tempfile = "3.1"
+tempfile = "3.4"
 indoc = "1.0"
 itertools = "0.10"
 serde_json = "1.0"
 serde = "1.0"
 serde_derive = "1.0"
-syn = "1.0.39"
+syn = "2.0.1"
 quote = "1.0"
-cxx-gen = "0.7.68"
+cxx-gen = "0.7.78"
 regex = "1.5"
 indexmap = "1.8"
 
 [dev-dependencies]
-assert_cmd = "1.0.3"
+assert_cmd = "2"
 tempfile = "3.1"
 indoc = "1.0"
-proc-macro2 = "1.0"
\ No newline at end of file
+proc-macro2 = "1.0"
diff --git a/third_party/autocxx/tools/reduce/src/main.rs b/third_party/autocxx/tools/reduce/src/main.rs
index 06110cd..d585dee 100644
--- a/third_party/autocxx/tools/reduce/src/main.rs
+++ b/third_party/autocxx/tools/reduce/src/main.rs
@@ -36,6 +36,13 @@
 "};
 
 fn main() {
+    // Assemble some defaults for command line arguments
+    let current_exe = std::env::current_exe().unwrap();
+    let our_dir = current_exe.parent().unwrap();
+    let default_gen_cmd = our_dir.join("autocxx-gen").to_str().unwrap().to_string();
+    let rust_libs_path1 = our_dir.to_str().unwrap().to_string();
+    let rust_libs_path2 = our_dir.join("deps").to_str().unwrap().to_string();
+    let default_rlibs = &[rust_libs_path1.as_str(), rust_libs_path2.as_str()];
     let matches = Command::new("autocxx-reduce")
         .version(crate_version!())
         .author(crate_authors!())
@@ -138,6 +145,24 @@
                 .long("gen-cmd")
                 .value_name("GEN-CMD")
                 .help("where to find autocxx-gen")
+                .default_value(&default_gen_cmd)
+                .takes_value(true),
+        )
+        .arg(
+            Arg::new("rustc")
+                .long("rustc")
+                .value_name("RUSTC")
+                .help("where to find rustc")
+                .default_value("rustc")
+                .takes_value(true),
+        )
+        .arg(
+            Arg::new("rlibs")
+                .long("rlibs")
+                .value_name("LIBDIR")
+                .help("where to find rlibs/rmetas for cxx and autocxx")
+                .default_values(default_rlibs)
+                .multiple_values(true)
                 .takes_value(true),
         )
         .arg(
@@ -172,6 +197,11 @@
                 .help("Do not post-compile the C++ generated by autocxxgen"),
         )
         .arg(
+            Arg::new("no-rustc")
+                .long("no-rustc")
+                .help("Do not compile the rust generated by autocxxgen"),
+        )
+        .arg(
             Arg::new("suppress-cxx-inclusions")
                 .long("suppress-cxx-inclusions")
                 .takes_value(true)
@@ -222,8 +252,7 @@
             let listing_path = tmp_dir.path().join("listing.h");
             create_concatenated_header(&headers, &listing_path)?;
             announce_progress(&format!(
-                "Preprocessing {:?} to {:?}",
-                listing_path, concat_path
+                "Preprocessing {listing_path:?} to {concat_path:?}"
             ));
             preprocess(&listing_path, &concat_path, &incs, &defs)?;
             let directives: Vec<_> = std::iter::once("#include \"concat.h\"\n".to_string())
@@ -231,7 +260,7 @@
                     submatches
                         .values_of("directive")
                         .unwrap_or_default()
-                        .map(|s| format!("{}\n", s)),
+                        .map(|s| format!("{s}\n")),
                 )
                 .collect();
             create_rs_file(&rs_path, &directives)?;
@@ -277,33 +306,25 @@
         .collect();
     let extra_clang_args: Vec<&str> = extra_clang_args.iter().map(|s| s.as_ref()).collect_vec();
 
-    let default_gen_cmd = std::env::current_exe()?
-        .parent()
-        .unwrap()
-        .join("autocxx-gen")
-        .to_str()
-        .unwrap()
-        .to_string();
-    let gen_cmd = matches.value_of("gen-cmd").unwrap_or(&default_gen_cmd);
+    let gen_cmd = matches.value_of("gen-cmd").unwrap();
     if !Path::new(gen_cmd).exists() {
         panic!(
-            "autocxx-gen not found in {}. hint: autocxx-reduce --gen-cmd /path/to/autocxx-gen",
-            gen_cmd
+            "autocxx-gen not found in {gen_cmd}. hint: autocxx-reduce --gen-cmd /path/to/autocxx-gen"
         );
     }
+
     run_sample_gen_cmd(gen_cmd, &rs_path, tmp_dir.path(), &extra_clang_args)?;
     // Create and run an interestingness test which does not filter its output through grep.
     let demo_interestingness_test_dir = tmp_dir.path().join("demo-interestingness-test");
     std::fs::create_dir(&demo_interestingness_test_dir).unwrap();
     let interestingness_test = demo_interestingness_test_dir.join("test-demo.sh");
     create_interestingness_test(
+        &matches,
         gen_cmd,
         &interestingness_test,
         None,
         &rs_path,
         &extra_clang_args,
-        !matches.is_present("no-precompile"),
-        !matches.is_present("no-postcompile"),
     )?;
     let demo_dir_concat_path = demo_interestingness_test_dir.join("concat.h");
     std::fs::copy(&concat_path, demo_dir_concat_path).unwrap();
@@ -312,13 +333,12 @@
     // Now the main interestingness test
     let interestingness_test = tmp_dir.path().join("test.sh");
     create_interestingness_test(
+        &matches,
         gen_cmd,
         &interestingness_test,
         Some(matches.value_of("problem").unwrap()),
         &rs_path,
         &extra_clang_args,
-        !matches.is_present("no-precompile"),
-        !matches.is_present("no-postcompile"),
     )?;
     run_creduce(
         matches.value_of("creduce").unwrap(),
@@ -331,7 +351,7 @@
     match output_path {
         None => print_minimized_case(&concat_path)?,
         Some(output_path) => {
-            std::fs::copy(&concat_path, &PathBuf::from(output_path))?;
+            std::fs::copy(&concat_path, PathBuf::from(output_path))?;
         }
     };
     Ok(())
@@ -349,13 +369,13 @@
 }
 
 fn announce_progress(msg: &str) {
-    println!("=== {} ===", msg);
+    println!("=== {msg} ===");
 }
 
 fn print_minimized_case(concat_path: &Path) -> Result<(), std::io::Error> {
     announce_progress("Completed. Minimized test case:");
     let contents = std::fs::read_to_string(concat_path)?;
-    println!("{}", contents);
+    println!("{contents}");
     Ok(())
 }
 
@@ -370,7 +390,7 @@
         .arg("--help")
         .output();
     let msg = match cmd {
-        Err(error) => panic!("failed to run creduce. creduce_cmd = {}. hint: autocxx-reduce --creduce /path/to/creduce. error = {}", creduce_cmd, error),
+        Err(error) => panic!("failed to run creduce. creduce_cmd = {creduce_cmd}. hint: autocxx-reduce --creduce /path/to/creduce. error = {error}"),
         Ok(result) => result.stdout
     };
     let msg = std::str::from_utf8(&msg).unwrap();
@@ -413,7 +433,7 @@
     let args = format_gen_cmd(rs_file, tmp_dir.to_str().unwrap(), extra_clang_args);
     let args = args.collect::<Vec<_>>();
     let args_str = args.join(" ");
-    announce_progress(&format!("Running sample gen cmd: {} {}", gen_cmd, args_str));
+    announce_progress(&format!("Running sample gen cmd: {gen_cmd} {args_str}"));
     std::process::Command::new(gen_cmd).args(args).status()?;
     Ok(())
 }
@@ -451,15 +471,26 @@
 }
 
 fn create_interestingness_test(
+    matches: &ArgMatches,
     gen_cmd: &str,
     test_path: &Path,
     problem: Option<&str>,
     rs_file: &Path,
     extra_clang_args: &[&str],
-    precompile: bool,
-    postcompile: bool,
 ) -> Result<(), std::io::Error> {
     announce_progress("Creating interestingness test");
+    let precompile = !matches.is_present("no-precompile");
+    let postcompile = !matches.is_present("no-postcompile");
+    let rustc = !matches.is_present("no-rustc");
+
+    let rustc_path = matches.value_of("rustc").unwrap();
+
+    let rust_libs_path: Vec<String> = matches
+        .get_many::<String>("rlibs")
+        .expect("No rlib path specified")
+        .cloned()
+        .collect();
+
     // Ensure we refer to the input header by relative path
     // because creduce will invoke us in some other directory with
     // a copy thereof.
@@ -469,37 +500,47 @@
     // For the compile afterwards, we have to avoid including any system headers.
     // We rely on equivalent content being hermetically inside concat.h.
     let postcompile_step = make_compile_step(postcompile, "gen0.cc", extra_clang_args);
+    let rustc_step = if rustc {
+        let rust_libs_path = rust_libs_path.iter().map(|p| format!(" -L{p}")).join(" ");
+        format!("{rustc_path} --extern cxx --extern autocxx {rust_libs_path} --crate-type rlib --emit=metadata --edition=2021 autocxx-ffi-default-gen.rs 2>&1")
+    } else {
+        "echo Skipping rustc".to_string()
+    };
+    // -q below to exit immediately as soon as a match is found, to avoid
+    // extra compile/codegen steps
     let problem_grep = problem
-        .map(|problem| format!("| grep \"{}\"  >/dev/null  2>&1", problem))
+        .map(|problem| format!("| grep -q \"{problem}\"  >/dev/null  2>&1"))
         .unwrap_or_default();
+    // We formerly had a 'trap' below but it seems to have caused problems
+    // (trap \"if [[ \\$? -eq 139 ]]; then echo Segfault; fi\" CHLD; {} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} && {} 2>&1 ) {}
     let content = format!(
         indoc! {"
-        #!/bin/sh
+        #!/bin/bash
         set -e
         echo Precompile
         {}
         echo Move
         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
-        (trap \"if [[ \\$? -eq 139 ]]; then echo Segfault; fi\" CHLD; {} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} 2>&1 ) {}
+        echo Codegen
+        ({} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} && {} 2>&1) {}
         echo Remove
         rm concat.h
         echo Swap back
         mv concat-body.h concat.h
         echo Done
     "},
-        precompile_step, gen_cmd, args, postcompile_step, problem_grep
+        precompile_step, gen_cmd, args, rustc_step, postcompile_step, problem_grep
     );
-    println!("Interestingness test:\n{}", content);
+    println!("Interestingness test:\n{content}");
     {
         let mut file = File::create(test_path)?;
         file.write_all(content.as_bytes())?;
     }
 
-    let mut perms = std::fs::metadata(&test_path)?.permissions();
+    let mut perms = std::fs::metadata(test_path)?.permissions();
     perms.set_mode(0o700);
-    std::fs::set_permissions(&test_path, perms)?;
+    std::fs::set_permissions(test_path, perms)?;
     Ok(())
 }
 
@@ -531,14 +572,14 @@
     announce_progress("Creating preprocessed header");
     let mut file = File::create(listing_path)?;
     for header in headers {
-        file.write_all(format!("#include \"{}\"\n", header).as_bytes())?;
+        file.write_all(format!("#include \"{header}\"\n").as_bytes())?;
     }
     Ok(())
 }
 
 fn create_file(path: &Path, content: &str) -> Result<(), std::io::Error> {
     let mut file = File::create(path)?;
-    write!(file, "{}", content)?;
+    write!(file, "{content}")?;
     Ok(())
 }
 
@@ -548,10 +589,7 @@
         .find_iter(cxx_gen::HEADER)
         .map(|m| m.as_str())
         .collect(); // for uniqueness
-    defines
-        .into_iter()
-        .map(|def| format!("-D{}", def))
-        .collect()
+    defines.into_iter().map(|def| format!("-D{def}")).collect()
 }
 
 #[test]
diff --git a/third_party/autocxx/tools/reduce/tests/reduce_test.rs b/third_party/autocxx/tools/reduce/tests/reduce_test.rs
index b277585..15cd627 100644
--- a/third_party/autocxx/tools/reduce/tests/reduce_test.rs
+++ b/third_party/autocxx/tools/reduce/tests/reduce_test.rs
@@ -55,9 +55,8 @@
     do_reduce(
         |header, demo_code_dir| {
             let config = format!(
-                "#include \"{}\" generate_all!() block!(\"First\")
-            safety!(unsafe_ffi)",
-                header
+                "#include \"{header}\" generate_all!() block!(\"First\")
+            safety!(unsafe_ffi)"
             );
             let json = serde_json::json!({
                 "header": INPUT_H,
@@ -173,7 +172,7 @@
     let demo_code_dir = tmp_dir.path().join("demo");
     std::fs::create_dir(&demo_code_dir).unwrap();
     let input_header = if include_cxx_h {
-        Cow::Owned(format!("#include \"cxx.h\"\n{}", INPUT_H))
+        Cow::Owned(format!("#include \"cxx.h\"\n{INPUT_H}"))
     } else {
         Cow::Borrowed(INPUT_H)
     };
@@ -205,7 +204,7 @@
             cmd = cmd.arg("repro").arg("-r").arg(repro_case);
         }
     }
-    eprintln!("Running {:?}", cmd);
+    eprintln!("Running {cmd:?}");
     let o = cmd.output()?;
     println!("Reduce output: {}", std::str::from_utf8(&o.stdout).unwrap());
     println!("Reduce error: {}", std::str::from_utf8(&o.stderr).unwrap());
@@ -220,7 +219,7 @@
 
 fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
     let path = dir.join(filename);
-    let mut f = File::create(&path).expect("Unable to create file");
+    let mut f = File::create(path).expect("Unable to create file");
     f.write_all(content).expect("Unable to write file");
 }
 
diff --git a/third_party/autocxx/tools/stress-test/Cargo.toml b/third_party/autocxx/tools/stress-test/Cargo.toml
index a651f60..7788fe3 100644
--- a/third_party/autocxx/tools/stress-test/Cargo.toml
+++ b/third_party/autocxx/tools/stress-test/Cargo.toml
@@ -8,14 +8,14 @@
 
 [package]
 name = "autocxx-stress-test"
-version = "0.22.3"
+version = "0.26.0"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2021"
 
 [dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
 
 [build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features=["fancy"]}
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/y2022/BUILD b/y2022/BUILD
index f8bf59f..f25925e 100644
--- a/y2022/BUILD
+++ b/y2022/BUILD
@@ -18,10 +18,10 @@
     data = [
         ":aos_config",
         ":message_bridge_client.sh",
+        "@ctre_phoenix6_api_cpp_athena//:shared_libraries",
+        "@ctre_phoenix6_tools_athena//:shared_libraries",
         "@ctre_phoenix_api_cpp_athena//:shared_libraries",
         "@ctre_phoenix_cci_athena//:shared_libraries",
-        "@ctre_phoenixpro_api_cpp_athena//:shared_libraries",
-        "@ctre_phoenixpro_tools_athena//:shared_libraries",
     ],
     dirs = [
         "//y2022/actors:splines",
@@ -270,7 +270,7 @@
         "//frc971/wpilib:wpilib_interface",
         "//frc971/wpilib:wpilib_robot_base",
         "//third_party:phoenix",
-        "//third_party:phoenixpro",
+        "//third_party:phoenix6",
         "//third_party:wpilib",
         "//y2022/control_loops/superstructure:led_indicator_lib",
         "//y2022/control_loops/superstructure:superstructure_can_position_fbs",
diff --git a/y2022_bot3/BUILD b/y2022_bot3/BUILD
index e7146ce..29debe7 100644
--- a/y2022_bot3/BUILD
+++ b/y2022_bot3/BUILD
@@ -8,8 +8,8 @@
     ],
     data = [
         ":aos_config",
-        "@ctre_phoenixpro_api_cpp_athena//:shared_libraries",
-        "@ctre_phoenixpro_tools_athena//:shared_libraries",
+        "@ctre_phoenix6_api_cpp_athena//:shared_libraries",
+        "@ctre_phoenix6_tools_athena//:shared_libraries",
     ],
     dirs = [
         "//y2022_bot3/actors:splines",
diff --git a/y2023/BUILD b/y2023/BUILD
index ed8f347..e200f87 100644
--- a/y2023/BUILD
+++ b/y2023/BUILD
@@ -1,7 +1,6 @@
 load("//frc971:downloader.bzl", "robot_downloader")
 load("//aos:config.bzl", "aos_config")
 load("//tools/build_rules:template.bzl", "jinja2_template")
-load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
 load("//aos/util:config_validator_macro.bzl", "config_validator_test")
 
 config_validator_test(
@@ -21,10 +20,10 @@
         "//aos/starter:roborio_irq_config.json",
         "//y2023/constants:constants.json",
         "//y2023/control_loops/superstructure/arm:arm_trajectories_generated.bfbs",
+        "@ctre_phoenix6_api_cpp_athena//:shared_libraries",
+        "@ctre_phoenix6_tools_athena//:shared_libraries",
         "@ctre_phoenix_api_cpp_athena//:shared_libraries",
         "@ctre_phoenix_cci_athena//:shared_libraries",
-        "@ctre_phoenixpro_api_cpp_athena//:shared_libraries",
-        "@ctre_phoenixpro_tools_athena//:shared_libraries",
     ],
     dirs = [
         "//y2023/www:www_files",
@@ -203,7 +202,7 @@
     name = "config_roborio",
     src = "y2023_roborio.json",
     flatbuffers = [
-        ":can_configuration_fbs",
+        "//frc971:can_configuration_fbs",
         "//aos/network:remote_message_fbs",
         "//aos/network:message_bridge_client_fbs",
         "//aos/network:message_bridge_server_fbs",
@@ -271,7 +270,6 @@
     ],
     target_compatible_with = ["//tools/platforms/hardware:roborio"],
     deps = [
-        ":can_configuration_fbs",
         ":constants",
         "//aos:init",
         "//aos:math",
@@ -283,6 +281,7 @@
         "//aos/util:log_interval",
         "//aos/util:phased_loop",
         "//aos/util:wrapping_counter",
+        "//frc971:can_configuration_fbs",
         "//frc971/autonomous:auto_mode_fbs",
         "//frc971/control_loops:control_loop",
         "//frc971/control_loops:control_loops_fbs",
@@ -303,7 +302,7 @@
         "//frc971/wpilib:wpilib_interface",
         "//frc971/wpilib:wpilib_robot_base",
         "//third_party:phoenix",
-        "//third_party:phoenixpro",
+        "//third_party:phoenix6",
         "//third_party:wpilib",
         "//y2023/control_loops/superstructure:led_indicator_lib",
         "//y2023/control_loops/superstructure:superstructure_output_fbs",
@@ -384,12 +383,3 @@
         "@com_github_google_glog//:glog",
     ],
 )
-
-flatbuffer_cc_library(
-    name = "can_configuration_fbs",
-    srcs = [
-        ":can_configuration.fbs",
-    ],
-    gen_reflections = 1,
-    visibility = ["//visibility:public"],
-)
diff --git a/y2023/wpilib_interface.cc b/y2023/wpilib_interface.cc
index d043e6f..abf11da 100644
--- a/y2023/wpilib_interface.cc
+++ b/y2023/wpilib_interface.cc
@@ -26,7 +26,7 @@
 #include "ctre/phoenix/cci/Diagnostics_CCI.h"
 #include "ctre/phoenix/motorcontrol/can/TalonFX.h"
 #include "ctre/phoenix/motorcontrol/can/TalonSRX.h"
-#include "ctre/phoenixpro/TalonFX.hpp"
+#include "ctre/phoenix6/TalonFX.hpp"
 
 #include "aos/commonmath.h"
 #include "aos/containers/sized_array.h"
@@ -40,6 +40,7 @@
 #include "aos/util/phased_loop.h"
 #include "aos/util/wrapping_counter.h"
 #include "frc971/autonomous/auto_mode_generated.h"
+#include "frc971/can_configuration_generated.h"
 #include "frc971/control_loops/drivetrain/drivetrain_can_position_generated.h"
 #include "frc971/control_loops/drivetrain/drivetrain_position_generated.h"
 #include "frc971/input/robot_state_generated.h"
@@ -56,7 +57,6 @@
 #include "frc971/wpilib/pdp_fetcher.h"
 #include "frc971/wpilib/sensor_reader.h"
 #include "frc971/wpilib/wpilib_robot_base.h"
-#include "y2023/can_configuration_generated.h"
 #include "y2023/constants.h"
 #include "y2023/control_loops/superstructure/led_indicator.h"
 #include "y2023/control_loops/superstructure/superstructure_output_generated.h"
@@ -67,6 +67,7 @@
             "devices on the CAN bus using Phoenix Tuner");
 
 using ::aos::monotonic_clock;
+using ::frc971::CANConfiguration;
 using ::y2023::constants::Values;
 namespace superstructure = ::y2023::control_loops::superstructure;
 namespace drivetrain = ::y2023::control_loops::drivetrain;
@@ -121,7 +122,7 @@
 class Falcon {
  public:
   Falcon(int device_id, std::string canbus,
-         std::vector<ctre::phoenixpro::BaseStatusSignalValue *> *signals)
+         std::vector<ctre::phoenix6::BaseStatusSignal *> *signals)
       : talon_(device_id, canbus),
         device_id_(device_id),
         device_temp_(talon_.GetDeviceTemp()),
@@ -152,7 +153,7 @@
   }
 
   void PrintConfigs() {
-    ctre::phoenixpro::configs::TalonFXConfiguration configuration;
+    ctre::phoenix6::configs::TalonFXConfiguration configuration;
     ctre::phoenix::StatusCode status =
         talon_.GetConfigurator().Refresh(configuration);
     if (!status.IsOK()) {
@@ -162,10 +163,10 @@
     AOS_LOG(INFO, "configuration: %s", configuration.ToString().c_str());
   }
 
-  void WriteConfigs(ctre::phoenixpro::signals::InvertedValue invert) {
+  void WriteConfigs(ctre::phoenix6::signals::InvertedValue invert) {
     inverted_ = invert;
 
-    ctre::phoenixpro::configs::CurrentLimitsConfigs current_limits;
+    ctre::phoenix6::configs::CurrentLimitsConfigs current_limits;
     current_limits.StatorCurrentLimit =
         constants::Values::kDrivetrainStatorCurrentLimit();
     current_limits.StatorCurrentLimitEnable = true;
@@ -173,14 +174,14 @@
         constants::Values::kDrivetrainSupplyCurrentLimit();
     current_limits.SupplyCurrentLimitEnable = true;
 
-    ctre::phoenixpro::configs::MotorOutputConfigs output_configs;
+    ctre::phoenix6::configs::MotorOutputConfigs output_configs;
     output_configs.NeutralMode =
-        ctre::phoenixpro::signals::NeutralModeValue::Brake;
+        ctre::phoenix6::signals::NeutralModeValue::Brake;
     output_configs.DutyCycleNeutralDeadband = 0;
 
     output_configs.Inverted = inverted_;
 
-    ctre::phoenixpro::configs::TalonFXConfiguration configuration;
+    ctre::phoenix6::configs::TalonFXConfiguration configuration;
     configuration.CurrentLimits = current_limits;
     configuration.MotorOutput = output_configs;
 
@@ -195,7 +196,7 @@
   }
 
   void WriteRollerConfigs() {
-    ctre::phoenixpro::configs::CurrentLimitsConfigs current_limits;
+    ctre::phoenix6::configs::CurrentLimitsConfigs current_limits;
     current_limits.StatorCurrentLimit =
         constants::Values::kRollerStatorCurrentLimit();
     current_limits.StatorCurrentLimitEnable = true;
@@ -203,12 +204,12 @@
         constants::Values::kRollerSupplyCurrentLimit();
     current_limits.SupplyCurrentLimitEnable = true;
 
-    ctre::phoenixpro::configs::MotorOutputConfigs output_configs;
+    ctre::phoenix6::configs::MotorOutputConfigs output_configs;
     output_configs.NeutralMode =
-        ctre::phoenixpro::signals::NeutralModeValue::Brake;
+        ctre::phoenix6::signals::NeutralModeValue::Brake;
     output_configs.DutyCycleNeutralDeadband = 0;
 
-    ctre::phoenixpro::configs::TalonFXConfiguration configuration;
+    ctre::phoenix6::configs::TalonFXConfiguration configuration;
     configuration.CurrentLimits = current_limits;
     configuration.MotorOutput = output_configs;
 
@@ -222,7 +223,7 @@
     PrintConfigs();
   }
 
-  ctre::phoenixpro::hardware::TalonFX *talon() { return &talon_; }
+  ctre::phoenix6::hardware::TalonFX *talon() { return &talon_; }
 
   flatbuffers::Offset<frc971::control_loops::CANFalcon> WritePosition(
       flatbuffers::FlatBufferBuilder *fbb) {
@@ -235,8 +236,7 @@
     builder.add_duty_cycle(duty_cycle());
 
     double invert =
-        (inverted_ ==
-                 ctre::phoenixpro::signals::InvertedValue::Clockwise_Positive
+        (inverted_ == ctre::phoenix6::signals::InvertedValue::Clockwise_Positive
              ? 1
              : -1);
 
@@ -266,26 +266,24 @@
   void RefreshNontimesyncedSignals() { device_temp_.Refresh(); };
 
  private:
-  ctre::phoenixpro::hardware::TalonFX talon_;
+  ctre::phoenix6::hardware::TalonFX talon_;
   int device_id_;
 
-  ctre::phoenixpro::signals::InvertedValue inverted_;
+  ctre::phoenix6::signals::InvertedValue inverted_;
 
-  ctre::phoenixpro::StatusSignalValue<units::temperature::celsius_t>
-      device_temp_;
-  ctre::phoenixpro::StatusSignalValue<units::voltage::volt_t> supply_voltage_;
-  ctre::phoenixpro::StatusSignalValue<units::current::ampere_t> supply_current_,
+  ctre::phoenix6::StatusSignal<units::temperature::celsius_t> device_temp_;
+  ctre::phoenix6::StatusSignal<units::voltage::volt_t> supply_voltage_;
+  ctre::phoenix6::StatusSignal<units::current::ampere_t> supply_current_,
       torque_current_;
-  ctre::phoenixpro::StatusSignalValue<units::angle::turn_t> position_;
-  ctre::phoenixpro::StatusSignalValue<units::dimensionless::scalar_t>
-      duty_cycle_;
+  ctre::phoenix6::StatusSignal<units::angle::turn_t> position_;
+  ctre::phoenix6::StatusSignal<units::dimensionless::scalar_t> duty_cycle_;
 };
 
 class CANSensorReader {
  public:
   CANSensorReader(
       aos::EventLoop *event_loop,
-      std::vector<ctre::phoenixpro::BaseStatusSignalValue *> signals_registry)
+      std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry)
       : event_loop_(event_loop),
         signals_(signals_registry.begin(), signals_registry.end()),
         can_position_sender_(
@@ -328,7 +326,7 @@
  private:
   void Loop() {
     ctre::phoenix::StatusCode status =
-        ctre::phoenixpro::BaseStatusSignalValue::WaitForAll(2000_ms, signals_);
+        ctre::phoenix6::BaseStatusSignal::WaitForAll(2000_ms, signals_);
 
     if (!status.IsOK()) {
       AOS_LOG(ERROR, "Failed to read signals from falcons: %s: %s",
@@ -384,7 +382,7 @@
 
   aos::EventLoop *event_loop_;
 
-  const std::vector<ctre::phoenixpro::BaseStatusSignalValue *> signals_;
+  const std::vector<ctre::phoenix6::BaseStatusSignal *> signals_;
   aos::Sender<frc971::control_loops::drivetrain::CANPosition>
       can_position_sender_;
 
@@ -761,7 +759,7 @@
   void WriteConfigs() { roller_falcon_->WriteRollerConfigs(); }
 
   void Write(const superstructure::Output &output) override {
-    ctre::phoenixpro::controls::DutyCycleOut roller_control(
+    ctre::phoenix6::controls::DutyCycleOut roller_control(
         SafeSpeed(-output.roller_voltage()));
     roller_control.UpdateFreqHz = 0_Hz;
     roller_control.EnableFOC = true;
@@ -777,7 +775,7 @@
 
   void Stop() override {
     AOS_LOG(WARNING, "Superstructure CAN output too old.\n");
-    ctre::phoenixpro::controls::DutyCycleOut stop_command(0.0);
+    ctre::phoenix6::controls::DutyCycleOut stop_command(0.0);
     stop_command.UpdateFreqHz = 0_Hz;
     stop_command.EnableFOC = true;
 
@@ -818,11 +816,11 @@
     left_under_ = std::move(left_under);
   }
 
-  void set_right_inverted(ctre::phoenixpro::signals::InvertedValue invert) {
+  void set_right_inverted(ctre::phoenix6::signals::InvertedValue invert) {
     right_inverted_ = invert;
   }
 
-  void set_left_inverted(ctre::phoenixpro::signals::InvertedValue invert) {
+  void set_left_inverted(ctre::phoenix6::signals::InvertedValue invert) {
     left_inverted_ = invert;
   }
 
@@ -851,12 +849,12 @@
 
   void Write(
       const ::frc971::control_loops::drivetrain::Output &output) override {
-    ctre::phoenixpro::controls::DutyCycleOut left_control(
+    ctre::phoenix6::controls::DutyCycleOut left_control(
         SafeSpeed(output.left_voltage()));
     left_control.UpdateFreqHz = 0_Hz;
     left_control.EnableFOC = true;
 
-    ctre::phoenixpro::controls::DutyCycleOut right_control(
+    ctre::phoenix6::controls::DutyCycleOut right_control(
         SafeSpeed(output.right_voltage()));
     right_control.UpdateFreqHz = 0_Hz;
     right_control.EnableFOC = true;
@@ -886,7 +884,7 @@
 
   void Stop() override {
     AOS_LOG(WARNING, "drivetrain output too old\n");
-    ctre::phoenixpro::controls::DutyCycleOut stop_command(0.0);
+    ctre::phoenix6::controls::DutyCycleOut stop_command(0.0);
     stop_command.UpdateFreqHz = 0_Hz;
     stop_command.EnableFOC = true;
 
@@ -901,7 +899,7 @@
     return (::aos::Clip(voltage, -kMaxBringupPower, kMaxBringupPower) / 12.0);
   }
 
-  ctre::phoenixpro::signals::InvertedValue left_inverted_, right_inverted_;
+  ctre::phoenix6::signals::InvertedValue left_inverted_, right_inverted_;
   std::shared_ptr<Falcon> right_front_, right_back_, right_under_, left_front_,
       left_back_, left_under_;
 };
@@ -934,7 +932,7 @@
     std::shared_ptr<frc::DigitalOutput> superstructure_reading =
         make_unique<frc::DigitalOutput>(25);
 
-    std::vector<ctre::phoenixpro::BaseStatusSignalValue *> signals_registry;
+    std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry;
     std::shared_ptr<Falcon> right_front =
         std::make_shared<Falcon>(1, "Drivetrain Bus", &signals_registry);
     std::shared_ptr<Falcon> right_back =
@@ -1013,9 +1011,9 @@
     drivetrain_writer.set_falcons(right_front, right_back, right_under,
                                   left_front, left_back, left_under);
     drivetrain_writer.set_right_inverted(
-        ctre::phoenixpro::signals::InvertedValue::Clockwise_Positive);
+        ctre::phoenix6::signals::InvertedValue::Clockwise_Positive);
     drivetrain_writer.set_left_inverted(
-        ctre::phoenixpro::signals::InvertedValue::CounterClockwise_Positive);
+        ctre::phoenix6::signals::InvertedValue::CounterClockwise_Positive);
 
     SuperstructureCANWriter superstructure_can_writer(&can_output_event_loop);
     superstructure_can_writer.set_roller_falcon(roller);
diff --git a/y2023/y2023_roborio.json b/y2023/y2023_roborio.json
index 33d9904..c1e42e6 100644
--- a/y2023/y2023_roborio.json
+++ b/y2023/y2023_roborio.json
@@ -300,7 +300,7 @@
     },
     {
       "name": "/roborio",
-      "type": "y2023.CANConfiguration",
+      "type": "frc971.CANConfiguration",
       "source_node": "roborio",
       "frequency": 2
     },