Merge pull request #15 from google:ssbr-patch-1

PiperOrigin-RevId: 822327211
Change-Id: If02bb2932506d25d4ecc054203cb2a000552f9a9
diff --git a/Cargo.toml b/Cargo.toml
index dd1de12..70dc8d8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,20 +15,21 @@
 # will be decided from here.
 [workspace.dependencies]
 anyhow = "1"
+clap = { version = "4", features = [ "derive" ] }
+either = "1"
 flagset = "0.4"
+heck = "0.5"
 itertools = "0.13"
+phf = { version = "0.11", features = [ "macros" ] }
 proc-macro2 = "1"
+quote = "1"
 regex = "1"
+rustversion = "1"
 serde = { version = "1", features = [ "derive", "rc" ] }
 serde_json = "1"
 syn = { version = "2", features = [ "extra-traits" ] }
-quote = "1"
+tracing = "0.1"
 unicode-ident = "1"
-either = "1"
-clap = { version = "4", features = [ "derive" ] }
-rustversion = "1"
-heck = "0.5"
-phf = { version = "0.11", features = [ "macros" ] }
 
 [workspace.package]
 
diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod
index 66ee456..33e2df3 100644
--- a/WORKSPACE.bzlmod
+++ b/WORKSPACE.bzlmod
@@ -122,6 +122,9 @@
         "tempfile": crate.spec(
             version = "=3.4.0",
         ),
+        "tracing": crate.spec(
+            version = ">=0.1.41",
+        ),
         "unicode-ident": crate.spec(
             version = ">0.0.0",
         ),
diff --git a/bazel/llvm.bzl b/bazel/llvm.bzl
index 026eb46..fb2823e 100644
--- a/bazel/llvm.bzl
+++ b/bazel/llvm.bzl
@@ -53,7 +53,7 @@
             executable = False,
         )
 
-LLVM_COMMIT_SHA = "891f002026df122b36813b9e1819769c94327503"
+LLVM_COMMIT_SHA = "32de3b9ef9e7e8debc14416e968456ca13b48bea"
 
 def llvm_loader_repository_dependencies():
     # This *declares* the dependency, but it won't actually be *downloaded* unless it's used.
diff --git a/cc_bindings_from_rs/BUILD b/cc_bindings_from_rs/BUILD
index 83f7f76..2e693a4 100644
--- a/cc_bindings_from_rs/BUILD
+++ b/cc_bindings_from_rs/BUILD
@@ -33,9 +33,9 @@
         "//common:crubit_feature",
         "//common:error_report",
         "//common:token_stream_printer",
-        "@crate_index//:clap",
-        "@crate_index//:flagset",
-        "@crate_index//:itertools",
+        "@crate_index//:clap",  # v4
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:itertools",  # v0_13
     ],
 )
 
@@ -49,8 +49,8 @@
     tags = ["not_run:mac"],
     deps = [
         ":run_compiler_test_support",
-        "@crate_index//:regex",
-        "@crate_index//:tempfile",
+        "@crate_index//:regex",  # v1
+        "@crate_index//:tempfile",  # v3
     ],
 )
 
@@ -79,9 +79,9 @@
     deps = [
         "//common:crubit_feature",
         "//common:dyn_format",
-        "@crate_index//:anyhow",
-        "@crate_index//:clap",
-        "@crate_index//:flagset",
+        "@crate_index//:anyhow",  # v1
+        "@crate_index//:clap",  # v4
+        "@crate_index//:flagset",  # v0_4
     ],
 )
 
@@ -95,8 +95,8 @@
     tags = ["not_run:mac"],
     deps = [
         ":run_compiler_test_support",
-        "@crate_index//:itertools",
-        "@crate_index//:tempfile",
+        "@crate_index//:itertools",  # v0_13
+        "@crate_index//:tempfile",  # v3
     ],
 )
 
@@ -104,13 +104,13 @@
     name = "crubit_attr",
     srcs = ["crubit_attr.rs"],
     proc_macro_deps = [
-        "@crate_index//:rustversion",
+        "@crate_index//:rustversion",  # v1
     ],
     # LINT.IfChange
     rustc_flags = ["-Zallow-features=rustc_private"],
     # LINT.ThenChange(//docs/overview/unstable_features.md)
     deps = [
-        "@crate_index//:anyhow",
+        "@crate_index//:anyhow",  # v1
     ],
 )
 
@@ -125,7 +125,7 @@
     deps = [
         ":crubit_attr",
         ":run_compiler_test_support",
-        "@crate_index//:anyhow",
+        "@crate_index//:anyhow",  # v1
     ],
 )
 
@@ -135,14 +135,14 @@
         "run_compiler.rs",
     ],
     proc_macro_deps = [
-        "@crate_index//:rustversion",
+        "@crate_index//:rustversion",  # v1
     ],
     # LINT.IfChange
     rustc_flags = ["-Zallow-features=rustc_private,cfg_accessible"],
     # LINT.ThenChange(//docs/overview/unstable_features.md)
     deps = [
         "//common:arc_anyhow",
-        "@crate_index//:either",
+        "@crate_index//:either",  # v1
     ],
 )
 
@@ -156,7 +156,7 @@
     tags = ["not_run:mac"],
     deps = [
         ":run_compiler_test_support",
-        "@crate_index//:tempfile",
+        "@crate_index//:tempfile",  # v3
     ],
 )
 
@@ -170,11 +170,11 @@
         "@rust_linux_x86_64__x86_64-unknown-linux-gnu__nightly_tools//:rust_std-x86_64-unknown-linux-gnu",
     ],
     proc_macro_deps = [
-        "@crate_index//:rustversion",
+        "@crate_index//:rustversion",  # v1
     ],
     rustc_flags = ["--cfg=oss"],
     deps = [
-        "@crate_index//:itertools",
+        "@crate_index//:itertools",  # v0_13
         "@rules_rust//tools/runfiles",
     ],
 )
diff --git a/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl b/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
index b12aa65..f091d76 100644
--- a/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
+++ b/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
@@ -312,8 +312,8 @@
     )
 
     dep_info, build_info, linkstamps = collect_deps(
-        deps = crate_info.deps,
-        proc_macro_deps = crate_info.proc_macro_deps,
+        deps = crate_info.deps.to_list(),
+        proc_macro_deps = crate_info.proc_macro_deps.to_list(),
         aliases = crate_info.aliases,
     )
 
diff --git a/cc_bindings_from_rs/cc_bindings_from_rs.rs b/cc_bindings_from_rs/cc_bindings_from_rs.rs
index 6d1078f..17c0ed5 100644
--- a/cc_bindings_from_rs/cc_bindings_from_rs.rs
+++ b/cc_bindings_from_rs/cc_bindings_from_rs.rs
@@ -112,15 +112,17 @@
     }
 
     {
-        let cc_api = cc_tokens_to_formatted_string(cc_api, &cmdline.clang_format_exe_path)?;
+        let cc_api =
+            cc_tokens_to_formatted_string(cc_api, cmdline.clang_format_exe_path.as_deref())?;
         let cc_api = turn_off_clang_format(cc_api);
         write_file(&cmdline.h_out, &cc_api)?;
     }
 
     {
-        let rustfmt_config =
-            RustfmtConfig::new(&cmdline.rustfmt_exe_path, cmdline.rustfmt_config_path.as_deref());
-        let cc_api_impl = rs_tokens_to_formatted_string(cc_api_impl, &rustfmt_config)?;
+        let rustfmt_config = cmdline.rustfmt_exe_path.as_ref().map(|rustfmt_path| {
+            RustfmtConfig::new(rustfmt_path, cmdline.rustfmt_config_path.as_deref())
+        });
+        let cc_api_impl = rs_tokens_to_formatted_string(cc_api_impl, rustfmt_config.as_ref())?;
         write_file(&cmdline.rs_out, &cc_api_impl)?;
     }
 
diff --git a/cc_bindings_from_rs/cmdline.rs b/cc_bindings_from_rs/cmdline.rs
index 68a8159..ab8e3a6 100644
--- a/cc_bindings_from_rs/cmdline.rs
+++ b/cc_bindings_from_rs/cmdline.rs
@@ -54,9 +54,9 @@
     pub crubit_debug_path_format: Option<Format<2>>,
 
     /// Path to a clang-format executable that will be used to format the
-    /// C++ header files generated by the tool.
+    /// C++ header files generated by the tool. If omitted, generated code will not be formatted.
     #[clap(long, value_parser, value_name = "FILE")]
-    pub clang_format_exe_path: PathBuf,
+    pub clang_format_exe_path: Option<PathBuf>,
 
     /// Include paths of bindings for dependency crates, generated by previous
     /// invocations of Crubit. Keys are crate names, and values are include
@@ -100,12 +100,13 @@
     pub crate_disabled_features: Vec<(String, flagset::FlagSet<crubit_feature::CrubitFeature>)>,
 
     /// Path to a rustfmt executable that will be used to format the
-    /// Rust source files generated by the tool.
+    /// Rust source files generated by the tool. If omitted, generated code will not be formatted.
     #[clap(long, value_parser, value_name = "FILE")]
-    pub rustfmt_exe_path: PathBuf,
+    pub rustfmt_exe_path: Option<PathBuf>,
 
     /// Path to a rustfmt.toml file that should replace the
-    /// default formatting of the .rs files generated by the tool.
+    /// default formatting of the .rs files generated by the tool. This flag is only used if
+    /// rustfmt_exe_path is specified.
     #[clap(long, value_parser, value_name = "FILE")]
     pub rustfmt_config_path: Option<PathBuf>,
 
@@ -292,8 +293,8 @@
             Format::parse_with_metavars("<crubit/support/{header}>", &["header"]).unwrap(),
             cmdline.crubit_support_path_format,
         );
-        assert_eq!(Path::new("clang-format.exe"), cmdline.clang_format_exe_path);
-        assert_eq!(Path::new("rustfmt.exe"), cmdline.rustfmt_exe_path);
+        assert_eq!(Some(PathBuf::from("clang-format.exe")), cmdline.clang_format_exe_path);
+        assert_eq!(Some(PathBuf::from("rustfmt.exe")), cmdline.rustfmt_exe_path);
         assert!(cmdline.crate_headers.is_empty());
         assert!(cmdline.rustfmt_config_path.is_none());
         // Ignoring `rustc_args` in this test - they are covered in a separate
@@ -472,4 +473,30 @@
             "Invalid Crubit feature name: \"\"",
         );
     }
+
+    #[test]
+    fn test_omit_rustfmt_exe_path() {
+        let cmdline = new_cmdline([
+            "--h-out=foo.h",
+            "--rs-out=foo_impl.rs",
+            "--crubit-support-path-format=<crubit/support/{header}>",
+            "--clang-format-exe-path=clang-format.exe",
+        ])
+        .unwrap();
+        assert_eq!(Some(PathBuf::from("clang-format.exe")), cmdline.clang_format_exe_path);
+        assert_eq!(None, cmdline.rustfmt_exe_path);
+    }
+
+    #[test]
+    fn test_omit_clang_format_exe_path() {
+        let cmdline = new_cmdline([
+            "--h-out=foo.h",
+            "--rs-out=foo_impl.rs",
+            "--crubit-support-path-format=<crubit/support/{header}>",
+            "--rustfmt-exe-path=rustfmt.exe",
+        ])
+        .unwrap();
+        assert_eq!(None, cmdline.clang_format_exe_path);
+        assert_eq!(Some(PathBuf::from("rustfmt.exe")), cmdline.rustfmt_exe_path);
+    }
 }
diff --git a/cc_bindings_from_rs/generate_bindings/BUILD b/cc_bindings_from_rs/generate_bindings/BUILD
index 0515f43..ebc9a8b 100644
--- a/cc_bindings_from_rs/generate_bindings/BUILD
+++ b/cc_bindings_from_rs/generate_bindings/BUILD
@@ -24,7 +24,7 @@
         "lib.rs",
     ],
     proc_macro_deps = [
-        "@crate_index//:rustversion",
+        "@crate_index//:rustversion",  # v1
     ],
     # LINT.IfChange
     rustc_flags = ["-Zallow-features=rustc_private,rustc_attr,cfg_accessible,stmt_expr_attributes,proc_macro_hygiene"],
@@ -41,11 +41,11 @@
         "//common:crubit_feature",
         "//common:dyn_format",
         "//common:error_report",
-        "@crate_index//:flagset",
-        "@crate_index//:itertools",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -60,7 +60,7 @@
         "//common:code_gen_utils",
         "//common:token_stream_matchers",
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -75,7 +75,7 @@
         "//common:token_stream_matchers",
         "//common:token_stream_printer",
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -87,9 +87,9 @@
         "//common:code_gen_utils",
         "//common:crubit_feature",
         "//common:token_stream_matchers",
-        "@crate_index//:flagset",
+        "@crate_index//:flagset",  # v0_4
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -99,7 +99,7 @@
     deps = [
         ":test_helpers",
         "//common:token_stream_matchers",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -123,7 +123,7 @@
         "//common:crubit_feature",
         "//common:dyn_format",
         "//common:error_report",
-        "@crate_index//:flagset",
+        "@crate_index//:flagset",  # v0_4
     ],
 )
 
diff --git a/cc_bindings_from_rs/generate_bindings/database/BUILD b/cc_bindings_from_rs/generate_bindings/database/BUILD
index 1555a63..6911315 100644
--- a/cc_bindings_from_rs/generate_bindings/database/BUILD
+++ b/cc_bindings_from_rs/generate_bindings/database/BUILD
@@ -34,9 +34,9 @@
         "//common:dyn_format",
         "//common:error_report",
         "//common:memoized",
-        "@crate_index//:flagset",
-        "@crate_index//:itertools",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
diff --git a/cc_bindings_from_rs/generate_bindings/format_type.rs b/cc_bindings_from_rs/generate_bindings/format_type.rs
index 6a6f060..d44b9b2 100644
--- a/cc_bindings_from_rs/generate_bindings/format_type.rs
+++ b/cc_bindings_from_rs/generate_bindings/format_type.rs
@@ -186,7 +186,7 @@
         return None;
     };
 
-    if !matches_qualified_name(db, adt.did(), ":: core :: mem :: maybe_uninit :: MaybeUninit")
+    if !matches_qualified_name(db, adt.did(), &["core", "mem", "maybe_uninit", "MaybeUninit"])
         || substs.len() != 1
     {
         return None;
@@ -252,27 +252,11 @@
             prereqs.includes.insert(CcInclude::array());
             // We need to be able to handle expressions at the type level that are not simple
             // numeric literals.
-            let normalized = match db.tcx().try_normalize_erasing_regions(
-                ty::TypingEnv {
-                    typing_mode: ty::TypingMode::PostAnalysis,
-                    param_env: ty::ParamEnv::empty(),
-                },
-                *length,
-            ) {
-                Ok(normalized) => normalized,
-                Err(_) => {
-                    panic!("Unable to normalize array length {{length}}.")
-                }
-            };
-            let Some(target_usize) = normalized.try_to_target_usize(db.tcx()) else {
-                panic!(
-                    "Unable to get array length from normalized type ({length} => {normalized})."
-                )
-            };
+            let target_size = evaluate_const_as_u64(db.tcx(), *length);
             let sugared_element_type = SugaredTy::missing_hir(*element_type);
             let cc_element_ty =
                 db.format_ty_for_cc(sugared_element_type, location)?.into_tokens(&mut prereqs);
-            let c_int = Literal::u64_unsuffixed(target_usize);
+            let c_int = Literal::u64_unsuffixed(target_size);
             CcSnippet { prereqs, tokens: quote! { std::array<#cc_element_ty, #c_int> } }
         }
 
@@ -758,7 +742,7 @@
     ty: &Ty<'tcx>,
 ) -> Result<TokenStream> {
     if let ty::TyKind::Adt(adt, substs) = ty.kind() {
-        if matches_qualified_name(db, adt.did(), ":: core :: mem :: maybe_uninit :: MaybeUninit") {
+        if matches_qualified_name(db, adt.did(), &["core", "mem", "maybe_uninit", "MaybeUninit"]) {
             let generic_ty = db.format_ty_for_rs(substs[0].expect_ty())?;
             return Ok(quote! { std::mem::MaybeUninit<#generic_ty> });
         }
@@ -853,22 +837,8 @@
         }
         ty::TyKind::Array(element_type, length) => {
             let rs_element_type = db.format_ty_for_rs(*element_type)?;
-            let normalized_length = match db.tcx().try_normalize_erasing_regions(
-                ty::TypingEnv {
-                    typing_mode: ty::TypingMode::PostAnalysis,
-                    param_env: ty::ParamEnv::empty(),
-                },
-                *length,
-            ) {
-                Ok(normalized) => normalized,
-                Err(_) => {
-                    panic!("Unable to normalize array length {{length}}.")
-                }
-            };
-            let Some(target_usize) = normalized_length.try_to_target_usize(db.tcx()) else {
-                panic!("Unable to get array length from normalized type ({length} => {normalized_length}).")
-            };
-            let unsuffixed_length = Literal::u64_unsuffixed(target_usize);
+            let target_size = evaluate_const_as_u64(db.tcx(), *length);
+            let unsuffixed_length = Literal::u64_unsuffixed(target_size);
             quote! { [ #rs_element_type; #unsuffixed_length ] }
         }
         ty::TyKind::Adt(adt, substs) => {
@@ -1377,3 +1347,21 @@
         _ => Ok(None),
     }
 }
+
+// Evaluates a constant (such as the length of an array type).
+pub fn evaluate_const_as_u64<'tcx>(tcx: ty::TyCtxt<'tcx>, cst: ty::Const<'tcx>) -> u64 {
+    // It would be nice if we knew that these types were already fully normalized.
+    let normalized = tcx
+        .try_normalize_erasing_regions(
+            ty::TypingEnv {
+                typing_mode: ty::TypingMode::PostAnalysis,
+                param_env: ty::ParamEnv::empty(),
+            },
+            cst,
+        )
+        .unwrap_or_else(|_| panic!("Unable to normalize type constant {{cst}}."));
+    let Some(target_u64) = normalized.try_to_target_usize(tcx) else {
+        panic!("Unable to get size from normalized type constant ({cst} => {normalized}).")
+    };
+    target_u64
+}
diff --git a/cc_bindings_from_rs/generate_bindings/generate_function.rs b/cc_bindings_from_rs/generate_bindings/generate_function.rs
index ab9fd58..bd05871 100644
--- a/cc_bindings_from_rs/generate_bindings/generate_function.rs
+++ b/cc_bindings_from_rs/generate_bindings/generate_function.rs
@@ -385,6 +385,9 @@
     #[rustversion::since(2025-07-29)]
     let impl_id = tcx.impl_of_assoc(def_id)?;
 
+    #[rustversion::since(2025-10-17)]
+    assert!(!tcx.impl_is_of_trait(impl_id), "Trait methods should be filtered by caller");
+    #[rustversion::before(2025-10-17)]
     assert!(tcx.impl_trait_ref(impl_id).is_none(), "Trait methods should be filtered by caller");
     Some(tcx.type_of(impl_id).instantiate_identity())
 }
diff --git a/cc_bindings_from_rs/generate_bindings/generate_function_thunk.rs b/cc_bindings_from_rs/generate_bindings/generate_function_thunk.rs
index 8dbf45a..da73b68 100644
--- a/cc_bindings_from_rs/generate_bindings/generate_function_thunk.rs
+++ b/cc_bindings_from_rs/generate_bindings/generate_function_thunk.rs
@@ -50,6 +50,20 @@
     Some(quote! { *const [*const core::ffi::c_void; #num_elements] })
 }
 
+/// Returns a C ABI-compatible C type to pass a [inner_ty; _].
+///
+/// Layout-compatible arrays are passed through memory.
+fn array_c_abi_c_type(inner_ty: &ty::Ty) -> Result<TokenStream> {
+    // TODO: b/451981992 - Is this test enough to avoid nested by-value arrays?
+    // This is also more conservative than what we probably need here, which is to exclude
+    // nested arrays containing types that are Drop but not Default (as these don't behave
+    // well in std::arrays; we currently treat single-level arrays as a special case).
+    match inner_ty.kind() {
+        ty::TyKind::Array(..) => bail!("b/260128806 - nested array {inner_ty} is not supported"),
+        _ => Ok(quote! { void* }),
+    }
+}
+
 /// Formats a C++ declaration of a C-ABI-compatible-function wrapper around a Rust function.
 pub fn generate_thunk_decl<'tcx>(
     db: &dyn BindingsGenerator<'tcx>,
@@ -86,6 +100,8 @@
                     Ok(quote! { #cpp_type })
                 } else if let Some(tuple_abi) = tuple_c_abi_c_type(ty) {
                     Ok(tuple_abi)
+                } else if let ty::TyKind::Array(inner_ty, _) = ty.kind() {
+                    array_c_abi_c_type(inner_ty)
                 } else if let Some(adt_def) = ty.ty_adt_def() {
                     let core = db.generate_adt_core(adt_def.did())?;
                     db.generate_move_ctor_and_assignment_operator(core).map_err(|_| {
@@ -106,6 +122,10 @@
     } else if let Some(tuple_abi) = tuple_c_abi_c_type(sig_mid.output()) {
         thunk_ret_type = quote! { void };
         thunk_params.push(quote! { #tuple_abi __ret_ptr });
+    } else if let ty::TyKind::Array(inner_ty, _) = sig_mid.output().kind() {
+        let c_type = array_c_abi_c_type(inner_ty)?;
+        thunk_ret_type = quote! { void };
+        thunk_params.push(quote! { #c_type __ret_ptr });
     } else if let Some(BridgedType::Composable(_)) = is_bridged_type(db, sig_mid.output())? {
         thunk_ret_type = quote! { void };
         thunk_params.push(quote! { unsigned char * __ret_ptr });
@@ -404,6 +424,8 @@
             #unpack
             #write_elements
         }
+    } else if let ty::TyKind::Array { .. } = rs_type.kind() {
+        write_directly()?
     } else if rs_type.ty_adt_def().is_some() {
         write_directly()?
     } else {
diff --git a/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs b/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs
index 41002b9..4e943b0 100644
--- a/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs
+++ b/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs
@@ -335,6 +335,9 @@
     };
     let mut map: HashMap<Ty<'tcx>, Vec<DefId>> = HashMap::new();
     for from_impl_id in impls_iter {
+        #[rustversion::since(2025-10-17)]
+        let middle_trait_header = tcx.impl_trait_header(from_impl_id);
+        #[rustversion::before(2025-10-17)]
         let middle_trait_header = tcx
             .impl_trait_header(from_impl_id)
             .expect("DefId for an `From` trait impl lacked a trait header");
@@ -366,6 +369,9 @@
     let from_map = db.from_trait_impls_by_argument(core.def_id.krate);
     let from_impls = from_map.get(&core.self_ty).into_iter().flat_map(|vec| vec.iter()).filter_map(
         |from_impl_id| {
+            #[rustversion::since(2025-10-17)]
+            let middle_trait_header = tcx.impl_trait_header(*from_impl_id);
+            #[rustversion::before(2025-10-17)]
             let middle_trait_header = tcx
                 .impl_trait_header(*from_impl_id)
                 .expect("DefId for a `From` trait impl lacked a trait header");
@@ -387,6 +393,9 @@
     );
     let into_impls =
         tcx.non_blanket_impls_for_ty(into_trait, core.self_ty).filter_map(|into_impl_id| {
+            #[rustversion::since(2025-10-17)]
+            let middle_trait_header = tcx.impl_trait_header(into_impl_id);
+            #[rustversion::before(2025-10-17)]
             let middle_trait_header = tcx
                 .impl_trait_header(into_impl_id)
                 .expect("DefId for an `Into` trait impl lacked a trait header");
diff --git a/cc_bindings_from_rs/generate_bindings/lib.rs b/cc_bindings_from_rs/generate_bindings/lib.rs
index f482ef7..52c896e 100644
--- a/cc_bindings_from_rs/generate_bindings/lib.rs
+++ b/cc_bindings_from_rs/generate_bindings/lib.rs
@@ -461,15 +461,22 @@
     name_map
 }
 
-/// Checks whether a definition matches a specific qualified name.
-fn matches_qualified_name(
-    db: &dyn BindingsGenerator<'_>,
-    item_did: DefId,
-    name_to_compare: &str,
-) -> bool {
-    // TODO(b/372153103): Compare the name via `tcx.def_path(adt.did())`.
-    let type_name = FullyQualifiedName::new(db, item_did);
-    type_name.format_for_rs().to_string() == name_to_compare
+/// Checks whether a definition matches a specific qualified name by matching it's definition path
+/// against `name`. Name must include the crate in it's path.
+fn matches_qualified_name(db: &dyn BindingsGenerator<'_>, item_did: DefId, name: &[&str]) -> bool {
+    let tcx = db.tcx();
+    let path = tcx.def_path(item_did);
+    if path.data.len() + 1 != name.len() {
+        return false;
+    }
+    // This will always return false for anonymous path data because the caller won't be able to
+    // specify the `disambiguator` that gets inserted into the symbol. That's fine. It is expected
+    // this function will only be called to check for non-anonymous paths.
+    [tcx.crate_name(path.krate)]
+        .into_iter()
+        .chain(path.data.into_iter().map(|seg| seg.as_sym(/*verbose=*/ false)))
+        .zip(name.iter().map(|s| Symbol::intern(s)))
+        .all(|(sym, expected)| sym == expected)
 }
 
 /// Checks that `ty` has the same ABI as `rs_std::SliceRef`.
diff --git a/cc_bindings_from_rs/test/aliasing_references/BUILD b/cc_bindings_from_rs/test/aliasing_references/BUILD
index ef3bfd9..3e5c232 100644
--- a/cc_bindings_from_rs/test/aliasing_references/BUILD
+++ b/cc_bindings_from_rs/test/aliasing_references/BUILD
@@ -41,6 +41,6 @@
     srcs = ["aliasing_references_test.cc"],
     deps = [
         ":aliasing_references_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/aliasing_references/aliasing_references_cc_api.h b/cc_bindings_from_rs/test/aliasing_references/aliasing_references_cc_api.h
index bd1158a..c151e6d 100644
--- a/cc_bindings_from_rs/test/aliasing_references/aliasing_references_cc_api.h
+++ b/cc_bindings_from_rs/test/aliasing_references/aliasing_references_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // aliasing_references_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_ALIASING_REFERENCES_ALIASING_REFERENCES_GOLDEN
diff --git a/cc_bindings_from_rs/test/aliasing_references/aliasing_references_cc_api_impl.rs b/cc_bindings_from_rs/test/aliasing_references/aliasing_references_cc_api_impl.rs
index 9a0e712..41fd346 100644
--- a/cc_bindings_from_rs/test/aliasing_references/aliasing_references_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/aliasing_references/aliasing_references_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // aliasing_references_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/arrays/BUILD b/cc_bindings_from_rs/test/arrays/BUILD
index 0acae3a..3bba626 100644
--- a/cc_bindings_from_rs/test/arrays/BUILD
+++ b/cc_bindings_from_rs/test/arrays/BUILD
@@ -41,6 +41,6 @@
     srcs = ["arrays_test.cc"],
     deps = [
         ":arrays_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/arrays/arrays.rs b/cc_bindings_from_rs/test/arrays/arrays.rs
index 7989493..c9716bc 100644
--- a/cc_bindings_from_rs/test/arrays/arrays.rs
+++ b/cc_bindings_from_rs/test/arrays/arrays.rs
@@ -8,15 +8,28 @@
 pub fn function_with_const_array_ptr_id(array_ptr: *const [i32; 2]) -> *const [i32; 2] {
     array_ptr
 }
-// TODO: b/260128806 - Support this use.
+
 pub fn function_with_array_id(array: [i32; 2]) -> [i32; 2] {
     array
 }
-// TODO: b/260128806 - Support this use.
+
 pub fn function_with_array_tuple_id(array_tup: ([i32; 2], [i32; 2])) -> ([i32; 2], [i32; 2]) {
     array_tup
 }
 
+// Will not generate a stub: tuple types cannot be used inside of compound data types,
+// because std::tuple is not layout-compatible with a Rust tuple.
+pub fn function_with_tuple_array_id(tup_array: [(i32, i32); 2]) -> [(i32, i32); 2] {
+    tup_array
+}
+
+const NAMED_SIZE: usize = 3;
+pub fn function_with_mut_array_named_size_ptr_id(
+    array_ptr: *const [i32; NAMED_SIZE],
+) -> *const [i32; NAMED_SIZE] {
+    array_ptr
+}
+
 #[derive(Default, Clone, Copy)]
 pub struct ArrayStruct {
     pub array: [i32; 2],
@@ -25,3 +38,56 @@
 pub fn function_with_array_struct_id(array_struct: ArrayStruct) -> ArrayStruct {
     array_struct
 }
+
+pub struct HasDrop {
+    pub x: i32,
+}
+
+impl HasDrop {
+    pub fn new(x: i32) -> HasDrop {
+        HasDrop { x }
+    }
+}
+
+impl Drop for HasDrop {
+    fn drop(&mut self) {}
+}
+
+pub fn function_with_has_drop_array_id(array: [HasDrop; 2]) -> [HasDrop; 2] {
+    dbg!(array[0].x);
+    dbg!(array[1].x);
+    array
+}
+
+pub fn function_with_has_drop_ret_only() -> [HasDrop; 2] {
+    [HasDrop::new(1), HasDrop::new(2)]
+}
+
+#[derive(Default)]
+pub struct HasDropAndDefault {
+    pub x: i32,
+}
+
+impl Drop for HasDropAndDefault {
+    fn drop(&mut self) {}
+}
+
+pub fn function_with_has_drop_and_default_array_id(
+    array: [HasDropAndDefault; 2],
+) -> [HasDropAndDefault; 2] {
+    array
+}
+
+// TODO: b/260128806 - we conservatively reject nested arrays.
+pub fn function_with_nested_arrays(array: [[i32; 2]; 2]) -> [[i32; 2]; 2] {
+    array
+}
+
+// TODO: b/451981992 - we don't support nested arrays with types that are Drop but not Default.
+pub fn function_with_nested_droponly_arrays(array: [[HasDrop; 2]; 2]) -> [[HasDrop; 2]; 2] {
+    array
+}
+
+pub fn function_with_empty_array(array: [i32; 0]) -> [i32; 0] {
+    array
+}
diff --git a/cc_bindings_from_rs/test/arrays/arrays_cc_api.h b/cc_bindings_from_rs/test/arrays/arrays_cc_api.h
index 9fe76ce..5b39c7e 100644
--- a/cc_bindings_from_rs/test/arrays/arrays_cc_api.h
+++ b/cc_bindings_from_rs/test/arrays/arrays_cc_api.h
@@ -4,18 +4,21 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // arrays_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_ARRAYS_ARRAYS_GOLDEN
 #define THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_ARRAYS_ARRAYS_GOLDEN
 
 #include "support/annotations_internal.h"
+#include "support/internal/memswap.h"
 #include "support/internal/slot.h"
 
 #include <array>
 #include <cstddef>
 #include <cstdint>
+#include <tuple>
 #include <type_traits>
 #include <utility>
 
@@ -26,16 +29,30 @@
 std::array<std::int32_t, 2> const* function_with_const_array_ptr_id(
     std::array<std::int32_t, 2> const* array_ptr);
 
-// Error generating bindings for `function_with_array_id` defined at
-// cc_bindings_from_rs/test/arrays/arrays.rs;l=12:
-// Unknown type
-
-// Error generating bindings for `function_with_array_tuple_id` defined at
-// cc_bindings_from_rs/test/arrays/arrays.rs;l=16:
-// Attempted to write out unknown type from Rust to C
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=12
+std::array<std::int32_t, 2> function_with_array_id(
+    std::array<std::int32_t, 2> array);
 
 // Generated from:
-// cc_bindings_from_rs/test/arrays/arrays.rs;l=21
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=16
+std::tuple<std::array<std::int32_t, 2>, std::array<std::int32_t, 2>>
+function_with_array_tuple_id(
+    std::tuple<std::array<std::int32_t, 2>, std::array<std::int32_t, 2>>
+        array_tup);
+
+// Error generating bindings for `function_with_tuple_array_id` defined at
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=22:
+// Tuple types cannot be used inside of compound data types, because std::tuple
+// is not layout-compatible with a Rust tuple.
+
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=27
+std::array<std::int32_t, 3> const* function_with_mut_array_named_size_ptr_id(
+    std::array<std::int32_t, 3> const* array_ptr);
+
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=34
 struct CRUBIT_INTERNAL_RUST_TYPE(":: arrays_golden :: ArrayStruct") alignas(4)
     [[clang::trivial_abi]] ArrayStruct final {
  public:
@@ -56,7 +73,7 @@
   }
   union {
     // Generated from:
-    // cc_bindings_from_rs/test/arrays/arrays.rs;l=22
+    // cc_bindings_from_rs/test/arrays/arrays.rs;l=35
     std::array<std::int32_t, 2> array;
   };
 
@@ -65,10 +82,105 @@
 };
 
 // Generated from:
-// cc_bindings_from_rs/test/arrays/arrays.rs;l=25
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=38
 ::arrays::ArrayStruct function_with_array_struct_id(
     ::arrays::ArrayStruct array_struct);
 
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=42
+struct CRUBIT_INTERNAL_RUST_TYPE(":: arrays_golden :: HasDrop") alignas(4)
+    [[clang::trivial_abi]] HasDrop final {
+ public:
+  // `HasDrop` doesn't implement the `Default` trait
+  HasDrop() = delete;
+
+  // Drop::drop
+  ~HasDrop();
+
+  // C++ moves are deleted because there's no non-destructive implementation
+  // available.
+  HasDrop(HasDrop&&) = delete;
+  HasDrop& operator=(HasDrop&&) = delete;
+  // `HasDrop` doesn't implement the `Clone` trait
+  HasDrop(const HasDrop&) = delete;
+  HasDrop& operator=(const HasDrop&) = delete;
+  HasDrop(::crubit::UnsafeRelocateTag, HasDrop&& value) {
+    memcpy(this, &value, sizeof(value));
+  }
+
+  // Generated from:
+  // cc_bindings_from_rs/test/arrays/arrays.rs;l=47
+  static ::arrays::HasDrop new_(std::int32_t x);
+
+  union {
+    // Generated from:
+    // cc_bindings_from_rs/test/arrays/arrays.rs;l=43
+    std::int32_t x;
+  };
+
+ private:
+  static void __crubit_field_offset_assertions();
+};
+
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=56
+std::array<::arrays::HasDrop, 2> function_with_has_drop_array_id(
+    std::array<::arrays::HasDrop, 2> array);
+
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=62
+std::array<::arrays::HasDrop, 2> function_with_has_drop_ret_only();
+
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=67
+struct CRUBIT_INTERNAL_RUST_TYPE(
+    ":: arrays_golden :: HasDropAndDefault") alignas(4) [[clang::trivial_abi]]
+HasDropAndDefault final {
+ public:
+  // Default::default
+  HasDropAndDefault();
+
+  // Drop::drop
+  ~HasDropAndDefault();
+
+  HasDropAndDefault(HasDropAndDefault&&);
+  HasDropAndDefault& operator=(HasDropAndDefault&&);
+
+  // `HasDropAndDefault` doesn't implement the `Clone` trait
+  HasDropAndDefault(const HasDropAndDefault&) = delete;
+  HasDropAndDefault& operator=(const HasDropAndDefault&) = delete;
+  HasDropAndDefault(::crubit::UnsafeRelocateTag, HasDropAndDefault&& value) {
+    memcpy(this, &value, sizeof(value));
+  }
+  union {
+    // Generated from:
+    // cc_bindings_from_rs/test/arrays/arrays.rs;l=68
+    std::int32_t x;
+  };
+
+ private:
+  static void __crubit_field_offset_assertions();
+};
+
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=75
+std::array<::arrays::HasDropAndDefault, 2>
+function_with_has_drop_and_default_array_id(
+    std::array<::arrays::HasDropAndDefault, 2> array);
+
+// Error generating bindings for `function_with_nested_arrays` defined at
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=82:
+// b/260128806 - nested array [i32; 2] is not supported
+
+// Error generating bindings for `function_with_nested_droponly_arrays` defined
+// at cc_bindings_from_rs/test/arrays/arrays.rs;l=87:
+// b/260128806 - nested array [HasDrop; 2] is not supported
+
+// Generated from:
+// cc_bindings_from_rs/test/arrays/arrays.rs;l=91
+std::array<std::int32_t, 0> function_with_empty_array(
+    std::array<std::int32_t, 0> array);
+
 namespace __crubit_internal {
 extern "C" std::array<std::int32_t, 2> const*
 __crubit_thunk_function_uwith_uconst_uarray_uptr_uid(
@@ -80,6 +192,58 @@
       __crubit_thunk_function_uwith_uconst_uarray_uptr_uid(array_ptr);
 }
 
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_function_uwith_uarray_uid(void*,
+                                                         void* __ret_ptr);
+}
+inline std::array<std::int32_t, 2> function_with_array_id(
+    std::array<std::int32_t, 2> array) {
+  crubit::Slot<std::array<std::int32_t, 2>> __return_value_ret_val_holder;
+  auto* __return_value_storage = __return_value_ret_val_holder.Get();
+  __crubit_internal::__crubit_thunk_function_uwith_uarray_uid(
+      &array, __return_value_storage);
+  return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue();
+}
+
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_function_uwith_uarray_utuple_uid(
+    void**, void** __ret_ptr);
+}
+inline std::tuple<std::array<std::int32_t, 2>, std::array<std::int32_t, 2>>
+function_with_array_tuple_id(
+    std::tuple<std::array<std::int32_t, 2>, std::array<std::int32_t, 2>>
+        array_tup) {
+  auto&& array_tup_0 = std::get<0>(array_tup);
+  auto&& array_tup_cabi_0 = &array_tup_0;
+  auto&& array_tup_1 = std::get<1>(array_tup);
+  auto&& array_tup_cabi_1 = &array_tup_1;
+  void* array_tup_cabi[] = {&array_tup_cabi_0, &array_tup_cabi_1};
+  crubit::Slot<std::array<std::int32_t, 2>> __return_value_0_ret_val_holder;
+  auto* __return_value_0_storage = __return_value_0_ret_val_holder.Get();
+  crubit::Slot<std::array<std::int32_t, 2>> __return_value_1_ret_val_holder;
+  auto* __return_value_1_storage = __return_value_1_ret_val_holder.Get();
+  void* __return_value_storage[] = {__return_value_0_storage,
+                                    __return_value_1_storage};
+  __crubit_internal::__crubit_thunk_function_uwith_uarray_utuple_uid(
+      array_tup_cabi, __return_value_storage);
+  return std::make_tuple(
+      std::move(__return_value_0_ret_val_holder).AssumeInitAndTakeValue(),
+      std::move(__return_value_1_ret_val_holder).AssumeInitAndTakeValue());
+}
+
+namespace __crubit_internal {
+extern "C" std::array<std::int32_t, 3> const*
+__crubit_thunk_function_uwith_umut_uarray_unamed_usize_uptr_uid(
+    std::array<std::int32_t, 3> const*);
+}
+inline std::array<std::int32_t, 3> const*
+function_with_mut_array_named_size_ptr_id(
+    std::array<std::int32_t, 3> const* array_ptr) {
+  return __crubit_internal::
+      __crubit_thunk_function_uwith_umut_uarray_unamed_usize_uptr_uid(
+          array_ptr);
+}
+
 static_assert(
     sizeof(ArrayStruct) == 8,
     "Verify that ADT layout didn't change since this header got generated");
@@ -113,5 +277,114 @@
   return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue();
 }
 
+static_assert(
+    sizeof(HasDrop) == 4,
+    "Verify that ADT layout didn't change since this header got generated");
+static_assert(
+    alignof(HasDrop) == 4,
+    "Verify that ADT layout didn't change since this header got generated");
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_drop(::arrays::HasDrop&);
+}
+inline HasDrop::~HasDrop() { __crubit_internal::__crubit_thunk_drop(*this); }
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_new(std::int32_t, ::arrays::HasDrop* __ret_ptr);
+}
+inline ::arrays::HasDrop HasDrop::new_(std::int32_t x) {
+  crubit::Slot<::arrays::HasDrop> __return_value_ret_val_holder;
+  auto* __return_value_storage = __return_value_ret_val_holder.Get();
+  __crubit_internal::__crubit_thunk_new(x, __return_value_storage);
+  return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue();
+}
+inline void HasDrop::__crubit_field_offset_assertions() {
+  static_assert(0 == offsetof(HasDrop, x));
+}
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_function_uwith_uhas_udrop_uarray_uid(
+    void*, void* __ret_ptr);
+}
+inline std::array<::arrays::HasDrop, 2> function_with_has_drop_array_id(
+    std::array<::arrays::HasDrop, 2> array) {
+  crubit::Slot array_slot((std::move(array)));
+  crubit::Slot<std::array<::arrays::HasDrop, 2>> __return_value_ret_val_holder;
+  auto* __return_value_storage = __return_value_ret_val_holder.Get();
+  __crubit_internal::__crubit_thunk_function_uwith_uhas_udrop_uarray_uid(
+      array_slot.Get(), __return_value_storage);
+  return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue();
+}
+
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_function_uwith_uhas_udrop_uret_uonly(
+    void* __ret_ptr);
+}
+inline std::array<::arrays::HasDrop, 2> function_with_has_drop_ret_only() {
+  crubit::Slot<std::array<::arrays::HasDrop, 2>> __return_value_ret_val_holder;
+  auto* __return_value_storage = __return_value_ret_val_holder.Get();
+  __crubit_internal::__crubit_thunk_function_uwith_uhas_udrop_uret_uonly(
+      __return_value_storage);
+  return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue();
+}
+
+static_assert(
+    sizeof(HasDropAndDefault) == 4,
+    "Verify that ADT layout didn't change since this header got generated");
+static_assert(
+    alignof(HasDropAndDefault) == 4,
+    "Verify that ADT layout didn't change since this header got generated");
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_default(::arrays::HasDropAndDefault* __ret_ptr);
+}
+inline HasDropAndDefault::HasDropAndDefault() {
+  __crubit_internal::__crubit_thunk_default(this);
+}
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_drop(::arrays::HasDropAndDefault&);
+}
+inline HasDropAndDefault::~HasDropAndDefault() {
+  __crubit_internal::__crubit_thunk_drop(*this);
+}
+inline HasDropAndDefault::HasDropAndDefault(HasDropAndDefault&& other)
+    : HasDropAndDefault() {
+  *this = std::move(other);
+}
+inline HasDropAndDefault& HasDropAndDefault::operator=(
+    HasDropAndDefault&& other) {
+  crubit::MemSwap(*this, other);
+  return *this;
+}
+inline void HasDropAndDefault::__crubit_field_offset_assertions() {
+  static_assert(0 == offsetof(HasDropAndDefault, x));
+}
+namespace __crubit_internal {
+extern "C" void
+__crubit_thunk_function_uwith_uhas_udrop_uand_udefault_uarray_uid(
+    void*, void* __ret_ptr);
+}
+inline std::array<::arrays::HasDropAndDefault, 2>
+function_with_has_drop_and_default_array_id(
+    std::array<::arrays::HasDropAndDefault, 2> array) {
+  crubit::Slot array_slot((std::move(array)));
+  crubit::Slot<std::array<::arrays::HasDropAndDefault, 2>>
+      __return_value_ret_val_holder;
+  auto* __return_value_storage = __return_value_ret_val_holder.Get();
+  __crubit_internal::
+      __crubit_thunk_function_uwith_uhas_udrop_uand_udefault_uarray_uid(
+          array_slot.Get(), __return_value_storage);
+  return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue();
+}
+
+namespace __crubit_internal {
+extern "C" void __crubit_thunk_function_uwith_uempty_uarray(void*,
+                                                            void* __ret_ptr);
+}
+inline std::array<std::int32_t, 0> function_with_empty_array(
+    std::array<std::int32_t, 0> array) {
+  crubit::Slot<std::array<std::int32_t, 0>> __return_value_ret_val_holder;
+  auto* __return_value_storage = __return_value_ret_val_holder.Get();
+  __crubit_internal::__crubit_thunk_function_uwith_uempty_uarray(
+      &array, __return_value_storage);
+  return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue();
+}
+
 }  // namespace arrays
 #endif  // THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_ARRAYS_ARRAYS_GOLDEN
diff --git a/cc_bindings_from_rs/test/arrays/arrays_cc_api_impl.rs b/cc_bindings_from_rs/test/arrays/arrays_cc_api_impl.rs
index 629650a..b1bfa10 100644
--- a/cc_bindings_from_rs/test/arrays/arrays_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/arrays/arrays_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // arrays_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
@@ -16,6 +16,54 @@
 ) -> *const [i32; 2] {
     unsafe { ::arrays_golden::function_with_const_array_ptr_id(array_ptr) }
 }
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_function_uwith_uarray_uid(
+    array: &'static mut ::core::mem::MaybeUninit<[i32; 2]>,
+    __ret_ptr: *mut core::ffi::c_void,
+) -> () {
+    unsafe {
+        let array = array.assume_init_read();
+        let __rs_return_value = ::arrays_golden::function_with_array_id(array);
+        (__ret_ptr as *mut [i32; 2]).write(__rs_return_value);
+    }
+}
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_function_uwith_uarray_utuple_uid(
+    array_tup: *const [*const core::ffi::c_void; 2usize],
+    __ret_ptr: *mut core::ffi::c_void,
+) -> () {
+    unsafe {
+        let array_tup = (
+            {
+                let array_tup_0: &'static mut ::core::mem::MaybeUninit<[i32; 2]> = ((*array_tup)
+                    [0usize]
+                    as *const &'static mut ::core::mem::MaybeUninit<[i32; 2]>)
+                    .read();
+                let array_tup_0 = array_tup_0.assume_init_read();
+                array_tup_0
+            },
+            {
+                let array_tup_1: &'static mut ::core::mem::MaybeUninit<[i32; 2]> = ((*array_tup)
+                    [1usize]
+                    as *const &'static mut ::core::mem::MaybeUninit<[i32; 2]>)
+                    .read();
+                let array_tup_1 = array_tup_1.assume_init_read();
+                array_tup_1
+            },
+        );
+        let __rs_return_value = ::arrays_golden::function_with_array_tuple_id(array_tup);
+        let (__rs_return_value_0, __rs_return_value_1) = __rs_return_value;
+        let [__ret_ptr_0, __ret_ptr_1] = *(__ret_ptr as *mut [*mut core::ffi::c_void; 2usize]);
+        (__ret_ptr_0 as *mut [i32; 2]).write(__rs_return_value_0);
+        (__ret_ptr_1 as *mut [i32; 2]).write(__rs_return_value_1);
+    }
+}
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_function_uwith_umut_uarray_unamed_usize_uptr_uid(
+    array_ptr: *const [i32; 3],
+) -> *const [i32; 3] {
+    unsafe { ::arrays_golden::function_with_mut_array_named_size_ptr_id(array_ptr) }
+}
 const _: () = assert!(::std::mem::size_of::<::arrays_golden::ArrayStruct>() == 8);
 const _: () = assert!(::std::mem::align_of::<::arrays_golden::ArrayStruct>() == 4);
 #[unsafe(no_mangle)]
@@ -38,3 +86,78 @@
         (__ret_ptr as *mut ::arrays_golden::ArrayStruct).write(__rs_return_value);
     }
 }
+const _: () = assert!(::std::mem::size_of::<::arrays_golden::HasDrop>() == 4);
+const _: () = assert!(::std::mem::align_of::<::arrays_golden::HasDrop>() == 4);
+#[unsafe(no_mangle)]
+extern "C" fn __crubit_thunk_drop(
+    __self: &'static mut ::core::mem::MaybeUninit<::arrays_golden::HasDrop>,
+) {
+    unsafe { __self.assume_init_drop() };
+}
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_new(x: i32, __ret_ptr: *mut core::ffi::c_void) -> () {
+    unsafe {
+        let __rs_return_value = ::arrays_golden::HasDrop::new(x);
+        (__ret_ptr as *mut ::arrays_golden::HasDrop).write(__rs_return_value);
+    }
+}
+const _: () = assert!(::core::mem::offset_of!(::arrays_golden::HasDrop, x) == 0);
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_function_uwith_uhas_udrop_uarray_uid(
+    array: &'static mut ::core::mem::MaybeUninit<[::arrays_golden::HasDrop; 2]>,
+    __ret_ptr: *mut core::ffi::c_void,
+) -> () {
+    unsafe {
+        let array = array.assume_init_read();
+        let __rs_return_value = ::arrays_golden::function_with_has_drop_array_id(array);
+        (__ret_ptr as *mut [::arrays_golden::HasDrop; 2]).write(__rs_return_value);
+    }
+}
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_function_uwith_uhas_udrop_uret_uonly(
+    __ret_ptr: *mut core::ffi::c_void,
+) -> () {
+    unsafe {
+        let __rs_return_value = ::arrays_golden::function_with_has_drop_ret_only();
+        (__ret_ptr as *mut [::arrays_golden::HasDrop; 2]).write(__rs_return_value);
+    }
+}
+const _: () = assert!(::std::mem::size_of::<::arrays_golden::HasDropAndDefault>() == 4);
+const _: () = assert!(::std::mem::align_of::<::arrays_golden::HasDropAndDefault>() == 4);
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_default(__ret_ptr: *mut core::ffi::c_void) -> () {
+    unsafe {
+        let __rs_return_value =
+            <::arrays_golden::HasDropAndDefault as ::core::default::Default>::default();
+        (__ret_ptr as *mut ::arrays_golden::HasDropAndDefault).write(__rs_return_value);
+    }
+}
+#[unsafe(no_mangle)]
+extern "C" fn __crubit_thunk_drop(
+    __self: &'static mut ::core::mem::MaybeUninit<::arrays_golden::HasDropAndDefault>,
+) {
+    unsafe { __self.assume_init_drop() };
+}
+const _: () = assert!(::core::mem::offset_of!(::arrays_golden::HasDropAndDefault, x) == 0);
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_function_uwith_uhas_udrop_uand_udefault_uarray_uid(
+    array: &'static mut ::core::mem::MaybeUninit<[::arrays_golden::HasDropAndDefault; 2]>,
+    __ret_ptr: *mut core::ffi::c_void,
+) -> () {
+    unsafe {
+        let array = array.assume_init_read();
+        let __rs_return_value = ::arrays_golden::function_with_has_drop_and_default_array_id(array);
+        (__ret_ptr as *mut [::arrays_golden::HasDropAndDefault; 2]).write(__rs_return_value);
+    }
+}
+#[unsafe(no_mangle)]
+unsafe extern "C" fn __crubit_thunk_function_uwith_uempty_uarray(
+    array: &'static mut ::core::mem::MaybeUninit<[i32; 0]>,
+    __ret_ptr: *mut core::ffi::c_void,
+) -> () {
+    unsafe {
+        let array = array.assume_init_read();
+        let __rs_return_value = ::arrays_golden::function_with_empty_array(array);
+        (__ret_ptr as *mut [i32; 0]).write(__rs_return_value);
+    }
+}
diff --git a/cc_bindings_from_rs/test/arrays/arrays_test.cc b/cc_bindings_from_rs/test/arrays/arrays_test.cc
index 7f3580d..81644fe 100644
--- a/cc_bindings_from_rs/test/arrays/arrays_test.cc
+++ b/cc_bindings_from_rs/test/arrays/arrays_test.cc
@@ -23,4 +23,46 @@
             array_struct.array);
 }
 
+TEST(ArraysTest, ArrayValueInOut) {
+  std::array<int32_t, 2> array = {1, 2};
+  EXPECT_EQ(arrays::function_with_array_id(array), array);
+}
+
+TEST(ArraysTest, TupleOfArraysValueInOut) {
+  std::tuple<std::array<int32_t, 2>, std::array<int32_t, 2>> array_tup{{1, 2},
+                                                                       {3, 4}};
+  EXPECT_EQ(arrays::function_with_array_tuple_id(array_tup), array_tup);
+}
+
+TEST(ArraysTest, DropOut) {
+  auto out = arrays::function_with_has_drop_ret_only();
+  EXPECT_EQ(out[0].x, 1);
+  EXPECT_EQ(out[1].x, 2);
+}
+
+TEST(ArraysTest, DropInOut) {
+  auto out = arrays::function_with_has_drop_array_id(
+      {arrays::HasDrop::new_(1), arrays::HasDrop::new_(2)});
+  EXPECT_EQ(out[0].x, 1);
+  EXPECT_EQ(out[1].x, 2);
+}
+
+TEST(ArraysTest, DropAndDefaultInOut) {
+  arrays::HasDropAndDefault a;
+  arrays::HasDropAndDefault b;
+  a.x = 1;
+  b.x = 2;
+  std::array<arrays::HasDropAndDefault, 2> array{std::move(a), std::move(b)};
+  EXPECT_EQ(array[0].x, 1);
+  EXPECT_EQ(array[1].x, 2);
+  auto out =
+      arrays::function_with_has_drop_and_default_array_id(std::move(array));
+  EXPECT_EQ(out[0].x, 1);
+  EXPECT_EQ(out[1].x, 2);
+}
+
+TEST(ArraysTest, EmptyArrayInOut) {
+  std::array<int32_t, 0> array;
+  EXPECT_EQ(arrays::function_with_empty_array(array), array);
+}
 }  // namespace
diff --git a/cc_bindings_from_rs/test/attribute/BUILD b/cc_bindings_from_rs/test/attribute/BUILD
index caefda2..dd9c800 100644
--- a/cc_bindings_from_rs/test/attribute/BUILD
+++ b/cc_bindings_from_rs/test/attribute/BUILD
@@ -43,7 +43,7 @@
     srcs = ["cpp_name_test.cc"],
     deps = [
         ":cpp_name_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -73,6 +73,6 @@
     srcs = ["must_bind_test.cc"],
     deps = [
         ":must_bind_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/attribute/cpp_name_cc_api.h b/cc_bindings_from_rs/test/attribute/cpp_name_cc_api.h
index 68281a0..c698310 100644
--- a/cc_bindings_from_rs/test/attribute/cpp_name_cc_api.h
+++ b/cc_bindings_from_rs/test/attribute/cpp_name_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // cpp_name_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_ATTRIBUTE_CPP_NAME_GOLDEN
diff --git a/cc_bindings_from_rs/test/attribute/cpp_name_cc_api_impl.rs b/cc_bindings_from_rs/test/attribute/cpp_name_cc_api_impl.rs
index d0e5f3a..899b32a 100644
--- a/cc_bindings_from_rs/test/attribute/cpp_name_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/attribute/cpp_name_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // cpp_name_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/attribute/must_bind_cc_api.h b/cc_bindings_from_rs/test/attribute/must_bind_cc_api.h
index a86bab5..20ae3d0 100644
--- a/cc_bindings_from_rs/test/attribute/must_bind_cc_api.h
+++ b/cc_bindings_from_rs/test/attribute/must_bind_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // must_bind_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_ATTRIBUTE_MUST_BIND_GOLDEN
diff --git a/cc_bindings_from_rs/test/attribute/must_bind_cc_api_impl.rs b/cc_bindings_from_rs/test/attribute/must_bind_cc_api_impl.rs
index dd6008f..c189f93 100644
--- a/cc_bindings_from_rs/test/attribute/must_bind_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/attribute/must_bind_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // must_bind_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/bazel/build_rs/BUILD b/cc_bindings_from_rs/test/bazel/build_rs/BUILD
index 15f1bcc..7428284 100644
--- a/cc_bindings_from_rs/test/bazel/build_rs/BUILD
+++ b/cc_bindings_from_rs/test/bazel/build_rs/BUILD
@@ -32,6 +32,6 @@
     srcs = ["build_rs_test.cc"],
     deps = [
         ":build_rs_user_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bazel/crate_features/BUILD b/cc_bindings_from_rs/test/bazel/crate_features/BUILD
index 8f45fba..a8839f6 100644
--- a/cc_bindings_from_rs/test/bazel/crate_features/BUILD
+++ b/cc_bindings_from_rs/test/bazel/crate_features/BUILD
@@ -30,6 +30,6 @@
     srcs = ["needs_feature_test.cc"],
     deps = [
         ":needs_feature_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bazel/crate_flags/BUILD b/cc_bindings_from_rs/test/bazel/crate_flags/BUILD
index 8e36beb..49c6106 100644
--- a/cc_bindings_from_rs/test/bazel/crate_flags/BUILD
+++ b/cc_bindings_from_rs/test/bazel/crate_flags/BUILD
@@ -34,6 +34,6 @@
     srcs = ["crate_flags_test.cc"],
     deps = [
         ":crate_flags_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bazel/crate_name/BUILD b/cc_bindings_from_rs/test/bazel/crate_name/BUILD
index 2d519cb..a82de1e 100644
--- a/cc_bindings_from_rs/test/bazel/crate_name/BUILD
+++ b/cc_bindings_from_rs/test/bazel/crate_name/BUILD
@@ -29,6 +29,6 @@
     srcs = ["crate_name_test.cc"],
     deps = [
         ":custom_crate_name_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bazel/cross_crate/BUILD b/cc_bindings_from_rs/test/bazel/cross_crate/BUILD
index 505c388..03bb367 100644
--- a/cc_bindings_from_rs/test/bazel/cross_crate/BUILD
+++ b/cc_bindings_from_rs/test/bazel/cross_crate/BUILD
@@ -35,6 +35,6 @@
     srcs = ["cross_crate_test.cc"],
     deps = [
         ":test_api_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bazel/env/BUILD b/cc_bindings_from_rs/test/bazel/env/BUILD
index 30c260b..da788ac 100644
--- a/cc_bindings_from_rs/test/bazel/env/BUILD
+++ b/cc_bindings_from_rs/test/bazel/env/BUILD
@@ -27,6 +27,6 @@
     srcs = ["env_test.cc"],
     deps = [
         ":env_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bazel/include_str/BUILD b/cc_bindings_from_rs/test/bazel/include_str/BUILD
index 7001c76..974c654 100644
--- a/cc_bindings_from_rs/test/bazel/include_str/BUILD
+++ b/cc_bindings_from_rs/test/bazel/include_str/BUILD
@@ -30,6 +30,6 @@
     srcs = ["include_str_test.cc"],
     deps = [
         ":include_str_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bridging/BUILD b/cc_bindings_from_rs/test/bridging/BUILD
index 60ef556..c41a9d7 100644
--- a/cc_bindings_from_rs/test/bridging/BUILD
+++ b/cc_bindings_from_rs/test/bridging/BUILD
@@ -58,6 +58,6 @@
     deps = [
         ":rust_pointer_types_cc_api",
         ":rust_type_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bridging/composable/BUILD b/cc_bindings_from_rs/test/bridging/composable/BUILD
index c78f03b..e6afd6c 100644
--- a/cc_bindings_from_rs/test/bridging/composable/BUILD
+++ b/cc_bindings_from_rs/test/bridging/composable/BUILD
@@ -55,6 +55,6 @@
     deps = [
         ":composable_bridging_cc_api",
         "//support/rs_std:slice_ref",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bridging/protobuf/BUILD b/cc_bindings_from_rs/test/bridging/protobuf/BUILD
index 06be4a7..ee148d4 100644
--- a/cc_bindings_from_rs/test/bridging/protobuf/BUILD
+++ b/cc_bindings_from_rs/test/bridging/protobuf/BUILD
@@ -64,6 +64,6 @@
     deps = [
         ":foo_cc_proto",
         ":rust_lib_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/bridging/protobuf/rust_lib_cc_api.h b/cc_bindings_from_rs/test/bridging/protobuf/rust_lib_cc_api.h
index 4a4b254..10f0957 100644
--- a/cc_bindings_from_rs/test/bridging/protobuf/rust_lib_cc_api.h
+++ b/cc_bindings_from_rs/test/bridging/protobuf/rust_lib_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // rust_lib_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_BRIDGING_PROTOBUF_RUST_LIB_GOLDEN
diff --git a/cc_bindings_from_rs/test/bridging/protobuf/rust_lib_cc_api_impl.rs b/cc_bindings_from_rs/test/bridging/protobuf/rust_lib_cc_api_impl.rs
index 6c7e25b..56d7930 100644
--- a/cc_bindings_from_rs/test/bridging/protobuf/rust_lib_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/bridging/protobuf/rust_lib_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // rust_lib_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/bridging/string/BUILD b/cc_bindings_from_rs/test/bridging/string/BUILD
index 8346ec5..d949374 100644
--- a/cc_bindings_from_rs/test/bridging/string/BUILD
+++ b/cc_bindings_from_rs/test/bridging/string/BUILD
@@ -30,6 +30,6 @@
     srcs = ["string_test.cc"],
     deps = [
         ":string_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/consts/BUILD b/cc_bindings_from_rs/test/consts/BUILD
index 7e1a559..73725ee 100644
--- a/cc_bindings_from_rs/test/consts/BUILD
+++ b/cc_bindings_from_rs/test/consts/BUILD
@@ -48,6 +48,6 @@
     srcs = ["consts_test.cc"],
     deps = [
         ":consts_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/enums/BUILD b/cc_bindings_from_rs/test/enums/BUILD
index 54aef16..0fbe9a6 100644
--- a/cc_bindings_from_rs/test/enums/BUILD
+++ b/cc_bindings_from_rs/test/enums/BUILD
@@ -48,7 +48,7 @@
     srcs = ["enums_test.cc"],
     deps = [
         ":enums_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -79,6 +79,6 @@
     srcs = ["cpp_enums_test.cc"],
     deps = [
         ":cpp_enums_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/enums/cpp_enums_cc_api.h b/cc_bindings_from_rs/test/enums/cpp_enums_cc_api.h
index 08ee44e..c2ef80e 100644
--- a/cc_bindings_from_rs/test/enums/cpp_enums_cc_api.h
+++ b/cc_bindings_from_rs/test/enums/cpp_enums_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // cpp_enums_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_ENUMS_CPP_ENUMS_GOLDEN
diff --git a/cc_bindings_from_rs/test/experimental_unions/BUILD b/cc_bindings_from_rs/test/experimental_unions/BUILD
index 55d3ea8..fa67ada 100644
--- a/cc_bindings_from_rs/test/experimental_unions/BUILD
+++ b/cc_bindings_from_rs/test/experimental_unions/BUILD
@@ -33,6 +33,6 @@
     srcs = ["unions_test.cc"],
     deps = [
         ":unions_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/function_pointers/BUILD b/cc_bindings_from_rs/test/function_pointers/BUILD
index c79c049..f711988 100644
--- a/cc_bindings_from_rs/test/function_pointers/BUILD
+++ b/cc_bindings_from_rs/test/function_pointers/BUILD
@@ -42,6 +42,6 @@
     srcs = ["function_pointers_test.cc"],
     deps = [
         ":function_pointers_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/functions/BUILD b/cc_bindings_from_rs/test/functions/BUILD
index 08a0ffb..444e4f0 100644
--- a/cc_bindings_from_rs/test/functions/BUILD
+++ b/cc_bindings_from_rs/test/functions/BUILD
@@ -34,6 +34,6 @@
     deps = [
         ":functions_cc_api",
         "//support/rs_std:char",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/impls/BUILD b/cc_bindings_from_rs/test/impls/BUILD
index ae1503a..421dd7c 100644
--- a/cc_bindings_from_rs/test/impls/BUILD
+++ b/cc_bindings_from_rs/test/impls/BUILD
@@ -36,6 +36,6 @@
     srcs = ["impls_test.cc"],
     deps = [
         ":impls_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/known_traits/clone/BUILD b/cc_bindings_from_rs/test/known_traits/clone/BUILD
index fed90b0..4e40f9d 100644
--- a/cc_bindings_from_rs/test/known_traits/clone/BUILD
+++ b/cc_bindings_from_rs/test/known_traits/clone/BUILD
@@ -33,6 +33,6 @@
     srcs = ["clone_test.cc"],
     deps = [
         ":rs_clone_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/known_traits/copy/BUILD b/cc_bindings_from_rs/test/known_traits/copy/BUILD
index 4e25d8f..105945b 100644
--- a/cc_bindings_from_rs/test/known_traits/copy/BUILD
+++ b/cc_bindings_from_rs/test/known_traits/copy/BUILD
@@ -31,6 +31,6 @@
     srcs = ["copy_test.cc"],
     deps = [
         ":copy_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/known_traits/default/BUILD b/cc_bindings_from_rs/test/known_traits/default/BUILD
index 7f088c5..1cd94fd 100644
--- a/cc_bindings_from_rs/test/known_traits/default/BUILD
+++ b/cc_bindings_from_rs/test/known_traits/default/BUILD
@@ -30,6 +30,6 @@
     srcs = ["default_test.cc"],
     deps = [
         ":rs_default_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/known_traits/drop/BUILD b/cc_bindings_from_rs/test/known_traits/drop/BUILD
index 57e88ac..46b8be2 100644
--- a/cc_bindings_from_rs/test/known_traits/drop/BUILD
+++ b/cc_bindings_from_rs/test/known_traits/drop/BUILD
@@ -31,6 +31,6 @@
     srcs = ["drop_test.cc"],
     deps = [
         ":drop_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/known_traits/from/BUILD b/cc_bindings_from_rs/test/known_traits/from/BUILD
index d807c3c..4ccfd5f 100644
--- a/cc_bindings_from_rs/test/known_traits/from/BUILD
+++ b/cc_bindings_from_rs/test/known_traits/from/BUILD
@@ -47,6 +47,6 @@
     srcs = ["from_test.cc"],
     deps = [
         ":from_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/known_traits/from/from_cc_api.h b/cc_bindings_from_rs/test/known_traits/from/from_cc_api.h
index 9d62c40..04db24f 100644
--- a/cc_bindings_from_rs/test/known_traits/from/from_cc_api.h
+++ b/cc_bindings_from_rs/test/known_traits/from/from_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // from_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_KNOWN_TRAITS_FROM_FROM_GOLDEN
diff --git a/cc_bindings_from_rs/test/known_traits/from/from_cc_api_impl.rs b/cc_bindings_from_rs/test/known_traits/from/from_cc_api_impl.rs
index 9c5c53a..63f7d75 100644
--- a/cc_bindings_from_rs/test/known_traits/from/from_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/known_traits/from/from_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // from_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/known_traits/into/BUILD b/cc_bindings_from_rs/test/known_traits/into/BUILD
index 1d356ea..aa58641 100644
--- a/cc_bindings_from_rs/test/known_traits/into/BUILD
+++ b/cc_bindings_from_rs/test/known_traits/into/BUILD
@@ -47,6 +47,6 @@
     srcs = ["into_test.cc"],
     deps = [
         ":into_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/known_traits/into/into_cc_api.h b/cc_bindings_from_rs/test/known_traits/into/into_cc_api.h
index 3390b65..a6b2059 100644
--- a/cc_bindings_from_rs/test/known_traits/into/into_cc_api.h
+++ b/cc_bindings_from_rs/test/known_traits/into/into_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // into_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_KNOWN_TRAITS_INTO_INTO_GOLDEN
diff --git a/cc_bindings_from_rs/test/known_traits/into/into_cc_api_impl.rs b/cc_bindings_from_rs/test/known_traits/into/into_cc_api_impl.rs
index 7821464..e8477d8 100644
--- a/cc_bindings_from_rs/test/known_traits/into/into_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/known_traits/into/into_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // into_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/library_config_aspect_hint/BUILD b/cc_bindings_from_rs/test/library_config_aspect_hint/BUILD
index 0a8ee88..742ba7c 100644
--- a/cc_bindings_from_rs/test/library_config_aspect_hint/BUILD
+++ b/cc_bindings_from_rs/test/library_config_aspect_hint/BUILD
@@ -58,6 +58,6 @@
     srcs = ["namespace_test.cc"],
     deps = [
         ":namespace_crate2_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/lifetimes/BUILD b/cc_bindings_from_rs/test/lifetimes/BUILD
index d175fc8..9c4f831 100644
--- a/cc_bindings_from_rs/test/lifetimes/BUILD
+++ b/cc_bindings_from_rs/test/lifetimes/BUILD
@@ -41,6 +41,6 @@
     srcs = ["lifetimes_test.cc"],
     deps = [
         ":lifetimes_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/lifetimes/lifetimes_cc_api.h b/cc_bindings_from_rs/test/lifetimes/lifetimes_cc_api.h
index ca54237..cc2ff70 100644
--- a/cc_bindings_from_rs/test/lifetimes/lifetimes_cc_api.h
+++ b/cc_bindings_from_rs/test/lifetimes/lifetimes_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // lifetimes_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_LIFETIMES_LIFETIMES_GOLDEN
diff --git a/cc_bindings_from_rs/test/lifetimes/lifetimes_cc_api_impl.rs b/cc_bindings_from_rs/test/lifetimes/lifetimes_cc_api_impl.rs
index 5525a6c..c5c6e99 100644
--- a/cc_bindings_from_rs/test/lifetimes/lifetimes_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/lifetimes/lifetimes_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // lifetimes_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/modules/BUILD b/cc_bindings_from_rs/test/modules/BUILD
index 0985967..b67683c 100644
--- a/cc_bindings_from_rs/test/modules/BUILD
+++ b/cc_bindings_from_rs/test/modules/BUILD
@@ -42,6 +42,6 @@
     srcs = ["modules_test.cc"],
     deps = [
         ":modules_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/modules/modules_cc_api.h b/cc_bindings_from_rs/test/modules/modules_cc_api.h
index 184ae50..0e3019e 100644
--- a/cc_bindings_from_rs/test/modules/modules_cc_api.h
+++ b/cc_bindings_from_rs/test/modules/modules_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // modules_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_MODULES_MODULES_GOLDEN
diff --git a/cc_bindings_from_rs/test/modules/modules_cc_api_impl.rs b/cc_bindings_from_rs/test/modules/modules_cc_api_impl.rs
index d5e8bf7..bf3b627 100644
--- a/cc_bindings_from_rs/test/modules/modules_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/modules/modules_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // modules_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/move_semantics/BUILD b/cc_bindings_from_rs/test/move_semantics/BUILD
index 6d2c8ac..d89bf0c 100644
--- a/cc_bindings_from_rs/test/move_semantics/BUILD
+++ b/cc_bindings_from_rs/test/move_semantics/BUILD
@@ -39,6 +39,6 @@
     srcs = ["move_test.cc"],
     deps = [
         ":move_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/move_semantics/move_cc_api.h b/cc_bindings_from_rs/test/move_semantics/move_cc_api.h
index 749485b..0788e8c 100644
--- a/cc_bindings_from_rs/test/move_semantics/move_cc_api.h
+++ b/cc_bindings_from_rs/test/move_semantics/move_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // move_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_MOVE_SEMANTICS_MOVE_GOLDEN
diff --git a/cc_bindings_from_rs/test/move_semantics/move_cc_api_impl.rs b/cc_bindings_from_rs/test/move_semantics/move_cc_api_impl.rs
index 306dd1c..ba6dedf 100644
--- a/cc_bindings_from_rs/test/move_semantics/move_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/move_semantics/move_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // move_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/never/BUILD b/cc_bindings_from_rs/test/never/BUILD
index 253cf36..30eecd8 100644
--- a/cc_bindings_from_rs/test/never/BUILD
+++ b/cc_bindings_from_rs/test/never/BUILD
@@ -38,6 +38,6 @@
     srcs = ["never_test.cc"],
     deps = [
         ":never_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/never/never_cc_api.h b/cc_bindings_from_rs/test/never/never_cc_api.h
index 7b4c218..7130d5d 100644
--- a/cc_bindings_from_rs/test/never/never_cc_api.h
+++ b/cc_bindings_from_rs/test/never/never_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // never_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_NEVER_NEVER_GOLDEN
diff --git a/cc_bindings_from_rs/test/never/never_cc_api_impl.rs b/cc_bindings_from_rs/test/never/never_cc_api_impl.rs
index 0fc8525..503ccd0 100644
--- a/cc_bindings_from_rs/test/never/never_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/never/never_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // never_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/non_local_crate_bindings/BUILD b/cc_bindings_from_rs/test/non_local_crate_bindings/BUILD
index 84a1901..6823e78 100644
--- a/cc_bindings_from_rs/test/non_local_crate_bindings/BUILD
+++ b/cc_bindings_from_rs/test/non_local_crate_bindings/BUILD
@@ -38,6 +38,6 @@
     srcs = ["core_bindings_test.cc"],
     deps = [
         ":core_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/primitive_types/BUILD b/cc_bindings_from_rs/test/primitive_types/BUILD
index eadc51a..6dc82f6 100644
--- a/cc_bindings_from_rs/test/primitive_types/BUILD
+++ b/cc_bindings_from_rs/test/primitive_types/BUILD
@@ -17,7 +17,7 @@
     aspect_hints = [
         "//features:experimental",
     ],
-    deps = ["@crate_index//:libc"],
+    deps = ["@crate_index//:libc"],  # v0_2
 )
 
 cc_bindings_from_rust(
@@ -31,6 +31,6 @@
     srcs = ["primitive_types_test.cc"],
     deps = [
         ":primitive_types_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/str/BUILD b/cc_bindings_from_rs/test/str/BUILD
index 42094db..0cc69ed 100644
--- a/cc_bindings_from_rs/test/str/BUILD
+++ b/cc_bindings_from_rs/test/str/BUILD
@@ -38,6 +38,6 @@
     srcs = ["str_test.cc"],
     deps = [
         ":str_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/str/str_cc_api.h b/cc_bindings_from_rs/test/str/str_cc_api.h
index 51d8273..24e5bb7 100644
--- a/cc_bindings_from_rs/test/str/str_cc_api.h
+++ b/cc_bindings_from_rs/test/str/str_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // str_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_STR_STR_GOLDEN
diff --git a/cc_bindings_from_rs/test/str/str_cc_api_impl.rs b/cc_bindings_from_rs/test/str/str_cc_api_impl.rs
index dfcf3b3..bca4ad3 100644
--- a/cc_bindings_from_rs/test/str/str_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/str/str_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // str_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/structs/BUILD b/cc_bindings_from_rs/test/structs/BUILD
index 8a14be2..b5c2385 100644
--- a/cc_bindings_from_rs/test/structs/BUILD
+++ b/cc_bindings_from_rs/test/structs/BUILD
@@ -47,6 +47,6 @@
     srcs = ["structs_test.cc"],
     deps = [
         ":structs_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/structs/structs_cc_api.h b/cc_bindings_from_rs/test/structs/structs_cc_api.h
index 8784a1c..770d29f 100644
--- a/cc_bindings_from_rs/test/structs/structs_cc_api.h
+++ b/cc_bindings_from_rs/test/structs/structs_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // structs_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_STRUCTS_STRUCTS_GOLDEN
diff --git a/cc_bindings_from_rs/test/structs/structs_cc_api_impl.rs b/cc_bindings_from_rs/test/structs/structs_cc_api_impl.rs
index 86855ee..ab370f7 100644
--- a/cc_bindings_from_rs/test/structs/structs_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/structs/structs_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // structs_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/structs/tuple_structs/BUILD b/cc_bindings_from_rs/test/structs/tuple_structs/BUILD
index 4adefcf..9557f31 100644
--- a/cc_bindings_from_rs/test/structs/tuple_structs/BUILD
+++ b/cc_bindings_from_rs/test/structs/tuple_structs/BUILD
@@ -50,6 +50,6 @@
     srcs = ["tuple_structs_test.cc"],
     deps = [
         ":tuple_structs_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/structs/tuple_structs/tuple_structs_cc_api.h b/cc_bindings_from_rs/test/structs/tuple_structs/tuple_structs_cc_api.h
index d31e536..389426e 100644
--- a/cc_bindings_from_rs/test/structs/tuple_structs/tuple_structs_cc_api.h
+++ b/cc_bindings_from_rs/test/structs/tuple_structs/tuple_structs_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // tuple_structs_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_STRUCTS_TUPLE_STRUCTS_TUPLE_STRUCTS_GOLDEN
diff --git a/cc_bindings_from_rs/test/structs/tuple_structs/tuple_structs_cc_api_impl.rs b/cc_bindings_from_rs/test/structs/tuple_structs/tuple_structs_cc_api_impl.rs
index 2d2acc4..3d14c64 100644
--- a/cc_bindings_from_rs/test/structs/tuple_structs/tuple_structs_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/structs/tuple_structs/tuple_structs_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // tuple_structs_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/structs/unit_structs/BUILD b/cc_bindings_from_rs/test/structs/unit_structs/BUILD
index 18321b8..e8d6940 100644
--- a/cc_bindings_from_rs/test/structs/unit_structs/BUILD
+++ b/cc_bindings_from_rs/test/structs/unit_structs/BUILD
@@ -47,6 +47,6 @@
     srcs = ["unit_structs_test.cc"],
     deps = [
         ":unit_structs_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/structs/unit_structs/unit_structs_cc_api.h b/cc_bindings_from_rs/test/structs/unit_structs/unit_structs_cc_api.h
index dd8cee1..70f5184 100644
--- a/cc_bindings_from_rs/test/structs/unit_structs/unit_structs_cc_api.h
+++ b/cc_bindings_from_rs/test/structs/unit_structs/unit_structs_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // unit_structs_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_STRUCTS_UNIT_STRUCTS_UNIT_STRUCTS_GOLDEN
diff --git a/cc_bindings_from_rs/test/structs/unit_structs/unit_structs_cc_api_impl.rs b/cc_bindings_from_rs/test/structs/unit_structs/unit_structs_cc_api_impl.rs
index dae6529..5f9d75d 100644
--- a/cc_bindings_from_rs/test/structs/unit_structs/unit_structs_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/structs/unit_structs/unit_structs_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // unit_structs_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/tuples/BUILD b/cc_bindings_from_rs/test/tuples/BUILD
index 3aacfc2..36cbe4a 100644
--- a/cc_bindings_from_rs/test/tuples/BUILD
+++ b/cc_bindings_from_rs/test/tuples/BUILD
@@ -41,6 +41,6 @@
     srcs = ["tuples_test.cc"],
     deps = [
         ":tuples_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/type_aliases/BUILD b/cc_bindings_from_rs/test/type_aliases/BUILD
index 4955004..ea9e962 100644
--- a/cc_bindings_from_rs/test/type_aliases/BUILD
+++ b/cc_bindings_from_rs/test/type_aliases/BUILD
@@ -29,6 +29,6 @@
     srcs = ["type_aliases_test.cc"],
     deps = [
         ":type_aliases_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/unions/BUILD b/cc_bindings_from_rs/test/unions/BUILD
index 6dab82d..85b4bec 100644
--- a/cc_bindings_from_rs/test/unions/BUILD
+++ b/cc_bindings_from_rs/test/unions/BUILD
@@ -47,6 +47,6 @@
     srcs = ["unions_test.cc"],
     deps = [
         ":unions_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/unions/unions_cc_api.h b/cc_bindings_from_rs/test/unions/unions_cc_api.h
index 748ec7b..4c992cc 100644
--- a/cc_bindings_from_rs/test/unions/unions_cc_api.h
+++ b/cc_bindings_from_rs/test/unions/unions_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // unions_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_UNIONS_UNIONS_GOLDEN
diff --git a/cc_bindings_from_rs/test/unions/unions_cc_api_impl.rs b/cc_bindings_from_rs/test/unions/unions_cc_api_impl.rs
index 9488413..e7307c0 100644
--- a/cc_bindings_from_rs/test/unions/unions_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/unions/unions_cc_api_impl.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // unions_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)]
 #![allow(improper_ctypes_definitions)]
diff --git a/cc_bindings_from_rs/test/unwinding/BUILD b/cc_bindings_from_rs/test/unwinding/BUILD
index 402e0b7..afa0a56 100644
--- a/cc_bindings_from_rs/test/unwinding/BUILD
+++ b/cc_bindings_from_rs/test/unwinding/BUILD
@@ -27,6 +27,6 @@
     srcs = ["unwinding_test.cc"],
     deps = [
         ":panic_function_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/cc_bindings_from_rs/test/uses/BUILD b/cc_bindings_from_rs/test/uses/BUILD
index 3ca4bc6..1880d0d 100644
--- a/cc_bindings_from_rs/test/uses/BUILD
+++ b/cc_bindings_from_rs/test/uses/BUILD
@@ -36,6 +36,6 @@
     srcs = ["uses_test.cc"],
     deps = [
         ":uses_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/common/BUILD b/common/BUILD
index 89da2d6..d1c0fbb 100644
--- a/common/BUILD
+++ b/common/BUILD
@@ -24,7 +24,7 @@
     name = "arc_anyhow",
     srcs = ["arc_anyhow.rs"],
     deps = [
-        "@crate_index//:anyhow",
+        "@crate_index//:anyhow",  # v1
     ],
 )
 
@@ -53,9 +53,9 @@
         ":annotation_reader",
         ":status_test_matchers",
         ":string_view_conversion",
+        "//testing/base/public:gunit_main",
         "@abseil-cpp//absl/log:check",
         "@abseil-cpp//absl/strings:string_view",
-        "@googletest//:gtest_main",
         "@llvm-project//clang:testing",
     ],
 )
@@ -74,11 +74,11 @@
     deps = [
         ":arc_anyhow",
         "//common:dyn_format",
-        "@crate_index//:heck",
-        "@crate_index//:phf",
+        "@crate_index//:heck",  # v0_5
+        "@crate_index//:phf",  # v0_11
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
         "@crate_index//:unicode-ident",
     ],
 )
@@ -94,7 +94,7 @@
         ":token_stream_matchers",
         ":token_stream_printer",
         "@crate_index//:googletest",
-        "@crate_index//:itertools",
+        "@crate_index//:itertools",  # v0_13
     ],
 )
 
@@ -102,8 +102,8 @@
     name = "crubit_feature",
     srcs = ["crubit_feature.rs"],
     deps = [
-        "@crate_index//:flagset",
-        "@crate_index//:serde",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:serde",  # v1
     ],
 )
 
@@ -111,7 +111,7 @@
     name = "dyn_format",
     srcs = ["dyn_format.rs"],
     deps = [
-        "@crate_index//:anyhow",
+        "@crate_index//:anyhow",  # v1
     ],
 )
 
@@ -129,7 +129,7 @@
     crate = ":crubit_feature",
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:serde_json",
+        "@crate_index//:serde_json",  # v1
     ],
 )
 
@@ -161,7 +161,7 @@
     name = "interner",
     srcs = ["interner.rs"],
     deps = [
-        "@crate_index//:bumpalo",
+        "@crate_index//:bumpalo",  # v3
     ],
 )
 
@@ -170,7 +170,7 @@
     srcs = ["interner_test.rs"],
     deps = [
         ":interner",
-        "@crate_index//:bumpalo",
+        "@crate_index//:bumpalo",  # v3
         "@crate_index//:googletest",
     ],
 )
@@ -228,8 +228,8 @@
     ],
     deps = [
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -279,8 +279,8 @@
     deps = [
         ":token_stream_matchers_fastpath",
         ":token_stream_printer",
-        "@crate_index//:anyhow",
-        "@crate_index//:indenter",
+        "@crate_index//:anyhow",  # v1
+        "@crate_index//:indenter",  # v0_3
         "@crate_index//:proc-macro2",
     ],
 )
@@ -295,7 +295,7 @@
     ],
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -306,7 +306,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":token_stream_printer",
-        "@crate_index//:anyhow",
+        "@crate_index//:anyhow",  # v1
         "@crate_index//:proc-macro2",
     ],
 )
@@ -318,7 +318,7 @@
     ],
     deps = [
         "//common:ffi_types",
-        "@crate_index//:anyhow",
+        "@crate_index//:anyhow",  # v1
         "@crate_index//:proc-macro2",
     ],
 )
@@ -332,8 +332,8 @@
     ],
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:quote",
-        "@crate_index//:tempfile",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:tempfile",  # v3
     ],
 )
 
@@ -384,7 +384,7 @@
     deps = [
         ":arc_anyhow",
         ":error_report",
-        "@crate_index//:anyhow",
+        "@crate_index//:anyhow",  # v1
     ],
 )
 
@@ -396,7 +396,7 @@
         ":error_report",
         ":errors",
         "@crate_index//:googletest",
-        "@crate_index//:serde_json",
+        "@crate_index//:serde_json",  # v1
     ],
 )
 
@@ -406,10 +406,10 @@
     visibility = ["//:__subpackages__"],
     deps = [
         ":arc_anyhow",
-        "@crate_index//:anyhow",
-        "@crate_index//:regex",
-        "@crate_index//:serde",
-        "@crate_index//:serde_json",
+        "@crate_index//:anyhow",  # v1
+        "@crate_index//:regex",  # v1
+        "@crate_index//:serde",  # v1
+        "@crate_index//:serde_json",  # v1
     ],
 )
 
@@ -418,7 +418,7 @@
     crate = ":error_report",
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:serde_json",
+        "@crate_index//:serde_json",  # v1
     ],
 )
 
@@ -430,7 +430,7 @@
     ],
     deps = [
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
diff --git a/common/test/bidirectional_deps/BUILD b/common/test/bidirectional_deps/BUILD
index 5438faa..b7b3cc1 100644
--- a/common/test/bidirectional_deps/BUILD
+++ b/common/test/bidirectional_deps/BUILD
@@ -88,6 +88,6 @@
     deps = [
         ":leaf_cc_lib",
         ":middle_rs_lib_cc_api",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/common/test/bidirectional_deps/leaf_rs_lib_cc_api.h b/common/test/bidirectional_deps/leaf_rs_lib_cc_api.h
index 6fd159b..ad0f721 100644
--- a/common/test/bidirectional_deps/leaf_rs_lib_cc_api.h
+++ b/common/test/bidirectional_deps/leaf_rs_lib_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // leaf_rs_lib_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_COMMON_TEST_BIDIRECTIONAL_DEPS_LEAF_RS_LIB_GOLDEN
diff --git a/common/token_stream_printer.rs b/common/token_stream_printer.rs
index a0ab8ac..6d4973f 100644
--- a/common/token_stream_printer.rs
+++ b/common/token_stream_printer.rs
@@ -74,16 +74,20 @@
 /// Like `tokens_to_string` but also runs the result through `rustfmt`.
 pub fn rs_tokens_to_formatted_string(
     tokens: TokenStream,
-    config: &RustfmtConfig,
+    config: Option<&RustfmtConfig>,
 ) -> Result<String> {
-    let tokens_string = tokens_to_string(tokens)?
+    let mut tokens_string = tokens_to_string(tokens)?
         // NOTE: This is a terrible hack. `rustfmt` became more strict about appearances of `...`
         // (the `DotDotDot` token) at some point in the past. This is not a precise or general
         // solution, but rewriting this token to a comment produces formattable code in some cases,
         // making test failure messages better.
         .replace("...", "/*...*/");
-    let err = format!("Failed to rustfmt the following Rust tokens:\n\n{tokens_string}");
-    rustfmt(tokens_string, config).context(err)
+    if let Some(config) = config {
+        tokens_string = rustfmt(tokens_string.clone(), config).with_context(|| {
+            format!("Failed to rustfmt the following Rust tokens:\n\n{tokens_string}")
+        })?;
+    }
+    Ok(tokens_string)
 }
 
 /// Like `rs_tokens_to_formatted_string`, but always using a Crubit-internal,
@@ -91,15 +95,19 @@
 /// should support custom `rustfmt.toml` and take the path to `rustfmt` binary
 /// as a cmdline argument.
 pub fn rs_tokens_to_formatted_string_for_tests(input: TokenStream) -> Result<String> {
-    rs_tokens_to_formatted_string(input, &RustfmtConfig::for_testing())
+    rs_tokens_to_formatted_string(input, Some(&RustfmtConfig::for_testing()))
 }
 
 /// Like `tokens_to_string` but also runs the result through `clang-format`.
 pub fn cc_tokens_to_formatted_string(
     tokens: TokenStream,
-    clang_format_exe_path: &Path,
+    clang_format_exe_path: Option<&Path>,
 ) -> Result<String> {
-    clang_format(tokens_to_string(tokens)?, clang_format_exe_path)
+    let mut result = tokens_to_string(tokens)?;
+    if let Some(clang_format_exe_path) = clang_format_exe_path {
+        result = clang_format(result, clang_format_exe_path)?;
+    }
+    Ok(result)
 }
 
 /// Like `cc_tokens_to_formatted_string`, but always using a hardcoded path to
@@ -470,7 +478,7 @@
             fn bar() {}
             fn foo(x: i32, y: i32) -> i32 { x + y }
         };
-        let output = rs_tokens_to_formatted_string(input, &cfg).unwrap();
+        let output = rs_tokens_to_formatted_string(input, Some(&cfg)).unwrap();
         assert_eq!(
             output,
             r#"fn bar() {}
@@ -498,7 +506,7 @@
             fn foo(x: i32, y: i32) -> i32 { x + y }
         };
 
-        let output = rs_tokens_to_formatted_string(input, &cfg).unwrap();
+        let output = rs_tokens_to_formatted_string(input, Some(&cfg)).unwrap();
         assert_eq!(
             output,
             r#"fn bar() {}
diff --git a/docs/index.md b/docs/index.md
index 4e60665..9ec01f4 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,3 +1,4 @@
 <!-- text located in README.md for GitHub and basic source viewing. -->
 
-<!--#include file="/README.md"-->
+{{#include ../README.md}}
+
diff --git a/examples/cpp/enum/example_generated.rs b/examples/cpp/enum/example_generated.rs
index bc0c5f4..994fbfa 100644
--- a/examples/cpp/enum/example_generated.rs
+++ b/examples/cpp/enum/example_generated.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //examples/cpp/enum:example_lib
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, register_tool)]
diff --git a/examples/cpp/function/example_generated.rs b/examples/cpp/function/example_generated.rs
index 0fa2220..7bd1e4a 100644
--- a/examples/cpp/function/example_generated.rs
+++ b/examples/cpp/function/example_generated.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //examples/cpp/function:example_lib
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes)]
diff --git a/examples/cpp/method/example_generated.rs b/examples/cpp/method/example_generated.rs
index a4a8af1..108c723 100644
--- a/examples/cpp/method/example_generated.rs
+++ b/examples/cpp/method/example_generated.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //examples/cpp/method:example_lib
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -32,18 +32,19 @@
 
     // Generated from: examples/cpp/method/example.h;l=12
     // Error while generating bindings for constructor 'Bar::Bar':
+    // Default constructors do yet receive bindings. See b/452726517.
     // Expected first constructor parameter to be a mutable reference, got: *mut crate::foo::Bar
     // Expected first reference parameter `__this` to have a lifetime, found *mut crate::foo::Bar
 
     // Generated from: examples/cpp/method/example.h;l=12
     // Error while generating bindings for constructor 'Bar::Bar':
-    // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+    // Move and copy constructors do yet receive bindings. See b/452726517.
     // Expected first constructor parameter to be a mutable reference, got: *mut crate::foo::Bar
     // Expected first reference parameter `__this` to have a lifetime, found *mut crate::foo::Bar
 
     // Generated from: examples/cpp/method/example.h;l=12
     // Error while generating bindings for constructor 'Bar::Bar':
-    // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+    // Move and copy constructors do yet receive bindings. See b/452726517.
     // Expected first constructor parameter to be a mutable reference, got: *mut crate::foo::Bar
     // Expected first reference parameter `__this` to have a lifetime, found *mut crate::foo::Bar
 
diff --git a/examples/cpp/trivial_abi_struct/example_generated.rs b/examples/cpp/trivial_abi_struct/example_generated.rs
index 30daef3..0e00d90 100644
--- a/examples/cpp/trivial_abi_struct/example_generated.rs
+++ b/examples/cpp/trivial_abi_struct/example_generated.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //examples/cpp/trivial_abi_struct:example_lib
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -35,12 +35,13 @@
 
 // Generated from: examples/cpp/trivial_abi_struct/example.h;l=12
 // Error while generating bindings for constructor 'Position::Position':
+// Default constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::Position
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::Position
 
 // Generated from: examples/cpp/trivial_abi_struct/example.h;l=12
 // Error while generating bindings for constructor 'Position::Position':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::Position
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::Position
 
diff --git a/examples/cpp/trivial_struct/example_generated.rs b/examples/cpp/trivial_struct/example_generated.rs
index 6561f68..42e34d2 100644
--- a/examples/cpp/trivial_struct/example_generated.rs
+++ b/examples/cpp/trivial_struct/example_generated.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //examples/cpp/trivial_struct:example_lib
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -32,18 +32,19 @@
 
 // Generated from: examples/cpp/trivial_struct/example.h;l=8
 // Error while generating bindings for constructor 'Position::Position':
+// Default constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::Position
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::Position
 
 // Generated from: examples/cpp/trivial_struct/example.h;l=8
 // Error while generating bindings for constructor 'Position::Position':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::Position
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::Position
 
 // Generated from: examples/cpp/trivial_struct/example.h;l=8
 // Error while generating bindings for constructor 'Position::Position':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::Position
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::Position
 
diff --git a/examples/cpp/unsafe_attributes/example_generated.rs b/examples/cpp/unsafe_attributes/example_generated.rs
index 22f288c..d24f909 100644
--- a/examples/cpp/unsafe_attributes/example_generated.rs
+++ b/examples/cpp/unsafe_attributes/example_generated.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //examples/cpp/unsafe_attributes:example_lib
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes)]
diff --git a/examples/rust/cpp_enum/BUILD b/examples/rust/cpp_enum/BUILD
index 297b9c7..b227fe4 100644
--- a/examples/rust/cpp_enum/BUILD
+++ b/examples/rust/cpp_enum/BUILD
@@ -23,7 +23,7 @@
         "//support:crubit_annotate",
     ],
     deps = [
-        "@crate_index//:open_enum",
+        "@crate_index//:open_enum",  # v0_5
     ],
 )
 
diff --git a/examples/rust/cpp_enum/example_generated.h b/examples/rust/cpp_enum/example_generated.h
index d13434a..5ae878b 100644
--- a/examples/rust/cpp_enum/example_generated.h
+++ b/examples/rust/cpp_enum/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_CPP_ENUM_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/enum/example_generated.h b/examples/rust/enum/example_generated.h
index 948316f..488c006 100644
--- a/examples/rust/enum/example_generated.h
+++ b/examples/rust/enum/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_ENUM_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/function/example_generated.h b/examples/rust/function/example_generated.h
index 74111ef..cfc87ad 100644
--- a/examples/rust/function/example_generated.h
+++ b/examples/rust/function/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_FUNCTION_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/library_config/example_generated.h b/examples/rust/library_config/example_generated.h
index 81e3c8c..5536c4d 100644
--- a/examples/rust/library_config/example_generated.h
+++ b/examples/rust/library_config/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_LIBRARY_CONFIG_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/non_trivial_struct/example_generated.h b/examples/rust/non_trivial_struct/example_generated.h
index c758610..a880b34 100644
--- a/examples/rust/non_trivial_struct/example_generated.h
+++ b/examples/rust/non_trivial_struct/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_NON_TRIVIAL_STRUCT_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/rust_union/example_generated.h b/examples/rust/rust_union/example_generated.h
index 2674217..e1841b3 100644
--- a/examples/rust/rust_union/example_generated.h
+++ b/examples/rust/rust_union/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_RUST_UNION_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/struct/example_generated.h b/examples/rust/struct/example_generated.h
index a563b0b..db3d9b4 100644
--- a/examples/rust/struct/example_generated.h
+++ b/examples/rust/struct/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_STRUCT_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/type_alias/example_generated.h b/examples/rust/type_alias/example_generated.h
index 6311af5..1b5318c 100644
--- a/examples/rust/type_alias/example_generated.h
+++ b/examples/rust/type_alias/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_TYPE_ALIAS_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/union/example_generated.h b/examples/rust/union/example_generated.h
index d62d586..ee2f182 100644
--- a/examples/rust/union/example_generated.h
+++ b/examples/rust/union/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_UNION_EXAMPLE_CRATE_GOLDEN
diff --git a/examples/rust/use_declaration/example_generated.h b/examples/rust/use_declaration/example_generated.h
index 328e9ec..54d47ab 100644
--- a/examples/rust/use_declaration/example_generated.h
+++ b/examples/rust/use_declaration/example_generated.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // example_crate_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_USE_DECLARATION_EXAMPLE_CRATE_GOLDEN
diff --git a/features/BUILD b/features/BUILD
index f940da7..593b789 100644
--- a/features/BUILD
+++ b/features/BUILD
@@ -23,11 +23,15 @@
 # See <internal link>
 crubit_feature_hint(
     name = "supported",
+    compatible_with = ["//buildenv/target:non_prod"],
     crubit_features = SUPPORTED_FEATURES,
     visibility = ["//visibility:public"],
 )
 
-_WRAPPER_FEATURES = ["wrapper"]
+_WRAPPER_FEATURES = [
+    "wrapper",
+    "non_unpin_ctor",
+]
 
 # A feature set containing wrapper-library Crubit features, in addition to the officially supported
 # features.
@@ -35,6 +39,7 @@
 # See <internal link>
 crubit_feature_hint(
     name = "wrapper",
+    compatible_with = ["//buildenv/target:non_prod"],
     crubit_features = SUPPORTED_FEATURES + _WRAPPER_FEATURES,
     visibility = _EXPERIMENTAL_CLIENTS,
 )
@@ -44,6 +49,7 @@
 # See <internal link>
 crubit_feature_hint(
     name = "infer_operator_lifetimes",
+    compatible_with = ["//buildenv/target:non_prod"],
     crubit_features = SUPPORTED_FEATURES + ["infer_operator_lifetimes"],
     visibility = ["//visibility:public"],
 )
@@ -53,6 +59,7 @@
 # See <internal link>
 crubit_feature_hint(
     name = "std_vector",
+    compatible_with = ["//buildenv/target:non_prod"],
     crubit_features = SUPPORTED_FEATURES,
     visibility = ["//visibility:public"],
 )
@@ -62,16 +69,28 @@
 # See <internal link>
 crubit_feature_hint(
     name = "std_unique_ptr",
+    compatible_with = ["//buildenv/target:non_prod"],
     crubit_features = SUPPORTED_FEATURES + ["std_unique_ptr"],
     visibility = ["//visibility:public"],
 )
 
+# A feature set which specifically enables non-`Unpin` type handling with the `ctor` crate.
+#
+# See <internal link>
+crubit_feature_hint(
+    name = "non_unpin_ctor",
+    compatible_with = ["//buildenv/target:non_prod"],
+    crubit_features = SUPPORTED_FEATURES + ["non_unpin_ctor"],
+    visibility = ["//visibility:public"],
+)
+
 # A feature set containing experimental Crubit features, in addition to the officially supported
 # features.
 #
 # See <internal link>
 crubit_feature_hint(
     name = "experimental",
+    compatible_with = ["//buildenv/target:non_prod"],
     # TODO(b/409128537): Add _WRAPPER_FEATURES on next binary release.
     crubit_features = ["all"],
     visibility = _EXPERIMENTAL_CLIENTS,
diff --git a/features/global_features.bzl b/features/global_features.bzl
index c56c638..77aee12 100644
--- a/features/global_features.bzl
+++ b/features/global_features.bzl
@@ -10,4 +10,5 @@
     "supported",
     "std_vector",
     "std_unique_ptr",
+    "do_not_hardcode_status_bridge",
 ]
diff --git a/features/internal/BUILD b/features/internal/BUILD
index 3926a26..da6c984 100644
--- a/features/internal/BUILD
+++ b/features/internal/BUILD
@@ -11,6 +11,7 @@
 # A feature set with a stable expansion, only for use in Bazel unit tests.
 crubit_feature_hint(
     name = "testonly_supported",
+    compatible_with = ["//buildenv/target:non_prod"],
     crubit_features = ["supported"],
     visibility = ["//:__subpackages__"],
 )
@@ -18,6 +19,7 @@
 # A feature set with a stable expansion, only for use in Bazel unit tests.
 crubit_feature_hint(
     name = "testonly_experimental",
+    compatible_with = ["//buildenv/target:non_prod"],
     crubit_features = ["experimental"],
     visibility = ["//:__subpackages__"],
 )
diff --git a/lifetime_analysis/BUILD b/lifetime_analysis/BUILD
index 291b4b1..c0dc9b2 100644
--- a/lifetime_analysis/BUILD
+++ b/lifetime_analysis/BUILD
@@ -149,7 +149,7 @@
         "//lifetime_annotations",
         "//lifetime_annotations:lifetime",
         "//lifetime_annotations/test:run_on_code",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
         "@llvm-project//clang:ast",
     ],
 )
@@ -180,7 +180,7 @@
         "//lifetime_annotations",
         "//lifetime_annotations:lifetime",
         "//lifetime_annotations/test:run_on_code",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
         "@llvm-project//clang:ast",
         "@llvm-project//clang:ast_matchers",
     ],
@@ -223,7 +223,7 @@
         ":pointer_compatibility",
         "//lifetime_annotations",
         "//lifetime_annotations/test:run_on_code",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
         "@llvm-project//clang:ast",
         "@llvm-project//clang:ast_matchers",
         "@llvm-project//llvm:Support",
diff --git a/lifetime_analysis/test/BUILD b/lifetime_analysis/test/BUILD
index 967c703..35056db 100644
--- a/lifetime_analysis/test/BUILD
+++ b/lifetime_analysis/test/BUILD
@@ -31,7 +31,7 @@
     srcs = ["builtin.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -40,7 +40,7 @@
     srcs = ["lifetime_params.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -49,7 +49,7 @@
     srcs = ["virtual_functions.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -58,7 +58,7 @@
     srcs = ["casts.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -67,7 +67,7 @@
     srcs = ["callbacks.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -76,7 +76,7 @@
     srcs = ["initializers.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -85,7 +85,7 @@
     srcs = ["recursion.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -94,7 +94,7 @@
     srcs = ["function_templates.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -103,7 +103,7 @@
     srcs = ["function_calls.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -112,7 +112,7 @@
     srcs = ["execution_order.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -121,7 +121,7 @@
     srcs = ["control_flow.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -130,7 +130,7 @@
     srcs = ["basic.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -139,7 +139,7 @@
     srcs = ["static_lifetime.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -148,7 +148,7 @@
     srcs = ["arrays.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -157,7 +157,7 @@
     srcs = ["records.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -166,7 +166,7 @@
     srcs = ["inheritance.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -175,7 +175,7 @@
     srcs = ["class_templates.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -184,7 +184,7 @@
     srcs = ["initialization.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -193,7 +193,7 @@
     srcs = ["expr.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -202,6 +202,6 @@
     srcs = ["defaulted_functions.cc"],
     deps = [
         ":lifetime_analysis_test",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/lifetime_annotations/BUILD b/lifetime_annotations/BUILD
index 69ca864..0018b3f 100644
--- a/lifetime_annotations/BUILD
+++ b/lifetime_annotations/BUILD
@@ -28,7 +28,7 @@
     srcs = ["lifetime_test.cc"],
     deps = [
         ":lifetime",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -86,11 +86,11 @@
         "//common:status_test_matchers",
         "//lifetime_annotations/test:named_func_lifetimes",
         "//lifetime_annotations/test:run_on_code",
+        "//testing/base/public:gunit_main",
         "@abseil-cpp//absl/status",
         "@abseil-cpp//absl/status:statusor",
         "@abseil-cpp//absl/strings",
         "@abseil-cpp//absl/strings:str_format",
-        "@googletest//:gtest_main",
         "@llvm-project//clang:ast",
         "@llvm-project//clang:ast_matchers",
         "@llvm-project//clang:tooling",
@@ -115,7 +115,7 @@
     deps = [
         ":lifetime",
         ":lifetime_substitutions",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -135,7 +135,7 @@
     deps = [
         ":lifetime",
         ":lifetime_symbol_table",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
diff --git a/lifetime_annotations/type_lifetimes.cc b/lifetime_annotations/type_lifetimes.cc
index 93f54c9..924f689 100644
--- a/lifetime_annotations/type_lifetimes.cc
+++ b/lifetime_annotations/type_lifetimes.cc
@@ -759,7 +759,7 @@
   } else if (auto record_type_loc = type_loc.getAs<clang::RecordTypeLoc>()) {
     if (auto specialization_decl =
             clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(
-                record_type_loc.getOriginalDecl())) {
+                record_type_loc.getDecl())) {
       if (specialization_decl->getTemplateArgsAsWritten()) {
         return GetTemplateArgs(
             specialization_decl->getTemplateArgsAsWritten()->arguments());
diff --git a/migrator/rs_from_cc/BUILD b/migrator/rs_from_cc/BUILD
index d6f9bcc..b7402c7 100644
--- a/migrator/rs_from_cc/BUILD
+++ b/migrator/rs_from_cc/BUILD
@@ -71,9 +71,9 @@
     deps = [
         ":rs_from_cc_lib",
         "//common:status_test_matchers",
+        "//testing/base/public:gunit_main",
         "@abseil-cpp//absl/status",
         "@abseil-cpp//absl/strings:string_view",
-        "@googletest//:gtest_main",
         "@llvm-project//clang:ast",
     ],
 )
diff --git a/nullability/BUILD b/nullability/BUILD
index f8d4616..60dbbbe 100644
--- a/nullability/BUILD
+++ b/nullability/BUILD
@@ -270,11 +270,11 @@
     hdrs = ["proto_matchers.h"],
     visibility = [":__subpackages__"],
     deps = [
+        "//third_party/protobuf",
         "@abseil-cpp//absl/base:nullability",
         "@llvm-project//llvm:Support",
         "@llvm-project//third-party/unittest:gmock",
         "@llvm-project//third-party/unittest:gtest",
-        "@protobuf",
     ],
 )
 
@@ -332,6 +332,7 @@
         "@llvm-project//clang:ast",
         "@llvm-project//clang:ast_matchers",
         "@llvm-project//clang:basic",
+        "@llvm-project//llvm:Support",
     ],
 )
 
diff --git a/nullability/annotations.h b/nullability/annotations.h
index 6da4f03..159a9a8 100644
--- a/nullability/annotations.h
+++ b/nullability/annotations.h
@@ -16,6 +16,8 @@
 inline constexpr llvm::StringLiteral AbslMacroNonnull = "absl_nonnull";
 inline constexpr llvm::StringLiteral AbslMacroUnknown =
     "absl_nullability_unknown";
+inline constexpr llvm::StringLiteral AbslMacroConflict =
+    "absl_nullability_conflict";
 }  // namespace clang::tidy::nullability
 
 #endif  // THIRD_PARTY_CRUBIT_NULLABILITY_ANNOTATIONS_H_
diff --git a/nullability/inference/BUILD b/nullability/inference/BUILD
index c1057eb..e2d1e6f 100644
--- a/nullability/inference/BUILD
+++ b/nullability/inference/BUILD
@@ -85,12 +85,12 @@
         ":inference_cc_proto",
         ":merge",
         "//nullability:proto_matchers",
+        "//third_party/protobuf",
         "@abseil-cpp//absl/log:check",
         "@llvm-project//llvm:Support",
         "@llvm-project//third-party/unittest:gmock",
         "@llvm-project//third-party/unittest:gtest",
         "@llvm-project//third-party/unittest:gtest_main",
-        "@protobuf",
     ],
 )
 
@@ -99,6 +99,7 @@
     srcs = ["inferable.cc"],
     hdrs = ["inferable.h"],
     deps = [
+        ":inference_cc_proto",
         "//nullability:type_nullability",
         "@llvm-project//clang:ast",
         "@llvm-project//clang:basic",
@@ -116,6 +117,7 @@
         "@llvm-project//clang:basic",
         "@llvm-project//clang:testing",
         "@llvm-project//llvm:Support",
+        "@llvm-project//third-party/unittest:gmock",
         "@llvm-project//third-party/unittest:gtest",
         "@llvm-project//third-party/unittest:gtest_main",
     ],
diff --git a/nullability/inference/collect_evidence.cc b/nullability/inference/collect_evidence.cc
index 6015dd9..23f90c2 100644
--- a/nullability/inference/collect_evidence.cc
+++ b/nullability/inference/collect_evidence.cc
@@ -141,19 +141,19 @@
         // Filter for Nullability relevance. Optimization note: we filter
         // *after* calling getOverridden on the assumption that, for irrelevant
         // methods, it is cheaper, on average, to call `getOverridden` than
-        // `countInferableSlots`. But, no data informed this choice.
-        int SlotCount = countInferableSlots(*MD);
+        // `getInferableSlotIndices`. But, no data informed this choice.
+        llvm::SmallVector<int> SlotIndices = getInferableSlotIndices(*MD);
         // No slots -> irrelevant method.
-        if (SlotCount == 0) return true;
+        if (SlotIndices.empty()) return true;
 
         std::string_view USR = getOrGenerateUSR(USRCache, *MD);
         if (USR.empty()) return true;
 
         for (auto &O : Overridden) {
           auto &S = Index.Overrides[O.getKey()];
-          // SlotCount of MD must equal that of any methods it overrides, so we
-          // can use it set their SlotCount.
-          S.SlotCount = SlotCount;
+          // MD must have the same inferable slot indices as any methods it
+          // overrides, so we can set their SlotIndices from MD's.
+          S.SlotIndices = SlotIndices;
           S.OverridingUSRs.insert(USR);
         }
         Index.Bases[USR] = std::move(Overridden);
@@ -511,6 +511,7 @@
     std::vector<std::string_view> Targets =
         getAdditionalTargetsForVirtualMethod(
             E.symbol().usr(), E.kind(), E.slot() == SLOT_RETURN_TYPE, Index);
+    *E.mutable_propagated_from() = E.symbol();
     for (std::string_view USR : Targets) {
       E.mutable_symbol()->set_usr(USR);
       Emit(E);
diff --git a/nullability/inference/collect_evidence.h b/nullability/inference/collect_evidence.h
index 89a61ca..b3e9406 100644
--- a/nullability/inference/collect_evidence.h
+++ b/nullability/inference/collect_evidence.h
@@ -7,6 +7,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <ostream>
 #include <utility>
 #include <vector>
 
@@ -22,10 +23,15 @@
 #include "clang/Analysis/FlowSensitive/Solver.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang::tidy::nullability {
@@ -51,9 +57,23 @@
 // Summarizes a method for the purpose of aligning evidence across
 // virtual-method overrides.
 struct MethodSummary {
-  int SlotCount;
+  llvm::SmallVector<int> SlotIndices;
   // USRs of all methods that (transitively) override this method.
   llvm::StringSet<> OverridingUSRs;
+
+  // Enable llvm StringMap testing utilities to print MethodSummary to ease
+  // debugging of tests.
+  friend std::ostream& operator<<(std::ostream& OS,
+                                  const MethodSummary& Summary) {
+    OS << "{\nSlotIndices: {"
+       << llvm::join(
+              llvm::map_range(Summary.SlotIndices,
+                              [](int Index) { return llvm::to_string(Index); }),
+              ", ")
+       << "},\n";
+    return OS << "OverridingUSRs: {"
+              << llvm::join(Summary.OverridingUSRs.keys(), ", ") << "}\n}\n";
+  }
 };
 
 struct VirtualMethodIndex {
@@ -86,7 +106,7 @@
   SortedFingerprintVector &operator=(const SortedFingerprintVector &) = delete;
   explicit SortedFingerprintVector(std::vector<SlotFingerprint> &&V)
       : Vector(std::move(V)) {
-    if (!std::is_sorted(Vector.begin(), Vector.end())) {
+    if (!llvm::is_sorted(Vector)) {
       // Performance is much improved if the incoming vector is already sorted,
       // but this is not a requirement.
       llvm::errs() << "Previous inferences are not sorted. Performance may be "
@@ -116,12 +136,12 @@
         }
       }
       // Remove the duplicates before continuing.
-      Vector.erase(std::unique(Vector.begin(), Vector.end()), Vector.end());
+      Vector.erase(llvm::unique(Vector), Vector.end());
     }
   }
 
   bool contains(SlotFingerprint Fingerprint) const {
-    return std::binary_search(Vector.begin(), Vector.end(), Fingerprint);
+    return llvm::binary_search(Vector, Fingerprint);
   }
 
  private:
diff --git a/nullability/inference/collect_evidence_test.cc b/nullability/inference/collect_evidence_test.cc
index 6fbb615..68a074c 100644
--- a/nullability/inference/collect_evidence_test.cc
+++ b/nullability/inference/collect_evidence_test.cc
@@ -31,6 +31,7 @@
 #include "clang/Testing/TestAST.h"
 #include "third_party/llvm/llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupport.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -38,7 +39,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Testing/ADT/StringMapEntry.h"
 #include "llvm/Testing/Support/Error.h"
-#include "external/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h"  // IWYU pragma: keep
+#include "external/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h"
 #include "external/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
 
 namespace clang::tidy::nullability {
@@ -84,11 +85,6 @@
 #define CHECK_NE(A, B) (A, B)
 )cc";
 
-MATCHER_P3(isEvidenceMatcher, SlotMatcher, KindMatcher, SymbolMatcher, "") {
-  return SlotMatcher.Matches(static_cast<Slot>(arg.slot())) &&
-         KindMatcher.Matches(arg.kind()) && SymbolMatcher.Matches(arg.symbol());
-}
-
 MATCHER_P(functionNamed, Name, "") {
   return llvm::StringRef(arg.usr()).contains(
       ("@" + llvm::Twine(Name) + "#").str());
@@ -127,25 +123,49 @@
              ("@F@" + llvm::Twine(FunctionName) + "#").str()) &&
          arg.usr().ends_with(("@" + llvm::Twine(VarName)).str());
 }
+}  // namespace
 
-auto localVarNamed(llvm::StringRef VarName,
-                   llvm::StringRef FunctionName = "target") {
+static auto localVarNamed(llvm::StringRef VarName,
+                          llvm::StringRef FunctionName = "target") {
   return localVarNamedImpl(VarName, FunctionName);
 }
 
-testing::Matcher<const Evidence&> evidence(
-    testing::Matcher<Slot> S, testing::Matcher<Evidence::Kind> Kind,
-    testing::Matcher<const Symbol&> SymbolMatcher = functionNamed("target")) {
-  return isEvidenceMatcher(S, Kind, SymbolMatcher);
+namespace {
+MATCHER_P3(isEvidenceMatcher, SlotMatcher, KindMatcher, SymbolMatcher, "") {
+  return SlotMatcher.Matches(static_cast<Slot>(arg.slot())) &&
+         KindMatcher.Matches(arg.kind()) && SymbolMatcher.Matches(arg.symbol());
 }
 
+MATCHER(notPropagated, "") { return !arg.has_propagated_from(); }
+
+MATCHER_P(propagatedFrom, PropagatedFromMatcher, "") {
+  return PropagatedFromMatcher.Matches(arg.propagated_from());
+}
+}  // namespace
+
+static testing::Matcher<const Evidence&> evidence(
+    testing::Matcher<Slot> S, testing::Matcher<Evidence::Kind> Kind,
+    testing::Matcher<const Symbol&> SymbolMatcher = functionNamed("target")) {
+  return AllOf(isEvidenceMatcher(S, Kind, SymbolMatcher), notPropagated());
+}
+
+static testing::Matcher<const Evidence&> evidencePropagatedFrom(
+    testing::Matcher<const Symbol&> PropagatedFromMatcher,
+    testing::Matcher<Slot> S, testing::Matcher<Evidence::Kind> Kind,
+    testing::Matcher<const Symbol&> SymbolMatcher = functionNamed("target")) {
+  return AllOf(isEvidenceMatcher(S, Kind, SymbolMatcher),
+               propagatedFrom(PropagatedFromMatcher));
+}
+
+namespace {
 enum class CollectionMode {
   kTestWithSummaries,
   kTestDirectly,
 };
+}  // namespace
 
 // Helper to get a string representation of the CollectionMode for test names.
-std::string printToString(CollectionMode Mode) {
+static std::string printToString(CollectionMode Mode) {
   switch (Mode) {
     case CollectionMode::kTestWithSummaries:
       return "WithSummaries";
@@ -155,7 +175,7 @@
   llvm_unreachable("Unknown CollectionMode");
 }
 
-std::vector<Evidence> collectFromDefinitionDirectly(
+static std::vector<Evidence> collectFromDefinitionDirectly(
     clang::TestAST& AST, const Decl& Definition,
     const NullabilityPragmas& Pragmas,
     PreviousInferences InputInferences = {}) {
@@ -173,8 +193,8 @@
   return Results;
 }
 
-llvm::Expected<CFGSummary> summarizeDefinitionNamed(llvm::StringRef TargetName,
-                                                    llvm::StringRef Source) {
+static llvm::Expected<CFGSummary> summarizeDefinitionNamed(
+    llvm::StringRef TargetName, llvm::StringRef Source) {
   USRCache UsrCache;
   NullabilityPragmas Pragmas;
   clang::TestAST AST(getAugmentedTestInputs(Source, Pragmas));
@@ -186,14 +206,14 @@
 /// Provides a default function-name-cased value for TargetName in
 /// collectEvidenceFromDefinitionNamed, which puts TargetName first for
 /// readability.
-llvm::Expected<CFGSummary> summarizeTargetFuncDefinition(
+static llvm::Expected<CFGSummary> summarizeTargetFuncDefinition(
     llvm::StringRef Source) {
   return summarizeDefinitionNamed("target", Source);
 }
 
 // Returns both an error and a vector to represent partial computations -- those
 // that fail after producing some results.
-std::pair<llvm::Error, std::vector<Evidence>>
+static std::pair<llvm::Error, std::vector<Evidence>>
 collectFromDefinitionViaSummaryWithErrors(
     clang::TestAST& AST, const Decl& Definition,
     const NullabilityPragmas& Pragmas,
@@ -222,7 +242,7 @@
           Results};
 }
 
-std::vector<Evidence> collectFromDefinitionViaSummary(
+static std::vector<Evidence> collectFromDefinitionViaSummary(
     clang::TestAST& AST, const Decl& Definition,
     const NullabilityPragmas& Pragmas, PreviousInferences InputInferences) {
   auto [Err, Results] = collectFromDefinitionViaSummaryWithErrors(
@@ -237,7 +257,7 @@
 }
 
 // Dispatcher to collect evidence based on the CollectionMode.
-std::vector<Evidence> collectFromDefinition(
+static std::vector<Evidence> collectFromDefinition(
     clang::TestAST& AST, const Decl& Definition,
     const NullabilityPragmas& Pragmas, CollectionMode Mode,
     PreviousInferences InputInferences = {}) {
@@ -252,7 +272,7 @@
   llvm_unreachable("Unexpected collection mode");
 }
 
-std::vector<Evidence> collectFromDefinitionNamed(
+static std::vector<Evidence> collectFromDefinitionNamed(
     llvm::StringRef TargetName, llvm::StringRef Source, CollectionMode Mode,
     PreviousInferences InputInferences = {}) {
   NullabilityPragmas Pragmas;
@@ -264,14 +284,14 @@
 
 /// Provides a default function-name-cased value for TargetName in
 /// collectFromDefinitionNamed, which puts TargetName first for readability.
-std::vector<Evidence> collectFromTargetFuncDefinition(
+static std::vector<Evidence> collectFromTargetFuncDefinition(
     llvm::StringRef Source, CollectionMode Mode,
     PreviousInferences InputInferences = {}) {
   return collectFromDefinitionNamed("target", Source, Mode, InputInferences);
 }
 
 template <typename MatcherT>
-std::vector<Evidence> collectFromDefinitionMatching(
+static std::vector<Evidence> collectFromDefinitionMatching(
     MatcherT Matcher, llvm::StringRef Source, CollectionMode Mode,
     PreviousInferences InputInferences = {}) {
   NullabilityPragmas Pragmas;
@@ -281,8 +301,8 @@
   return collectFromDefinition(AST, Definition, Pragmas, Mode, InputInferences);
 }
 
-std::vector<Evidence> collectFromDecl(llvm::StringRef Source,
-                                      llvm::StringRef DeclName) {
+static std::vector<Evidence> collectFromDecl(llvm::StringRef Source,
+                                             llvm::StringRef DeclName) {
   std::vector<Evidence> Results;
   NullabilityPragmas Pragmas;
   clang::TestAST AST(getAugmentedTestInputs(Source, Pragmas));
@@ -296,16 +316,17 @@
   return Results;
 }
 
-auto collectFromTargetVarDecl(llvm::StringRef Source) {
+static auto collectFromTargetVarDecl(llvm::StringRef Source) {
   return collectFromDecl(Source, "Target");
 }
 
-auto collectFromTargetFuncDecl(llvm::StringRef Source) {
+static auto collectFromTargetFuncDecl(llvm::StringRef Source) {
   return collectFromDecl(Source, "target");
 }
 
-MATCHER_P2(methodSummary, SlotCount, USRs, "") {
-  return arg.SlotCount == SlotCount && arg.OverridingUSRs == USRs;
+namespace {
+MATCHER_P2(methodSummary, SlotIndices, USRs, "") {
+  return arg.SlotIndices == SlotIndices && arg.OverridingUSRs == USRs;
 }
 
 TEST(GetVirtualMethodIndexTest, DerivedMultipleLayers) {
@@ -313,7 +334,7 @@
 
   static constexpr llvm::StringRef Src = R"cc(
     struct Base {
-      virtual int* foo() { return nullptr; }
+      virtual int* foo(char, bool, int**) { return nullptr; }
 
       // A Nullability-irrelevant method - verify omitted.
       virtual int irrelevant() { return 4; }
@@ -323,12 +344,12 @@
     };
 
     struct Derived : public Base {
-      int* foo() override;
+      int* foo(char, bool, int**) override;
       int irrelevant() override { return 5; }
     };
 
     struct DerivedDerived : public Derived {
-      int* foo() override { return nullptr; };
+      int* foo(char, bool, int**) override { return nullptr; };
     };
   )cc";
 
@@ -356,8 +377,10 @@
       Index.Overrides,
       UnorderedElementsAre(
           IsStringMapEntry(BaseFooUSR,
-                           methodSummary(1, USRSet({DFooUSR, DDFooUSR}))),
-          IsStringMapEntry(DFooUSR, methodSummary(1, USRSet({DDFooUSR})))));
+                           methodSummary(llvm::SmallVector<int>{0, 3},
+                                         USRSet({DFooUSR, DDFooUSR}))),
+          IsStringMapEntry(DFooUSR, methodSummary(llvm::SmallVector<int>{0, 3},
+                                                  USRSet({DDFooUSR})))));
 }
 
 // TODO: b/440317964 -- Expand SummarizeDefinitionTest to cover all summarized
@@ -472,7 +495,7 @@
 class CollectEvidenceFromDefinitionTest
     : public testing::TestWithParam<CollectionMode> {
  protected:
-  CollectionMode GetMode() const { return GetParam(); }
+  CollectionMode getMode() const { return GetParam(); }
 };
 
 INSTANTIATE_TEST_SUITE_P(
@@ -500,7 +523,7 @@
   //                      12345678901234567890123456
   //                      0        1         2
 
-  auto Evidence = collectFromTargetFuncDefinition(Code, GetMode());
+  auto Evidence = collectFromTargetFuncDefinition(Code, getMode());
   ASSERT_THAT(Evidence, ElementsAre(evidence(paramSlot(0),
                                              Evidence::UNCHECKED_DEREFERENCE)));
   EXPECT_EQ("input.cc:1:23", Evidence.front().location());
@@ -512,7 +535,7 @@
   //                      123456789012345678901234567890123456789012
   //                      0        1         2         3         4
 
-  auto Evidence = collectFromTargetFuncDefinition(Code, GetMode());
+  auto Evidence = collectFromTargetFuncDefinition(Code, getMode());
   ASSERT_THAT(Evidence, ElementsAre(evidence(paramSlot(0),
                                              Evidence::UNCHECKED_DEREFERENCE)));
   EXPECT_EQ("input.cc:2:39", Evidence.front().location());
@@ -522,14 +545,14 @@
   static constexpr llvm::StringRef Src = R"cc(
     void target() {}
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, OneParamUnused) {
   static constexpr llvm::StringRef Src = R"cc(
     void target(int *P) {}
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, OneParamUsedWithoutRestriction) {
@@ -538,7 +561,7 @@
 
     void target(int *P) { takesUnknown(P); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, _, functionNamed("target")))));
 }
 
@@ -551,7 +574,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE)));
 }
@@ -567,7 +590,7 @@
       B->y();
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE),
                   evidence(paramSlot(1), Evidence::UNCHECKED_DEREFERENCE)));
@@ -586,7 +609,7 @@
       P->y();
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE),
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE),
@@ -599,7 +622,7 @@
       *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, DereferenceBeforeAssignment) {
@@ -611,7 +634,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       Contains(evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE)));
 }
 
@@ -623,7 +646,7 @@
       *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, Evidence::UNCHECKED_DEREFERENCE))));
 }
 
@@ -639,7 +662,7 @@
       *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, Evidence::UNCHECKED_DEREFERENCE,
                                     functionNamed("target")))));
 }
@@ -653,7 +676,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE)));
 }
@@ -670,7 +693,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE),
                   evidence(paramSlot(1), Evidence::UNCHECKED_DEREFERENCE),
@@ -690,7 +713,7 @@
       int A = *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE)));
 }
@@ -704,7 +727,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE)));
 }
@@ -723,7 +746,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE),
                   evidence(Slot(0), Evidence::ASSIGNED_FROM_UNKNOWN,
@@ -748,7 +771,7 @@
       *A;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE),
                   evidence(Slot(0), Evidence::ASSIGNED_FROM_NONNULL,
@@ -768,7 +791,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(1), Evidence::UNCHECKED_DEREFERENCE)));
 }
@@ -782,7 +805,7 @@
       int A = *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, UnreachableCode) {
@@ -802,7 +825,7 @@
       int A = *P3;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE)));
 }
@@ -820,7 +843,7 @@
   // Pointers to members are not supported pointer types, so no evidence is
   // collected. If they become a supported pointer type, this test should start
   // failing.
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, PointerToMemberMethod) {
@@ -837,7 +860,7 @@
   // Pointers to members are not supported pointer types, so no evidence is
   // collected. If they become a supported pointer type, this test should start
   // failing.
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, PointerToMemberMethodArgs) {
@@ -856,7 +879,7 @@
   // test should start failing.
   // TODO(b/309625642) We should still collect evidence for the use of `Q` as an
   // argument for param `I`.
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, CheckMacro) {
@@ -886,7 +909,7 @@
   )cc";
   EXPECT_THAT(
       collectFromTargetFuncDefinition((CheckMacroDefinitions + BaseSrc).str(),
-                                      GetMode()),
+                                      getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ABORT_IF_NULL),
                            evidence(paramSlot(1), Evidence::ABORT_IF_NULL),
                            evidence(paramSlot(2), Evidence::ABORT_IF_NULL),
@@ -908,7 +931,7 @@
   )cc";
   EXPECT_THAT(
       collectFromTargetFuncDefinition((CheckMacroDefinitions + BaseSrc).str(),
-                                      GetMode()),
+                                      getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ABORT_IF_NULL),
                            evidence(paramSlot(1), Evidence::ABORT_IF_NULL),
                            evidence(paramSlot(2), Evidence::ABORT_IF_NULL)));
@@ -928,7 +951,7 @@
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
                   functionDecl(hasName("Target")),
-                  (CheckMacroDefinitions + BaseSrc).str(), GetMode()),
+                  (CheckMacroDefinitions + BaseSrc).str(), getMode()),
               IsSupersetOf({(evidence(Slot(0), Evidence::ASSIGNED_FROM_NONNULL,
                                       fieldNamed("Target::Shared")),
                              evidence(paramSlot(0), Evidence::ABORT_IF_NULL,
@@ -959,7 +982,7 @@
   )cc";
   EXPECT_THAT(
       collectFromTargetFuncDefinition((CheckMacroDefinitions + BaseSrc).str(),
-                                      GetMode()),
+                                      getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ABORT_IF_NULL),
                            evidence(paramSlot(1), Evidence::ABORT_IF_NULL),
                            evidence(paramSlot(2), Evidence::ABORT_IF_NULL),
@@ -982,7 +1005,7 @@
   )cc";
   EXPECT_THAT(
       collectFromTargetFuncDefinition((CheckMacroDefinitions + BaseSrc).str(),
-                                      GetMode()),
+                                      getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ABORT_IF_NULL),
                            evidence(paramSlot(1), Evidence::ABORT_IF_NULL),
                            evidence(paramSlot(3), Evidence::ABORT_IF_NULL)));
@@ -993,7 +1016,7 @@
     void callee(int *Q);
     void target(Nullable<int *> P) { callee(P); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                 functionNamed("callee"))));
 }
@@ -1003,7 +1026,7 @@
     void callee(int *Q);
     void target(Nonnull<int *> P) { callee(P); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT,
                                 functionNamed("callee"))));
 }
@@ -1013,7 +1036,7 @@
     void callee(int *Q);
     void target(int *P) { callee(P); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT,
                                 functionNamed("callee"))));
 }
@@ -1027,7 +1050,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                 functionNamed("callee"))));
 }
@@ -1039,7 +1062,7 @@
       if (P) callee(P);
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT,
                                 functionNamed("callee"))));
 }
@@ -1054,7 +1077,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                     functionNamed("callee")),
                            evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
@@ -1068,7 +1091,7 @@
     void callee(int Q);
     void target(int P) { callee(P); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, LValueReferenceArgsPassed) {
@@ -1081,7 +1104,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(paramSlot(0), Evidence::NULLABLE_REFERENCE_ARGUMENT,
                    functionNamed("constCallee")),
@@ -1111,7 +1134,7 @@
       universalRef(std::move(q));  // Nonnull
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   // RValue references don't have the same invariance as lvalue
                   // references, because accesses through the reference and
@@ -1132,7 +1155,7 @@
       return callee(P, Q, R);
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(SmartPointerCollectEvidenceFromDefinitionTest, ArgsAndParams) {
@@ -1148,7 +1171,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       AllOf(IsSupersetOf(
                 {evidence(paramSlot(1), Evidence::ASSIGNED_TO_NONNULL,
                           functionNamed("target")),
@@ -1180,14 +1203,14 @@
       hasDefaultExpressionOfVariable();
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, NullableReturn) {
   static constexpr llvm::StringRef Src = R"cc(
     int* target() { return nullptr; }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN)));
 }
@@ -1201,7 +1224,7 @@
       // compiles, as the lack of return in a path is only a warning.
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN)));
 }
@@ -1212,7 +1235,7 @@
       return P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN)));
 }
@@ -1221,7 +1244,7 @@
   static constexpr llvm::StringRef Src = R"cc(
     int* target(int* P) { return P; }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::UNKNOWN_RETURN)));
 }
@@ -1236,7 +1259,7 @@
       // compiles, as the lack of return in a path is only a warning.
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN)));
 }
@@ -1249,7 +1272,7 @@
       return P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN),
                   evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN),
@@ -1265,7 +1288,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_REFERENCE_RETURN),
           evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_REFERENCE_RETURN),
@@ -1282,7 +1305,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_REFERENCE_RETURN),
           evidence(SLOT_RETURN_TYPE,
@@ -1296,7 +1319,7 @@
       return A;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL)));
 }
@@ -1308,7 +1331,7 @@
   )cc";
   EXPECT_THAT(
       collectFromTargetFuncDefinition(
-          Src, GetMode(),
+          Src, getMode(),
           {.Nonnull = std::make_shared<SortedFingerprintVector>(
                std::vector<SlotFingerprint>{
                    fingerprint("c:@F@target#*I#", 0)})}),
@@ -1333,7 +1356,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target"))));
 }
@@ -1346,7 +1369,7 @@
     };
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
                                     functionNamed("target"))));
 }
@@ -1365,7 +1388,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN),
           evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN),
@@ -1385,7 +1408,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL),
           // evidence for the move constructor, which we don't care much about.
@@ -1398,7 +1421,7 @@
     void target() { *makePtr(); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       Contains(evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
                         functionNamed("makePtr"))));
 }
@@ -1413,7 +1436,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       Contains(evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
                         functionNamed("makePtr"))));
 }
@@ -1428,7 +1451,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       Not(Contains(evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
                             functionNamed("makePtr")))));
 }
@@ -1443,7 +1466,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       Contains(evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
                         functionNamed("makePtr"))));
 }
@@ -1458,7 +1481,7 @@
     void target() { makePtr()->member(); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       Contains(evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
                         functionNamed("makePtr"))));
 }
@@ -1469,14 +1492,14 @@
     Nonnull<int*> makeNonnullPtr();
     void target() { *makeNonnullPtr(); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, FunctionPointerCall) {
   static constexpr llvm::StringRef Src = R"cc(
     void target(void (*F)()) { F(); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE)));
 }
@@ -1502,7 +1525,7 @@
   // Ideally, we would see the Nonnull from `P`'s template parameter and collect
   // ASSIGNED_TO_NONNULL evidence for `I`, but the sugar doesn't carry through
   // the BindingDecl's `auto` type.
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, ConstAccessorDereferencedAfterCheck) {
@@ -1518,7 +1541,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest,
@@ -1535,7 +1558,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest,
@@ -1554,7 +1577,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(evidence(SLOT_RETURN_TYPE,
                                             Evidence::UNCHECKED_DEREFERENCE,
                                             functionNamed("accessor"))));
@@ -1572,7 +1595,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       Contains(evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
                         functionNamed("operator()"))));
 }
@@ -1584,7 +1607,7 @@
     };
     void target() { S{} + nullptr; }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                 functionNamed("operator+"))));
 }
@@ -1595,7 +1618,7 @@
     bool operator+(const S&, int*);
     void target() { S{} + nullptr; }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(1), Evidence::NULLABLE_ARGUMENT,
                                 functionNamed("operator+"))));
 }
@@ -1606,7 +1629,7 @@
     void target() { callee(nullptr, nullptr); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                     functionNamed("callee"))));
 }
@@ -1619,7 +1642,7 @@
     void target() { S{}(nullptr, nullptr); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                     functionNamed("operator()"))));
 }
@@ -1632,7 +1655,7 @@
     void target(int* P) { S AnS(P, nullptr); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target")),
                            evidence(paramSlot(1), Evidence::NULLABLE_ARGUMENT,
@@ -1648,7 +1671,7 @@
     void target(int* P) { std::make_unique<S>(P, nullptr); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target")),
                            evidence(paramSlot(1), Evidence::NULLABLE_ARGUMENT,
@@ -1664,7 +1687,7 @@
       Target(int *I) : TakeNonnull(I) {}
     };
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("Target", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("Target", Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                 functionNamed("Target"))));
 }
@@ -1680,7 +1703,7 @@
 
   EXPECT_THAT(
       collectFromDefinitionMatching(
-          functionDecl(hasName("Target"), parameterCountIs(0)), Src, GetMode()),
+          functionDecl(hasName("Target"), parameterCountIs(0)), Src, getMode()),
       Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                         functionNamed("Target"))));
 }
@@ -1693,7 +1716,7 @@
     void target(int* P, int* Q) { S AnS(P, Q); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target"))));
 }
@@ -1708,7 +1731,7 @@
     void target(int* P, int* Q) { std::make_unique<S>(P, Q); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target"))));
 }
@@ -1726,7 +1749,7 @@
     };
     void target(int* P) { S AnS(ConvertibleToIntPtr{P}); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::ASSIGNED_TO_NONNULL,
                            functionNamed("operator int *")),
@@ -1754,7 +1777,7 @@
   // evidence. However, we collect the evidence from the make_unique
   // instantiation and will do inference from that.
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT,
                                     functionNamed("ConvertibleToIntPtr"))));
 }
@@ -1778,7 +1801,7 @@
     void target(Bar b) { std::make_unique<Foo>(b); }
       )cc";
 
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest,
@@ -1790,7 +1813,7 @@
     };
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("Target"))));
 }
@@ -1803,7 +1826,7 @@
     };
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
-                  cxxConstructorDecl(isDefaultConstructor()), Src, GetMode()),
+                  cxxConstructorDecl(isDefaultConstructor()), Src, getMode()),
               UnorderedElementsAre(evidence(
                   Slot(0), Evidence::NULLPTR_DEFAULT_MEMBER_INITIALIZER,
                   fieldNamed("Target::I"))));
@@ -1819,7 +1842,7 @@
   )cc";
   EXPECT_THAT(
       collectFromDefinitionMatching(cxxConstructorDecl(isDefaultConstructor()),
-                                    Src, GetMode()),
+                                    Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     fieldNamed("Target::I"))));
 }
@@ -1835,7 +1858,7 @@
     };
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("Target"))));
 }
@@ -1856,7 +1879,7 @@
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
                   cxxConstructorDecl(isDefaultConstructor(), hasName("Target")),
-                  Src, GetMode()),
+                  Src, getMode()),
               UnorderedElementsAre(evidence(
                   Slot(0), Evidence::NULLPTR_DEFAULT_MEMBER_INITIALIZER,
                   fieldNamed("Target@Sa::I"))));
@@ -1870,7 +1893,7 @@
     };
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     fieldNamed("Target::I"))));
 }
@@ -1888,7 +1911,7 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     fieldNamed("UnionType::I"))));
 }
@@ -1901,7 +1924,7 @@
       Nonnull<int*> I;
     };
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("Target", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("Target", Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::ASSIGNED_TO_NONNULL,
                            functionNamed("getIntPtr")),
@@ -1923,7 +1946,7 @@
     Target T;
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
-                  cxxConstructorDecl(isDefaultConstructor()), Src, GetMode()),
+                  cxxConstructorDecl(isDefaultConstructor()), Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::ASSIGNED_TO_NONNULL,
                            functionNamed("getIntPtr")),
@@ -1944,7 +1967,7 @@
   EXPECT_THAT(
       collectFromDefinitionMatching(
           cxxConstructorDecl(unless(isImplicit()), hasName("Target")), Src,
-          GetMode()),
+          getMode()),
       UnorderedElementsAre(
           evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                    functionNamed("Target")),
@@ -1965,7 +1988,7 @@
   EXPECT_THAT(
       collectFromDefinitionMatching(
           cxxConstructorDecl(unless(isImplicit()), hasName("Target")), Src,
-          GetMode()),
+          getMode()),
       UnorderedElementsAre(
           evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                    fieldNamed("Target::I")),
@@ -1987,7 +2010,7 @@
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
                   cxxConstructorDecl(isDefaultConstructor(), hasName("Target")),
-                  Src, GetMode()),
+                  Src, getMode()),
               UnorderedElementsAre(evidence(
                   Slot(0), Evidence::NULLPTR_DEFAULT_MEMBER_INITIALIZER,
                   fieldNamed("Target::I"))));
@@ -2007,7 +2030,7 @@
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
                   cxxConstructorDecl(isDefaultConstructor(), hasName("Target")),
-                  Src, GetMode()),
+                  Src, getMode()),
               // By the end of the constructor body, the field is still only
               // default-initialized, which for smart pointers means it is null.
               UnorderedElementsAre(
@@ -2027,7 +2050,7 @@
   EXPECT_THAT(
       collectFromDefinitionMatching(
           cxxConstructorDecl(unless(isImplicit()), hasName("Target")), Src,
-          GetMode()),
+          getMode()),
       // Evidence collected from constructor body, which assigns a Nonnull
       // value, but no evidence collected from *implicit* member initializer
       // which default constructs to null.
@@ -2054,7 +2077,7 @@
   EXPECT_THAT(
       collectFromDefinitionMatching(
           cxxConstructorDecl(unless(isImplicit()), hasName("Target")), Src,
-          GetMode()),
+          getMode()),
       // By the end of the constructor body, the field is still potentially
       // default-initialized, which for smart pointers means it may be null.
       // We also collect from the Nonnull value assignment in the body, though
@@ -2084,7 +2107,7 @@
   EXPECT_THAT(
       collectFromDefinitionMatching(
           cxxConstructorDecl(unless(isImplicit()), hasName("Target")), Src,
-          GetMode()),
+          getMode()),
       // By the end of the constructor body, the field is no longer default
       // initialized to null, but is assigned from an unknown.
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_UNKNOWN,
@@ -2110,7 +2133,7 @@
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
                   cxxConstructorDecl(unless(isImplicit()), hasName("Target")),
-                  Src, GetMode()),
+                  Src, getMode()),
               IsEmpty());
 }
 
@@ -2130,7 +2153,7 @@
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
                   cxxConstructorDecl(unless(isImplicit()), hasName("Target")),
-                  Src, GetMode()),
+                  Src, getMode()),
               IsEmpty());
 }
 
@@ -2155,7 +2178,7 @@
   EXPECT_THAT(
       collectFromDefinitionMatching(
           cxxMethodDecl(hasName("SetUp"), ofClass(hasName("Target"))), Src,
-          GetMode()),
+          getMode()),
       AllOf(Contains(evidence(Slot(0),
                               Evidence::LEFT_NOT_NULLABLE_BY_LATE_INITIALIZER,
                               fieldNamed("Target::FieldInitializedInSetUp"))),
@@ -2187,7 +2210,7 @@
   )cc";
   EXPECT_THAT(collectFromDefinitionMatching(
                   cxxMethodDecl(hasName("SetUp"), ofClass(hasName("Target"))),
-                  Src, GetMode()),
+                  Src, getMode()),
               Contains(evidence(
                   Slot(0), Evidence::LEFT_NOT_NULLABLE_BY_LATE_INITIALIZER,
                   fieldNamed("Target::FieldInitializedInSetUp"))));
@@ -2200,7 +2223,7 @@
     void target(int* P) { callee(P); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target"))));
 }
@@ -2212,7 +2235,7 @@
     void target(int* P, int* Q) { callee(P, Q); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL_REFERENCE,
                    functionNamed("target")),
@@ -2232,7 +2255,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target"))));
 }
@@ -2244,7 +2267,7 @@
       Callee(P);
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                            functionNamed("target")),
@@ -2263,7 +2286,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                    functionNamed("target")),
@@ -2283,7 +2306,7 @@
     void target(int* P) { MyStruct().Callee(P); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target")),
                            evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
@@ -2298,7 +2321,7 @@
     void target(int* P) { (&callee)(P); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target"))));
 }
@@ -2311,7 +2334,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                                     functionNamed("target"))));
 }
@@ -2323,7 +2346,7 @@
       Callee(P);
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL,
                            functionNamed("target")),
@@ -2338,7 +2361,7 @@
 
     void target() { callee(makeIntPtr()); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(evidence(SLOT_RETURN_TYPE,
                                             Evidence::ASSIGNED_TO_NONNULL,
                                             functionNamed("makeIntPtr"))));
@@ -2351,7 +2374,7 @@
 
     void target(void (*Callee)(Nonnull<int*> I)) { Callee(makeIntPtr()); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::ASSIGNED_TO_NONNULL,
                            functionNamed("makeIntPtr")),
@@ -2365,7 +2388,7 @@
 
     void target(int* P) { callee(P); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, _, functionNamed("target")))));
 }
 
@@ -2375,7 +2398,7 @@
 
     void target(int* P) { callee(P); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_MUTABLE_NULLABLE,
                            functionNamed("target"))));
@@ -2392,7 +2415,7 @@
       callee(P);
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   // The object taken by reference (P) needs to be nullable, not
                   // necessarily the source of its value (producer).
@@ -2409,7 +2432,7 @@
 
     void target() { callee(producer()); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(evidence(
                   SLOT_RETURN_TYPE, Evidence::ASSIGNED_TO_MUTABLE_NULLABLE,
                   functionNamed("producer"))));
@@ -2421,7 +2444,7 @@
     void target(int* P) { callee(&P); }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       AllOf(UnorderedElementsAre(evidence(paramSlot(0),
                                           Evidence::NONNULL_ARGUMENT,
                                           functionNamed("callee"))),
@@ -2440,7 +2463,7 @@
       A = R;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL),
                   evidence(paramSlot(1), Evidence::ASSIGNED_TO_NONNULL),
@@ -2455,7 +2478,7 @@
       A = B ? R : S;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
   // TODO(b/293609145) When value nullability for conditional operators is
   // carried through for glvalues, this should collect the following:
   // UnorderedElementsAre(evidence(paramSlot(1), Evidence::ASSIGNED_TO_NONNULL),
@@ -2485,7 +2508,7 @@
       Nonnull<std::unique_ptr<int>> nonnull = std::move(T);
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL),
                   evidence(paramSlot(2), Evidence::ASSIGNED_TO_NONNULL),
@@ -2509,7 +2532,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL_REFERENCE),
           // `A = Q;` copies Q into P; it doesn't make a reference to Q,
@@ -2528,7 +2551,7 @@
       Q = R;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(1), Evidence::ASSIGNED_FROM_UNKNOWN),
                   evidence(Slot(0), Evidence::ASSIGNED_FROM_UNKNOWN,
@@ -2545,7 +2568,7 @@
       A = Q;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_MUTABLE_NULLABLE)
                   // `A = Q;` copies Q into P; it doesn't make a reference to Q,
@@ -2561,7 +2584,7 @@
       Nullable<std::unique_ptr<int>>& A = P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_MUTABLE_NULLABLE,
                            functionNamed("target"))));
@@ -2573,7 +2596,7 @@
       Nullable<int*>& A = P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(evidence(
                   paramSlot(0), Evidence::ASSIGNED_TO_MUTABLE_NULLABLE)));
 }
@@ -2590,7 +2613,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(paramSlot(0), Evidence::ASSIGNED_TO_MUTABLE_NULLABLE),
           evidence(paramSlot(1), Evidence::ASSIGNED_TO_MUTABLE_NULLABLE)));
@@ -2602,7 +2625,7 @@
       P = nullptr;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE)));
 }
@@ -2614,7 +2637,7 @@
       P = A;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE),
                   evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
@@ -2625,7 +2648,7 @@
   static constexpr llvm::StringRef Src = R"cc(
     void target(int* P) { P = 0; }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE)));
 }
@@ -2635,7 +2658,7 @@
     Nullable<int*> getNullable();
     void target(int* P) { P = getNullable(); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(evidence(paramSlot(0),
                                             Evidence::ASSIGNED_FROM_NULLABLE,
                                             functionNamed("target"))));
@@ -2648,7 +2671,7 @@
       P = A;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE)));
 }
@@ -2661,7 +2684,7 @@
 
     void target(S AnS) { AnS.getPtrRef() = nullptr; }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(evidence(SLOT_RETURN_TYPE,
                                             Evidence::ASSIGNED_FROM_NULLABLE,
                                             functionNamed("getPtrRef"))));
@@ -2674,7 +2697,7 @@
       *&P = nullptr;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE)));
 }
@@ -2692,7 +2715,7 @@
       P = nullptr;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, AssignedFromNonnull) {
@@ -2702,7 +2725,7 @@
       P = &A;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NONNULL)));
 }
@@ -2713,7 +2736,7 @@
       P = Q;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_FROM_UNKNOWN)));
 }
@@ -2734,7 +2757,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       // From the constructor call constructing an S; no evidence from
       // assignments or initializations.
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT,
@@ -2751,7 +2774,7 @@
   // Could in theory collect evidence for both A and B as nullable, but we don't
   // track null state through the conditional operator, so we don't collect
   // evidence for either.
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, Arithmetic) {
@@ -2769,7 +2792,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ARITHMETIC),
                            evidence(paramSlot(1), Evidence::ARITHMETIC),
                            evidence(paramSlot(2), Evidence::ARITHMETIC),
@@ -2810,7 +2833,7 @@
   )cc";
   EXPECT_THAT(
       collectFromTargetFuncDefinition((CheckMacroDefinitions + BaseSrc).str(),
-                                      GetMode()),
+                                      getMode()),
       IsSupersetOf(
           {evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                     fieldNamed("S::Deref")),
@@ -2861,7 +2884,7 @@
   )cc";
   EXPECT_THAT(
       collectFromTargetFuncDefinition((CheckMacroDefinitions + BaseSrc).str(),
-                                      GetMode()),
+                                      getMode()),
       IsSupersetOf(
           {evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                     staticFieldNamed("MyStruct::Deref")),
@@ -2911,7 +2934,7 @@
   )cc";
   EXPECT_THAT(
       collectFromTargetFuncDefinition((CheckMacroDefinitions + BaseSrc).str(),
-                                      GetMode()),
+                                      getMode()),
       IsSupersetOf({evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                              globalVarNamed("Deref")),
                     evidence(Slot(0), Evidence::ASSIGNED_TO_NONNULL,
@@ -2939,7 +2962,7 @@
     int* Target = static_cast<int*>(getNullableFromNonnull(getPtr()));
   )cc";
 
-  EXPECT_THAT(collectFromDefinitionNamed("Target", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("Target", Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::ASSIGNED_TO_NONNULL,
                            functionNamed("getPtr")),
@@ -2952,7 +2975,7 @@
     int* foo();
     Nonnull<int*> Target = foo();
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("Target", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("Target", Src, getMode()),
               UnorderedElementsAre(evidence(SLOT_RETURN_TYPE,
                                             Evidence::ASSIGNED_TO_NONNULL,
                                             functionNamed("foo"))));
@@ -2965,7 +2988,7 @@
     std::unique_ptr<int> Target;
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     globalVarNamed("Target"))));
 }
@@ -2976,7 +2999,7 @@
     std::unique_ptr<int> Target = nullptr;
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     globalVarNamed("Target"))));
 }
@@ -2994,7 +3017,7 @@
     S Target(&GInt, AssignedToNonnull);
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT,
                                     functionNamed("S")),
                            evidence(Slot(0), Evidence::ASSIGNED_TO_NONNULL,
@@ -3014,7 +3037,7 @@
     std::unique_ptr<S> Target = std::make_unique<S>(&GInt, AssignedToNonnull);
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NONNULL,
                                     globalVarNamed("Target")),
                            evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT,
@@ -3038,7 +3061,7 @@
     std::unique_ptr<S> Target = std::make_unique<S>(&GInt, AssignedToNonnull);
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NONNULL,
                                     globalVarNamed("Target")),
                            evidence(Slot(0), Evidence::ASSIGNED_FROM_NONNULL,
@@ -3054,7 +3077,7 @@
     };
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Target", Src, GetMode()),
+      collectFromDefinitionNamed("Target", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     staticFieldNamed("MyStruct::Target"))));
 }
@@ -3069,7 +3092,7 @@
     int* MyStruct::Target = nullptr;
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionMatching(varDecl(hasInit()), Src, GetMode()),
+      collectFromDefinitionMatching(varDecl(hasInit()), Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     staticFieldNamed("MyStruct::Target"))));
 }
@@ -3081,7 +3104,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     localVarNamed("P"))));
 }
@@ -3101,7 +3124,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT),
                            evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT),
                            evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT)));
@@ -3115,7 +3138,7 @@
       *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, _, functionNamed("target")))));
 }
 
@@ -3127,7 +3150,7 @@
       *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, _, functionNamed("target")))));
 }
 
@@ -3140,7 +3163,7 @@
       *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
                                 functionNamed("target"))));
 }
@@ -3155,7 +3178,7 @@
       **P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, _, functionNamed("target")))));
 }
 
@@ -3168,7 +3191,7 @@
       *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
                                 functionNamed("target"))));
 }
@@ -3183,7 +3206,7 @@
       *P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, _, functionNamed("target")))));
 }
 
@@ -3198,7 +3221,7 @@
       }
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
                                 functionNamed("target"))));
 }
@@ -3214,7 +3237,7 @@
            // dereference safe, so we do not collect evidence for P.
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Not(Contains(evidence(_, _, functionNamed("target")))));
 }
 
@@ -3225,7 +3248,7 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromDefinitionNamed("operator()", Src, GetMode()),
+      collectFromDefinitionNamed("operator()", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                                     globalVarNamed("P"))));
 }
@@ -3240,7 +3263,7 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromDefinitionNamed("operator()", Src, GetMode()),
+      collectFromDefinitionNamed("operator()", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                                     localVarNamed("P", "foo"))));
 }
@@ -3255,7 +3278,7 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromDefinitionNamed("operator()", Src, GetMode()),
+      collectFromDefinitionNamed("operator()", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                                     localVarNamed("P", "foo"))));
 }
@@ -3272,7 +3295,7 @@
     }
   )cc";
 
-  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
                            functionNamed("foo")),
@@ -3309,7 +3332,7 @@
     }
   )cc";
 
-  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
                            functionNamed("foo")),
@@ -3340,7 +3363,7 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromDefinitionNamed("operator()", Src, GetMode()),
+      collectFromDefinitionNamed("operator()", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                                     fieldNamed("A::P")),
                            evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
@@ -3358,7 +3381,7 @@
     }
   )cc";
 
-  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
                            functionNamed("bar")),
@@ -3382,7 +3405,7 @@
       }
     };
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, getMode()),
               UnorderedElementsAre(
                   evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                            fieldNamed("S::F")),
@@ -3408,7 +3431,7 @@
       }
     };
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, getMode()),
               UnorderedElementsAre(
                   evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                            fieldNamed("S::F")),
@@ -3436,7 +3459,7 @@
       collectFromDefinitionMatching(
           cxxMethodDecl(hasName("operator()"),
                         hasAncestor(lambdaExpr(hasAncestor(lambdaExpr())))),
-          Src, GetMode()),
+          Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                                     localVarNamed("A", "foo")),
                            evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
@@ -3452,7 +3475,7 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromDefinitionNamed("operator()", Src, GetMode()),
+      collectFromDefinitionNamed("operator()", Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::UNCHECKED_DEREFERENCE,
                                     localVarNamed("Q", "operator()"))));
 }
@@ -3465,7 +3488,7 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromDefinitionMatching(varDecl(hasName("Q")), Src, GetMode()),
+      collectFromDefinitionMatching(varDecl(hasName("Q")), Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     localVarNamed("Q", "operator()"))));
 }
@@ -3478,7 +3501,7 @@
     };
   )cc";
 
-  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("operator()", Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
                            functionNamed("operator()")),
@@ -3525,9 +3548,9 @@
                            evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     fieldNamed("MyStruct::B")));
 
-  EXPECT_THAT(collectFromTargetFuncDefinition(BracesAggInit, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(BracesAggInit, getMode()),
               ExpectedEvidenceMatcher);
-  EXPECT_THAT(collectFromTargetFuncDefinition(ParensAggInit, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(ParensAggInit, getMode()),
               ExpectedEvidenceMatcher);
 }
 
@@ -3553,7 +3576,7 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     fieldNamed("Base::BaseB")),
                            evidence(paramSlot(1), Evidence::ASSIGNED_TO_NONNULL,
@@ -3579,7 +3602,7 @@
     void target(int* Int) { S AnS(ConvertibleToIntPtr{Int}); }
   )cc";
 
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(SLOT_RETURN_TYPE, Evidence::ASSIGNED_TO_NONNULL,
                            functionNamed("operator int *")),
@@ -3607,7 +3630,7 @@
   // evidence. However, we collect the evidence from the make_unique
   // instantiation and will do inference from that.
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT,
                                     functionNamed("ConvertibleToIntPtr"))));
 }
@@ -3627,7 +3650,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(
           evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                    fieldNamed("MyStruct::P")),
@@ -3645,7 +3668,7 @@
   static constexpr llvm::StringRef Src = R"cc(
     void target() { int A[3] = {}; }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest,
@@ -3656,7 +3679,7 @@
     void foo(int*);
     void target(Nullable<std::unique_ptr<int>> P) { foo(P.get()); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                 functionNamed("foo"))));
 }
@@ -3671,7 +3694,7 @@
 
     void target() { foo({get()}); }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()), IsEmpty());
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()), IsEmpty());
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, ArraySubscript) {
@@ -3679,7 +3702,7 @@
     void target(int* P) { P[0]; }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ARRAY_SUBSCRIPT)));
 }
 
@@ -3691,7 +3714,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::ARRAY_SUBSCRIPT)));
 }
 
@@ -3716,11 +3739,11 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Derived::foo", Src, GetMode()),
+      collectFromDefinitionNamed("Derived::foo", Src, getMode()),
       UnorderedElementsAre(evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN,
                                     functionNamed("Derived@F@foo"))));
 
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(evidence(SLOT_RETURN_TYPE,
                                             Evidence::UNCHECKED_DEREFERENCE,
                                             functionNamed("Derived@F@foo"))));
@@ -3736,12 +3759,13 @@
       int* foo() override { return nullptr; }
     };
   )cc";
-  EXPECT_THAT(
-      collectFromDefinitionNamed("Derived::foo", Src, GetMode()),
-      UnorderedElementsAre(evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
-                                    functionNamed("Derived@F@foo")),
-                           evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
-                                    functionNamed("Base@F@foo"))));
+  EXPECT_THAT(collectFromDefinitionNamed("Derived::foo", Src, getMode()),
+              UnorderedElementsAre(
+                  evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
+                           functionNamed("Derived@F@foo")),
+                  evidencePropagatedFrom(
+                      functionNamed("Derived@F@foo"), SLOT_RETURN_TYPE,
+                      Evidence::NULLABLE_RETURN, functionNamed("Base@F@foo"))));
 
   // We don't currently have any evidence kinds that can force a non-reference
   // top-level pointer return type to be nullable from its usage, so no other
@@ -3765,18 +3789,22 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
-      UnorderedElementsAre(evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT,
-                                    functionNamed("Derived@F@foo")),
-                           evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT,
-                                    functionNamed("Base@F@foo"))));
+      collectFromTargetFuncDefinition(Src, getMode()),
+      UnorderedElementsAre(
+          evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT,
+                   functionNamed("Derived@F@foo")),
+          evidencePropagatedFrom(functionNamed("Derived@F@foo"), paramSlot(0),
+                                 Evidence::NONNULL_ARGUMENT,
+                                 functionNamed("Base@F@foo"))));
 
-  EXPECT_THAT(collectFromDefinitionNamed("Derived::foo", Src, GetMode()),
-              UnorderedElementsAre(
-                  evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
-                           functionNamed("Derived@F@foo")),
-                  evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
-                           functionNamed("Base@F@foo"))));
+  EXPECT_THAT(
+      collectFromDefinitionNamed("Derived::foo", Src, getMode()),
+      UnorderedElementsAre(
+          evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
+                   functionNamed("Derived@F@foo")),
+          evidencePropagatedFrom(functionNamed("Derived@F@foo"), paramSlot(0),
+                                 Evidence::UNCHECKED_DEREFERENCE,
+                                 functionNamed("Base@F@foo"))));
 }
 
 // Evidence for parameter nullable-ness should flow only from base to derived,
@@ -3797,11 +3825,11 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                     functionNamed("Derived@F@foo"))));
 
-  EXPECT_THAT(collectFromDefinitionNamed("Derived::foo", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("Derived::foo", Src, getMode()),
               UnorderedElementsAre(evidence(paramSlot(0),
                                             Evidence::ASSIGNED_FROM_NULLABLE,
                                             functionNamed("Derived@F@foo"))));
@@ -3826,18 +3854,22 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Base::foo", Src, GetMode()),
-      UnorderedElementsAre(evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN,
-                                    functionNamed("Base@F@foo")),
-                           evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN,
-                                    functionNamed("Derived@F@foo"))));
+      collectFromDefinitionNamed("Base::foo", Src, getMode()),
+      UnorderedElementsAre(
+          evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN,
+                   functionNamed("Base@F@foo")),
+          evidencePropagatedFrom(functionNamed("Base@F@foo"), SLOT_RETURN_TYPE,
+                                 Evidence::NONNULL_RETURN,
+                                 functionNamed("Derived@F@foo"))));
 
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
-              UnorderedElementsAre(
-                  evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
-                           functionNamed("Base@F@foo")),
-                  evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
-                           functionNamed("Derived@F@foo"))));
+  EXPECT_THAT(
+      collectFromTargetFuncDefinition(Src, getMode()),
+      UnorderedElementsAre(
+          evidence(SLOT_RETURN_TYPE, Evidence::UNCHECKED_DEREFERENCE,
+                   functionNamed("Base@F@foo")),
+          evidencePropagatedFrom(functionNamed("Base@F@foo"), SLOT_RETURN_TYPE,
+                                 Evidence::UNCHECKED_DEREFERENCE,
+                                 functionNamed("Derived@F@foo"))));
 }
 
 // Evidence for return type nullable-ness should flow only from derived to base,
@@ -3853,7 +3885,7 @@
     };
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("Base::foo", Src, GetMode()),
+      collectFromDefinitionNamed("Base::foo", Src, getMode()),
       UnorderedElementsAre(evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
                                     functionNamed("Base@F@foo"))));
 
@@ -3881,11 +3913,11 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
+      collectFromTargetFuncDefinition(Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT,
                                     functionNamed("Base@F@foo"))));
 
-  EXPECT_THAT(collectFromDefinitionNamed("Base::foo", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("Base::foo", Src, getMode()),
               UnorderedElementsAre(evidence(paramSlot(0),
                                             Evidence::UNCHECKED_DEREFERENCE,
                                             functionNamed("Base@F@foo"))));
@@ -3907,18 +3939,22 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromTargetFuncDefinition(Src, GetMode()),
-      UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
-                                    functionNamed("Base@F@foo")),
-                           evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
-                                    functionNamed("Derived@F@foo"))));
+      collectFromTargetFuncDefinition(Src, getMode()),
+      UnorderedElementsAre(
+          evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
+                   functionNamed("Base@F@foo")),
+          evidencePropagatedFrom(functionNamed("Base@F@foo"), paramSlot(0),
+                                 Evidence::NULLABLE_ARGUMENT,
+                                 functionNamed("Derived@F@foo"))));
 
-  EXPECT_THAT(collectFromDefinitionNamed("Base::foo", Src, GetMode()),
-              UnorderedElementsAre(
-                  evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE,
-                           functionNamed("Base@F@foo")),
-                  evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE,
-                           functionNamed("Derived@F@foo"))));
+  EXPECT_THAT(
+      collectFromDefinitionNamed("Base::foo", Src, getMode()),
+      UnorderedElementsAre(
+          evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE,
+                   functionNamed("Base@F@foo")),
+          evidencePropagatedFrom(functionNamed("Base@F@foo"), paramSlot(0),
+                                 Evidence::ASSIGNED_FROM_NULLABLE,
+                                 functionNamed("Derived@F@foo"))));
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, FromVirtualDerivedMultipleLayers) {
@@ -3937,13 +3973,16 @@
   )cc";
 
   EXPECT_THAT(
-      collectFromDefinitionNamed("DerivedDerived::foo", Src, GetMode()),
-      UnorderedElementsAre(evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
-                                    functionNamed("DerivedDerived@F@foo")),
-                           evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
-                                    functionNamed("Derived@F@foo")),
-                           evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
-                                    functionNamed("Base@F@foo"))));
+      collectFromDefinitionNamed("DerivedDerived::foo", Src, getMode()),
+      UnorderedElementsAre(
+          evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
+                   functionNamed("DerivedDerived@F@foo")),
+          evidencePropagatedFrom(functionNamed("DerivedDerived@F@foo"),
+                                 SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
+                                 functionNamed("Derived@F@foo")),
+          evidencePropagatedFrom(functionNamed("DerivedDerived@F@foo"),
+                                 SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN,
+                                 functionNamed("Base@F@foo"))));
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, FromVirtualBaseMultipleLayers) {
@@ -3961,14 +4000,17 @@
     };
   )cc";
 
-  EXPECT_THAT(collectFromDefinitionNamed("Base::foo", Src, GetMode()),
-              UnorderedElementsAre(
-                  evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE,
-                           functionNamed("DerivedDerived@F@foo")),
-                  evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE,
-                           functionNamed("Derived@F@foo")),
-                  evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE,
-                           functionNamed("Base@F@foo"))));
+  EXPECT_THAT(
+      collectFromDefinitionNamed("Base::foo", Src, getMode()),
+      UnorderedElementsAre(
+          evidencePropagatedFrom(functionNamed("Base@F@foo"), paramSlot(0),
+                                 Evidence::ASSIGNED_FROM_NULLABLE,
+                                 functionNamed("DerivedDerived@F@foo")),
+          evidencePropagatedFrom(functionNamed("Base@F@foo"), paramSlot(0),
+                                 Evidence::ASSIGNED_FROM_NULLABLE,
+                                 functionNamed("Derived@F@foo")),
+          evidence(paramSlot(0), Evidence::ASSIGNED_FROM_NULLABLE,
+                   functionNamed("Base@F@foo"))));
 }
 
 TEST_P(CollectEvidenceFromDefinitionTest, FunctionTemplate) {
@@ -3985,7 +4027,7 @@
     }
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("usage", Src, GetMode()),
+      collectFromDefinitionNamed("usage", Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                     functionNamed("tmpl<#I>")),
                            evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
@@ -3998,21 +4040,21 @@
   EXPECT_THAT(
       collectFromDefinitionMatching(
           functionDecl(hasTemplateArgument(0, refersToType(asString("int")))),
-          Src, GetMode()),
+          Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0),
                                     Evidence::UNCHECKED_DEREFERENCE,
                                     functionNamed("tmpl<#I>"))));
   EXPECT_THAT(
       collectFromDefinitionMatching(
           functionDecl(hasTemplateArgument(0, refersToType(booleanType()))),
-          Src, GetMode()),
+          Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0),
                                     Evidence::UNCHECKED_DEREFERENCE,
                                     functionNamed("tmpl<#b>"))));
   EXPECT_THAT(
       collectFromDefinitionMatching(functionDecl(hasTemplateArgument(
                                         0, refersToType(asString("char *")))),
-                                    Src, GetMode()),
+                                    Src, getMode()),
       UnorderedElementsAre(evidence(paramSlot(0),
                                     Evidence::UNCHECKED_DEREFERENCE,
                                     functionNamed("tmpl<#*C>"))));
@@ -4035,7 +4077,7 @@
     void usage() { tmpl<int*>(nullptr, nullptr); }
   )cc";
   EXPECT_THAT(
-      collectFromDefinitionNamed("usage", Src, GetMode()),
+      collectFromDefinitionNamed("usage", Src, getMode()),
       // Evidence is emitted for the explicit specialization, not the template.
       UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                                     functionNamed("tmpl<#*I>")),
@@ -4044,7 +4086,7 @@
   EXPECT_THAT(
       collectFromDefinitionMatching(
           functionDecl(hasTemplateArgument(0, refersToType(asString("int *")))),
-          Src, GetMode()),
+          Src, getMode()),
       // Evidence is emitted for the explicit specialization, not the template.
       UnorderedElementsAre(
           evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
@@ -4065,7 +4107,7 @@
   )cc";
   EXPECT_THAT(
       collectFromDefinitionMatching(functionDecl(isTemplateInstantiation()),
-                                    Src, GetMode()),
+                                    Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     localVarNamed("A", "tmpl<#I>")),
                            evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
@@ -4093,7 +4135,7 @@
       CCharPtr.method(nullptr);
     }
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("usage", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("usage", Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                            AllOf(functionNamed("method"),
@@ -4111,7 +4153,7 @@
   EXPECT_THAT(collectFromDefinitionMatching(
                   functionDecl(isTemplateInstantiation(),
                                hasParameter(0, hasType(asString("int *")))),
-                  Src, GetMode()),
+                  Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
                            AllOf(functionNamed("method"),
@@ -4145,7 +4187,7 @@
       CBoolPtr.method(nullptr);
     }
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("usage", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("usage", Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                            AllOf(functionNamed("method"),
@@ -4167,7 +4209,7 @@
   EXPECT_THAT(collectFromDefinitionMatching(
                   functionDecl(isTemplateInstantiation(),
                                hasParameter(0, hasType(asString("int *")))),
-                  Src, GetMode()),
+                  Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE,
                            AllOf(functionNamed("method"),
@@ -4204,7 +4246,7 @@
       CInt.Field = nullptr;
     }
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("usage", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("usage", Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                            AllOf(functionNamed("method"),
@@ -4239,7 +4281,7 @@
       CIntBool.Field = nullptr;
     }
   )cc";
-  EXPECT_THAT(collectFromDefinitionNamed("usage", Src, GetMode()),
+  EXPECT_THAT(collectFromDefinitionNamed("usage", Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
                            AllOf(functionNamed("method"),
@@ -4258,7 +4300,7 @@
   )cc";
   EXPECT_THAT(
       collectFromDefinitionMatching(varDecl(isTemplateInstantiation()), Src,
-                                    GetMode()),
+                                    getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     globalVarNamed("Global>#I"))));
 }
@@ -4279,7 +4321,7 @@
   )cc";
   EXPECT_THAT(
       collectFromDefinitionMatching(
-          varDecl(isVarTemplateCompleteSpecializationDecl()), Src, GetMode()),
+          varDecl(isVarTemplateCompleteSpecializationDecl()), Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     globalVarNamed("Global>#I"))));
 }
@@ -4297,7 +4339,7 @@
   )cc";
   EXPECT_THAT(
       collectFromDefinitionMatching(
-          varDecl(isVarTemplateCompleteSpecializationDecl()), Src, GetMode()),
+          varDecl(isVarTemplateCompleteSpecializationDecl()), Src, getMode()),
       UnorderedElementsAre(evidence(Slot(0), Evidence::ASSIGNED_FROM_NULLABLE,
                                     globalVarNamed("Global>#I#b"))));
 }
@@ -4333,14 +4375,14 @@
   // results contain the evidence needed to produce our expected inferences and
   // do not contain the evidence only found from propagating inferences from the
   // first round.
-  auto FirstRoundResults = collectFromTargetFuncDefinition(Src, GetMode());
+  auto FirstRoundResults = collectFromTargetFuncDefinition(Src, getMode());
   ASSERT_THAT(FirstRoundResults, IsSupersetOf(ExpectedBothRoundResults));
   for (const auto& E : ExpectedSecondRoundResults) {
     ASSERT_THAT(FirstRoundResults, Not(Contains(E)));
   }
 
   EXPECT_THAT(collectFromTargetFuncDefinition(
-                  Src, GetMode(),
+                  Src, getMode(),
                   {.Nullable = std::make_shared<SortedFingerprintVector>(
                        std::vector<SlotFingerprint>{
                            fingerprint(TargetUsr, paramSlot(0))}),
@@ -4394,7 +4436,7 @@
 
   // Assert first round results because they don't rely on previous inference
   // propagation at all and in this case are test setup and preconditions.
-  auto FirstRoundResults = collectFromTargetFuncDefinition(Src, GetMode());
+  auto FirstRoundResults = collectFromTargetFuncDefinition(Src, getMode());
   ASSERT_THAT(FirstRoundResults,
               IsSupersetOf(ExpectedNewResultsPerRound.at(0)));
   for (const auto& E : ExpectedNewResultsPerRound.at(1)) {
@@ -4402,7 +4444,7 @@
   }
 
   auto SecondRoundResults = collectFromTargetFuncDefinition(
-      Src, GetMode(),
+      Src, getMode(),
       {.Nonnull = std::make_shared<SortedFingerprintVector>(
            std::vector<SlotFingerprint>{
                fingerprint(TargetUsr, paramSlot(0))})});
@@ -4414,7 +4456,7 @@
   }
 
   auto ThirdRoundResults = collectFromTargetFuncDefinition(
-      Src, GetMode(),
+      Src, getMode(),
       {.Nonnull = std::make_shared<SortedFingerprintVector>(
            std::vector<SlotFingerprint>{
                fingerprint(TargetUsr, paramSlot(0)),
@@ -4428,7 +4470,7 @@
   }
 
   auto FourthRoundResults = collectFromTargetFuncDefinition(
-      Src, GetMode(),
+      Src, getMode(),
       {.Nonnull = std::make_shared<SortedFingerprintVector>(
            std::vector<SlotFingerprint>{
                fingerprint(TargetUsr, paramSlot(0)),
@@ -4458,7 +4500,7 @@
   // This test confirms that we use that information when collecting from
   // target's definition.
   EXPECT_THAT(collectFromTargetFuncDefinition(
-                  Src, GetMode(),
+                  Src, getMode(),
                   {.Nonnull = std::make_shared<SortedFingerprintVector>(
                        std::vector<SlotFingerprint>{
                            fingerprint(TakesToBeNonnullUsr, paramSlot(0))})}),
@@ -4473,7 +4515,7 @@
       return P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL)));
 }
@@ -4485,7 +4527,7 @@
       int* local_top_level_pointer = P;
     }
   )cc";
-  EXPECT_THAT(collectFromTargetFuncDefinition(Src, GetMode()),
+  EXPECT_THAT(collectFromTargetFuncDefinition(Src, getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL)));
 }
@@ -4500,7 +4542,7 @@
     }
   )cc";
   EXPECT_THAT(collectFromTargetFuncDefinition(
-                  (CheckMacroDefinitions + BaseSrc).str(), GetMode()),
+                  (CheckMacroDefinitions + BaseSrc).str(), getMode()),
               UnorderedElementsAre(
                   evidence(paramSlot(0), Evidence::ASSIGNED_TO_NONNULL),
                   evidence(paramSlot(0), Evidence::ABORT_IF_NULL)));
@@ -4530,7 +4572,7 @@
   auto& Decl = *selectFirst<VarTemplateSpecializationDecl>(
       "d", match(varDecl(isTemplateInstantiation()).bind("d"), AST.context()));
 
-  switch (GetMode()) {
+  switch (getMode()) {
     case CollectionMode::kTestWithSummaries:
       EXPECT_THAT_EXPECTED(
           summarizeDefinition(Decl, UsrCache, Pragmas),
@@ -4580,7 +4622,7 @@
   auto& Decl = *selectFirst<VarTemplateSpecializationDecl>(
       "d", match(varDecl(isTemplateInstantiation()).bind("d"), AST.context()));
 
-  switch (GetMode()) {
+  switch (getMode()) {
     case CollectionMode::kTestWithSummaries:
       EXPECT_THAT_EXPECTED(
           summarizeDefinition(Decl, UsrCache, Pragmas),
@@ -4619,7 +4661,7 @@
     return std::make_unique<dataflow::WatchedLiteralsSolver>(
         /*MaxSATIterations=*/100);
   };
-  switch (GetMode()) {
+  switch (getMode()) {
     case CollectionMode::kTestWithSummaries: {
       auto [Err, Results] = collectFromDefinitionViaSummaryWithErrors(
           AST, Decl, Pragmas,
@@ -4996,6 +5038,8 @@
     struct Action : public SyntaxOnlyAction {
       NullabilityPragmas& Pragmas;
       Action(NullabilityPragmas& Pragmas) : Pragmas(Pragmas) {}
+
+     protected:
       std::unique_ptr<ASTConsumer> CreateASTConsumer(
           CompilerInstance& CI, llvm::StringRef File) override {
         registerPragmaHandler(CI.getPreprocessor(), Pragmas);
diff --git a/nullability/inference/eligible_ranges.cc b/nullability/inference/eligible_ranges.cc
index 08e7bf1..6269749 100644
--- a/nullability/inference/eligible_ranges.cc
+++ b/nullability/inference/eligible_ranges.cc
@@ -85,6 +85,7 @@
       .Case(AbslMacroNullable, true)
       .Case(AbslMacroNonnull, true)
       .Case(AbslMacroUnknown, true)
+      .Case(AbslMacroConflict, true)
       .Default(false);
 }
 
diff --git a/nullability/inference/eligible_ranges_test.cc b/nullability/inference/eligible_ranges_test.cc
index ff7e0c3..23028fe 100644
--- a/nullability/inference/eligible_ranges_test.cc
+++ b/nullability/inference/eligible_ranges_test.cc
@@ -1455,6 +1455,18 @@
                       removalRanges({Input.range("")})))));
 }
 
+TEST(RemovalRangesTest, AbslMacroConflict) {
+  auto Input = Annotations(R"(
+  void target(int *$conflict^$conflict_removal[[ absl_nullability_conflict]] P);
+  )");
+  EXPECT_THAT(
+      getFunctionRanges(Input.code()),
+      AllOf(Each(AllOf(hasPath(MainFileName), hasNoPragmaNullability())),
+            UnorderedElementsAre(
+                AllOf(eligibleRange(1, Input.point("conflict")),
+                      removalRanges({Input.range("conflict_removal")})))));
+}
+
 TEST(RemovalRangesTest, SimpleAlias) {
   auto Input = Annotations(R"(
   using IntPtr = int *;
diff --git a/nullability/inference/infer_tu.cc b/nullability/inference/infer_tu.cc
index dfad556..7b8b4f1 100644
--- a/nullability/inference/infer_tu.cc
+++ b/nullability/inference/infer_tu.cc
@@ -20,6 +20,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -28,15 +29,42 @@
 
 class InferenceManager {
  public:
-  InferenceManager(ASTContext& Ctx, unsigned Iterations,
+  InferenceManager(ASTContext& Ctx, bool UseSummaries, unsigned Iterations,
                    llvm::function_ref<bool(const Decl&)> Filter,
                    const NullabilityPragmas& Pragmas)
-      : Ctx(Ctx), Iterations(Iterations), Filter(Filter), Pragmas(Pragmas) {}
+      : Ctx(Ctx),
+        UseSummaries(UseSummaries),
+        Iterations(Iterations),
+        Filter(Filter),
+        Pragmas(Pragmas) {}
 
-  InferenceResults inferenceRound(
+  InferenceResults groupAndMergeEvidence(
+      std::vector<Evidence> AllEvidence) const {
+    // Group by symbol and then slot number.
+    llvm::sort(AllEvidence, [&](const Evidence& L, const Evidence& R) {
+      if (L.symbol().usr() != R.symbol().usr())
+        return L.symbol().usr() < R.symbol().usr();
+      return L.slot() < R.slot();
+    });
+    // For each symbol, for each slot, combine evidence into an inference.
+    llvm::ArrayRef<Evidence> RemainingEvidence = AllEvidence;
+
+    InferenceResults AllInference;
+    while (!RemainingEvidence.empty()) {
+      auto Batch = RemainingEvidence.take_while([&](const Evidence& E) {
+        return E.symbol().usr() == RemainingEvidence.front().symbol().usr() &&
+               E.slot() == RemainingEvidence.front().slot();
+      });
+      RemainingEvidence = RemainingEvidence.drop_front(Batch.size());
+      AllInference[Batch.front().symbol().usr()][Slot(Batch.front().slot())] =
+          mergeEvidence(Batch);
+    }
+    return AllInference;
+  }
+
+  InferenceResults inferenceRoundWithAST(
       EvidenceSites Sites, USRCache USRCache,
       const PreviousInferences& InferencesFromLastRound) const {
-    InferenceResults AllInference;
     std::vector<Evidence> AllEvidence;
 
     // Collect all evidence.
@@ -55,25 +83,78 @@
                      << toString(std::move(Err)) << "\n";
       }
     }
-    // Group by symbol and then slot number.
-    llvm::sort(AllEvidence, [&](const Evidence& L, const Evidence& R) {
-      if (L.symbol().usr() != R.symbol().usr())
-        return L.symbol().usr() < R.symbol().usr();
-      return L.slot() < R.slot();
-    });
-    // For each symbol, for each slot, combine evidence into an inference.
-    llvm::ArrayRef<Evidence> RemainingEvidence = AllEvidence;
 
-    while (!RemainingEvidence.empty()) {
-      auto Batch = RemainingEvidence.take_while([&](const Evidence& E) {
-        return E.symbol().usr() == RemainingEvidence.front().symbol().usr() &&
-               E.slot() == RemainingEvidence.front().slot();
-      });
-      RemainingEvidence = RemainingEvidence.drop_front(Batch.size());
-      AllInference[Batch.front().symbol().usr()][Slot(Batch.front().slot())] =
-          mergeEvidence(Batch);
+    return groupAndMergeEvidence(std::move(AllEvidence));
+  }
+
+  struct FunctionSummariesAndEvidence {
+    TUSummary Summary;
+    std::vector<Evidence> DeclarationsEvidence;
+    llvm::StringMap<MethodSummary> BaseToOverrides;
+  };
+
+  FunctionSummariesAndEvidence summarizeFromEvidenceSites(
+      const EvidenceSites& Sites, USRCache& USRCache) const {
+    FunctionSummariesAndEvidence Result;
+
+    VirtualMethodIndex VMI = getVirtualMethodIndex(Ctx, USRCache);
+    // Test the that we can properly round-trip parts of the VMI
+    // with saveVirtualMethodsMap and loadVirtualMethodsMap.
+    *Result.Summary.mutable_overrides_to_bases() =
+        saveVirtualMethodsMap(VMI.Bases);
+
+    Result.BaseToOverrides = VMI.Overrides;
+
+    // Collect evidence for decls, and summaries for definitions.
+    auto DeclEmitter = evidenceEmitterWithPropagation(
+        [&](Evidence E) { Result.DeclarationsEvidence.push_back(E); },
+        std::move(VMI));
+    for (const auto* Decl : Sites.Declarations) {
+      if (Filter && !Filter(*Decl)) continue;
+      collectEvidenceFromTargetDeclaration(*Decl, DeclEmitter, USRCache,
+                                           Pragmas);
     }
-    return AllInference;
+    if (auto MainFile = Ctx.getSourceManager().getFileEntryRefForID(
+            Ctx.getSourceManager().getMainFileID()))
+      *Result.Summary.mutable_path() = MainFile->getName().str();
+    for (const auto* Impl : Sites.Definitions) {
+      if (Filter && !Filter(*Impl)) continue;
+
+      if (llvm::Expected<CFGSummary> Summary =
+              summarizeDefinition(*Impl, USRCache, Pragmas)) {
+        *Result.Summary.add_cfg_summaries() = *std::move(Summary);
+      } else {
+        llvm::errs() << "Error summarizing definition: " << Summary.takeError()
+                     << "\n";
+      }
+    }
+    return Result;
+  }
+
+  InferenceResults inferenceRoundWithSummaries(
+      const FunctionSummariesAndEvidence& SummariesAndEvidence,
+      const PreviousInferences& InferencesFromLastRound) const {
+    std::vector<Evidence> AllEvidence =
+        SummariesAndEvidence.DeclarationsEvidence;
+
+    VirtualMethodIndex VMI;
+    VMI.Bases = loadVirtualMethodsMap(
+        SummariesAndEvidence.Summary.overrides_to_bases());
+    VMI.Overrides = SummariesAndEvidence.BaseToOverrides;
+
+    // Collect evidence from summaries.
+    auto Emitter = evidenceEmitterWithPropagation(
+        [&](Evidence E) { AllEvidence.push_back(E); }, std::move(VMI));
+    for (const auto& FuncSummary :
+         SummariesAndEvidence.Summary.cfg_summaries()) {
+      if (llvm::Error Err = collectEvidenceFromSummary(
+              FuncSummary, Emitter, InferencesFromLastRound)) {
+        llvm::errs() << "Error collecting evidence from summary "
+                     << llvm::toString(std::move(Err)) << "\n";
+      }
+    }
+
+    return groupAndMergeEvidence(std::move(AllEvidence));
   }
 
   InferenceResults iterativelyInfer() const {
@@ -89,9 +170,13 @@
     auto Sites = EvidenceSites::discover(Ctx);
     USRCache USRCache;
 
-    InferenceResults AllInference = inferenceRound(Sites, USRCache, {});
+    InferenceResults AllInference;
+    FunctionSummariesAndEvidence SummariesAndEvidence;
+    if (UseSummaries) {
+      SummariesAndEvidence = summarizeFromEvidenceSites(Sites, USRCache);
+    }
 
-    for (unsigned Iteration = 1; Iteration < Iterations; ++Iteration) {
+    for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) {
       std::vector<SlotFingerprint> NullableFromLastRound;
       std::vector<SlotFingerprint> NonnullFromLastRound;
 
@@ -111,18 +196,28 @@
         }
       }
 
-      AllInference =
-          inferenceRound(Sites, USRCache,
-                         {.Nullable = std::make_shared<SortedFingerprintVector>(
-                              std::move(NullableFromLastRound)),
-                          .Nonnull = std::make_shared<SortedFingerprintVector>(
-                              std::move(NonnullFromLastRound))});
+      if (UseSummaries) {
+        AllInference = inferenceRoundWithSummaries(
+            SummariesAndEvidence,
+            {.Nullable = std::make_shared<SortedFingerprintVector>(
+                 std::move(NullableFromLastRound)),
+             .Nonnull = std::make_shared<SortedFingerprintVector>(
+                 std::move(NonnullFromLastRound))});
+      } else {
+        AllInference = inferenceRoundWithAST(
+            Sites, USRCache,
+            {.Nullable = std::make_shared<SortedFingerprintVector>(
+                 std::move(NullableFromLastRound)),
+             .Nonnull = std::make_shared<SortedFingerprintVector>(
+                 std::move(NonnullFromLastRound))});
+      }
     }
     return AllInference;
   }
 
  private:
   ASTContext& Ctx;
+  bool UseSummaries;
   unsigned Iterations;
   llvm::function_ref<bool(const Decl&)> Filter;
   const NullabilityPragmas& Pragmas;
@@ -130,9 +225,10 @@
 }  // namespace
 
 InferenceResults inferTU(ASTContext& Ctx, const NullabilityPragmas& Pragmas,
-                         unsigned Iterations,
+                         bool UseSummaries, unsigned Iterations,
                          llvm::function_ref<bool(const Decl&)> Filter) {
-  return InferenceManager(Ctx, Iterations, Filter, Pragmas).iterativelyInfer();
+  return InferenceManager(Ctx, UseSummaries, Iterations, Filter, Pragmas)
+      .iterativelyInfer();
 }
 
 }  // namespace clang::tidy::nullability
diff --git a/nullability/inference/infer_tu.h b/nullability/inference/infer_tu.h
index e62116a..c786ae1 100644
--- a/nullability/inference/infer_tu.h
+++ b/nullability/inference/infer_tu.h
@@ -26,10 +26,13 @@
 // useful in observing the behavior of the inference system.
 // It also lets us write tests for the whole inference system.
 //
+// If UseSummaries is true, generates the AST only once to produce a Summary
+// and then uses that Summary for iteration.
 // If Filter is provided, only considers decls that return true.
 InferenceResults inferTU(
-    ASTContext &, const NullabilityPragmas &, unsigned Iterations = 1,
-    llvm::function_ref<bool(const Decl &)> Filter = nullptr);
+    ASTContext&, const NullabilityPragmas&, bool UseSummaries,
+    unsigned Iterations = 1,
+    llvm::function_ref<bool(const Decl&)> Filter = nullptr);
 
 }  // namespace clang::tidy::nullability
 
diff --git a/nullability/inference/infer_tu_main.cc b/nullability/inference/infer_tu_main.cc
index 539c984..646e293 100644
--- a/nullability/inference/infer_tu_main.cc
+++ b/nullability/inference/infer_tu_main.cc
@@ -75,6 +75,13 @@
     llvm::cl::desc("Include trivial inferences (annotated, no conflicts)"),
     llvm::cl::init(false),
 };
+// TODO: b/417692223 remove this flag once summaries are the default.
+llvm::cl::opt<bool> UseSummaries{
+    "use-summaries",
+    llvm::cl::desc("Generate the AST only once to produce a Summary and then "
+                   "use that Summary for iteration"),
+    llvm::cl::init(false),
+};
 llvm::cl::opt<std::string> FileFilter{
     "file-filter",
     llvm::cl::desc("Regular expression filenames must match to be analyzed. "
@@ -232,7 +239,7 @@
         llvm::errs() << "Running inference...\n";
 
         InferenceResults Results =
-            inferTU(Ctx, Pragmas, Iterations, DeclFilter());
+            inferTU(Ctx, Pragmas, UseSummaries, Iterations, DeclFilter());
         if (PrintProtos) {
           for (const auto &[USR, InferencesBySlot] : Results) {
             llvm::outs() << "USR: " << absl::StrCat(USR) << "\n";
diff --git a/nullability/inference/infer_tu_test.cc b/nullability/inference/infer_tu_test.cc
index ee5aafe..63b9b8d 100644
--- a/nullability/inference/infer_tu_test.cc
+++ b/nullability/inference/infer_tu_test.cc
@@ -22,6 +22,7 @@
 #include "clang/Testing/TestAST.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "external/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h"
 #include "external/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
 
@@ -62,7 +63,28 @@
 
 AST_MATCHER(Decl, isCanonical) { return Node.isCanonicalDecl(); }
 
-class InferTUTest : public ::testing::Test {
+// TODO: b/417692223 remove the parameterization once summaries are the default.
+enum class InferenceMode {
+  kTestWithSummaries,
+  kTestDirectly,
+};
+
+}  // namespace
+
+// Helper to get a string representation of the InferenceMode for test names.
+static std::string printToString(InferenceMode Mode) {
+  switch (Mode) {
+    case InferenceMode::kTestWithSummaries:
+      return "WithSummaries";
+    case InferenceMode::kTestDirectly:
+      return "Directly";
+  }
+  llvm_unreachable("Unknown InferenceMode");
+}
+
+namespace {
+
+class InferTUTest : public testing::TestWithParam<InferenceMode> {
  protected:
   std::optional<TestAST> AST;
   NullabilityPragmas Pragmas;
@@ -71,7 +93,10 @@
     AST.emplace(getAugmentedTestInputs(Code, Pragmas));
   }
 
-  auto infer() { return inferTU(AST->context(), Pragmas); }
+  auto infer() {
+    return inferTU(AST->context(), Pragmas,
+                   GetParam() == InferenceMode::kTestWithSummaries);
+  }
 
   // Returns a matcher for an InferenceResults entry.
   // The DeclMatcher should uniquely identify the symbol being described.
@@ -95,7 +120,14 @@
   }
 };
 
-TEST_F(InferTUTest, UncheckedDeref) {
+INSTANTIATE_TEST_SUITE_P(InferTUTests, InferTUTest,
+                         testing::Values(InferenceMode::kTestWithSummaries,
+                                         InferenceMode::kTestDirectly),
+                         [](const testing::TestParamInfo<InferenceMode>& Info) {
+                           return printToString(Info.param);
+                         });
+
+TEST_P(InferTUTest, UncheckedDeref) {
   build(R"cc(
     void target(int *P, bool Cond) {
       if (Cond) *P;
@@ -111,7 +143,7 @@
                                     {inferredSlot(1, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUTest, Samples) {
+TEST_P(InferTUTest, Samples) {
   llvm::StringRef Code =
       "void target(int * P) { *P + *P; }\n"
       "void another(int X) { target(&X); }";
@@ -133,7 +165,7 @@
                                    kind: UNCHECKED_DEREFERENCE)pb")));
 }
 
-TEST_F(InferTUTest, Annotations) {
+TEST_P(InferTUTest, Annotations) {
   build(R"cc(
     int *_Nonnull target(int *A, int *B);
     int *_Nonnull target(int *A, int *_Nullable P) { *P; }
@@ -147,7 +179,7 @@
                                     })));
 }
 
-TEST_F(InferTUTest, AnnotationsConflict) {
+TEST_P(InferTUTest, AnnotationsConflict) {
   build(R"cc(
     int *_Nonnull target();
     int *_Nullable target();
@@ -158,7 +190,7 @@
                                     {inferredSlot(0, Nullability::UNKNOWN)})));
 }
 
-TEST_F(InferTUTest, ParamsFromCallSite) {
+TEST_P(InferTUTest, ParamsFromCallSite) {
   build(R"cc(
     void callee(int *P, int *Q, int *R);
     void target(int *A, int *_Nonnull B, int *_Nullable C) { callee(A, B, C); }
@@ -173,7 +205,7 @@
                                  })));
 }
 
-TEST_F(InferTUTest, ReturnTypeNullable) {
+TEST_P(InferTUTest, ReturnTypeNullable) {
   build(R"cc(
     int* target() { return nullptr; }
   )cc");
@@ -182,7 +214,7 @@
                                     {inferredSlot(0, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, ReturnTypeNonnull) {
+TEST_P(InferTUTest, ReturnTypeNonnull) {
   build(R"cc(
     int *_Nonnull providesNonnull();
     int *target() { return providesNonnull(); }
@@ -192,7 +224,7 @@
                                  {inferredSlot(0, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUTest, ReturnTypeNonnullAndUnknown) {
+TEST_P(InferTUTest, ReturnTypeNonnullAndUnknown) {
   build(R"cc(
     int *_Nonnull providesNonnull();
     int *target(bool B, int *Q) {
@@ -205,7 +237,7 @@
                                  {inferredSlot(0, Nullability::UNKNOWN)})));
 }
 
-TEST_F(InferTUTest, ReturnTypeNonnullAndNullable) {
+TEST_P(InferTUTest, ReturnTypeNonnullAndNullable) {
   build(R"cc(
     int *_Nonnull providesNonnull();
     int *target(bool B) {
@@ -218,7 +250,7 @@
                                  {inferredSlot(0, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, ReturnTypeDereferenced) {
+TEST_P(InferTUTest, ReturnTypeDereferenced) {
   build(R"cc(
     struct S {
       void member();
@@ -232,7 +264,7 @@
                                     {inferredSlot(0, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUTest, PassedToNonnull) {
+TEST_P(InferTUTest, PassedToNonnull) {
   build(R"cc(
     void takesNonnull(int *_Nonnull);
     void target(int *P) { takesNonnull(P); }
@@ -242,7 +274,7 @@
                                  {inferredSlot(1, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUTest, PassedToMutableNullableRef) {
+TEST_P(InferTUTest, PassedToMutableNullableRef) {
   build(R"cc(
     void takesMutableNullableRef(int *_Nullable &);
     void target(int *P) { takesMutableNullableRef(P); }
@@ -252,7 +284,7 @@
                                  {inferredSlot(1, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, AssignedFromNullable) {
+TEST_P(InferTUTest, AssignedFromNullable) {
   build(R"cc(
     void target(int* P) { P = nullptr; }
   )cc");
@@ -261,7 +293,7 @@
                                  {inferredSlot(1, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, CHECKMacro) {
+TEST_P(InferTUTest, CHECKMacro) {
   build(R"cc(
     // macro must use the parameter, but otherwise body doesn't matter
 #define CHECK(X) X
@@ -272,7 +304,7 @@
                                  {inferredSlot(1, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUTest, CHECKNEMacro) {
+TEST_P(InferTUTest, CHECKNEMacro) {
   build(R"cc(
     // macro must use the first parameter, but otherwise body doesn't matter
 #define CHECK_NE(X, Y) X
@@ -295,7 +327,7 @@
            inference(hasName("A"), {inferredSlot(0, Nullability::NULLABLE)})}));
 }
 
-TEST_F(InferTUTest, Fields) {
+TEST_P(InferTUTest, Fields) {
   build(R"cc(
     int* getIntPtr();
     struct S {
@@ -353,7 +385,7 @@
                     {inferredSlot(0, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, FieldsImplicitlyDeclaredConstructorNeverUsed) {
+TEST_P(InferTUTest, FieldsImplicitlyDeclaredConstructorNeverUsed) {
   build(R"cc(
     bool *_Nullable getNullable();
     struct S {
@@ -373,7 +405,7 @@
                           Not(Contains(inference(hasName("C"), {_}))))));
 }
 
-TEST_F(InferTUTest, FieldsImplicitlyDeclaredConstructorUsed) {
+TEST_P(InferTUTest, FieldsImplicitlyDeclaredConstructorUsed) {
   build(R"cc(
     bool *_Nullable getNullable();
     struct S {
@@ -394,7 +426,7 @@
            inference(hasName("C"), {inferredSlot(0, Nullability::NULLABLE)})}));
 }
 
-TEST_F(InferTUTest, ConstructorCallThroughMakeUnique) {
+TEST_P(InferTUTest, ConstructorCallThroughMakeUnique) {
   build(R"cc(
 #include <memory>
     struct S {
@@ -414,7 +446,7 @@
               }));
 }
 
-TEST_F(InferTUTest, ConstructorCallWithConversionOperator) {
+TEST_P(InferTUTest, ConstructorCallWithConversionOperator) {
   build(R"cc(
 #include <memory>
     struct S {
@@ -437,7 +469,7 @@
               }));
 }
 
-TEST_F(InferTUTest, ConstructorCallThroughMakeUniqueWithConversionOperator) {
+TEST_P(InferTUTest, ConstructorCallThroughMakeUniqueWithConversionOperator) {
   build(R"cc(
 #include <memory>
     struct S {
@@ -460,7 +492,7 @@
               }));
 }
 
-TEST_F(InferTUTest, GlobalVariables) {
+TEST_P(InferTUTest, GlobalVariables) {
   build(R"cc(
     int* getIntPtr();
 
@@ -482,7 +514,7 @@
                     {inferredSlot(0, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUTest, StaticMemberVariables) {
+TEST_P(InferTUTest, StaticMemberVariables) {
   build(R"cc(
     struct S {
       static int* SI;
@@ -501,7 +533,7 @@
           inference(hasName("SB"), {inferredSlot(0, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, Locals) {
+TEST_P(InferTUTest, Locals) {
   build(R"cc(
     void target() {
       int* A = nullptr;
@@ -515,20 +547,22 @@
           inference(hasName("B"), {inferredSlot(0, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, Filter) {
+TEST_P(InferTUTest, Filter) {
   build(R"cc(
     int* target1() { return nullptr; }
     int* target2() { return nullptr; }
   )cc");
-  EXPECT_THAT(inferTU(AST->context(), Pragmas, /*Iterations=*/1,
-                      [&](const Decl &D) {
+  EXPECT_THAT(inferTU(AST->context(), Pragmas,
+                      GetParam() == InferenceMode::kTestWithSummaries,
+                      /*Iterations=*/1,
+                      [&](const Decl& D) {
                         return cast<NamedDecl>(D).getNameAsString() !=
                                "target2";
                       }),
               ElementsAre(inference(hasName("target1"), {_})));
 }
 
-TEST_F(InferTUTest, AutoNoStarType) {
+TEST_P(InferTUTest, AutoNoStarType) {
   build(R"cc(
     int *_Nullable getNullable();
     int *_Nonnull getNonnull();
@@ -592,7 +626,7 @@
               {inferredSlot(0, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, AutoStarType) {
+TEST_P(InferTUTest, AutoStarType) {
   build(R"cc(
     int *_Nullable getNullable();
 
@@ -657,7 +691,7 @@
                     {inferredSlot(0, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUTest, IterationsPropagateInferences) {
+TEST_P(InferTUTest, IterationsPropagateInferences) {
   build(R"cc(
     void takesToBeNonnull(int* X) { *X; }
     int* returnsToBeNonnull(int* A) { return A; }
@@ -668,8 +702,10 @@
       return returnsToBeNonnull(P);
     }
   )cc");
+  bool UseSummaries = GetParam() == InferenceMode::kTestWithSummaries;
   EXPECT_THAT(
-      inferTU(AST->context(), Pragmas, /*Iterations=*/1),
+      inferTU(AST->context(), Pragmas, UseSummaries,
+              /*Iterations=*/1),
       UnorderedElementsAre(
           inference(hasName("target"), {inferredSlot(0, Nullability::UNKNOWN),
                                         inferredSlot(1, Nullability::NONNULL),
@@ -680,7 +716,7 @@
           inference(hasName("takesToBeNonnull"),
                     {inferredSlot(1, Nullability::NONNULL)})));
   EXPECT_THAT(
-      inferTU(AST->context(), Pragmas, /*Iterations=*/2),
+      inferTU(AST->context(), Pragmas, UseSummaries, /*Iterations=*/2),
       UnorderedElementsAre(
           inference(hasName("target"), {inferredSlot(0, Nullability::UNKNOWN),
                                         inferredSlot(1, Nullability::NONNULL),
@@ -691,7 +727,7 @@
           inference(hasName("takesToBeNonnull"),
                     {inferredSlot(1, Nullability::NONNULL)})));
   EXPECT_THAT(
-      inferTU(AST->context(), Pragmas, /*Iterations=*/3),
+      inferTU(AST->context(), Pragmas, UseSummaries, /*Iterations=*/3),
       UnorderedElementsAre(
           inference(hasName("target"), {inferredSlot(0, Nullability::UNKNOWN),
                                         inferredSlot(1, Nullability::NONNULL),
@@ -703,7 +739,7 @@
           inference(hasName("takesToBeNonnull"),
                     {inferredSlot(1, Nullability::NONNULL)})));
   EXPECT_THAT(
-      inferTU(AST->context(), Pragmas, /*Iterations=*/4),
+      inferTU(AST->context(), Pragmas, UseSummaries, /*Iterations=*/4),
       UnorderedElementsAre(
           inference(hasName("target"), {inferredSlot(0, Nullability::NONNULL),
                                         inferredSlot(1, Nullability::NONNULL),
@@ -718,7 +754,7 @@
 
 // This tests a case where the initial analysis before inference queries the SAT
 // solver (when doing a Join).
-TEST_F(InferTUTest, MultipleIterationsWithJoinAndDeadCode) {
+TEST_P(InferTUTest, MultipleIterationsWithJoinAndDeadCode) {
   build(R"cc(
     int* otherFunction(int* P) {
       *P = 0;
@@ -744,7 +780,9 @@
     void caller(int X) { target(&X, nullptr); }
   )cc");
   EXPECT_THAT(
-      inferTU(AST->context(), Pragmas, /*Iterations=*/3),
+      inferTU(AST->context(), Pragmas,
+              GetParam() == InferenceMode::kTestWithSummaries,
+              /*Iterations=*/3),
       UnorderedElementsAre(inference(hasName("otherFunction"),
                                      {inferredSlot(0, Nullability::NONNULL),
                                       inferredSlot(1, Nullability::NONNULL)}),
@@ -758,7 +796,7 @@
                                      {inferredSlot(0, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUTest, Pragma) {
+TEST_P(InferTUTest, Pragma) {
   build(R"cc(
 #pragma nullability file_default nonnull
     void target(int *DefaultNonnull, int *_Null_unspecified InferredNonnull,
@@ -790,7 +828,7 @@
                   })));
 }
 
-TEST_F(InferTUTest, FunctionTemplate) {
+TEST_P(InferTUTest, FunctionTemplate) {
   build(R"cc(
     template <typename T>
     T functionTemplate(int *P, int *_Nullable Q, T *R, T *_Nullable S, T U) {
@@ -837,7 +875,7 @@
                       inferredSlot(4, Nullability::NULLABLE)})}));
 }
 
-TEST_F(InferTUTest, LambdaWithCaptureInit) {
+TEST_P(InferTUTest, LambdaWithCaptureInit) {
   build(R"cc(
     void foo() {
       int* P;
@@ -845,7 +883,9 @@
     }
   )cc");
   EXPECT_THAT(
-      inferTU(AST->context(), Pragmas, /*Iterations=*/2),
+      inferTU(AST->context(), Pragmas,
+              GetParam() == InferenceMode::kTestWithSummaries,
+              /*Iterations=*/2),
       UnorderedElementsAre(
           inference(hasName("P"), {inferredSlot(0, Nullability::NONNULL)}),
           inference(hasName("Q"), {inferredSlot(0, Nullability::NONNULL)})));
@@ -853,7 +893,14 @@
 
 using InferTUSmartPointerTest = InferTUTest;
 
-TEST_F(InferTUSmartPointerTest, Annotations) {
+INSTANTIATE_TEST_SUITE_P(InferTUSmartPointerTests, InferTUSmartPointerTest,
+                         testing::Values(InferenceMode::kTestWithSummaries,
+                                         InferenceMode::kTestDirectly),
+                         [](const testing::TestParamInfo<InferenceMode>& Info) {
+                           return printToString(Info.param);
+                         });
+
+TEST_P(InferTUSmartPointerTest, Annotations) {
   build(R"cc(
 #include <memory>
     _Nonnull std::unique_ptr<int> target(std::unique_ptr<int> A,
@@ -872,7 +919,7 @@
                                  })));
 }
 
-TEST_F(InferTUSmartPointerTest, ParamsFromCallSite) {
+TEST_P(InferTUSmartPointerTest, ParamsFromCallSite) {
   build(R"cc(
 #include <memory>
 #include <utility>
@@ -893,7 +940,7 @@
                                  })));
 }
 
-TEST_F(InferTUSmartPointerTest, ReturnTypeNullable) {
+TEST_P(InferTUSmartPointerTest, ReturnTypeNullable) {
   build(R"cc(
 #include <memory>
     std::unique_ptr<int> target() { return std::unique_ptr<int>(); }
@@ -903,7 +950,7 @@
                                  {inferredSlot(0, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUSmartPointerTest, ReturnTypeNonnull) {
+TEST_P(InferTUSmartPointerTest, ReturnTypeNonnull) {
   build(R"cc(
 #include <memory>
     std::unique_ptr<int> target() { return std::make_unique<int>(0); }
@@ -913,7 +960,7 @@
                                  {inferredSlot(0, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUSmartPointerTest,
+TEST_P(InferTUSmartPointerTest,
        DefaultFieldInitializersAbsentSomeLateInitializationInTestSetUp) {
   build(R"cc(
 #include <memory>
@@ -979,7 +1026,14 @@
 
 using InferTUVirtualMethodsTest = InferTUTest;
 
-TEST_F(InferTUVirtualMethodsTest, SafeVarianceNoConflicts) {
+INSTANTIATE_TEST_SUITE_P(InferTUVirtualMethodsTests, InferTUVirtualMethodsTest,
+                         testing::Values(InferenceMode::kTestWithSummaries,
+                                         InferenceMode::kTestDirectly),
+                         [](const testing::TestParamInfo<InferenceMode>& Info) {
+                           return printToString(Info.param);
+                         });
+
+TEST_P(InferTUVirtualMethodsTest, SafeVarianceNoConflicts) {
   build(R"cc(
     struct Base {
       virtual int* foo(int* P) {
@@ -1007,7 +1061,7 @@
                              inferredSlot(1, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUVirtualMethodsTest, BaseConstrainsDerived) {
+TEST_P(InferTUVirtualMethodsTest, BaseConstrainsDerived) {
   build(R"cc(
     struct Base {
       virtual int *_Nonnull foo(int *P) {
@@ -1032,7 +1086,7 @@
                              inferredSlot(1, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUVirtualMethodsTest, DerivedConstrainsBase) {
+TEST_P(InferTUVirtualMethodsTest, DerivedConstrainsBase) {
   build(R"cc(
     struct Base {
       virtual int* foo(int* P);
@@ -1055,7 +1109,7 @@
                                       inferredSlot(1, Nullability::NONNULL)})));
 }
 
-TEST_F(InferTUVirtualMethodsTest, Conflict) {
+TEST_P(InferTUVirtualMethodsTest, Conflict) {
   build(R"cc(
     struct Base {
       virtual int* foo(int* P);
@@ -1089,7 +1143,7 @@
                inferredSlot(1, Nullability::NONNULL, /*Conflict*/ true)})));
 }
 
-TEST_F(InferTUVirtualMethodsTest, MultipleDerived) {
+TEST_P(InferTUVirtualMethodsTest, MultipleDerived) {
   build(R"cc(
     struct Base {
       virtual void foo(int* P) { P = nullptr; }
@@ -1113,7 +1167,7 @@
                             {inferredSlot(1, Nullability::NULLABLE)})));
 }
 
-TEST_F(InferTUVirtualMethodsTest, MultipleBase) {
+TEST_P(InferTUVirtualMethodsTest, MultipleBase) {
   build(R"cc(
     struct BaseA {
       virtual void foo(int* P);
diff --git a/nullability/inference/inferable.cc b/nullability/inference/inferable.cc
index a2ae15c..aada32f 100644
--- a/nullability/inference/inferable.cc
+++ b/nullability/inference/inferable.cc
@@ -7,6 +7,7 @@
 #include <cassert>
 #include <optional>
 
+#include "nullability/inference/inference.proto.h"
 #include "nullability/type_nullability.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
@@ -17,6 +18,7 @@
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -198,6 +200,31 @@
   return 0;
 }
 
+llvm::SmallVector<int> getInferableSlotIndices(const Decl& D) {
+  if (const auto* Func = dyn_cast<FunctionDecl>(&D)) {
+    llvm::SmallVector<int> Slots;
+    if (hasInferable(Func->getReturnType())) Slots.push_back(SLOT_RETURN_TYPE);
+    // Intentionally match the iteration pattern evidence collection uses over
+    // function parameters when gathering inferable slots, to avoid any subtle
+    // differences for complex function contexts.
+    auto Parameters = Func->parameters();
+    for (auto I = 0; I < Parameters.size(); ++I) {
+      const ParmVarDecl* Param = Parameters[I];
+      if (hasInferable(Param->getType())) Slots.push_back(I + SLOT_PARAM);
+    }
+    return Slots;
+  }
+  if (const auto* Field = dyn_cast<FieldDecl>(&D)) {
+    if (hasInferable(Field->getType())) return {0};
+    return {};
+  }
+  if (const auto* Var = dyn_cast<VarDecl>(&D)) {
+    if (hasInferable(Var->getType())) return {0};
+    return {};
+  }
+  return {};
+}
+
 bool isInferenceTarget(const Decl& D) {
   if (const auto* Func = dyn_cast<FunctionDecl>(&D)) {
     return
diff --git a/nullability/inference/inferable.h b/nullability/inference/inferable.h
index 54a0921..2568427 100644
--- a/nullability/inference/inferable.h
+++ b/nullability/inference/inferable.h
@@ -7,6 +7,7 @@
 
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/Type.h"
+#include "llvm/ADT/SmallVector.h"
 
 namespace clang::tidy::nullability {
 
@@ -19,9 +20,16 @@
 /// The number of nullability slots in this symbol's type which can be inferred.
 ///
 /// This may not be all the slots in the type: e.g. `int** X` has outer and
-/// inner nullability, we may support only inferring outer.
+/// inner nullability; we may support only inferring outer.
 int countInferableSlots(const clang::Decl &);
 
+/// The indices of nullability slots in this symbol's type which can be
+/// inferred.
+///
+/// This may not be all the slots in the type: e.g. `int** X` has outer and
+/// inner nullability; we may support only inferring outer.
+llvm::SmallVector<int> getInferableSlotIndices(const clang::Decl&);
+
 }  // namespace clang::tidy::nullability
 
 #endif  // THIRD_PARTY_CRUBIT_NULLABILITY_INFERENCE_INFERRABLE_H_
diff --git a/nullability/inference/inferable_test.cc b/nullability/inference/inferable_test.cc
index 9df12e7..d1fbacf 100644
--- a/nullability/inference/inferable_test.cc
+++ b/nullability/inference/inferable_test.cc
@@ -15,10 +15,10 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Testing/TestAST.h"
 #include "llvm/ADT/StringRef.h"
+#include "external/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h"
 #include "external/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
 
 namespace clang::tidy::nullability {
-namespace {
 using ::clang::ast_matchers::anything;
 using ::clang::ast_matchers::equalsNode;
 using ::clang::ast_matchers::functionDecl;
@@ -30,19 +30,21 @@
 using ::clang::ast_matchers::namedDecl;
 using ::clang::ast_matchers::selectFirst;
 using ::clang::ast_matchers::unless;
+using ::testing::IsEmpty;
+using ::testing::UnorderedElementsAre;
 
 template <class T = NamedDecl>
-const T &lookup(llvm::StringRef Name, ASTContext &Ctx,
-                const Decl *DeclContext = nullptr) {
-  const auto &ContextMatcher =
+static const T& lookup(llvm::StringRef Name, ASTContext& Ctx,
+                       const Decl* DeclContext = nullptr) {
+  const auto& ContextMatcher =
       DeclContext ? hasDeclContext(equalsNode(DeclContext)) : anything();
-  const auto &BoundNodes =
+  const auto& BoundNodes =
       match(namedDecl(hasName(Name), ContextMatcher, unless(isImplicit()))
                 .bind("decl"),
             Ctx);
-  const T *Match = nullptr;
-  for (const auto &N : BoundNodes) {
-    if (const auto *NAsT = N.getNodeAs<T>("decl")) {
+  const T* Match = nullptr;
+  for (const auto& N : BoundNodes) {
+    if (const auto* NAsT = N.getNodeAs<T>("decl")) {
       if (Match)
         ADD_FAILURE() << "Found more than one matching node for " << Name;
       Match = NAsT;
@@ -52,6 +54,7 @@
   return *Match;
 }
 
+namespace {
 constexpr llvm::StringRef SmartPointerHeader = R"cc(
   namespace std {
   template <typename T>
@@ -386,7 +389,8 @@
     int*** ThreePointersOneInferable;
   )cc");
   auto &Ctx = AST.context();
-  EXPECT_EQ(1, countInferableSlots(lookup("ThreePointersOneInferable", Ctx)));
+  EXPECT_THAT(getInferableSlotIndices(lookup("ThreePointersOneInferable", Ctx)),
+              UnorderedElementsAre(0));
 }
 
 TEST(InferableTest, TemplateArgumentPointersNotInferable) {
@@ -630,5 +634,52 @@
   }
 }
 
+TEST(InferableTest, GetInferableSlotIndices) {
+  TestAST AST((SmartPointerHeader + R"cc(
+                void f1(int**, int, char, char*);
+                int* f2(bool, bool*);
+                void f3(std::unique_ptr<int>);
+                void f4(custom_smart_ptr<int>);
+
+                int v1;
+                int* v2;
+                int** v3;
+
+                class C {
+                  int field1;
+                  int* field2;
+                  int** field3;
+
+                  int* method(bool, bool*);
+                };
+              )cc")
+                  .str());
+  auto& Ctx = AST.context();
+
+  EXPECT_THAT(getInferableSlotIndices(lookup("f1", Ctx)),
+              UnorderedElementsAre(1, 4));
+  EXPECT_THAT(getInferableSlotIndices(lookup("f2", Ctx)),
+              UnorderedElementsAre(0, 2));
+  EXPECT_THAT(getInferableSlotIndices(lookup("f3", Ctx)),
+              UnorderedElementsAre(1));
+  EXPECT_THAT(getInferableSlotIndices(lookup("f4", Ctx)),
+              UnorderedElementsAre(1));
+
+  EXPECT_THAT(getInferableSlotIndices(lookup("v1", Ctx)), IsEmpty());
+  EXPECT_THAT(getInferableSlotIndices(lookup("v2", Ctx)),
+              UnorderedElementsAre(0));
+  EXPECT_THAT(getInferableSlotIndices(lookup("v3", Ctx)),
+              UnorderedElementsAre(0));
+
+  EXPECT_THAT(getInferableSlotIndices(lookup("field1", Ctx)), IsEmpty());
+  EXPECT_THAT(getInferableSlotIndices(lookup("field2", Ctx)),
+              UnorderedElementsAre(0));
+  EXPECT_THAT(getInferableSlotIndices(lookup("field3", Ctx)),
+              UnorderedElementsAre(0));
+
+  EXPECT_THAT(getInferableSlotIndices(lookup("method", Ctx)),
+              UnorderedElementsAre(0, 2));
+}
+
 }  // namespace
 }  // namespace clang::tidy::nullability
diff --git a/nullability/inference/inference.proto b/nullability/inference/inference.proto
index ec93518..ee2476c 100644
--- a/nullability/inference/inference.proto
+++ b/nullability/inference/inference.proto
@@ -48,6 +48,9 @@
   optional Kind kind = 3;
   // Source location: file:line:col. Optional, for debugging only.
   optional string location = 4;
+  // If set, this evidence was originally collected from the virtual or override
+  // function identified by this symbol and propagated to `symbol`.
+  optional Symbol propagated_from = 5;
 
   // A pattern in the code that might help us determine nullability.
   enum Kind {
@@ -183,11 +186,11 @@
 message SlotPartial {
   map</*Kind*/ uint32, uint32> kind_count = 1;
 
-  message SampleLocations {
-    // A bounded number of locations are stored.
-    repeated string location = 1;
+  message SampleEvidence {
+    // A bounded number of evidence pieces are stored.
+    repeated Evidence evidence = 1;
   }
-  map</*Kind*/ uint32, SampleLocations> kind_samples = 2;
+  map</*Kind*/ uint32, SampleEvidence> kind_samples = 2;
 
   // Slot identifiers for which this partial is relevant. Used only for
   // debugging information.
@@ -365,7 +368,7 @@
 message LogicalContext {
   // optional, string-encoded formula providing an invariant across all formulas
   // represented in this logical context.
-  optional FormulaProto invariant  = 1;
+  optional FormulaProto invariant = 1;
 
   // Int-encoded atoms and their definitions as formulas.
   map<uint32, FormulaProto> atom_defs = 2;
@@ -400,3 +403,11 @@
   // key. These must be USRs.
   map<string, SymbolSet> related_symbols = 1;
 }
+
+// A summary for a whole translation unit.
+message TUSummary {
+  repeated CFGSummary cfg_summaries = 1;
+  optional RelatedSymbols overrides_to_bases = 2;
+  // Information about the TU that could be useful for debug logs.
+  optional string path = 3;
+}
diff --git a/nullability/inference/merge.cc b/nullability/inference/merge.cc
index 3f1f0fd..36203fe 100644
--- a/nullability/inference/merge.cc
+++ b/nullability/inference/merge.cc
@@ -6,35 +6,42 @@
 
 #include <array>
 #include <optional>
-#include <utility>
+#include <string>
 
 #include "absl/log/check.h"
 #include "nullability/inference/inference.proto.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
 
 namespace clang::tidy::nullability {
-namespace {
 
-static void mergeSampleLocations(SlotPartial::SampleLocations &LHS,
-                                 const SlotPartial::SampleLocations &RHS) {
+static void mergeSampleEvidence(SlotPartial::SampleEvidence& LHS,
+                                const SlotPartial::SampleEvidence& RHS) {
   static constexpr unsigned Limit = 3;
-  // We don't care which we pick, but they should be unique.
+  // We don't care which we pick, but their locations should be unique.
   // Multiple instantiations of the same template are not interesting.
-  for (const auto &Loc : RHS.location()) {
-    if (LHS.location_size() >= Limit) break;
-    // Linear scan is fine because Limit is tiny.
-    if (!llvm::is_contained(LHS.location(), Loc)) LHS.add_location(Loc);
+  // Linear scans are fine because Limit is tiny.
+  llvm::SmallVector<std::string, Limit> LHSEvidenceLocations(llvm::map_range(
+      LHS.evidence(), [](const Evidence& E) { return E.location(); }));
+  for (const auto& E : RHS.evidence()) {
+    if (LHS.evidence_size() >= Limit) break;
+    if (!llvm::is_contained(LHSEvidenceLocations, E.location()))
+      *LHS.add_evidence() = E;
   }
 }
 
-}  // namespace
-
 SlotPartial partialFromEvidence(const Evidence &E) {
   SlotPartial P;
   ++(*P.mutable_kind_count())[E.kind()];
-  if (E.has_location())
-    (*P.mutable_kind_samples())[E.kind()].add_location(E.location());
+  // Save the evidence as a sample, only if it has a location.
+  if (E.has_location()) {
+    Evidence& Sample = *(*P.mutable_kind_samples())[E.kind()].add_evidence();
+    Sample = E;
+    // Clear the symbol and slot, which are extraneous for debugging samples.
+    Sample.clear_symbol();
+    Sample.clear_slot();
+  }
   return P;
 }
 
@@ -42,7 +49,7 @@
   for (auto [Kind, Count] : RHS.kind_count())
     (*LHS.mutable_kind_count())[Kind] += Count;
   for (const auto &[Kind, Samples] : RHS.kind_samples())
-    mergeSampleLocations((*LHS.mutable_kind_samples())[Kind], Samples);
+    mergeSampleEvidence((*LHS.mutable_kind_samples())[Kind], Samples);
 }
 
 // Form a nullability conclusion from a set of evidence.
@@ -50,12 +57,9 @@
   SlotInference Inference;
   if (P.kind_count_size() == 0) return Inference;
 
-  // Reconstitute samples, if we have them.
   for (const auto &[Kind, Samples] : P.kind_samples()) {
-    for (const auto &Loc : Samples.location()) {
-      auto *Sample = Inference.add_sample_evidence();
-      Sample->set_location(Loc);
-      Sample->set_kind(static_cast<Evidence::Kind>(Kind));
+    for (const auto& Sample : Samples.evidence()) {
+      *Inference.add_sample_evidence() = Sample;
     }
   }
   llvm::stable_sort(*Inference.mutable_sample_evidence(),
@@ -73,9 +77,8 @@
   return Inference;
 }
 
-namespace {
-void update(std::optional<InferResult> &Result,
-            Nullability ImpliedNullability) {
+static void update(std::optional<InferResult>& Result,
+                   Nullability ImpliedNullability) {
   if (!Result) {
     Result = {ImpliedNullability};
     return;
@@ -84,7 +87,6 @@
     // Leave the existing Nullability.
     Result->Conflict = true;
 }
-}  // namespace
 
 InferResult infer(llvm::ArrayRef<unsigned> Counts, bool EnableSoftRules) {
   CHECK_EQ(Counts.size(), Evidence::Kind_MAX + 1);
diff --git a/nullability/inference/merge_test.cc b/nullability/inference/merge_test.cc
index 6e726d6..03cf9d7 100644
--- a/nullability/inference/merge_test.cc
+++ b/nullability/inference/merge_test.cc
@@ -15,29 +15,31 @@
 #include "google/protobuf/text_format.h"
 
 namespace clang::tidy::nullability {
-namespace {
-
 template <typename T>
-T proto(llvm::StringRef Text) {
+static T proto(llvm::StringRef Text) {
   T Result;
   CHECK(google::protobuf::TextFormat::ParseFromString(Text, &Result));
   return Result;
 }
 
+namespace {
 TEST(PartialFromEvidenceTest, ContainsEvidenceInfo) {
-  EXPECT_THAT(partialFromEvidence(proto<Evidence>(R"pb(
-                symbol { usr: "func" }
-                slot: 1
-                kind: UNCHECKED_DEREFERENCE
-                location: "foo.cc:42"
-              )pb")),
-              EqualsProto(R"pb(
-                kind_count { key: 3 value: 1 }
-                kind_samples {
-                  key: 3
-                  value { location: "foo.cc:42" }
-                }
-              )pb"));
+  EXPECT_THAT(
+      partialFromEvidence(proto<Evidence>(R"pb(
+        symbol { usr: "func" }
+        slot: 1
+        kind: UNCHECKED_DEREFERENCE
+        location: "foo.cc:42"
+      )pb")),
+      EqualsProto(R"pb(
+        kind_count { key: 3 value: 1 }
+        kind_samples {
+          key: 3
+          value {
+            evidence { kind: UNCHECKED_DEREFERENCE location: "foo.cc:42" }
+          }
+        }
+      )pb"));
 }
 
 TEST(MergePartialsTest, BothContainEvidence) {
@@ -46,7 +48,10 @@
     kind_count { key: 0 value: 2 }
     kind_samples {
       key: 0
-      value { location: "a" location: "b" }
+      value {
+        evidence { kind: ANNOTATED_UNKNOWN location: "a" }
+        evidence { kind: ANNOTATED_UNKNOWN location: "b" }
+      }
     }
   )pb");
   auto R = proto<SlotPartial>(
@@ -54,7 +59,11 @@
            kind_count { key: 0 value: 2 }
            kind_samples {
              key: 0
-             value { location: "c" location: "a" location: "d" }
+             value {
+               evidence { kind: ANNOTATED_UNKNOWN location: "c" }
+               evidence { kind: ANNOTATED_UNKNOWN location: "a" }
+               evidence { kind: ANNOTATED_UNKNOWN location: "d" }
+             }
            })pb");
 
   mergePartials(L, R);
@@ -64,7 +73,11 @@
                 kind_count { key: 1 value: 1 }
                 kind_samples {
                   key: 0
-                  value { location: "a" location: "b" location: "c" }
+                  value {
+                    evidence { kind: ANNOTATED_UNKNOWN location: "a" }
+                    evidence { kind: ANNOTATED_UNKNOWN location: "b" }
+                    evidence { kind: ANNOTATED_UNKNOWN location: "c" }
+                  }
                 }
               )pb"));
 }
@@ -84,24 +97,25 @@
 }
 
 TEST(FinalizeTest, ConflictingAnnotations) {
-  EXPECT_THAT(finalize(proto<SlotPartial>(R"pb(
-                kind_count { key: 1 value: 1 }  # ANNOTATED_NULLABLE
-                kind_count { key: 2 value: 1 }  # ANNOTATED_NONNULL
-                kind_samples {
-                  key: 1
-                  value { location: "decl" }
-                }
-                kind_samples {
-                  key: 2
-                  value { location: "def" }
-                }
-              )pb")),
-              EqualsProto(R"pb(
-                nullability: UNKNOWN
-                conflict: true
-                sample_evidence { kind: ANNOTATED_NULLABLE location: "decl" }
-                sample_evidence { kind: ANNOTATED_NONNULL location: "def" }
-              )pb"));
+  EXPECT_THAT(
+      finalize(proto<SlotPartial>(R"pb(
+        kind_count { key: 1 value: 1 }  # ANNOTATED_NULLABLE
+        kind_count { key: 2 value: 1 }  # ANNOTATED_NONNULL
+        kind_samples {
+          key: 1
+          value { evidence { kind: ANNOTATED_NULLABLE location: "decl" } }
+        }
+        kind_samples {
+          key: 2
+          value { evidence { kind: ANNOTATED_NONNULL location: "def" } }
+        }
+      )pb")),
+      EqualsProto(R"pb(
+        nullability: UNKNOWN
+        conflict: true
+        sample_evidence { kind: ANNOTATED_NULLABLE location: "decl" }
+        sample_evidence { kind: ANNOTATED_NONNULL location: "def" }
+      )pb"));
 }
 
 TEST(FinalizeTest, Empty) {
diff --git a/nullability/pointer_nullability_matchers.cc b/nullability/pointer_nullability_matchers.cc
index e445b14..f132a11 100644
--- a/nullability/pointer_nullability_matchers.cc
+++ b/nullability/pointer_nullability_matchers.cc
@@ -7,7 +7,6 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/OperationKinds.h"
 #include "clang/AST/Stmt.h"
-#include "clang/AST/Type.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/ASTMatchers/ASTMatchersInternal.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -62,7 +61,6 @@
 using ast_matchers::isInteger;
 using ast_matchers::isMemberInitializer;
 using ast_matchers::memberExpr;
-using ast_matchers::ofClass;
 using ast_matchers::parameterCountIs;
 using ast_matchers::pointee;
 using ast_matchers::pointerType;
@@ -183,6 +181,12 @@
                      "allocate_shared_for_overwrite"))));
 }
 
+Matcher<Stmt> isWrapUniqueCall() {
+  return callExpr(
+      hasType(isSupportedSmartPointer()),
+      callee(functionDecl(hasName("WrapUnique"), isInAbslNamespace())));
+}
+
 Matcher<Stmt> isSmartPointerComparisonOpCall() {
   return cxxOperatorCallExpr(
       hasAnyOverloadedOperatorName("==", "!="), argumentCountIs(2),
@@ -224,4 +228,11 @@
                          hasDeclaration(decl().bind("member-decl"))))))))))))));
 }
 
+Matcher<Stmt> isStatusOrValueOrCall() {
+  return cxxMemberCallExpr(
+      thisPointerType(qualType(hasCanonicalType(qualType(
+          hasDeclaration(cxxRecordDecl(hasName("::absl::StatusOr"))))))),
+      callee(cxxMethodDecl(hasName("value_or"))));
+}
+
 }  // namespace clang::tidy::nullability
diff --git a/nullability/pointer_nullability_matchers.h b/nullability/pointer_nullability_matchers.h
index b09186e..cdbfcf3 100644
--- a/nullability/pointer_nullability_matchers.h
+++ b/nullability/pointer_nullability_matchers.h
@@ -6,11 +6,12 @@
 #define CRUBIT_NULLABILITY_POINTER_NULLABILITY_MATCHERS_H_
 
 #include "nullability/type_nullability.h"
+#include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
-#include "clang/AST/Type.h"
+#include "clang/AST/TypeBase.h"
 #include "clang/ASTMatchers/ASTMatchersInternal.h"
 #include "clang/ASTMatchers/ASTMatchersMacros.h"
 #include "clang/Basic/LLVM.h"
@@ -84,10 +85,12 @@
 ast_matchers::internal::Matcher<Stmt> isSmartPointerFreeSwapCall();
 ast_matchers::internal::Matcher<Stmt> isSmartPointerBoolConversionCall();
 ast_matchers::internal::Matcher<Stmt> isSmartPointerFactoryCall();
+ast_matchers::internal::Matcher<Stmt> isWrapUniqueCall();
 ast_matchers::internal::Matcher<Stmt> isSmartPointerComparisonOpCall();
 ast_matchers::internal::Matcher<Stmt> isSharedPtrCastCall();
 ast_matchers::internal::Matcher<Stmt> isWeakPtrLockCall();
 ast_matchers::internal::Matcher<Stmt> isSupportedPointerAccessorCall();
+ast_matchers::internal::Matcher<Stmt> isStatusOrValueOrCall();
 
 AST_MATCHER(Stmt, isNullPointerDefaultInit) {
   const auto* DefaultInit = dyn_cast<CXXDefaultInitExpr>(&Node);
@@ -96,6 +99,37 @@
                                         Builder);
 }
 
+// Checks if the given declaration is within the `absl` namespace.
+// Traverses the parent namespaces up to the top-level namespace.
+// For example, `absl::nested::f()` is considered within the `absl` namespace.
+// The logic is similar to `isDeclaredInAbseilOrUtil()` in the value transferer:
+// https://github.com/google/crubit/blob/55767d191778d2a421a229d3fe446a65912c9865/nullability/value_transferer.cc#L882
+// This is unlike `Decl::isInStdNamespace()`
+// (https://clang.llvm.org/doxygen/classclang_1_1Decl.html#a066b012f94431b5bba21d19715a274f4),
+// which only traverses up inline namespaces
+// (https://en.cppreference.com/w/cpp/language/namespace.html#Inline_namespaces)
+// and "transparent contexts" such as those induced by unscoped enums
+// (https://clang.llvm.org/doxygen/classclang_1_1DeclContext.html#a1d3b0ef59e3e789890485aa141c4712e).
+AST_MATCHER(Decl, isInAbslNamespace) {
+  const DeclContext* DC = Node.getDeclContext();
+  if (DC == nullptr || DC->isTranslationUnit()) {
+    return false;
+  }
+
+  // Traverse the parent namespaces up to the top-level namespace.
+  while (DC->getParent() != nullptr && !DC->getParent()->isTranslationUnit()) {
+    DC = DC->getParent();
+  }
+
+  if (!DC->isNamespace()) {
+    return false;
+  }
+
+  const NamespaceDecl* ND = cast<NamespaceDecl>(DC);
+  const IdentifierInfo* II = ND->getIdentifier();
+  return II != nullptr && II->isStr("absl");
+}
+
 }  // namespace nullability
 }  // namespace tidy
 }  // namespace clang
diff --git a/nullability/pointer_nullability_matchers_test.cc b/nullability/pointer_nullability_matchers_test.cc
index e68f8f3..a76ffc6 100644
--- a/nullability/pointer_nullability_matchers_test.cc
+++ b/nullability/pointer_nullability_matchers_test.cc
@@ -5,6 +5,7 @@
 #include "nullability/pointer_nullability_matchers.h"
 
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Testing/TestAST.h"
 #include "llvm/ADT/StringRef.h"
 #include "external/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
@@ -12,7 +13,20 @@
 namespace clang::tidy::nullability {
 namespace {
 
+using ast_matchers::callee;
+using ast_matchers::callExpr;
+using ast_matchers::declRefExpr;
+using ast_matchers::enumConstantDecl;
+using ast_matchers::functionDecl;
+using ast_matchers::hasName;
 using ast_matchers::match;
+using ast_matchers::to;
+
+template <typename MatcherT>
+bool matches(llvm::StringRef test_input, MatcherT Matcher) {
+  TestAST InputAST(test_input.str());
+  return !match(Matcher, InputAST.context()).empty();
+}
 
 template <typename MatcherT>
 bool matches(llvm::StringRef base_input, llvm::StringRef test_input,
@@ -155,5 +169,199 @@
               isSmartPointerBoolConversionCall()));
 }
 
+TEST(AbslNamespaceTest, MatchesAbslFunctionCall) {
+  llvm::StringRef Input(R"cc(
+    namespace absl {
+    void f() {}
+    }  // namespace absl
+  )cc");
+  auto AbslFunctionCall =
+      callExpr(callee(functionDecl(isInAbslNamespace(), hasName("f"))));
+  EXPECT_TRUE(matches(Input, "void target(){ absl::f(); }", AbslFunctionCall));
+  EXPECT_TRUE(matches(Input, "using namespace absl; void target(){ f(); }",
+                      AbslFunctionCall));
+  EXPECT_TRUE(
+      matches(Input, "using absl::f; void target(){ f(); }", AbslFunctionCall));
+}
+
+TEST(AbslNamespaceTest, DoesNotMatchFunctionCallWithNoNamespace) {
+  llvm::StringRef Input(R"cc(
+    void f() {}
+    void target() { f(); }
+  )cc");
+  EXPECT_FALSE(matches(Input, isInAbslNamespace()));
+}
+
+TEST(AbslNamespaceTest, DoesNotMatchFunctionCallWithDifferentNamespace) {
+  llvm::StringRef Input(R"cc(
+    namespace util {
+    void f() {}
+    }  // namespace util
+  )cc");
+  EXPECT_FALSE(
+      matches(Input, "void target(){ util::f(); }", isInAbslNamespace()));
+  EXPECT_FALSE(matches(Input, "using namespace util; void target(){ f(); }",
+                       isInAbslNamespace()));
+  EXPECT_FALSE(matches(Input, "using util::f; void target(){ f(); }",
+                       isInAbslNamespace()));
+}
+
+TEST(AbslNamespaceTest, DoesNotMatchEnumWithAbslType) {
+  llvm::StringRef Input(R"cc(
+    enum absl {
+      kValue,
+    };
+  )cc");
+  EXPECT_FALSE(
+      matches(Input, "void target() { kValue; }", isInAbslNamespace()));
+  EXPECT_FALSE(
+      matches(Input, "void target() { absl::kValue; }", isInAbslNamespace()));
+}
+
+TEST(AbslNamespaceTest, DoesNotMatchEnumClassWithQualifiedAbslType) {
+  llvm::StringRef Input(R"cc(
+    enum class absl {
+      kValue,
+    };
+    void target() { absl::kValue; }
+  )cc");
+  EXPECT_FALSE(matches(Input, isInAbslNamespace()));
+}
+
+TEST(AbslNamespaceTest, MatchesEnumInAbslNamespace) {
+  llvm::StringRef Input(R"cc(
+    namespace absl {
+    enum Type {
+      kValue,
+    };
+    }  // namespace absl
+  )cc");
+  EXPECT_TRUE(matches(Input, "void target() { absl::kValue; }",
+                      declRefExpr(to(enumConstantDecl(isInAbslNamespace(),
+                                                      hasName("kValue"))))));
+  EXPECT_TRUE(matches(Input, "void target() { absl::Type::kValue; }",
+                      declRefExpr(to(enumConstantDecl(isInAbslNamespace(),
+                                                      hasName("kValue"))))));
+}
+
+TEST(AbslNamespaceTest, MatchesEnumClassInAbslNamespace) {
+  llvm::StringRef Input(R"cc(
+    namespace absl {
+    enum class Type {
+      kValue,
+    };
+    void target() { absl::Type::kValue; }
+    }  // namespace absl
+  )cc");
+  EXPECT_TRUE(matches(Input, declRefExpr(to(enumConstantDecl(
+                                 isInAbslNamespace(), hasName("kValue"))))));
+}
+
+TEST(AbslNamespaceTest, MatchesFunctionCallWithNestedNamespace) {
+  llvm::StringRef Input(R"cc(
+    namespace absl {
+    namespace nested {
+    void f() {}
+    }  // namespace nested
+    }  // namespace absl
+  )cc");
+  auto AbslFunctionCall =
+      callExpr(callee(functionDecl(isInAbslNamespace(), hasName("f"))));
+  EXPECT_TRUE(
+      matches(Input, "void target(){ absl::nested::f(); }", AbslFunctionCall));
+  EXPECT_TRUE(matches(Input,
+                      "using namespace absl; void target(){ nested::f(); }",
+                      AbslFunctionCall));
+  EXPECT_TRUE(matches(Input,
+                      "using namespace absl::nested; void target(){ f(); }",
+                      AbslFunctionCall));
+  EXPECT_TRUE(matches(Input, "using absl::nested::f; void target(){ f(); }",
+                      AbslFunctionCall));
+}
+
+TEST(AbslNamespaceTest, MatchesFunctionCallWithInlineNamespace) {
+  llvm::StringRef Input(R"cc(
+    namespace absl {
+    inline namespace latest {
+    void f() {}
+    }  // namespace latest
+    }  // namespace absl
+  )cc");
+  auto AbslFunctionCall =
+      callExpr(callee(functionDecl(isInAbslNamespace(), hasName("f"))));
+  EXPECT_TRUE(matches(Input, "void target(){ absl::f(); }", AbslFunctionCall));
+  EXPECT_TRUE(
+      matches(Input, "void target(){ absl::latest::f(); }", AbslFunctionCall));
+  EXPECT_TRUE(matches(Input, "using namespace absl; void target(){ f(); }",
+                      AbslFunctionCall));
+  EXPECT_TRUE(matches(Input,
+                      "using namespace absl; void target(){ latest::f(); }",
+                      AbslFunctionCall));
+  EXPECT_TRUE(matches(Input,
+                      "using namespace absl::latest; void target(){ f(); }",
+                      AbslFunctionCall));
+  EXPECT_TRUE(
+      matches(Input, "using absl::f; void target(){ f(); }", AbslFunctionCall));
+  EXPECT_TRUE(matches(Input, "using absl::latest::f; void target(){ f(); }",
+                      AbslFunctionCall));
+}
+
+TEST(AbslNamespaceTest, MatchesFunctionCallWithNestedInlineNamespace) {
+  llvm::StringRef Input(R"cc(
+    namespace absl {
+    inline namespace latest {
+    inline namespace nested {
+    void f() {}
+    }  // namespace nested
+    }  // namespace latest
+    }  // namespace absl
+  )cc");
+  auto AbslFunctionCall =
+      callExpr(callee(functionDecl(isInAbslNamespace(), hasName("f"))));
+  EXPECT_TRUE(matches(Input, "void target(){ absl::f(); }", AbslFunctionCall));
+  EXPECT_TRUE(
+      matches(Input, "void target(){ absl::latest::f(); }", AbslFunctionCall));
+  EXPECT_TRUE(matches(Input, "void target(){ absl::latest::nested::f(); }",
+                      AbslFunctionCall));
+}
+
+TEST(AbslNamespaceTest, DoesNotMatchFunctionCallWithNestedAbslNamespace) {
+  llvm::StringRef Input(R"cc(
+    namespace util {
+    namespace absl {
+    void f() {}
+    }  // namespace absl
+    }  // namespace util
+  )cc");
+  EXPECT_FALSE(
+      matches(Input, "void target(){ util::absl::f(); }", isInAbslNamespace()));
+  EXPECT_FALSE(matches(Input,
+                       "using namespace util; void target(){ absl::f(); }",
+                       isInAbslNamespace()));
+  EXPECT_FALSE(matches(Input,
+                       "using namespace util::absl; void target(){ f(); }",
+                       isInAbslNamespace()));
+}
+
+TEST(AbslNamespaceTest, DoesNotMatchFunctionCallWithInlineAbslNamespace) {
+  llvm::StringRef Input(R"cc(
+    namespace util {
+    inline namespace absl {
+    void f() {}
+    }  // namespace absl
+    }  // namespace util
+  )cc");
+  EXPECT_FALSE(
+      matches(Input, "void target(){ util::f(); }", isInAbslNamespace()));
+  EXPECT_FALSE(
+      matches(Input, "void target(){ util::absl::f(); }", isInAbslNamespace()));
+  EXPECT_FALSE(matches(Input,
+                       "using namespace util; void target(){ absl::f(); }",
+                       isInAbslNamespace()));
+  EXPECT_FALSE(matches(Input,
+                       "using namespace util::absl; void target(){ f(); }",
+                       isInAbslNamespace()));
+}
+
 }  // namespace
 }  // namespace clang::tidy::nullability
diff --git a/nullability/test/nullability_annotations.h b/nullability/test/nullability_annotations.h
index ad67498..0157080 100644
--- a/nullability/test/nullability_annotations.h
+++ b/nullability/test/nullability_annotations.h
@@ -15,3 +15,4 @@
 #define absl_nullable _Nullable
 #define absl_nonnull _Nonnull
 #define absl_nullability_unknown _Null_unspecified
+#define absl_nullability_conflict _Null_unspecified
diff --git a/nullability/test/smart_pointers.cc b/nullability/test/smart_pointers.cc
index 32d288d..5320889 100644
--- a/nullability/test/smart_pointers.cc
+++ b/nullability/test/smart_pointers.cc
@@ -414,6 +414,43 @@
   nonnull(std::make_unique_for_overwrite<int[]>(5));
 }
 
+namespace absl {
+template <typename T>
+std::unique_ptr<T> WrapUnique(T* P) {
+  return std::unique_ptr<T>(P);
+}
+}  // namespace absl
+
+TEST void abslWrapUnique() {
+  nonnull(absl::WrapUnique(makeNonnullRaw()));
+  nullable(absl::WrapUnique(makeNullableRaw()));
+  unknown(absl::WrapUnique(makeUnknownRaw()));
+}
+
+template <typename T>
+std::unique_ptr<T> WrapUnique(T* P) {
+  return std::unique_ptr<T>(P);
+}
+
+TEST void customWrapUnique() {
+  unknown(WrapUnique(makeNonnullRaw()));
+  unknown(WrapUnique(makeNullableRaw()));
+  unknown(WrapUnique(makeUnknownRaw()));
+}
+
+namespace util {
+template <typename T>
+std::unique_ptr<T> WrapUnique(T* P) {
+  return std::unique_ptr<T>(P);
+}
+}  // namespace util
+
+TEST void nonAbslNamespaceWrapUnique() {
+  unknown(util::WrapUnique(makeNonnullRaw()));
+  unknown(util::WrapUnique(makeNullableRaw()));
+  unknown(util::WrapUnique(makeUnknownRaw()));
+}
+
 TEST void makeShared() {
   nonnull(std::make_shared<int>());
   nonnull(std::make_shared<int>(42));
diff --git a/nullability/value_transferer.cc b/nullability/value_transferer.cc
index 5946368..fe25df0 100644
--- a/nullability/value_transferer.cc
+++ b/nullability/value_transferer.cc
@@ -6,6 +6,7 @@
 
 #include <cassert>
 #include <functional>
+#include <optional>
 
 #include "absl/base/nullability.h"
 #include "absl/log/check.h"
@@ -40,6 +41,7 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/Specifiers.h"
+#include "llvm/Support/raw_ostream.h"
 
 namespace clang::tidy::nullability {
 using ast_matchers::MatchFinder;
@@ -550,6 +552,21 @@
   }
 }
 
+static void transferWrapUniqueCall(
+    const CallExpr* CE, const MatchFinder::MatchResult& Result,
+    TransferState<PointerNullabilityLattice>& State) {
+  if (CE->getNumArgs() != 1) {
+    return;
+  }
+  const Expr* Arg = CE->getArg(0);
+  RecordStorageLocation& Loc = State.Env.getResultObjectLocation(*CE);
+  if (isSupportedRawPointerType(Arg->getType()) &&
+      isPointerTypeConvertible(Arg->getType(),
+                               underlyingRawPointerTypeFromSmartPointer(Loc))) {
+    setSmartPointerValue(Loc, getRawPointerValue(Arg, State.Env), State.Env);
+  }
+}
+
 static void transferSmartPointerComparisonOpCall(
     const CXXOperatorCallExpr* OpCall, const MatchFinder::MatchResult& Result,
     TransferState<PointerNullabilityLattice>& State) {
@@ -961,6 +978,16 @@
     Env.assume(Env.arena().makeEquals(Val->formula(), *IsNull));
 }
 
+static bool isMethodOfAbslStatusOr(const FunctionDecl* F) {
+  const auto* Method = dyn_cast<CXXMethodDecl>(F);
+  if (!Method) return false;
+  const CXXRecordDecl* Parent = Method->getParent();
+  if (!Parent) return false;
+  const CXXRecordDecl* CanonicalParent = Parent->getCanonicalDecl();
+  if (!CanonicalParent) return false;
+  return CanonicalParent->getQualifiedNameAsString() == "absl::StatusOr";
+}
+
 static void transferCallExpr(const CallExpr* absl_nonnull CE,
                              const MatchFinder::MatchResult& Result,
                              TransferState<PointerNullabilityLattice>& State) {
@@ -969,9 +996,9 @@
   // function calls and handle value creation for certain types.
 
   const auto* FuncDecl = CE->getDirectCallee();
+  const IdentifierInfo* FunII = nullptr;
   if (FuncDecl != nullptr) {
-    if (const IdentifierInfo* FunII =
-            FuncDecl->getDeclName().getAsIdentifierInfo()) {
+    if ((FunII = FuncDecl->getDeclName().getAsIdentifierInfo())) {
       if (FunII->isStr("__assert_nullability")) return;
 
       // This is part of the implementation of `CHECK_NE`.
@@ -1002,9 +1029,9 @@
     }
   }
 
+  // Create a pointer value for any supported pointer type so that we can attach
+  // nullability to it and have the nullability propagate with the pointer.
   if (isSupportedRawPointerType(CE->getType())) {
-    // Create a pointer so that we can attach nullability to it and have the
-    // nullability propagate with the pointer.
     auto* PointerVal = getRawPointerValue(CE, State.Env);
     if (!PointerVal) {
       PointerVal = cast<PointerValue>(State.Env.createValue(CE->getType()));
@@ -1018,16 +1045,29 @@
       // `Loc` is set iff `CE` is a glvalue, so we know here that it must
       // be a prvalue.
       State.Env.setValue(*CE, *PointerVal);
+  } else if (isSupportedSmartPointerType(CE->getType())) {
+    if (Loc == nullptr) {
+      // `CE` must be a prvalue; see above.
+      Loc = &State.Env.getResultObjectLocation(*CE);
+    }
+    StorageLocation& PtrLoc =
+        cast<RecordStorageLocation>(Loc)->getSyntheticField(PtrField);
+    auto* Val = State.Env.get<PointerValue>(PtrLoc);
+    if (Val == nullptr) {
+      Val = cast<PointerValue>(State.Env.createValue(PtrLoc.getType()));
+      State.Env.setValue(PtrLoc, *Val);
+    }
+
+    initPointerFromTypeNullability(*Val, CE, State);
   }
 
   if (CE->isCallToStdMove() || FuncDecl == nullptr) return;
 
   // Don't treat parameters of our macro replacement argument-capture functions
-  // as output parameters.
-  if (const IdentifierInfo* FunII =
-          FuncDecl->getDeclName().getAsIdentifierInfo();
-      FunII && (FunII->isStr(ArgCaptureAbortIfFalse) ||
-                FunII->isStr(ArgCaptureAbortIfEqual)))
+  // or of absl::StatusOr::value_or as output parameters.
+  if (FunII && (FunII->isStr(ArgCaptureAbortIfFalse) ||
+                FunII->isStr(ArgCaptureAbortIfEqual) ||
+                (FunII->isStr("value_or") && isMethodOfAbslStatusOr(FuncDecl))))
     return;
   // Make output parameters (with unknown nullability) initialized to unknown.
   for (ParamAndArgIterator<CallExpr> Iter(*FuncDecl, *CE); Iter; ++Iter)
@@ -1230,6 +1270,111 @@
   handleNonConstMemberCall(OCE, RecordLoc, Result, State);
 }
 
+static void transferStatusOrValueOrCall(
+    const CXXMemberCallExpr* absl_nonnull MCE,
+    const MatchFinder::MatchResult& Result,
+    TransferState<PointerNullabilityLattice>& State) {
+  // Some overloads are const, some are not. Start with default handling as
+  // appropriate for constness. Do this first so that the result value will be
+  // initialized for us; we don't mind that the const method cache is cleared
+  // before we proceed if this is a non-const call.
+  if (auto* MethodDecl = MCE->getMethodDecl();
+      MethodDecl && !MethodDecl->isConst()) {
+    transferNonConstMemberCall(MCE, Result, State);
+  } else {
+    transferCallExpr(MCE, Result, State);
+  }
+
+  // absl::StatusOr::value_or can take any argument convertible to the template
+  // argument type, and claims to return the template argument type. But it
+  // considers a nullable value or type to be convertible to a nonnull type.
+  // Rather than warn on an actually non-convertible argument to value_or, we
+  // model the return value as having the null state of potentially either of
+  // the argument or the StatusOr's contained pointer, depending on the
+  // untracked internal state of the StatusOr, and warn on any incompatible
+  // usage later.
+  if (!isSupportedPointerType(MCE->getType()) ||
+      // getNumArgs does not count the implicit *this argument.
+      MCE->getNumArgs() != 1)
+    return;
+  Arena& A = State.Env.arena();
+
+  PointerValue* ResultPV = getPointerValue(MCE, State.Env);
+  if (!ResultPV) return;
+  PointerNullState ResultState = getPointerNullState(*ResultPV);
+
+  // Get the null state of the value_or argument.
+  const Expr* ArgExpr = MCE->getArg(0);
+  if (!ArgExpr) return;
+  PointerValue* Arg = getPointerValue(ArgExpr, State.Env);
+  std::optional<PointerNullState> ArgState;
+  if (Arg) {
+    ArgState = getPointerNullState(*Arg);
+  } else if (ArgExpr->getType()->isNullPtrType() &&
+             isReachableNullptrLiteral(State.Env)) {
+    ArgState = PointerNullState{.FromNullable = &A.makeLiteral(true),
+                                .IsNull = &A.makeLiteral(true)};
+  } else {
+    // This is never expected to happen, so always log, and assert-fail when
+    // enabled.
+    llvm::errs() << "Unable to determine PointerNullState for an argument to "
+                    "absl::StatusOr<SupportedPointerType>::value_or. Please "
+                    "file a bug at <internal link> if you see this.\n";
+    assert(false);
+    return;
+  }
+
+  // The null state corresponding to the StatusOr's template argument type is
+  // captured as the current null state of the call's result. Re-assign the null
+  // state properties of the call's result to be fresh atoms implied by the
+  // untracked state of the StatusOr, also modeled as a fresh atom, to be equal
+  // to the null state properties drawn from either the template argument type
+  // or the value_or argument.
+  //
+  // value_or creates copies of the contained pointer or the argument, so only
+  // the current null states are relevant; we don't need to account for later
+  // modification of e.g. a referenced decl.
+  const Formula& StatusOrIsOk = A.makeAtomRef(A.makeAtom());
+  DataflowAnalysisContext& DACtx = State.Env.getDataflowAnalysisContext();
+  if (ResultState.FromNullable != nullptr) {
+    if (ArgState->FromNullable == nullptr) {
+      ResultState.FromNullable = nullptr;
+    } else {
+      const Formula& OldResultFromNullable = *ResultState.FromNullable;
+      ResultState.FromNullable = &A.makeAtomRef(A.makeAtom());
+      DACtx.addInvariant(A.makeImplies(
+          StatusOrIsOk,
+          A.makeEquals(*ResultState.FromNullable, OldResultFromNullable)));
+      DACtx.addInvariant(A.makeImplies(
+          A.makeNot(StatusOrIsOk),
+          A.makeEquals(*ResultState.FromNullable, *ArgState->FromNullable)));
+    }
+  }
+  if (ResultState.IsNull != nullptr) {
+    if (ArgState->IsNull == nullptr) {
+      ResultState.IsNull = nullptr;
+    } else {
+      const Formula& OldResultIsNull = *ResultState.IsNull;
+      ResultState.IsNull = &A.makeAtomRef(A.makeAtom());
+      DACtx.addInvariant(A.makeImplies(
+          StatusOrIsOk, A.makeEquals(*ResultState.IsNull, OldResultIsNull)));
+      DACtx.addInvariant(
+          A.makeImplies(A.makeNot(StatusOrIsOk),
+                        A.makeEquals(*ResultState.IsNull, *ArgState->IsNull)));
+    }
+  }
+
+  auto& NewPointerVal =
+      State.Env.create<PointerValue>(ResultPV->getPointeeLoc());
+  initPointerNullState(NewPointerVal, DACtx, ResultState);
+  if (isSupportedRawPointerType(MCE->getType())) {
+    State.Env.setValue(*MCE, NewPointerVal);
+  } else if (isSupportedSmartPointerType(MCE->getType())) {
+    setSmartPointerValue(State.Env.getResultObjectLocation(*MCE),
+                         &NewPointerVal, State.Env);
+  }
+}
+
 dataflow::CFGMatchSwitch<dataflow::TransferState<PointerNullabilityLattice>>
 buildValueTransferer() {
   // The value transfer functions must establish:
@@ -1269,7 +1414,7 @@
           transferSmartPointerMemberSwapCall)
       .CaseOfCFGStmt<CallExpr>(isSmartPointerFreeSwapCall(),
                                transferSmartPointerFreeSwapCall)
-      .CaseOfCFGStmt<CXXMemberCallExpr>(isSmartPointerMethodCall("get"),
+      .CaseOfCFGStmt<CXXMemberCallExpr>(isSmartPointerMethodCall("get", "Get"),
                                         transferSmartPointerGetCall)
       .CaseOfCFGStmt<CXXMemberCallExpr>(isSmartPointerBoolConversionCall(),
                                         transferSmartPointerBoolConversionCall)
@@ -1279,6 +1424,7 @@
                                           transferSmartPointerOperatorArrow)
       .CaseOfCFGStmt<CallExpr>(isSmartPointerFactoryCall(),
                                transferSmartPointerFactoryCall)
+      .CaseOfCFGStmt<CallExpr>(isWrapUniqueCall(), transferWrapUniqueCall)
       .CaseOfCFGStmt<CXXOperatorCallExpr>(isSmartPointerComparisonOpCall(),
                                           transferSmartPointerComparisonOpCall)
       .CaseOfCFGStmt<CallExpr>(isSharedPtrCastCall(), transferSharedPtrCastCall)
@@ -1326,6 +1472,8 @@
                 CE, getImplicitObjectLocation(*CE, State.Env), State,
                 initCallbackForStorageLocationIfSmartPointer(CE, State.Env));
           })
+      .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrValueOrCall(),
+                                        transferStatusOrValueOrCall)
       .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
                                         transferConstMemberCall)
       .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
diff --git a/rs_bindings_from_cc/BUILD b/rs_bindings_from_cc/BUILD
index 22181ba..8479678 100644
--- a/rs_bindings_from_cc/BUILD
+++ b/rs_bindings_from_cc/BUILD
@@ -40,7 +40,6 @@
     deps_for_generated_cc_file = [
         "//support/public:bindings_support",
         "//support:bridge_cpp",
-        "//support:status_bridge_cpp",
     ],
     deps_for_generated_rs_file = [
         "//support:ctor",
@@ -49,9 +48,9 @@
         "//support:bridge_rust",
         # Required for `Copy` trait assertions added to the generated Rust
         # code.
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
         # Required for `cxx.rs` integration.
-        "@crate_index//:cxx",
+        "@crate_index//:cxx",  # v1
     ],
     visibility = ["//:__subpackages__"],
 )
@@ -236,12 +235,12 @@
         "//common:cc_ffi_types",
         "//common:status_macros",
         "//common:status_test_matchers",
+        "//testing/base/public:gunit_main",
         "@abseil-cpp//absl/flags:flag",
         "@abseil-cpp//absl/status",
         "@abseil-cpp//absl/status:statusor",
         "@abseil-cpp//absl/strings",
         "@abseil-cpp//absl/strings:string_view",
-        "@googletest//:gtest_main",
     ],
 )
 
@@ -345,9 +344,9 @@
         ":cc_ir",
         ":ir_from_cc",
         "//common:status_test_matchers",
+        "//testing/base/public:gunit_main",
         "@abseil-cpp//absl/status",
         "@abseil-cpp//absl/strings",
-        "@googletest//:gtest_main",
     ],
 )
 
@@ -384,12 +383,12 @@
         "//common:code_gen_utils",
         "//common:crubit_feature",
         "//common:error_report",
-        "@crate_index//:flagset",
-        "@crate_index//:itertools",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:serde",
-        "@crate_index//:serde_json",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:serde",  # v1
+        "@crate_index//:serde_json",  # v1
     ],
 )
 
@@ -413,8 +412,8 @@
         "//common:crubit_feature",
         "//common:ffi_types",
         "//common:multiplatform_testing",
-        "@crate_index//:flagset",
-        "@crate_index//:itertools",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:itertools",  # v0_13
     ],
 )
 
@@ -429,7 +428,7 @@
         ":ir_matchers",
         ":ir_testing",
         "@crate_index//:googletest",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -487,9 +486,9 @@
         "//common:arc_anyhow",
         "//common:multiplatform_testing",
         "@crate_index//:googletest",
-        "@crate_index//:itertools",
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -555,10 +554,10 @@
         ":ir_testing",
         "//common:token_stream_matchers",
         "//common:token_stream_printer",
-        "@crate_index//:anyhow",
-        "@crate_index//:itertools",
+        "@crate_index//:anyhow",  # v1
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -607,7 +606,7 @@
         ":cc_collect_instantiations",
         "//common:status_test_matchers",
         "//common:test_utils",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -618,8 +617,8 @@
         "//common:arc_anyhow",
         "//common:ffi_types",
         "@crate_index//:proc-macro2",
-        "@crate_index//:serde_json",
-        "@crate_index//:syn",
+        "@crate_index//:serde_json",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -633,7 +632,7 @@
     crate = ":collect_instantiations",
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -659,10 +658,10 @@
         ":cc_ir",
         ":collect_namespaces",
         ":ir_from_cc",
+        "//testing/base/public:gunit_main",
         "@abseil-cpp//absl/status",
         "@abseil-cpp//absl/status:statusor",
         "@abseil-cpp//absl/strings",
         "@abseil-cpp//absl/types:span",
-        "@googletest//:gtest_main",
     ],
 )
diff --git a/rs_bindings_from_cc/bazel_support/compile_rust.bzl b/rs_bindings_from_cc/bazel_support/compile_rust.bzl
index a8eede0..42b1c05 100644
--- a/rs_bindings_from_cc/bazel_support/compile_rust.bzl
+++ b/rs_bindings_from_cc/bazel_support/compile_rust.bzl
@@ -91,8 +91,8 @@
             type = "rlib",
             root = src,
             srcs = depset([src] + extra_srcs),
-            deps = deps,
-            proc_macro_deps = depset([]),
+            deps = deps.to_list(),
+            proc_macro_deps = [],
             aliases = {},
             output = lib,
             metadata = rmeta,
diff --git a/rs_bindings_from_cc/cmdline.cc b/rs_bindings_from_cc/cmdline.cc
index 36be22a..40ea936 100644
--- a/rs_bindings_from_cc/cmdline.cc
+++ b/rs_bindings_from_cc/cmdline.cc
@@ -54,11 +54,13 @@
           "`#include \"crubit/support/support_header.h\", specify "
           "`\"crubit/support/{header}`,");
 ABSL_FLAG(std::string, clang_format_exe_path, "",
-          "Path to a clang-format executable that will be used to format the "
+          "(optional) Path to a clang-format executable that will be used to "
+          "format the "
           ".cc files generated by the tool.");
-ABSL_FLAG(std::string, rustfmt_exe_path, "",
-          "Path to a rustfmt executable that will be used to format the "
-          ".rs files generated by the tool.");
+ABSL_FLAG(
+    std::string, rustfmt_exe_path, "",
+    "(optional) Path to a rustfmt executable that will be used to format the "
+    ".rs files generated by the tool.");
 ABSL_FLAG(std::string, rustfmt_config_path, "",
           "(optional) path to a rustfmt.toml file that should replace the "
           "default formatting of the .rs files generated by the tool.");
@@ -267,12 +269,6 @@
   if (args.public_headers.empty()) {
     absl::StrAppend(&error, "please specify --public_headers\n");
   }
-  if (args.clang_format_exe_path.empty()) {
-    absl::StrAppend(&error, "please specify --clang_format_exe_path\n");
-  }
-  if (args.rustfmt_exe_path.empty()) {
-    absl::StrAppend(&error, "please specify --rustfmt_exe_path\n");
-  }
 
   if (args.crubit_support_path_format.empty()) {
     absl::StrAppend(&error, "please specify --crubit_support_path_format\n");
diff --git a/rs_bindings_from_cc/cmdline_test.cc b/rs_bindings_from_cc/cmdline_test.cc
index cad758c..ea3bcb1 100644
--- a/rs_bindings_from_cc/cmdline_test.cc
+++ b/rs_bindings_from_cc/cmdline_test.cc
@@ -336,17 +336,13 @@
 TEST(CmdlineTest, ClangFormatExePathEmpty) {
   ASSERT_OK_AND_ASSIGN(CmdlineArgs args, TestCmdlineArgs());
   args.clang_format_exe_path = "";
-  EXPECT_THAT(Cmdline::Create(std::move(args)),
-              StatusIs(absl::StatusCode::kInvalidArgument,
-                       HasSubstr("please specify --clang_format_exe_path")));
+  EXPECT_OK(Cmdline::Create(std::move(args)));
 }
 
 TEST(CmdlineTest, RustfmtExePathEmpty) {
   ASSERT_OK_AND_ASSIGN(CmdlineArgs args, TestCmdlineArgs());
   args.rustfmt_exe_path = "";
-  EXPECT_THAT(Cmdline::Create(std::move(args)),
-              StatusIs(absl::StatusCode::kInvalidArgument,
-                       HasSubstr("please specify --rustfmt_exe_path")));
+  EXPECT_OK(Cmdline::Create(std::move(args)));
 }
 
 TEST(CmdlineTest, SupportPathEmpty) {
diff --git a/rs_bindings_from_cc/generate_bindings/BUILD b/rs_bindings_from_cc/generate_bindings/BUILD
index c3a0046..548e481 100644
--- a/rs_bindings_from_cc/generate_bindings/BUILD
+++ b/rs_bindings_from_cc/generate_bindings/BUILD
@@ -52,11 +52,11 @@
         "//common:token_stream_printer",
         "//rs_bindings_from_cc:ir",
         "//rs_bindings_from_cc/generate_bindings/database",
-        "@crate_index//:flagset",
-        "@crate_index//:itertools",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
         "@crate_index//:unicode-ident",
     ],
 )
@@ -82,9 +82,9 @@
         "//rs_bindings_from_cc/generate_bindings/database",
         "@crate_index//:googletest",
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:static_assertions",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:static_assertions",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -101,7 +101,7 @@
         "//rs_bindings_from_cc:ir",
         "//rs_bindings_from_cc/generate_bindings/database",
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -122,10 +122,10 @@
         "//common:error_report",
         "//rs_bindings_from_cc:ir",
         "//rs_bindings_from_cc/generate_bindings/database",
-        "@crate_index//:flagset",
-        "@crate_index//:itertools",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -153,9 +153,9 @@
         "//rs_bindings_from_cc/generate_bindings/database",
         "@crate_index//:googletest",
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:static_assertions",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:static_assertions",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -204,9 +204,9 @@
         "//common:ffi_types",
         "//rs_bindings_from_cc:ir",
         "//rs_bindings_from_cc/generate_bindings/database",
-        "@crate_index//:itertools",
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -229,7 +229,7 @@
         "//rs_bindings_from_cc:ir_testing",
         "//rs_bindings_from_cc/generate_bindings/database",
         "@crate_index//:googletest",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -249,7 +249,7 @@
         "//rs_bindings_from_cc:ir",
         "//rs_bindings_from_cc/generate_bindings/database",
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -267,7 +267,7 @@
         "//common:arc_anyhow",
         "//common:token_stream_matchers",
         "@crate_index//:googletest",
-        "@crate_index//:quote",
+        "@crate_index//:quote",  # v1
     ],
 )
 
@@ -287,10 +287,10 @@
         "//common:error_report",
         "//rs_bindings_from_cc:ir",
         "//rs_bindings_from_cc/generate_bindings/database",
-        "@crate_index//:itertools",
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
         "@crate_index//:unicode-ident",
     ],
 )
@@ -301,7 +301,7 @@
         "generate_function.rs",
     ],
     proc_macro_deps = [
-        "@crate_index//:rustversion",
+        "@crate_index//:rustversion",  # v1
     ],
     visibility = [
         "//rs_bindings_from_cc:__subpackages__",
@@ -318,11 +318,11 @@
         "//common:errors",
         "//rs_bindings_from_cc:ir",
         "//rs_bindings_from_cc/generate_bindings/database",
-        "@crate_index//:flagset",
-        "@crate_index//:itertools",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -349,9 +349,9 @@
         "//rs_bindings_from_cc:ir_testing",
         "//rs_bindings_from_cc/generate_bindings/database",
         "@crate_index//:googletest",
-        "@crate_index//:quote",
-        "@crate_index//:static_assertions",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:static_assertions",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -368,7 +368,7 @@
         "//common:crubit_feature",
         "//rs_bindings_from_cc:ir",
         "//rs_bindings_from_cc/generate_bindings/database",
-        "@crate_index//:heck",
+        "@crate_index//:heck",  # v0_5
     ],
 )
 
diff --git a/rs_bindings_from_cc/generate_bindings/cpp_type_name.rs b/rs_bindings_from_cc/generate_bindings/cpp_type_name.rs
index cc66cd2..452ac7b 100644
--- a/rs_bindings_from_cc/generate_bindings/cpp_type_name.rs
+++ b/rs_bindings_from_cc/generate_bindings/cpp_type_name.rs
@@ -124,6 +124,7 @@
         RsTypeKind::ExistingRustType(existing_rust_type) => {
             cpp_type_name_for_item(&Item::ExistingRustType(Rc::clone(existing_rust_type)), ir)
         }
+        RsTypeKind::C9Co { original_type, .. } => cpp_type_name_for_record(original_type, ir),
     }
 }
 
diff --git a/rs_bindings_from_cc/generate_bindings/database/BUILD b/rs_bindings_from_cc/generate_bindings/database/BUILD
index 3bc3c17..c6b6f99 100644
--- a/rs_bindings_from_cc/generate_bindings/database/BUILD
+++ b/rs_bindings_from_cc/generate_bindings/database/BUILD
@@ -30,13 +30,13 @@
         "//common:memoized",
         "//common:token_stream_printer",
         "//rs_bindings_from_cc:ir",
-        "@crate_index//:flagset",
-        "@crate_index//:heck",
-        "@crate_index//:itertools",
+        "@crate_index//:flagset",  # v0_4
+        "@crate_index//:heck",  # v0_5
+        "@crate_index//:itertools",  # v0_13
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:serde_json",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:serde_json",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
diff --git a/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs b/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs
index 738f7e4..3d52e6d 100644
--- a/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs
+++ b/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs
@@ -359,13 +359,12 @@
     fn new(
         db: &dyn BindingsGenerator,
         template_specialization: Option<&TemplateSpecialization>,
-        have_reference_param: bool,
         is_return_type: bool,
     ) -> Result<Option<Rc<Self>>> {
         let Some(template_specialization) = template_specialization else {
             return Ok(None);
         };
-        let type_arg = |template_arg: &TemplateArg, may_bridge: bool| -> Result<RsTypeKind> {
+        let type_arg = |template_arg: &TemplateArg| -> Result<RsTypeKind> {
             let arg_type = match &template_arg.type_ {
                 Ok(arg_type) => arg_type.clone(),
                 Err(e) => bail!("{e}"),
@@ -373,7 +372,7 @@
             // Importantly, `is_return_type` is not propagated through inner types.
             let arg_type_kind = db.rs_type_kind(arg_type)?;
             ensure!(
-                may_bridge || !arg_type_kind.is_bridge_type(),
+                !arg_type_kind.is_bridge_type(),
                 "Bridge types cannot be used as template arguments"
             );
             // We don't do this in required_crubit_features() because it doesn't know which
@@ -391,7 +390,7 @@
         ) {
             ("std::unique_ptr", [t, deleter]) => {
                 let has_std_deleter = is_default_delete(db, t, deleter)?;
-                let t = type_arg(t, /*may_bridge=*/ false)?;
+                let t = type_arg(t)?;
                 ensure!(t.is_complete(), "Rust std::unique_ptr<T> cannot be used with incomplete types, and `{}` is incomplete", t.display(db));
                 ensure!(t.is_destructible(), "Rust std::unique_ptr<T> requires that `T` be destructible, but the destructor of `{}` is non-public or deleted", t.display(db));
                 if !has_std_deleter {
@@ -404,7 +403,7 @@
             }
             ("std::vector", [t, allocator]) => {
                 let has_std_allocator = is_std_allocator(db, t, allocator)?;
-                let t = type_arg(t, /*may_bridge=*/ false)?;
+                let t = type_arg(t)?;
                 ensure!(t.is_destructible(), "Rust std::vector<T> requires that `T` be destructible, but the destructor of `{}` is non-public or deleted", t.display(db));
                 if !has_std_allocator {
                     return Ok(None);
@@ -421,7 +420,7 @@
             }
             ("absl::Span", [t]) => {
                 // Revisit the CcType of _t to see if it is const.
-                let element_type = type_arg(t, /*may_bridge=*/ false)?;
+                let element_type = type_arg(t)?;
                 let is_const = t.type_.as_ref().expect("should be valid because type_args is the successful result of get_template_args").is_const;
                 Self::AbslSpan {
                     is_const,
@@ -444,7 +443,7 @@
                 // If all else fails, it's some unknown template type. Read any errors from the
                 // template arguments.
                 for t in &template_specialization.template_args {
-                    type_arg(t, /*may_bridge=*/ false)?;
+                    type_arg(t)?;
                 }
                 return Ok(None);
             }
@@ -552,6 +551,39 @@
     /// which is used on types like `SliceRef`, `StrRef`, and C++ types generated from Rust
     /// types by cc_bindings_from_rs.
     ExistingRustType(Rc<ExistingRustType>),
+    /// c9::Co<T>
+    C9Co {
+        have_reference_param: bool,
+        result_type: Rc<RsTypeKind>,
+        original_type: Rc<Record>,
+    },
+}
+
+fn new_c9_co_record(
+    have_reference_param: bool,
+    record: Rc<Record>,
+    db: &dyn BindingsGenerator,
+) -> Result<Option<RsTypeKind>> {
+    let Some(ts) = record.template_specialization.as_ref() else {
+        return Ok(None);
+    };
+    if ts.template_name.as_ref() != "c9::Co" {
+        return Ok(None);
+    }
+    let arg_type = ts.template_args[0]
+        .type_
+        .as_ref()
+        .map_err(|e: &String| anyhow!("c9::Co T argument is not Crubit compatible: {e}"))?
+        .clone();
+    let arg_type_kind = db.rs_type_kind(arg_type)?;
+    if let RsTypeKind::Error { error, .. } = arg_type_kind {
+        return Err(error);
+    };
+    Ok(Some(RsTypeKind::C9Co {
+        have_reference_param,
+        result_type: Rc::new(arg_type_kind),
+        original_type: record,
+    }))
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -720,6 +752,11 @@
         if let Some(bridge_type) = BridgeRsTypeKind::new(&record, db)? {
             return Ok(RsTypeKind::BridgeType { bridge_type, original_type: record });
         }
+
+        if let Some(c9_co) = new_c9_co_record(have_reference_param, Rc::clone(&record), db)? {
+            return Ok(c9_co);
+        }
+
         let crate_path = Rc::new(CratePath::new(
             ir,
             ir.namespace_qualifier(&record),
@@ -729,7 +766,6 @@
             uniform_repr_template_type: UniformReprTemplateType::new(
                 db,
                 record.template_specialization.as_ref(),
-                have_reference_param,
                 is_return_type,
             )?,
             record,
@@ -818,6 +854,13 @@
         matches!(self.unalias(), RsTypeKind::BridgeType { .. })
     }
 
+    pub fn as_c9_co(&self) -> Option<&RsTypeKind> {
+        match self.unalias() {
+            RsTypeKind::C9Co { result_type, .. } => Some(result_type),
+            _ => None,
+        }
+    }
+
     pub fn is_pointer_bridge_type(&self) -> bool {
         matches!(
             self.unalias(),
@@ -979,6 +1022,7 @@
                     }
                 }
                 RsTypeKind::ExistingRustType(_) => require_feature(CrubitFeature::Supported, None),
+                RsTypeKind::C9Co { .. } => require_feature(CrubitFeature::Supported, None),
             }
         }
         (missing_features, reasons.into_iter().join(", "))
@@ -988,6 +1032,11 @@
     /// thunks.
     pub fn is_c_abi_compatible_by_value(&self) -> bool {
         match self.unalias() {
+            RsTypeKind::Error { .. } => true,
+            RsTypeKind::Pointer { .. } => true,
+            RsTypeKind::Reference { .. } => true,
+            RsTypeKind::RvalueReference { .. } => true,
+            RsTypeKind::FuncPtr { .. } => true,
             RsTypeKind::IncompleteRecord { .. } => {
                 // Incomplete record (forward declaration) as parameter type or return type is
                 // unusual but it's a valid cc_library and such a header can be made to work
@@ -1004,9 +1053,12 @@
             // TODO(b/274177296): Return `true` for structs where bindings replicate the type of
             // all the fields.
             RsTypeKind::Record { .. } => false,
+            RsTypeKind::Enum { .. } => true,
+            RsTypeKind::TypeAlias { .. } => unreachable!(),
+            RsTypeKind::Primitive(_) => true,
             RsTypeKind::BridgeType { .. } => false,
             RsTypeKind::ExistingRustType(existing_rust_type) => existing_rust_type.is_same_abi,
-            _ => true,
+            RsTypeKind::C9Co { .. } => false,
         }
     }
 
@@ -1062,6 +1114,7 @@
             RsTypeKind::Primitive(_) => true,
             RsTypeKind::BridgeType { .. } => false,
             RsTypeKind::ExistingRustType(_) => true,
+            RsTypeKind::C9Co { .. } => false,
         }
     }
 
@@ -1168,6 +1221,18 @@
                 BridgeRsTypeKind::StdString { .. } => false,
             },
             RsTypeKind::ExistingRustType(_) => true,
+            RsTypeKind::C9Co { .. } => false,
+        }
+    }
+
+    pub fn is_pointer(&self) -> bool {
+        matches!(self.unalias(), RsTypeKind::Pointer { .. })
+    }
+
+    pub fn is_pointer_to(&self, expected_record: &Record) -> bool {
+        match self.unalias() {
+            RsTypeKind::Pointer { pointee, .. } => pointee.is_record(expected_record),
+            _ => false,
         }
     }
 
@@ -1208,6 +1273,10 @@
         matches!(self.unalias(), RsTypeKind::Primitive(Primitive::Bool))
     }
 
+    pub fn is_void(&self) -> bool {
+        matches!(self.unalias(), RsTypeKind::Primitive(Primitive::Void))
+    }
+
     pub fn is_complete(&self) -> bool {
         !matches!(self.unalias(), RsTypeKind::IncompleteRecord { .. })
     }
@@ -1480,31 +1549,16 @@
                 Primitive::Uint64T | Primitive::StdUint64T => quote! { u64 },
             },
             RsTypeKind::BridgeType { bridge_type, original_type } => {
-                let make_path = |rust_name: &str| {
-                    let is_absolute_path = rust_name.starts_with("::");
-                    // If the name starts with "::", then it is an absolute path. In this case, we
-                    // need to skip the first part of the split, since it returns the empty string.
-                    let name_parts =
-                        rust_name.split("::").skip(is_absolute_path as usize).map(make_rs_ident);
-                    let target =
-                        original_type.defining_target().unwrap_or(&original_type.owning_target);
-
-                    let prefix = if is_absolute_path {
-                        quote! {}
-                    } else if db.ir().is_current_target(target) {
-                        quote! { crate }
-                    } else {
-                        let ident = make_rs_ident(target.target_name());
-                        quote! { :: #ident }
-                    };
-                    quote! { #prefix :: #(#name_parts)::* }
-                };
                 match bridge_type {
                     BridgeRsTypeKind::BridgeVoidConverters { rust_name, .. } => {
-                        make_path(rust_name)
+                        fully_qualify_type(db, ir::Item::Record(original_type.clone()), rust_name)
                     }
                     BridgeRsTypeKind::Bridge { rust_name, generic_types, .. } => {
-                        let path = make_path(rust_name);
+                        let path = fully_qualify_type(
+                            db,
+                            ir::Item::Record(original_type.clone()),
+                            rust_name,
+                        );
 
                         // If there are no generic types, then we're done.
                         if generic_types.is_empty() {
@@ -1515,7 +1569,9 @@
                             generic_types.iter().map(|t| t.to_token_stream(db));
                         quote! { #path < #(#generic_types_tokens),* > }
                     }
-                    BridgeRsTypeKind::ProtoMessageBridge { rust_name, .. } => make_path(rust_name),
+                    BridgeRsTypeKind::ProtoMessageBridge { rust_name, .. } => {
+                        fully_qualify_type(db, ir::Item::Record(original_type.clone()), rust_name)
+                    }
                     BridgeRsTypeKind::StdOptional(inner) => {
                         let inner = inner.to_token_stream(db);
                         quote! { ::core::option::Option< #inner > }
@@ -1534,13 +1590,117 @@
                     }
                 }
             }
-            RsTypeKind::ExistingRustType(existing_rust_type) => {
-                existing_rust_type.rs_name.parse().expect("Invalid RsType::name in the IR")
+            RsTypeKind::ExistingRustType(existing_rust_type) => fully_qualify_type(
+                db,
+                ir::Item::ExistingRustType(existing_rust_type.clone()),
+                &existing_rust_type.rs_name,
+            ),
+            RsTypeKind::C9Co { have_reference_param, result_type, .. } => {
+                let result_type_tokens = if result_type.is_void() {
+                    quote! { () }
+                } else {
+                    result_type.to_token_stream(db)
+                };
+                // When there are reference parameters, the coroutine must finish before they are
+                // invalidated (http://shortn/_XPma06AwZh).
+                match have_reference_param {
+                    false => quote! { ::co::Co<'static, #result_type_tokens> },
+                    true => quote! { ::co::Co<'_, #result_type_tokens> },
+                }
             }
         }
     }
 }
 
+/// Take a user defined path, like `foo` or `::bar`, and convert it to
+/// an absolute path, like `crate::foo` or `::bar` respectively.
+///
+/// The path is taken to be relative to crate defining the item.
+///
+/// This has _very_ limited support for other type expressions, like `&T`,
+/// and special-cases well known builtin types like `char`.
+fn fully_qualify_type(
+    db: &dyn BindingsGenerator,
+    item: ir::Item,
+    type_expression: &str,
+) -> TokenStream {
+    let root_crate = || {
+        let target = item.defining_target().cloned().or_else(|| item.owning_target()).unwrap();
+        if db.ir().is_current_target(&target) {
+            quote! { crate }
+        } else {
+            let ident = make_rs_ident(target.target_name());
+            quote! { :: #ident }
+        }
+    };
+    fully_qualify_type_impl(type_expression, root_crate)
+}
+
+/// Broken out for testing :/
+fn fully_qualify_type_impl(
+    type_expression: &str,
+    root_crate: impl Fn() -> TokenStream,
+) -> TokenStream {
+    let mut type_expression_suffix = type_expression;
+    'fix: loop {
+        type_expression_suffix = type_expression_suffix.trim_start();
+        for prefix in ["&", "*", "const", "mut"] {
+            if let Some(suffix) = type_expression_suffix.strip_prefix(prefix) {
+                type_expression_suffix = suffix;
+                continue 'fix;
+            }
+        }
+        break;
+    }
+
+    let prefix = &type_expression[..type_expression.len() - type_expression_suffix.len()];
+    let prefix: TokenStream = prefix.parse().unwrap();
+
+    // Primitive types are special-cased.
+    if matches!(
+        type_expression_suffix.trim(),
+        "char"
+            | "bool"
+            | "i8"
+            | "u8"
+            | "i16"
+            | "u16"
+            | "i32"
+            | "u32"
+            | "i64"
+            | "u64"
+            | "i128"
+            | "u128"
+            | "isize"
+            | "usize"
+            | "str"
+            | "f32"
+            | "f64"
+    ) {
+        let suffix: TokenStream = type_expression_suffix.parse().unwrap();
+        return quote! { #prefix #suffix };
+    }
+
+    // Otherwise, we assume it's a path.
+    let is_absolute_path = type_expression_suffix.starts_with("::");
+    // If the name starts with "::", then it is an absolute path. In this case, we
+    // need to skip the first part of the split, since it returns the empty string.
+    // Note: Crubit can generate poorly formatted names, like `:: foo :: bar`, so we also
+    // need to trim whitespace to create valid identifiers.
+    let name_parts = type_expression_suffix
+        .split("::")
+        .skip(is_absolute_path as usize)
+        .map(str::trim)
+        .map(make_rs_ident);
+
+    let top_level_crate = if is_absolute_path {
+        quote! {}
+    } else {
+        root_crate()
+    };
+    quote! { #prefix  #top_level_crate :: #(#name_parts)::* }
+}
+
 struct RsTypeKindIter<'ty> {
     todo: Vec<&'ty RsTypeKind>,
 }
@@ -1584,6 +1744,9 @@
                         BridgeRsTypeKind::StdString { .. } => {}
                     },
                     RsTypeKind::ExistingRustType(_) => {}
+                    RsTypeKind::C9Co { result_type, .. } => {
+                        self.todo.push(result_type);
+                    }
                 };
                 Some(curr)
             }
@@ -1615,9 +1778,9 @@
     fn test_dfs_iter_ordering_for_func_ptr() {
         // Set up a test input representing: fn(A, B) -> C
         let f = {
-            let a = make_existing_rust_type("A".into(), true);
-            let b = make_existing_rust_type("B".into(), true);
-            let c = make_existing_rust_type("C".into(), true);
+            let a = make_existing_rust_type("::A".into(), true);
+            let b = make_existing_rust_type("::B".into(), true);
+            let c = make_existing_rust_type("::C".into(), true);
             RsTypeKind::FuncPtr {
                 option: false,
                 cc_calling_conv: CcCallingConv::C,
@@ -1635,7 +1798,7 @@
                 _ => unreachable!("Only FuncPtr and ExistingRustType kinds are used in this test"),
             })
             .collect_vec();
-        assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
+        assert_eq!(vec!["fn", "::A", "::B", "::C"], dfs_names);
     }
 
     struct EmptyDatabase;
@@ -1643,18 +1806,18 @@
 
     #[gtest]
     fn test_lifetime_elision_for_references() {
-        let referent = Rc::new(make_existing_rust_type("T".into(), true));
+        let referent = Rc::new(make_existing_rust_type("::T".into(), true));
         let reference = RsTypeKind::Reference {
             referent,
             mutability: Mutability::Const,
             lifetime: Lifetime::new("_"),
         };
-        assert_rs_matches!(reference.to_token_stream(&EmptyDatabase), quote! {&T});
+        assert_rs_matches!(reference.to_token_stream(&EmptyDatabase), quote! {&::T});
     }
 
     #[gtest]
     fn test_lifetime_elision_for_rvalue_references() {
-        let referent = Rc::new(make_existing_rust_type("T".into(), true));
+        let referent = Rc::new(make_existing_rust_type("::T".into(), true));
         let reference = RsTypeKind::RvalueReference {
             referent,
             mutability: Mutability::Mut,
@@ -1662,13 +1825,13 @@
         };
         assert_rs_matches!(
             reference.to_token_stream(&EmptyDatabase),
-            quote! {RvalueReference<'_, T>}
+            quote! {RvalueReference<'_, ::T>}
         );
     }
 
     #[gtest]
     fn test_format_as_self_param_rvalue_reference() -> Result<()> {
-        let referent = Rc::new(make_existing_rust_type("T".into(), true));
+        let referent = Rc::new(make_existing_rust_type("::T".into(), true));
         let result = RsTypeKind::RvalueReference {
             referent,
             mutability: Mutability::Mut,
@@ -1682,7 +1845,7 @@
 
     #[gtest]
     fn test_format_as_self_param_const_rvalue_reference() -> Result<()> {
-        let referent = Rc::new(make_existing_rust_type("T".into(), true));
+        let referent = Rc::new(make_existing_rust_type("::T".into(), true));
         let result = RsTypeKind::RvalueReference {
             referent,
             mutability: Mutability::Const,
@@ -1836,4 +1999,34 @@
         expect_that!(void_ptr.allowed_behind_single_element_ptr(), eq(true));
         expect_that!(void_ptr.allowed_behind_multi_element_ptr(), eq(false));
     }
+
+    #[gtest]
+    fn test_fully_qualify_type() {
+        assert_rs_matches!(
+            fully_qualify_type_impl("A", || {
+                quote! {crate}
+            }),
+            quote! {crate::A},
+        );
+    }
+
+    #[gtest]
+    fn test_fully_qualify_i32() {
+        assert_rs_matches!(
+            fully_qualify_type_impl("i32", || {
+                quote! {crate}
+            }),
+            quote! {i32},
+        );
+    }
+
+    #[gtest]
+    fn test_fully_qualify_ref() {
+        assert_rs_matches!(
+            fully_qualify_type_impl("&mut *const X", || {
+                quote! {crate}
+            }),
+            quote! {&mut *const crate::X},
+        );
+    }
 }
diff --git a/rs_bindings_from_cc/generate_bindings/generate_function.rs b/rs_bindings_from_cc/generate_bindings/generate_function.rs
index bbefc4d..5676e44 100644
--- a/rs_bindings_from_cc/generate_bindings/generate_function.rs
+++ b/rs_bindings_from_cc/generate_bindings/generate_function.rs
@@ -573,14 +573,32 @@
 }
 
 fn api_func_shape_for_identifier(
+    db: &dyn BindingsGenerator,
     func: &Func,
     maybe_record: Option<&Rc<Record>>,
     param_types: &mut [RsTypeKind],
     id: &Identifier,
-    is_unsafe: bool,
 ) -> (Ident, ImplKind) {
+    let is_unsafe = match func.safety_annotation {
+        SafetyAnnotation::Unannotated => {
+            let mut param_type_iter = param_types.iter();
+            if func.cc_name.is_constructor() {
+                // This is a renamed constructor.
+                //
+                // Discard the `this` parameter, as constructors of unsafe types are not
+                // automatically considered unsafe. Similarly to Rust's raw pointer types, creating
+                // an unsafe type is safe, but using one is not.
+                let _ = param_type_iter.next();
+            }
+            param_type_iter.any(|p| db.is_rs_type_kind_unsafe(p.clone()))
+        }
+        SafetyAnnotation::Unsafe => true,
+        SafetyAnnotation::DisableUnsafe => false,
+    };
+
     let func_name = make_rs_ident(&id.identifier);
     let Some(record) = maybe_record else { return (func_name, ImplKind::None { is_unsafe }) };
+    let is_renamed_unpin_constructor = func.cc_name.is_constructor() && record.is_unpin();
     let format_first_param_as_self = if func.is_instance_method() {
         let Some(first_param) = param_types.first() else {
             panic!("Missing `__this` parameter in an instance method: {:?}", func);
@@ -589,7 +607,6 @@
     } else {
         false
     };
-    let is_renamed_unpin_constructor = func.cc_name.is_constructor() && record.is_unpin();
     (
         func_name,
         ImplKind::Struct {
@@ -660,11 +677,75 @@
     }
 }
 
+/// Issue any errors related to unsafe constructors being unsupported.
+fn issue_unsafe_constructor_errors(
+    db: &dyn BindingsGenerator,
+    func: &Func,
+    record: &Record,
+    param_types: &[RsTypeKind],
+    errors: &Errors,
+) {
+    match func.safety_annotation {
+        SafetyAnnotation::DisableUnsafe => {}
+        SafetyAnnotation::Unsafe => {
+            errors.add(anyhow!(
+                "Constructors cannot be `unsafe`, but an explicit unsafe annotation was provided. See b/216648347."));
+        }
+        SafetyAnnotation::Unannotated => {
+            // Move and copy constructors are excepted from this check, as Google C++ style
+            // disallows move and copy constructors which require invariants to hold on public
+            // fields of the source object.
+            let is_move_or_copy_ctor = matches!(param_types, [_this, arg] if arg.is_ref_to(record));
+            if is_move_or_copy_ctor {
+                return;
+            }
+
+            // TODO: b/452726517 - remove this special case once we infer lifetimes of default
+            // constructors by default.
+            let is_lifetimeless_default_ctor = matches!(param_types, [this] if this.is_pointer());
+            if is_lifetimeless_default_ctor {
+                errors
+                    .add(anyhow!("Default constructors do yet receive bindings. See b/452726517."));
+                return;
+            }
+
+            // TODO: b/452726517 - remove this special case once we infer lifetimes of copy and move
+            // constructors by default.
+            let is_lifetimeless_move_or_copy_ctor =
+                matches!(param_types, [_this, arg] if arg.is_pointer_to(record));
+            if is_lifetimeless_move_or_copy_ctor {
+                errors.add(anyhow!(
+                    "Move and copy constructors do yet receive bindings. See b/452726517."
+                ));
+                return;
+            }
+
+            // We skip the first parameter because it's the implicit `this` parameter.
+            // Constructors of unsafe types are not automatically considered unsafe.
+            let param_names = func.params.iter().map(|p| &p.identifier);
+            let unsafe_params = param_names
+                .zip(param_types)
+                .skip(1)
+                .filter(|(_name, p_type)| db.is_rs_type_kind_unsafe((*p_type).clone()))
+                .map(|(param_name, param_type)| {
+                    format!("\n    `{param_name}` of unsafe type `{}`", param_type.display(db))
+                })
+                .collect::<Vec<String>>()
+                .join("");
+            if !unsafe_params.is_empty() {
+                errors.add(anyhow!(
+                    "Constructors cannot be `unsafe`, but this constructor accepts:{unsafe_params}"
+                ));
+            }
+        }
+    }
+}
+
 fn api_func_shape_for_constructor(
+    db: &dyn BindingsGenerator,
     func: &Func,
     maybe_record: Option<&Rc<Record>>,
     param_types: &mut [RsTypeKind],
-    is_unsafe: bool,
     errors: &Errors,
 ) -> Option<(Ident, ImplKind)> {
     let Some(record) = maybe_record else {
@@ -674,6 +755,8 @@
         errors.add(err);
     }
     materialize_ctor_in_caller(func, param_types);
+    issue_unsafe_constructor_errors(db, func, record, param_types, errors);
+
     if !record.is_unpin() {
         let func_name = make_rs_ident("ctor_new");
         let [_this, params @ ..] = param_types else {
@@ -738,19 +821,6 @@
                 // generate move constructor bindings explicitly.
                 return None;
             }
-
-            // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
-            // translating C++ constructors into static methods in Rust).
-            //
-            // Note: move and copy constructors are excepted from this check, as Google C++ style
-            // disallows move and copy constructors which require invariants to hold on public
-            // fields of the source object.
-            if is_unsafe && !param_types[1].is_ref_to(record) {
-                errors.add(anyhow!(
-                    "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
-                    are intentionally not supported. See b/216648347."
-                ));
-            }
             let param_type = &param_types[1];
             let func_name = make_rs_ident("from");
             let impl_kind = ImplKind::new_trait(
@@ -808,35 +878,18 @@
         return None;
     }
 
-    let is_unsafe = match func.safety_annotation {
-        SafetyAnnotation::Unannotated => {
-            let mut params_iter = param_types.iter();
-            let has_unsafe_self = func.cc_name.is_constructor()
-                && params_iter
-                    .next()
-                    .map(|this_arg| {
-                        let Some(self_type) = this_arg.referent() else { return false };
-                        db.is_rs_type_kind_unsafe(self_type.clone())
-                    })
-                    .unwrap_or(false);
-            has_unsafe_self || params_iter.any(|p| db.is_rs_type_kind_unsafe(p.clone()))
-        }
-        SafetyAnnotation::Unsafe => true,
-        SafetyAnnotation::DisableUnsafe => false,
-    };
-
     match &func.rs_name {
         UnqualifiedIdentifier::Operator(op) => {
             api_func_shape_for_operator(db, func, maybe_record, param_types, op, errors)
         }
         UnqualifiedIdentifier::Identifier(id) => {
-            Some(api_func_shape_for_identifier(func, maybe_record, param_types, id, is_unsafe))
+            Some(api_func_shape_for_identifier(db, func, maybe_record, param_types, id))
         }
         UnqualifiedIdentifier::Destructor => {
             api_func_shape_for_destructor(db, func, maybe_record, param_types)
         }
         UnqualifiedIdentifier::Constructor => {
-            api_func_shape_for_constructor(func, maybe_record, param_types, is_unsafe, errors)
+            api_func_shape_for_constructor(db, func, maybe_record, param_types, errors)
         }
     }
 }
@@ -1047,7 +1100,51 @@
                     };
                     return_type.to_token_stream_replacing_by_self(db, record)
                 };
-                if return_type.is_crubit_abi_bridge_type() {
+                if let Some(result_type) = return_type.as_c9_co() {
+                    let consume_result_fn = if result_type.is_void() {
+                        quote! { ::co::internal_crubit::consume_void_result }
+                    } else {
+                        let crubit_abi_type = db.crubit_abi_type(result_type.clone())?;
+                        let crubit_abi_type_tokens = CrubitAbiTypeToRustTokens(&crubit_abi_type);
+
+                        // Give the generated Rust API a reference to a monomorphized Rust function
+                        // that knows how to decode this result type.
+                        //
+                        // In theory this might not be necessary with some type erasure and dynamic
+                        // allocation of buffers, but doing it this way is more efficient.
+                        //
+                        // Also in theory we only need to provide A, the Crubit ABI, not A::Size.
+                        // Doing so works around shortcomings in Rust's support for const generics
+                        // being used in const positions:
+                        //
+                        //     https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f52065765bba85ea38c09571d80acd0d
+                        //
+                        // Note that the safety of consume_result depends on the correct size being
+                        // passed in. This is not a big deal though because all calls to this
+                        // function are intended to be generated by Crubit, so we don't have to
+                        // worry about folks accidentally doing the wrong thing.
+                        quote! {
+                            ::co::internal_crubit::consume_result::
+                                    <#crubit_abi_type_tokens, {
+                                <#crubit_abi_type_tokens as ::bridge_rust::CrubitAbi>::SIZE
+                            }>
+                        }
+                    };
+                    quote! {
+                        // TODO(b/274177296): When C structs can be passed by value in function
+                        // pointers, make the thunk just return a CoVtable by value.
+                        let mut __co_vtable_slot = ::co_vtable::c9::internal::rust::CoVtable {
+                            addr: ::core::ptr::null_mut(),
+                            start_coroutine: None,
+                            destroy_at_initial_suspend: None,
+                        };
+                        #crate_root_path::detail::#thunk_ident(
+                            &raw mut __co_vtable_slot
+                            #( , #clone_prefixes #thunk_args #clone_suffixes )*
+                        );
+                        ::co::Co::from_raw(__co_vtable_slot, #consume_result_fn)
+                    }
+                } else if return_type.is_crubit_abi_bridge_type() {
                     let crubit_abi_type = db.crubit_abi_type(return_type.clone())?;
                     let crubit_abi_type_tokens = CrubitAbiTypeToRustTokens(&crubit_abi_type);
                     quote! {
diff --git a/rs_bindings_from_cc/generate_bindings/generate_function_thunk.rs b/rs_bindings_from_cc/generate_bindings/generate_function_thunk.rs
index be4ca74..84bb9ae 100644
--- a/rs_bindings_from_cc/generate_bindings/generate_function_thunk.rs
+++ b/rs_bindings_from_cc/generate_bindings/generate_function_thunk.rs
@@ -161,6 +161,11 @@
         };
         out_param = Some(quote! { *mut ::core::ffi::c_void });
         out_param_ident = Some(param_idents.next().unwrap().clone());
+    } else if return_type.as_c9_co().is_some() {
+        // Returning a Co involves passing a CoVtable out ptr.
+        out_param = Some(quote! { *mut ::co_vtable::c9::internal::rust::CoVtable });
+        out_param_ident = Some(make_rs_ident("__return_co_vtable"));
+        return_type_fragment = None;
     } else if return_type.is_crubit_abi_bridge_type() {
         out_param = Some(quote! { *mut ::core::ffi::c_uchar });
         out_param_ident = Some(make_rs_ident("__return_abi_buffer"));
@@ -523,13 +528,16 @@
     let is_return_value_c_abi_compatible = return_type_kind.is_c_abi_compatible_by_value();
     let return_type_cpp_spelling = cpp_type_name::format_cpp_type(&return_type_kind, ir)?;
 
-    let return_type_name = if return_type_kind.is_crubit_abi_bridge_type() {
+    let return_type_name = if return_type_kind.as_c9_co().is_some() {
+        param_idents.insert(0, expect_format_cc_ident("__return_co_vtable"));
+        param_types.insert(0, quote! { c9::internal::rust::CoVtable* });
+        quote! {void}
+    } else if return_type_kind.is_crubit_abi_bridge_type() {
         param_idents.insert(0, expect_format_cc_ident("__return_abi_buffer"));
         param_types.insert(0, quote! {unsigned char *});
         quote! { void }
     } else if !is_return_value_c_abi_compatible {
         param_idents.insert(0, expect_format_cc_ident("__return"));
-        let return_type_name = cpp_type_name::format_cpp_type(&return_type_kind, ir)?;
         if let RsTypeKind::BridgeType {
             bridge_type: BridgeRsTypeKind::BridgeVoidConverters { cpp_to_rust_converter, .. },
             ..
@@ -541,7 +549,7 @@
             });
             param_types.insert(0, quote! {void *});
         } else {
-            param_types.insert(0, quote! {#return_type_name *});
+            param_types.insert(0, quote! {#return_type_cpp_spelling *});
         }
         quote! {void}
     } else {
@@ -578,7 +586,27 @@
         };
 
     let return_expr = quote! {#implementation_function( #( #arg_expressions ),* )};
-    let return_stmt = if return_type_kind.is_crubit_abi_bridge_type() {
+    let return_stmt = if let Some(result_type_kind) = return_type_kind.as_c9_co() {
+        // The result_type_kind is the T in Co<T>
+        let start_coroutine = if result_type_kind.is_void() {
+            // For coroutines that return void, we use the non-templated version.
+            quote! { &c9::internal::rust::StartCoroutineFromRust }
+        } else {
+            let result_type_crubit_abi_type = db.crubit_abi_type(result_type_kind.clone())?;
+            let result_type_crubit_abi_type_tokens =
+                CrubitAbiTypeToCppTokens(&result_type_crubit_abi_type);
+
+            // For coroutines that return a non-void value, we use the templated version.
+            quote! { &c9::internal::rust::StartCoroutineFromRust<#result_type_crubit_abi_type_tokens> }
+        };
+        let out_param = &param_idents[0];
+        let result_type_cpp_spelling = cpp_type_name::format_cpp_type(result_type_kind, ir)?;
+        quote! {
+            #out_param->addr = #return_expr.release_handle(c9::internal::PassKey()).address();
+            #out_param->start_coroutine = #start_coroutine;
+            #out_param->destroy_at_initial_suspend = &c9::internal::rust::DestroyCoroutineFrameFromRust<#result_type_cpp_spelling>;
+        }
+    } else if return_type_kind.is_crubit_abi_bridge_type() {
         let out_param = &param_idents[0];
         let crubit_abi_type = db.crubit_abi_type(return_type_kind)?;
         let crubit_abi_type_tokens = CrubitAbiTypeToCppTokens(&crubit_abi_type);
diff --git a/rs_bindings_from_cc/generate_bindings/generate_struct_and_union.rs b/rs_bindings_from_cc/generate_bindings/generate_struct_and_union.rs
index ecdedc5..57b0587 100644
--- a/rs_bindings_from_cc/generate_bindings/generate_struct_and_union.rs
+++ b/rs_bindings_from_cc/generate_bindings/generate_struct_and_union.rs
@@ -358,6 +358,9 @@
     ) {
         return Ok(ApiSnippets::default());
     }
+    if record_rs_type_kind.as_c9_co().is_some() {
+        return Ok(ApiSnippets::default());
+    }
     if record_rs_type_kind.is_bridge_type() {
         return Ok(ApiSnippets::default());
     }
diff --git a/rs_bindings_from_cc/generate_bindings/has_bindings.rs b/rs_bindings_from_cc/generate_bindings/has_bindings.rs
index 0a68905..f91cdc3 100644
--- a/rs_bindings_from_cc/generate_bindings/has_bindings.rs
+++ b/rs_bindings_from_cc/generate_bindings/has_bindings.rs
@@ -217,13 +217,7 @@
                 return;
             }
             has_nonunpin = true;
-            // TODO: b/446717938 - On next binary release, add `"non_unpin_ctor"` to `:wrapper` and
-            // and then change this to:
-            //  `!enabled_features.contains(crubit_feature::CrubitFeature::NonUnpinCtor)`.
-            if !enabled_features.is_disjoint(
-                crubit_feature::CrubitFeature::Wrapper
-                    | crubit_feature::CrubitFeature::NonUnpinCtor,
-            ) {
+            if enabled_features.contains(crubit_feature::CrubitFeature::NonUnpinCtor) {
                 return;
             }
             let location = location();
diff --git a/rs_bindings_from_cc/generate_bindings/lib.rs b/rs_bindings_from_cc/generate_bindings/lib.rs
index 6e6944e..3f56bd2 100644
--- a/rs_bindings_from_cc/generate_bindings/lib.rs
+++ b/rs_bindings_from_cc/generate_bindings/lib.rs
@@ -59,16 +59,25 @@
         environment,
     )?;
     let rs_api = {
-        let rustfmt_exe_path = Path::new(rustfmt_exe_path);
+        let rustfmt_exe_path =
+            if rustfmt_exe_path.is_empty() { None } else { Some(Path::new(rustfmt_exe_path)) };
         let rustfmt_config_path = if rustfmt_config_path.is_empty() {
             None
         } else {
             Some(Path::new(rustfmt_config_path))
         };
-        let rustfmt_config = RustfmtConfig::new(rustfmt_exe_path, rustfmt_config_path);
-        rs_tokens_to_formatted_string(rs_api, &rustfmt_config)?
+        let rustfmt_config =
+            rustfmt_exe_path.map(|path| RustfmtConfig::new(path, rustfmt_config_path));
+        rs_tokens_to_formatted_string(rs_api, rustfmt_config.as_ref())?
     };
-    let rs_api_impl = cc_tokens_to_formatted_string(rs_api_impl, Path::new(clang_format_exe_path))?;
+    let rs_api_impl = {
+        let clang_format_exe_path = if clang_format_exe_path.is_empty() {
+            None
+        } else {
+            Some(Path::new(clang_format_exe_path))
+        };
+        cc_tokens_to_formatted_string(rs_api_impl, clang_format_exe_path)?
+    };
 
     let top_level_comment = generate_top_level_comment(&ir, environment);
     // TODO(lukasza): Try to remove `#![rustfmt:skip]` - in theory it shouldn't
@@ -434,6 +443,10 @@
             BridgeRsTypeKind::StdString { .. } => false,
         },
         RsTypeKind::Record { record, .. } => is_record_unsafe(db, &record),
+        RsTypeKind::C9Co { result_type, .. } => {
+            // A Co<T> logically produces a T, so it is unsafe iff T is unsafe.
+            db.is_rs_type_kind_unsafe(result_type.as_ref().clone())
+        }
     }
 }
 
@@ -495,21 +508,22 @@
                     "bridge.h".into()
                 },
             ));
-
-            // TODO(b/436862191): Remove this once the migration is complete.
-            if !db
-                .ir()
-                .target_crubit_features(&record.owning_target)
-                .contains(crubit_feature::CrubitFeature::DoNotHardcodeStatusBridge)
-                && (record.owning_target == "@abseil-cpp//absl/status".into()
-                    || record.owning_target == "@abseil-cpp//absl/status:statusor".into())
-            {
-                internal_includes.insert(CcInclude::SupportLibHeader(
-                    crubit_support_path_format.clone(),
-                    "status_bridge.h".into(),
-                ));
-            }
         }
+
+        if let Ok(rs_type_kind) = db.rs_type_kind((&**record).into()) {
+            if rs_type_kind.as_c9_co().is_some() {
+                let includes = [
+                    "util/c9/internal/rust/co_vtable.h",
+                    "util/c9/internal/rust/destroy_coroutine_frame_from_rust.h",
+                    "util/c9/internal/rust/start_coroutine_from_rust.h",
+                    "util/c9/internal/pass_key.h",
+                ];
+
+                for file in includes {
+                    internal_includes.insert(CcInclude::user_header(file.into()));
+                }
+            }
+        };
     }
 
     for type_alias in ir.type_aliases() {
diff --git a/rs_bindings_from_cc/importers/BUILD b/rs_bindings_from_cc/importers/BUILD
index 61147ed..4666cc1 100644
--- a/rs_bindings_from_cc/importers/BUILD
+++ b/rs_bindings_from_cc/importers/BUILD
@@ -151,7 +151,10 @@
         "//rs_bindings_from_cc:ast_util",
         "//rs_bindings_from_cc:cc_ir",
         "//rs_bindings_from_cc:decl_importer",
+        "@abseil-cpp//absl/algorithm:container",
+        "@abseil-cpp//absl/log",
         "@abseil-cpp//absl/log:check",
+        "@abseil-cpp//absl/strings",
         "@llvm-project//clang:ast",
         "@llvm-project//clang:basic",
     ],
diff --git a/rs_bindings_from_cc/importers/cxx_record.cc b/rs_bindings_from_cc/importers/cxx_record.cc
index 3ac2cf3..5110188 100644
--- a/rs_bindings_from_cc/importers/cxx_record.cc
+++ b/rs_bindings_from_cc/importers/cxx_record.cc
@@ -519,21 +519,6 @@
       }
       bridge_type = *std::move(builtin_bridge_type);
     }
-
-    // TODO(b/436862191): Remove this once the migration is complete.
-    if (!bridge_type.has_value()) {
-      const clang::CXXRecordDecl* cxx_record_decl =
-          specialization_decl->getSpecializedTemplate()->getTemplatedDecl();
-      if (ictx_.GetOwningTarget(cxx_record_decl) ==
-              BazelLabel("@abseil-cpp//absl/status:statusor") &&
-          cxx_record_decl->getName() == "StatusOr") {
-        bridge_type = BridgeType{BridgeType::Bridge{
-            .rust_name = "::status::absl::StatusOr",
-            .abi_rust = "::status::absl::StatusOrAbi",
-            .abi_cpp = "::crubit::StatusOrAbi",
-        }};
-      }
-    }
   } else {
     const clang::NamedDecl* named_decl = record_decl;
     if (record_decl->getName().empty()) {
@@ -561,19 +546,6 @@
     }
   }
 
-  // TODO(b/436862191): Remove this once the migration is complete.
-  if (!bridge_type.has_value()) {
-    if (ictx_.GetOwningTarget(record_decl) ==
-            BazelLabel("@abseil-cpp//absl/status:status") &&
-        record_decl->getName() == "Status") {
-      bridge_type = BridgeType{BridgeType::Bridge{
-          .rust_name = "absl::Status",
-          .abi_rust = "absl::StatusAbi",
-          .abi_cpp = "::crubit::StatusAbi",
-      }};
-    }
-  }
-
   auto enclosing_item_id = ictx_.GetEnclosingItemId(record_decl);
   if (!enclosing_item_id.ok()) {
     return ictx_.ImportUnsupportedItem(
diff --git a/rs_bindings_from_cc/importers/enum.cc b/rs_bindings_from_cc/importers/enum.cc
index 1287d75..9ccedc5 100644
--- a/rs_bindings_from_cc/importers/enum.cc
+++ b/rs_bindings_from_cc/importers/enum.cc
@@ -111,9 +111,39 @@
   }
 
   if (ictx_.IsFromProtoTarget(*enum_decl)) {
-    // TODO(b/406221412): Proto enums aren't at the expected location.
-    return unsupported(
-        FormattedError::Static("b/406221412: Proto enums are not supported"));
+    // Supporting a top-level `Foo_Bar_Baz` enum is hard! It could be any of
+    // these four things:
+    // * A top-level `enum Foo_Bar_Baz`
+    // * A nested `message Foo { enum Bar_Baz }`
+    // * A differently nested `message Foo_Bar { enum Baz }`
+    // * A deeply nested `message Foo { message Bar { message Baz } }`
+    //
+    // There is no signal on the enum itself to distinguish, so we would need to
+    // iterate over all the records in the header file to find any aliases that
+    // refer to this enum, and from that, learn the name.
+    //
+    // At least for now, we're going to forgo that exercise. If the name does
+    // not contain an underscore, then we know it's the first case.
+    // If the name does contain an underscore, but is retrieved via the alias,
+    // then we can know the case above perfectly, and can handle this
+    // in type_alias.cc. But if the name contains an underscore, and is
+    // accessed at the top level: give up!
+    if (enum_decl->getName().contains('_')) {
+      return unsupported(FormattedError::Static(
+          "b/406221412: Proto enums with underscores are not supported "
+          "except via Message::Enum syntax."));
+    }
+    ictx_.MarkAsSuccessfullyImported(enum_decl);
+    return ExistingRustType{
+        .rs_name = std::string(enum_decl->getName()),
+        .cc_name = enum_decl->getQualifiedNameAsString(),
+        .type_parameters = {},
+        .owning_target = ictx_.GetOwningTarget(enum_decl),
+        .size_align = std::nullopt,
+        // To be paranoid, assume Rust proto enums are not ABI compatible.
+        .is_same_abi = false,
+        .id = ictx_.GenerateItemId(enum_decl),
+    };
   }
 
   ictx_.MarkAsSuccessfullyImported(enum_decl);
diff --git a/rs_bindings_from_cc/importers/function.cc b/rs_bindings_from_cc/importers/function.cc
index df09b31..31906d7 100644
--- a/rs_bindings_from_cc/importers/function.cc
+++ b/rs_bindings_from_cc/importers/function.cc
@@ -589,7 +589,8 @@
                    clang::isa<clang::OverrideAttr>(attr) ||
                    clang::isa<clang::PureAttr>(attr) ||
                    clang::isa<clang::ReinitializesAttr>(attr) ||
-                   clang::isa<clang::UnusedAttr>(attr)) {
+                   clang::isa<clang::UnusedAttr>(attr) ||
+                   clang::isa<clang::AlwaysInlineAttr>(attr)) {
           // These attributes don't affect Rust.
           return true;
         }
diff --git a/rs_bindings_from_cc/importers/type_alias.cc b/rs_bindings_from_cc/importers/type_alias.cc
index 53780d5..36e1636 100644
--- a/rs_bindings_from_cc/importers/type_alias.cc
+++ b/rs_bindings_from_cc/importers/type_alias.cc
@@ -8,20 +8,78 @@
 #include <string>
 #include <utility>
 
+#include "absl/algorithm/container.h"
 #include "absl/log/check.h"
+#include "absl/log/log.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_join.h"
+#include "absl/strings/string_view.h"
 #include "lifetime_annotations/type_lifetimes.h"
 #include "rs_bindings_from_cc/ast_util.h"
 #include "rs_bindings_from_cc/ir.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/LLVM.h"
 
 namespace crubit {
+namespace {
+
+std::string ProtoMessageToRustModName(absl::string_view message_name) {
+  std::string mod_name = "";
+  for (char c : message_name) {
+    if (absl::ascii_isupper(c) && !mod_name.empty()) {
+      absl::StrAppend(&mod_name, "_");
+    }
+    // StrAppend doesn't accept `char`. :'(
+    c = absl::ascii_tolower(static_cast<unsigned char>(c));
+    absl::StrAppend(&mod_name, absl::string_view(&c, 1));
+  }
+  return mod_name;
+}
+
+// Returns the relative name of a Rust Proto Enum corresponding to the
+// C++ Proto Enum. For example, the C++ enum `MyMessage::MyEnum` would become
+// `my_message::MyEnum`.
+std::string ProtoEnumToRustName(clang::NamedDecl& decl) {
+  std::vector<std::string> mod_chain;
+  for (clang::DeclContext* decl_context = decl.getDeclContext();
+       decl_context->isRecord(); decl_context = decl_context->getParent()) {
+    auto* record_decl = clang::dyn_cast<clang::RecordDecl>(decl_context);
+    mod_chain.push_back(ProtoMessageToRustModName(record_decl->getName()));
+  }
+  absl::c_reverse(mod_chain);
+  mod_chain.push_back(std::string(decl.getName()));
+  return absl::StrJoin(mod_chain, "::");
+}
+}  // namespace
 
 std::optional<IR::Item> crubit::TypeAliasImporter::Import(
     clang::NamedDecl* decl) {
+  // Special-case proto enums. We handle them under the alias, rather than
+  // the enum declaration, because the enum declaration gives no useful
+  // way to obtain the message names that it is a part of unless/until
+  // `_` is forbidden in enum identifiers.
+  if (auto* alias_decl = clang::dyn_cast<clang::TypedefNameDecl>(decl)) {
+    if (ictx_.IsFromProtoTarget(*alias_decl) &&
+        alias_decl->getUnderlyingType()->isEnumeralType()) {
+      ictx_.MarkAsSuccessfullyImported(decl);
+      return ExistingRustType{
+          .rs_name = ProtoEnumToRustName(*decl),
+          .cc_name = decl->getQualifiedNameAsString(),
+          .type_parameters = {},
+          .owning_target = ictx_.GetOwningTarget(decl),
+          .size_align = std::nullopt,
+          // To be paranoid, assume Rust proto enums are not ABI compatible.
+          .is_same_abi = false,
+          .id = ictx_.GenerateItemId(decl),
+      };
+    }
+  }
+
   clang::DeclContext* decl_context = decl->getDeclContext();
   clang::QualType underlying_qualtype;
   if (auto* typedef_name_decl = clang::dyn_cast<clang::TypedefNameDecl>(decl)) {
diff --git a/rs_bindings_from_cc/test/annotations/do_not_bind_api_impl.cc b/rs_bindings_from_cc/test/annotations/do_not_bind_api_impl.cc
index bbb7b62..d39e415 100644
--- a/rs_bindings_from_cc/test/annotations/do_not_bind_api_impl.cc
+++ b/rs_bindings_from_cc/test/annotations/do_not_bind_api_impl.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/annotations:do_not_bind
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/annotations/do_not_bind_rs_api.rs b/rs_bindings_from_cc/test/annotations/do_not_bind_rs_api.rs
index e13105c..b7feaf1 100644
--- a/rs_bindings_from_cc/test/annotations/do_not_bind_rs_api.rs
+++ b/rs_bindings_from_cc/test/annotations/do_not_bind_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/annotations:do_not_bind
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -33,18 +33,19 @@
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=12
         // Error while generating bindings for constructor 'ArgumentToBoundOverload::ArgumentToBoundOverload':
+        // Default constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::ArgumentToBoundOverload
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::ArgumentToBoundOverload
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=12
         // Error while generating bindings for constructor 'ArgumentToBoundOverload::ArgumentToBoundOverload':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::ArgumentToBoundOverload
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::ArgumentToBoundOverload
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=12
         // Error while generating bindings for constructor 'ArgumentToBoundOverload::ArgumentToBoundOverload':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::ArgumentToBoundOverload
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::ArgumentToBoundOverload
 
@@ -72,18 +73,19 @@
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=13
         // Error while generating bindings for constructor 'ArgumentToUnboundOverload::ArgumentToUnboundOverload':
+        // Default constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::ArgumentToUnboundOverload
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::ArgumentToUnboundOverload
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=13
         // Error while generating bindings for constructor 'ArgumentToUnboundOverload::ArgumentToUnboundOverload':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::ArgumentToUnboundOverload
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::ArgumentToUnboundOverload
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=13
         // Error while generating bindings for constructor 'ArgumentToUnboundOverload::ArgumentToUnboundOverload':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::ArgumentToUnboundOverload
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::ArgumentToUnboundOverload
 
@@ -123,13 +125,13 @@
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=26
         // Error while generating bindings for constructor 'StructWithDoNotBindConstructor::StructWithDoNotBindConstructor':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::StructWithDoNotBindConstructor
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::StructWithDoNotBindConstructor
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=26
         // Error while generating bindings for constructor 'StructWithDoNotBindConstructor::StructWithDoNotBindConstructor':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::StructWithDoNotBindConstructor
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::StructWithDoNotBindConstructor
 
@@ -162,18 +164,19 @@
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=32
         // Error while generating bindings for constructor 'StructWithDoNotBindMethod::StructWithDoNotBindMethod':
+        // Default constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::StructWithDoNotBindMethod
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::StructWithDoNotBindMethod
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=32
         // Error while generating bindings for constructor 'StructWithDoNotBindMethod::StructWithDoNotBindMethod':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::StructWithDoNotBindMethod
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::StructWithDoNotBindMethod
 
         // Generated from: rs_bindings_from_cc/test/annotations/do_not_bind.h;l=32
         // Error while generating bindings for constructor 'StructWithDoNotBindMethod::StructWithDoNotBindMethod':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::crubit::test::StructWithDoNotBindMethod
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::crubit::test::StructWithDoNotBindMethod
 
diff --git a/rs_bindings_from_cc/test/annotations/rust_name_api_impl.cc b/rs_bindings_from_cc/test/annotations/rust_name_api_impl.cc
index a877eea..16d7af6 100644
--- a/rs_bindings_from_cc/test/annotations/rust_name_api_impl.cc
+++ b/rs_bindings_from_cc/test/annotations/rust_name_api_impl.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/annotations:rust_name
-// Features: infer_operator_lifetimes, std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, infer_operator_lifetimes, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/annotations/rust_name_rs_api.rs b/rs_bindings_from_cc/test/annotations/rust_name_rs_api.rs
index b8ef68a..09c2df5 100644
--- a/rs_bindings_from_cc/test/annotations/rust_name_rs_api.rs
+++ b/rs_bindings_from_cc/test/annotations/rust_name_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/annotations:rust_name
-// Features: infer_operator_lifetimes, std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, infer_operator_lifetimes, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
diff --git a/rs_bindings_from_cc/test/crate_derive/BUILD b/rs_bindings_from_cc/test/crate_derive/BUILD
index 9ad02b8..bac112a 100644
--- a/rs_bindings_from_cc/test/crate_derive/BUILD
+++ b/rs_bindings_from_cc/test/crate_derive/BUILD
@@ -22,6 +22,6 @@
     ],
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
diff --git a/rs_bindings_from_cc/test/forward_declaration/type_ownership/BUILD b/rs_bindings_from_cc/test/forward_declaration/type_ownership/BUILD
index 5ba1ea6..bce049e 100644
--- a/rs_bindings_from_cc/test/forward_declaration/type_ownership/BUILD
+++ b/rs_bindings_from_cc/test/forward_declaration/type_ownership/BUILD
@@ -28,7 +28,7 @@
     deps = [
         ":definition",
         ":forward_declaration",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
diff --git a/rs_bindings_from_cc/test/function/inline/inline_api_impl.cc b/rs_bindings_from_cc/test/function/inline/inline_api_impl.cc
index ee09cd5..4640828 100644
--- a/rs_bindings_from_cc/test/function/inline/inline_api_impl.cc
+++ b/rs_bindings_from_cc/test/function/inline/inline_api_impl.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/function/inline:inline
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/function/inline/inline_rs_api.rs b/rs_bindings_from_cc/test/function/inline/inline_rs_api.rs
index 1ce99c6..bb39301 100644
--- a/rs_bindings_from_cc/test/function/inline/inline_rs_api.rs
+++ b/rs_bindings_from_cc/test/function/inline/inline_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/function/inline:inline
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -40,18 +40,19 @@
 
 // Generated from: rs_bindings_from_cc/test/function/inline/inline.h;l=12
 // Error while generating bindings for constructor 'SomeStruct::SomeStruct':
+// Default constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::SomeStruct
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::SomeStruct
 
 // Generated from: rs_bindings_from_cc/test/function/inline/inline.h;l=12
 // Error while generating bindings for constructor 'SomeStruct::SomeStruct':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::SomeStruct
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::SomeStruct
 
 // Generated from: rs_bindings_from_cc/test/function/inline/inline.h;l=12
 // Error while generating bindings for constructor 'SomeStruct::SomeStruct':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::SomeStruct
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::SomeStruct
 
diff --git a/rs_bindings_from_cc/test/function/simple/simple_api_impl.cc b/rs_bindings_from_cc/test/function/simple/simple_api_impl.cc
index f3e026c..cf31385 100644
--- a/rs_bindings_from_cc/test/function/simple/simple_api_impl.cc
+++ b/rs_bindings_from_cc/test/function/simple/simple_api_impl.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/function/simple:simple
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/function/simple/simple_rs_api.rs b/rs_bindings_from_cc/test/function/simple/simple_rs_api.rs
index 4693f5b..af618ba 100644
--- a/rs_bindings_from_cc/test/function/simple/simple_rs_api.rs
+++ b/rs_bindings_from_cc/test/function/simple/simple_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/function/simple:simple
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes)]
diff --git a/rs_bindings_from_cc/test/golden/BUILD b/rs_bindings_from_cc/test/golden/BUILD
index 89222b7..c4c9cff 100644
--- a/rs_bindings_from_cc/test/golden/BUILD
+++ b/rs_bindings_from_cc/test/golden/BUILD
@@ -51,6 +51,12 @@
     "depends_on_nested_types": ["nested_types"],
 }
 
+# Maps a test to the list of additional aspect hints it needs.
+ASPECT_HINTS = {
+    "nontrivial_type": ["//features:non_unpin_ctor"],
+    "user_of_unsupported": ["//features:non_unpin_ctor"],
+}
+
 TAGS = {}
 
 rust_bindings_from_cc_cli_flag(
@@ -64,7 +70,7 @@
     aspect_hints = [
         "//features:infer_operator_lifetimes",
         ":disable_source_location_in_doc_comment",
-    ],
+    ] + (ASPECT_HINTS[name] if name in ASPECT_HINTS else []),
     deps = [
         ((d + "_cc") if d in TESTS else d)
         for d in (DEPS[name] if name in DEPS else [])
diff --git a/rs_bindings_from_cc/test/golden/nontrivial_type_rs_api.rs b/rs_bindings_from_cc/test/golden/nontrivial_type_rs_api.rs
index b58cb36..6d96e32 100644
--- a/rs_bindings_from_cc/test/golden/nontrivial_type_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/nontrivial_type_rs_api.rs
@@ -119,9 +119,20 @@
 // Can't generate bindings for Nontrivial::operator=, because of missing required features (<internal link>):
 // //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:experimental] for Nontrivial::operator= (return type: references are not supported)
 
-// Error while generating bindings for function 'Nontrivial::operator=':
-// Can't generate bindings for Nontrivial::operator=, because of missing required features (<internal link>):
-// //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:non_unpin_ctor] for Nontrivial::operator= (<internal link>_relocatable_error: the return type is not rust-movable)
+impl ::ctor::Assign<f32> for Nontrivial {
+    #[inline(always)]
+    fn assign<'a>(self: ::core::pin::Pin<&'a mut Self>, __param_0: f32) {
+        unsafe {
+            let _ = ::ctor::emplace!(::ctor::FnCtor::new(move |dest: *mut Self| {
+                crate::detail::__rust_thunk___ZN10NontrivialaSEf(
+                    dest as *mut ::core::ffi::c_void,
+                    self,
+                    __param_0,
+                );
+            }));
+        }
+    }
+}
 
 impl ::ctor::PinnedDrop for Nontrivial {
     #[inline(always)]
@@ -446,15 +457,33 @@
     }
 }
 
-// Error while generating bindings for function 'TakesByValue':
-// Can't generate bindings for TakesByValue, because of missing required features (<internal link>):
-// //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:non_unpin_ctor] for TakesByValue (<internal link>_relocatable_error: the return type is not rust-movable)
-// //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:non_unpin_ctor] for TakesByValue (<internal link>_relocatable_error: nontrivial (parameter #0) is not rust-movable)
+#[inline(always)]
+pub fn TakesByValue(
+    nontrivial: impl ::ctor::Ctor<Output = crate::Nontrivial, Error = ::ctor::Infallible>,
+) -> impl ::ctor::Ctor<Output = crate::Nontrivial, Error = ::ctor::Infallible> {
+    unsafe {
+        ::ctor::FnCtor::new(move |dest: *mut crate::Nontrivial| {
+            crate::detail::__rust_thunk___Z12TakesByValue10Nontrivial(
+                dest as *mut ::core::ffi::c_void,
+                ::core::pin::Pin::into_inner_unchecked(::ctor::emplace!(nontrivial)),
+            );
+        })
+    }
+}
 
-// Error while generating bindings for function 'TakesByValueInline':
-// Can't generate bindings for TakesByValueInline, because of missing required features (<internal link>):
-// //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:non_unpin_ctor] for TakesByValueInline (<internal link>_relocatable_error: the return type is not rust-movable)
-// //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:non_unpin_ctor] for TakesByValueInline (<internal link>_relocatable_error: nontrivial (parameter #0) is not rust-movable)
+#[inline(always)]
+pub fn TakesByValueInline(
+    nontrivial: impl ::ctor::Ctor<Output = crate::NontrivialInline, Error = ::ctor::Infallible>,
+) -> impl ::ctor::Ctor<Output = crate::NontrivialInline, Error = ::ctor::Infallible> {
+    unsafe {
+        ::ctor::FnCtor::new(move |dest: *mut crate::NontrivialInline| {
+            crate::detail::__rust_thunk___Z18TakesByValueInline16NontrivialInline(
+                dest as *mut ::core::ffi::c_void,
+                ::core::pin::Pin::into_inner_unchecked(::ctor::emplace!(nontrivial)),
+            );
+        })
+    }
+}
 
 #[inline(always)]
 pub fn TakesByValueUnpin(mut nontrivial: crate::NontrivialUnpin) -> crate::NontrivialUnpin {
@@ -540,9 +569,22 @@
 // //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:experimental] for NontrivialByValue::operator= (return type: references are not supported)
 // //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:experimental] for NontrivialByValue::operator= (the type of other (parameter #1): references are not supported)
 
-// Error while generating bindings for function 'NontrivialByValue::operator=':
-// Can't generate bindings for NontrivialByValue::operator=, because of missing required features (<internal link>):
-// //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:non_unpin_ctor] for NontrivialByValue::operator= (<internal link>_relocatable_error: other (parameter #1) is not rust-movable)
+impl<'other> ::ctor::UnpinAssign<::ctor::RvalueReference<'other, crate::Nontrivial>>
+    for NontrivialByValue
+{
+    #[inline(always)]
+    fn unpin_assign<'a>(&'a mut self, other: ::ctor::RvalueReference<'other, crate::Nontrivial>) {
+        unsafe {
+            let mut __return = ::core::mem::MaybeUninit::<Self>::uninit();
+            crate::detail::__rust_thunk___ZN17NontrivialByValueaSE10Nontrivial(
+                &raw mut __return as *mut ::core::ffi::c_void,
+                self,
+                other,
+            );
+            __return.assume_init();
+        }
+    }
+}
 
 #[diagnostic::on_unimplemented(
     message = "binding generation for function failed\nExpected first operator== param reference to be immutable, but found mutable reference: &'a mut crate::NontrivialByValue\ncomparison operator return type must be `bool`, found: crate::NontrivialByValue"
@@ -603,13 +645,34 @@
     }
 }
 
-// Error while generating bindings for function 'TakesNonmovableByValue':
-// Can't generate bindings for TakesNonmovableByValue, because of missing required features (<internal link>):
-// //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:non_unpin_ctor] for TakesNonmovableByValue (<internal link>_relocatable_error: nonmovable (parameter #0) is not rust-movable)
+#[diagnostic::on_unimplemented(
+    message = "binding generation for function failed\nNon-movable, non-trivial_abi type 'crate::Nonmovable' is not supported by value as parameter #0"
+)]
+pub trait BindingFailedFor_Z22TakesNonmovableByValue10Nonmovable {}
+#[inline(always)]
+pub fn TakesNonmovableByValue<'error>(
+    nonmovable: impl ::ctor::Ctor<Output = crate::Nonmovable, Error = ::ctor::Infallible>,
+) where
+    &'error (): BindingFailedFor_Z22TakesNonmovableByValue10Nonmovable,
+{
+    #![allow(unused_variables)]
+    unreachable!(
+        "This impl can never be instantiated. \
+                    If this message appears at runtime, please report a <internal link>."
+    )
+}
 
-// Error while generating bindings for function 'ReturnsNonmovableByValue':
-// Can't generate bindings for ReturnsNonmovableByValue, because of missing required features (<internal link>):
-// //rs_bindings_from_cc/test/golden:nontrivial_type_cc needs [//features:non_unpin_ctor] for ReturnsNonmovableByValue (<internal link>_relocatable_error: the return type is not rust-movable)
+#[inline(always)]
+pub fn ReturnsNonmovableByValue(
+) -> impl ::ctor::Ctor<Output = crate::Nonmovable, Error = ::ctor::Infallible> {
+    unsafe {
+        ::ctor::FnCtor::new(move |dest: *mut crate::Nonmovable| {
+            crate::detail::__rust_thunk___Z24ReturnsNonmovableByValuev(
+                dest as *mut ::core::ffi::c_void,
+            );
+        })
+    }
+}
 
 mod detail {
     #[allow(unused_imports)]
@@ -628,6 +691,11 @@
             field: ::core::ffi::c_int,
             unused: ::core::ffi::c_int,
         );
+        pub(crate) unsafe fn __rust_thunk___ZN10NontrivialaSEf<'a>(
+            __return: *mut ::core::ffi::c_void,
+            __this: ::core::pin::Pin<&'a mut crate::Nontrivial>,
+            __param_0: f32,
+        );
         #[link_name = "_ZN10NontrivialD1Ev"]
         pub(crate) unsafe fn __rust_thunk___ZN10NontrivialD1Ev<'a>(
             __this: ::core::pin::Pin<&'a mut crate::Nontrivial>,
@@ -689,10 +757,23 @@
         pub(crate) unsafe fn __rust_thunk___ZN15NontrivialUnpin14MemberFunctionEv<'a>(
             __this: &'a mut crate::NontrivialUnpin,
         );
+        pub(crate) unsafe fn __rust_thunk___Z12TakesByValue10Nontrivial(
+            __return: *mut ::core::ffi::c_void,
+            nontrivial: &mut crate::Nontrivial,
+        );
+        pub(crate) unsafe fn __rust_thunk___Z18TakesByValueInline16NontrivialInline(
+            __return: *mut ::core::ffi::c_void,
+            nontrivial: &mut crate::NontrivialInline,
+        );
         pub(crate) unsafe fn __rust_thunk___Z17TakesByValueUnpin15NontrivialUnpin(
             __return: *mut ::core::ffi::c_void,
             nontrivial: &mut crate::NontrivialUnpin,
         );
+        pub(crate) unsafe fn __rust_thunk___ZN17NontrivialByValueaSE10Nontrivial<'a, 'other>(
+            __return: *mut ::core::ffi::c_void,
+            __this: &'a mut crate::NontrivialByValue,
+            other: ::ctor::RvalueReference<'other, crate::Nontrivial>,
+        );
         #[link_name = "_ZN10NonmovableC1Ev"]
         pub(crate) unsafe fn __rust_thunk___ZN10NonmovableC1Ev(__this: *mut ::core::ffi::c_void);
         #[link_name = "_ZN10NonmovableD1Ev"]
@@ -703,6 +784,9 @@
         pub(crate) unsafe fn __rust_thunk___ZN10Nonmovable14MemberFunctionEv<'a>(
             __this: ::core::pin::Pin<&'a mut crate::Nonmovable>,
         );
+        pub(crate) unsafe fn __rust_thunk___Z24ReturnsNonmovableByValuev(
+            __return: *mut ::core::ffi::c_void,
+        );
     }
 }
 
diff --git a/rs_bindings_from_cc/test/golden/nontrivial_type_rs_api_impl.cc b/rs_bindings_from_cc/test/golden/nontrivial_type_rs_api_impl.cc
index 63966da..67497b1 100644
--- a/rs_bindings_from_cc/test/golden/nontrivial_type_rs_api_impl.cc
+++ b/rs_bindings_from_cc/test/golden/nontrivial_type_rs_api_impl.cc
@@ -22,6 +22,12 @@
 static_assert(alignof(struct Nontrivial) == 4);
 static_assert(CRUBIT_OFFSET_OF(field, struct Nontrivial) == 0);
 
+extern "C" void __rust_thunk___ZN10NontrivialaSEf(struct Nontrivial* __return,
+                                                  struct Nontrivial* __this,
+                                                  float __param_0) {
+  new (__return) auto(__this->operator=(__param_0));
+}
+
 static_assert((void (::Nontrivial::*)())&Nontrivial::Unqualified);
 
 static_assert((void (::Nontrivial::*)() const) & Nontrivial::ConstQualified);
@@ -84,6 +90,21 @@
 
 static_assert((void (::NontrivialUnpin::*)())&NontrivialUnpin::MemberFunction);
 
+extern "C" void __rust_thunk___Z12TakesByValue10Nontrivial(
+    struct Nontrivial* __return, struct Nontrivial* nontrivial) {
+  new (__return) auto(TakesByValue(std::move(*nontrivial)));
+}
+
+static_assert((struct Nontrivial (*)(struct Nontrivial))&TakesByValue);
+
+extern "C" void __rust_thunk___Z18TakesByValueInline16NontrivialInline(
+    struct NontrivialInline* __return, struct NontrivialInline* nontrivial) {
+  new (__return) auto(TakesByValueInline(std::move(*nontrivial)));
+}
+
+static_assert(
+    (struct NontrivialInline (*)(struct NontrivialInline))&TakesByValueInline);
+
 extern "C" void __rust_thunk___Z17TakesByValueUnpin15NontrivialUnpin(
     struct NontrivialUnpin* __return, struct NontrivialUnpin* nontrivial) {
   new (__return) auto(TakesByValueUnpin(std::move(*nontrivial)));
@@ -95,9 +116,22 @@
 static_assert(sizeof(struct NontrivialByValue) == 1);
 static_assert(alignof(struct NontrivialByValue) == 1);
 
+extern "C" void __rust_thunk___ZN17NontrivialByValueaSE10Nontrivial(
+    struct NontrivialByValue* __return, struct NontrivialByValue* __this,
+    struct Nontrivial* other) {
+  new (__return) auto(__this->operator=(std::move(*other)));
+}
+
 static_assert(sizeof(struct Nonmovable) == 1);
 static_assert(alignof(struct Nonmovable) == 1);
 
 static_assert((void (::Nonmovable::*)())&Nonmovable::MemberFunction);
 
+extern "C" void __rust_thunk___Z24ReturnsNonmovableByValuev(
+    struct Nonmovable* __return) {
+  new (__return) auto(ReturnsNonmovableByValue());
+}
+
+static_assert((struct Nonmovable (*)())&ReturnsNonmovableByValue);
+
 #pragma clang diagnostic pop
diff --git a/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api.rs b/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api.rs
index 7daaa4b..b8ddc5a 100644
--- a/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api.rs
@@ -6,7 +6,7 @@
 // //rs_bindings_from_cc/test/golden:user_of_unsupported_cc
 
 #![rustfmt::skip]
-#![feature(allocator_api, cfg_sanitize, custom_inner_attributes)]
+#![feature(allocator_api, cfg_sanitize, custom_inner_attributes, impl_trait_in_assoc_type)]
 #![allow(stable_features)]
 #![no_std]
 #![allow(improper_ctypes)]
@@ -14,6 +14,26 @@
 #![allow(dead_code, unused_mut)]
 #![deny(warnings)]
 
-// Error while generating bindings for function 'UseNontrivialCustomType':
-// Can't generate bindings for UseNontrivialCustomType, because of missing required features (<internal link>):
-// //rs_bindings_from_cc/test/golden:user_of_unsupported_cc needs [//features:non_unpin_ctor] for UseNontrivialCustomType (<internal link>_relocatable_error: non_trivial_custom_type (parameter #0) is not rust-movable)
+#[inline(always)]
+pub fn UseNontrivialCustomType(
+    non_trivial_custom_type: impl ::ctor::Ctor<
+        Output = unsupported_cc::NontrivialCustomType,
+        Error = ::ctor::Infallible,
+    >,
+) {
+    unsafe {
+        crate::detail::__rust_thunk___Z23UseNontrivialCustomType20NontrivialCustomType(
+            ::core::pin::Pin::into_inner_unchecked(::ctor::emplace!(non_trivial_custom_type)),
+        )
+    }
+}
+
+mod detail {
+    #[allow(unused_imports)]
+    use super::*;
+    unsafe extern "C" {
+        pub(crate) unsafe fn __rust_thunk___Z23UseNontrivialCustomType20NontrivialCustomType(
+            non_trivial_custom_type: &mut unsupported_cc::NontrivialCustomType,
+        );
+    }
+}
diff --git a/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api_impl.cc b/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api_impl.cc
index a534a06..81baec6 100644
--- a/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api_impl.cc
+++ b/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api_impl.cc
@@ -18,4 +18,11 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wthread-safety-analysis"
 
+extern "C" void __rust_thunk___Z23UseNontrivialCustomType20NontrivialCustomType(
+    struct NontrivialCustomType* non_trivial_custom_type) {
+  UseNontrivialCustomType(std::move(*non_trivial_custom_type));
+}
+
+static_assert((void (*)(struct NontrivialCustomType))&UseNontrivialCustomType);
+
 #pragma clang diagnostic pop
diff --git a/rs_bindings_from_cc/test/namespace/inline/inline_api_impl.cc b/rs_bindings_from_cc/test/namespace/inline/inline_api_impl.cc
index 2657914..9e07fd3 100644
--- a/rs_bindings_from_cc/test/namespace/inline/inline_api_impl.cc
+++ b/rs_bindings_from_cc/test/namespace/inline/inline_api_impl.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/namespace/inline:inline
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/namespace/inline/inline_rs_api.rs b/rs_bindings_from_cc/test/namespace/inline/inline_rs_api.rs
index fb71812..6e97dc0 100644
--- a/rs_bindings_from_cc/test/namespace/inline/inline_rs_api.rs
+++ b/rs_bindings_from_cc/test/namespace/inline/inline_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/namespace/inline:inline
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -38,18 +38,19 @@
 
         // Generated from: rs_bindings_from_cc/test/namespace/inline/inline.h;l=11
         // Error while generating bindings for constructor 'MyStruct::MyStruct':
+        // Default constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::foo::inline1::MyStruct
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::foo::inline1::MyStruct
 
         // Generated from: rs_bindings_from_cc/test/namespace/inline/inline.h;l=11
         // Error while generating bindings for constructor 'MyStruct::MyStruct':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::foo::inline1::MyStruct
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::foo::inline1::MyStruct
 
         // Generated from: rs_bindings_from_cc/test/namespace/inline/inline.h;l=11
         // Error while generating bindings for constructor 'MyStruct::MyStruct':
-        // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+        // Move and copy constructors do yet receive bindings. See b/452726517.
         // Expected first constructor parameter to be a mutable reference, got: *mut crate::foo::inline1::MyStruct
         // Expected first reference parameter `__this` to have a lifetime, found *mut crate::foo::inline1::MyStruct
 
diff --git a/rs_bindings_from_cc/test/references/references_api_impl.cc b/rs_bindings_from_cc/test/references/references_api_impl.cc
index 508b688..5631cb6 100644
--- a/rs_bindings_from_cc/test/references/references_api_impl.cc
+++ b/rs_bindings_from_cc/test/references/references_api_impl.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/references:references
-// Features: infer_operator_lifetimes, std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, infer_operator_lifetimes, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/references/references_rs_api.rs b/rs_bindings_from_cc/test/references/references_rs_api.rs
index 4022196..4252e97 100644
--- a/rs_bindings_from_cc/test/references/references_rs_api.rs
+++ b/rs_bindings_from_cc/test/references/references_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/references:references
-// Features: infer_operator_lifetimes, std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, infer_operator_lifetimes, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -30,7 +30,7 @@
 }
 
 #[diagnostic::on_unimplemented(
-    message = "binding generation for function failed\nUnsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347."
+    message = "binding generation for function failed\nConstructors cannot be `unsafe`, but this constructor accepts:\n    `ptr` of unsafe type `*mut::core::ffi::c_int`"
 )]
 pub trait BindingFailedFor_ZN22TypeWithPtrConstructorC1EPi {}
 /// Generated from: rs_bindings_from_cc/test/references/references.h;l=10
@@ -63,7 +63,7 @@
 }
 
 #[diagnostic::on_unimplemented(
-    message = "binding generation for function failed\nUnsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347."
+    message = "binding generation for function failed\nConstructors cannot be `unsafe`, but this constructor accepts:\n    `ptr` of unsafe type `*mut::core::ffi::c_int`"
 )]
 pub trait BindingFailedFor_ZN29TypeWithNonNullPtrConstructorC1EPi {}
 /// Generated from: rs_bindings_from_cc/test/references/references.h;l=15
diff --git a/rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh b/rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh
index e3a2101..cf365d9 100755
--- a/rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh
+++ b/rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh
@@ -44,25 +44,6 @@
       --target=//:target \
       --rs_out=\"${rs_out}\" \
       --cc_out=\"${cc_out}\" 2>&1 \
-      --crubit_support_path_format=\"<test/crubit/support/path/{header}>\" | \
-      grep 'please specify --clang_format_exe_path' > /dev/null" \
-    "generator should show help message for --clang_format_exe_path"
-
-  EXPECT_SUCCEED \
-    "\"${RS_BINDINGS_FROM_CC}\" \
-      --target=//:target \
-      --rs_out=\"${rs_out}\" \
-      --cc_out=\"${cc_out}\" 2>&1 \
-      --crubit_support_path_format=\"<test/crubit/support/path/{header}>\" \
-      --clang_format_exe_path=\"${DEFAULT_CLANG_FORMAT_EXE_PATH}\" | \
-      grep 'please specify --rustfmt_exe_path' > /dev/null" \
-    "generator should show help message for --rustfmt_exe_path"
-
-  EXPECT_SUCCEED \
-    "\"${RS_BINDINGS_FROM_CC}\" \
-      --target=//:target \
-      --rs_out=\"${rs_out}\" \
-      --cc_out=\"${cc_out}\" 2>&1 \
       --crubit_support_path_format=\"<test/crubit/support/path/{header}>\" \
       --clang_format_exe_path=\"${DEFAULT_CLANG_FORMAT_EXE_PATH}\" \
       --rustfmt_exe_path=\"${DEFAULT_RUSTFMT_EXE_PATH}\" | \
@@ -105,6 +86,30 @@
   EXPECT_FILE_NOT_EMPTY "${cc_out}"
 }
 
+function test::optional_formatting_paths() {
+  local rs_out="${TEST_TMPDIR}/rs_api.rs"
+  local cc_out="${TEST_TMPDIR}/rs_api_impl.cc"
+  local hdr="no_such_file.h"
+  local json
+  json="$(cat <<-EOT
+  [{"t": "//foo/bar:baz", "h": ["${hdr}"], "f": ["experimental", "supported"]}]
+EOT
+)"
+
+  EXPECT_SUCCEED \
+    "\"${RS_BINDINGS_FROM_CC}\" \
+      --target=//:target \
+      --rs_out=\"${rs_out}\" \
+      --cc_out=\"${cc_out}\" 2>&1 \
+      --crubit_support_path_format=\"<test/crubit/support/path/{header}>\" \
+      --public_headers=\"${hdr}\" \
+      --target_args=\"$(echo "${json}" | quote_escape)\" \
+      --do_nothing"
+
+  EXPECT_FILE_NOT_EMPTY "${rs_out}"
+  EXPECT_FILE_NOT_EMPTY "${cc_out}"
+}
+
 function test::do_nothing() {
   local rs_out="${TEST_TMPDIR}/rs_api.rs"
   local cc_out="${TEST_TMPDIR}/rs_api_impl.cc"
diff --git a/rs_bindings_from_cc/test/struct/constructors/BUILD b/rs_bindings_from_cc/test/struct/constructors/BUILD
index 7bd416e..196042f 100644
--- a/rs_bindings_from_cc/test/struct/constructors/BUILD
+++ b/rs_bindings_from_cc/test/struct/constructors/BUILD
@@ -22,6 +22,6 @@
     deps = [
         "//support:ctor",
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
diff --git a/rs_bindings_from_cc/test/struct/default_member_functions/BUILD b/rs_bindings_from_cc/test/struct/default_member_functions/BUILD
index 8b3693a..4be842e 100644
--- a/rs_bindings_from_cc/test/struct/default_member_functions/BUILD
+++ b/rs_bindings_from_cc/test/struct/default_member_functions/BUILD
@@ -22,6 +22,6 @@
     deps = [
         "//support:ctor",
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
diff --git a/rs_bindings_from_cc/test/struct/destructors/destructors_api_impl.cc b/rs_bindings_from_cc/test/struct/destructors/destructors_api_impl.cc
index e203e3d..25b3ae0 100644
--- a/rs_bindings_from_cc/test/struct/destructors/destructors_api_impl.cc
+++ b/rs_bindings_from_cc/test/struct/destructors/destructors_api_impl.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/struct/destructors:destructors
-// Features: infer_operator_lifetimes, std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, infer_operator_lifetimes, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/struct/destructors/destructors_rs_api.rs b/rs_bindings_from_cc/test/struct/destructors/destructors_rs_api.rs
index ae4d0ea..28ff665 100644
--- a/rs_bindings_from_cc/test/struct/destructors/destructors_rs_api.rs
+++ b/rs_bindings_from_cc/test/struct/destructors/destructors_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/struct/destructors:destructors
-// Features: infer_operator_lifetimes, std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, infer_operator_lifetimes, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
diff --git a/rs_bindings_from_cc/test/struct/forward_declarations/BUILD b/rs_bindings_from_cc/test/struct/forward_declarations/BUILD
index 95eacf4..e69bd59 100644
--- a/rs_bindings_from_cc/test/struct/forward_declarations/BUILD
+++ b/rs_bindings_from_cc/test/struct/forward_declarations/BUILD
@@ -56,6 +56,6 @@
         "//support:ctor",
         "//support:forward_declare",
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
diff --git a/rs_bindings_from_cc/test/struct/nonunpin/BUILD b/rs_bindings_from_cc/test/struct/nonunpin/BUILD
index f08404d..9784ee2 100644
--- a/rs_bindings_from_cc/test/struct/nonunpin/BUILD
+++ b/rs_bindings_from_cc/test/struct/nonunpin/BUILD
@@ -8,7 +8,7 @@
 crubit_test_cc_library(
     name = "nonunpin",
     hdrs = ["nonunpin.h"],
-    aspect_hints = ["//features:experimental"],
+    aspect_hints = ["//features:non_unpin_ctor"],
     deps = ["@abseil-cpp//absl/log:check"],
 )
 
@@ -24,3 +24,23 @@
         "@crate_index//:googletest",
     ],
 )
+
+crubit_test_cc_library(
+    name = "nonunpin_experimental",
+    hdrs = ["nonunpin.h"],
+    aspect_hints = ["//features:experimental"],
+    deps = ["@abseil-cpp//absl/log:check"],
+)
+
+crubit_rust_test(
+    name = "nonunpin_experimental_test",
+    srcs = ["nonunpin_experimental_test.rs"],
+    cc_deps = [":nonunpin_experimental"],
+    # LINT.IfChange
+    rustc_flags = ["-Zallow-features=negative_impls"],
+    # LINT.ThenChange(//docs/overview/unstable_features.md)
+    deps = [
+        "//support:ctor",
+        "@crate_index//:googletest",
+    ],
+)
diff --git a/rs_bindings_from_cc/test/struct/nonunpin/nonunpin_experimental_test.rs b/rs_bindings_from_cc/test/struct/nonunpin/nonunpin_experimental_test.rs
new file mode 100644
index 0000000..889ec56
--- /dev/null
+++ b/rs_bindings_from_cc/test/struct/nonunpin/nonunpin_experimental_test.rs
@@ -0,0 +1,159 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#![feature(negative_impls)]
+
+use ctor::{
+    ctor, emplace, mov, Assign, ConstRvalueReference, Ctor, CtorNew, Emplace, Infallible,
+    RvalueReference,
+};
+use googletest::prelude::*;
+use nonunpin_experimental::Nonunpin;
+use std::pin::Pin;
+
+#[gtest]
+fn test_move_construct() {
+    ctor::emplace! {
+        let mut x = Nonunpin::ctor_new(42);
+        let mut y = ctor::mov!(x.as_mut());
+    }
+
+    assert_eq!(x.value(), 0); // moved-from
+    assert_eq!(y.value(), 42); // moved-to
+
+    assert_eq!(x.addr(), &*x as *const _ as usize);
+    assert_eq!(y.addr(), &*y as *const _ as usize);
+}
+
+#[gtest]
+fn test_move_assign() {
+    ctor::emplace! {
+        let mut x = Nonunpin::ctor_new(42);
+        let mut y = Nonunpin::ctor_new(8);
+    }
+
+    y.as_mut().assign(ctor::mov!(x.as_mut()));
+
+    assert_eq!(x.value(), 0); // moved-from
+    assert_eq!(y.value(), 42); // moved-to
+
+    assert_eq!(x.addr(), &*x as *const _ as usize);
+    assert_eq!(y.addr(), &*y as *const _ as usize);
+}
+
+#[gtest]
+fn test_copy_construct() {
+    ctor::emplace! {
+        let x = Nonunpin::ctor_new(42);
+        let y = ctor::copy(&*x);
+    }
+
+    assert_eq!(x.value(), 42);
+    assert_eq!(y.value(), 42);
+
+    assert_eq!(x.addr(), &*x as *const _ as usize);
+    assert_eq!(y.addr(), &*y as *const _ as usize);
+}
+
+#[gtest]
+fn test_copy_assign() {
+    ctor::emplace! {
+        let x = Nonunpin::ctor_new(42);
+        let mut y = Nonunpin::ctor_new(8);
+    }
+    y.as_mut().assign(&*x);
+
+    assert_eq!(x.value(), 42);
+    assert_eq!(y.value(), 42);
+
+    assert_eq!(x.addr(), &*x as *const _ as usize);
+    assert_eq!(y.addr(), &*y as *const _ as usize);
+}
+
+/// Test that the struct can be returned and passed as all the reference
+/// types, and passed by value.
+#[gtest]
+fn test_ref() {
+    ctor::emplace! {
+        let mut x = Nonunpin::ctor_new(42);
+    }
+    {
+        let x_ref: Pin<&mut Nonunpin> = x.as_mut().AsMutRef();
+        assert_eq!(nonunpin_experimental::GetValueFromMutRef(x_ref), 42);
+        assert_eq!(nonunpin_experimental::GetValueFromMutRef(x.as_mut()), 42);
+    }
+    {
+        let x_ref: &Nonunpin = x.AsConstRef();
+        assert_eq!(nonunpin_experimental::GetValueFromConstRef(x_ref), 42);
+        assert_eq!(nonunpin_experimental::GetValueFromConstRef(&x), 42);
+    }
+    {
+        let x_ref: RvalueReference<Nonunpin> = x.as_mut().AsRvalueRef();
+        assert_eq!(nonunpin_experimental::GetValueFromRvalueRef(x_ref), 42);
+        assert_eq!(nonunpin_experimental::GetValueFromRvalueRef(ctor::mov!(x.as_mut())), 42);
+    }
+    {
+        let x_ref: ConstRvalueReference<Nonunpin> = x.AsConstRvalueRef();
+        assert_eq!(nonunpin_experimental::GetValueFromConstRvalueRef(x_ref), 42);
+        assert_eq!(nonunpin_experimental::GetValueFromConstRvalueRef(ctor::const_mov!(&*x)), 42);
+        assert_eq!(
+            nonunpin_experimental::GetValueFromConstRvalueRef(ctor::const_mov!(x.as_mut())),
+            42
+        );
+    }
+    {
+        assert_eq!(nonunpin_experimental::GetValueFromValue(ctor::copy(&*x)), 42);
+        assert_eq!(nonunpin_experimental::GetValueFromValue(ctor::mov!(x)), 42);
+    }
+}
+
+/// An example showing a C++ non-trivially-relocatable class as a field in a
+/// Rust struct. There are two ways to do this:
+///
+///   1. storing C++ class indirectly (e.g., in a Box), or,
+///   2. storing by-value.
+///
+/// This test specicially demonstrates the second: storing a C++ class by
+/// value, even in the worst case of it not being trivially-relocatable.
+/// In that case, the struct containing it must *also* become
+/// non-trivially-relocatable, and it becomes ~exactly as difficult to deal
+/// with as the C++ class it contains.
+#[gtest]
+fn test_struct_field() {
+    #[ctor::recursively_pinned]
+    struct MyStruct {
+        field_1: u32,
+        field_2: Nonunpin,
+    }
+
+    impl MyStruct {
+        fn new() -> impl Ctor<Output = Self, Error = Infallible> {
+            ctor!(MyStruct { field_1: 4, field_2: Nonunpin::ctor_new(2) })
+        }
+    }
+
+    emplace! { let mut my_struct = MyStruct::new(); }
+    assert_eq!(my_struct.field_1, 4);
+    assert_eq!(my_struct.field_2.value(), 2);
+    // use projection (from recursively_pinned/pin_project) to mutate the struct:
+    let mut my_struct = my_struct.project_pin();
+    *my_struct.field_1 = 5;
+    my_struct.field_2.as_mut().assign(mov!(emplace!(Nonunpin::ctor_new(3))));
+    assert_eq!(*my_struct.field_1, 5);
+    assert_eq!(my_struct.field_2.value(), 3);
+}
+
+/// The example from the ctor.rs docs; copy-pasted.
+#[gtest]
+fn test_swap() {
+    fn swap(mut x: Pin<&mut Nonunpin>, mut y: Pin<&mut Nonunpin>) {
+        emplace! { let mut tmp = mov!(x.as_mut()); }
+        x.assign(mov!(y.as_mut()));
+        y.assign(mov!(tmp));
+    }
+    let mut c1 = Box::emplace(Nonunpin::ctor_new(1));
+    let mut c2 = Box::emplace(Nonunpin::ctor_new(2));
+    swap(c1.as_mut(), c2.as_mut());
+    assert_eq!(c1.value(), 2);
+    assert_eq!(c2.value(), 1);
+}
diff --git a/rs_bindings_from_cc/test/struct/nonunpin/nonunpin_test.rs b/rs_bindings_from_cc/test/struct/nonunpin/nonunpin_test.rs
index 4a408cc..fb1c189 100644
--- a/rs_bindings_from_cc/test/struct/nonunpin/nonunpin_test.rs
+++ b/rs_bindings_from_cc/test/struct/nonunpin/nonunpin_test.rs
@@ -3,8 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 #![feature(negative_impls)]
 
-use ctor::{ctor, emplace, mov, ConstRvalueReference, Ctor, Emplace, Infallible, RvalueReference};
-use ctor::{Assign as _, CtorNew as _, ReconstructUnchecked as _};
+use ctor::{ctor, emplace, CtorNew, ReconstructUnchecked};
 use googletest::prelude::*;
 use nonunpin::{Nonmovable, Nonunpin, NonunpinStruct, ReturnsNonmovable};
 use std::pin::Pin;
@@ -30,65 +29,6 @@
 }
 
 #[gtest]
-fn test_move_construct() {
-    ctor::emplace! {
-        let mut x = Nonunpin::ctor_new(42);
-        let mut y = ctor::mov!(x.as_mut());
-    }
-
-    assert_eq!(x.value(), 0); // moved-from
-    assert_eq!(y.value(), 42); // moved-to
-
-    assert_eq!(x.addr(), &*x as *const _ as usize);
-    assert_eq!(y.addr(), &*y as *const _ as usize);
-}
-
-#[gtest]
-fn test_move_assign() {
-    ctor::emplace! {
-        let mut x = Nonunpin::ctor_new(42);
-        let mut y = Nonunpin::ctor_new(8);
-    }
-
-    y.as_mut().assign(ctor::mov!(x.as_mut()));
-
-    assert_eq!(x.value(), 0); // moved-from
-    assert_eq!(y.value(), 42); // moved-to
-
-    assert_eq!(x.addr(), &*x as *const _ as usize);
-    assert_eq!(y.addr(), &*y as *const _ as usize);
-}
-
-#[gtest]
-fn test_copy_construct() {
-    ctor::emplace! {
-        let x = Nonunpin::ctor_new(42);
-        let y = ctor::copy(&*x);
-    }
-
-    assert_eq!(x.value(), 42);
-    assert_eq!(y.value(), 42);
-
-    assert_eq!(x.addr(), &*x as *const _ as usize);
-    assert_eq!(y.addr(), &*y as *const _ as usize);
-}
-
-#[gtest]
-fn test_copy_assign() {
-    ctor::emplace! {
-        let x = Nonunpin::ctor_new(42);
-        let mut y = Nonunpin::ctor_new(8);
-    }
-    y.as_mut().assign(&*x);
-
-    assert_eq!(x.value(), 42);
-    assert_eq!(y.value(), 42);
-
-    assert_eq!(x.addr(), &*x as *const _ as usize);
-    assert_eq!(y.addr(), &*y as *const _ as usize);
-}
-
-#[gtest]
 fn test_methods() {
     ctor::emplace! {
         let mut x = Nonunpin::ctor_new(42);
@@ -97,40 +37,6 @@
     assert_eq!(x.value(), 24);
 }
 
-/// Test that the struct can be returned and passed as all the reference
-/// types, and passed by value.
-#[gtest]
-fn test_ref() {
-    ctor::emplace! {
-        let mut x = Nonunpin::ctor_new(42);
-    }
-    {
-        let x_ref: Pin<&mut Nonunpin> = x.as_mut().AsMutRef();
-        assert_eq!(nonunpin::GetValueFromMutRef(x_ref), 42);
-        assert_eq!(nonunpin::GetValueFromMutRef(x.as_mut()), 42);
-    }
-    {
-        let x_ref: &Nonunpin = x.AsConstRef();
-        assert_eq!(nonunpin::GetValueFromConstRef(x_ref), 42);
-        assert_eq!(nonunpin::GetValueFromConstRef(&x), 42);
-    }
-    {
-        let x_ref: RvalueReference<Nonunpin> = x.as_mut().AsRvalueRef();
-        assert_eq!(nonunpin::GetValueFromRvalueRef(x_ref), 42);
-        assert_eq!(nonunpin::GetValueFromRvalueRef(ctor::mov!(x.as_mut())), 42);
-    }
-    {
-        let x_ref: ConstRvalueReference<Nonunpin> = x.AsConstRvalueRef();
-        assert_eq!(nonunpin::GetValueFromConstRvalueRef(x_ref), 42);
-        assert_eq!(nonunpin::GetValueFromConstRvalueRef(ctor::const_mov!(&*x)), 42);
-        assert_eq!(nonunpin::GetValueFromConstRvalueRef(ctor::const_mov!(x.as_mut())), 42);
-    }
-    {
-        assert_eq!(nonunpin::GetValueFromValue(ctor::copy(&*x)), 42);
-        assert_eq!(nonunpin::GetValueFromValue(ctor::mov!(x)), 42);
-    }
-}
-
 #[gtest]
 fn test_aggregate() {
     ctor::emplace! {
@@ -180,42 +86,6 @@
 }
 
 /// An example showing a C++ non-trivially-relocatable class as a field in a
-/// Rust struct. There are two ways to do this:
-///
-///   1. storing C++ class indirectly (e.g., in a Box), or,
-///   2. storing by-value.
-///
-/// This test specicially demonstrates the second: storing a C++ class by
-/// value, even in the worst case of it not being trivially-relocatable.
-/// In that case, the struct containing it must *also* become
-/// non-trivially-relocatable, and it becomes ~exactly as difficult to deal
-/// with as the C++ class it contains.
-#[gtest]
-fn test_struct_field() {
-    #[ctor::recursively_pinned]
-    struct MyStruct {
-        field_1: u32,
-        field_2: Nonunpin,
-    }
-
-    impl MyStruct {
-        fn new() -> impl Ctor<Output = Self, Error = Infallible> {
-            ctor!(MyStruct { field_1: 4, field_2: Nonunpin::ctor_new(2) })
-        }
-    }
-
-    emplace! { let mut my_struct = MyStruct::new(); }
-    assert_eq!(my_struct.field_1, 4);
-    assert_eq!(my_struct.field_2.value(), 2);
-    // use projection (from recursively_pinned/pin_project) to mutate the struct:
-    let mut my_struct = my_struct.project_pin();
-    *my_struct.field_1 = 5;
-    my_struct.field_2.as_mut().assign(mov!(emplace!(Nonunpin::ctor_new(3))));
-    assert_eq!(*my_struct.field_1, 5);
-    assert_eq!(my_struct.field_2.value(), 3);
-}
-
-/// An example showing a C++ non-trivially-relocatable class as a field in a
 /// Rust union. This mirrors the struct case, storing by value.
 ///
 /// It is also quite ugly, but, fortunately, these unions are not common.
@@ -242,18 +112,3 @@
         assert_eq!(my_union.int, 2);
     }
 }
-
-/// The example from the ctor.rs docs; copy-pasted.
-#[gtest]
-fn test_swap() {
-    fn swap(mut x: Pin<&mut Nonunpin>, mut y: Pin<&mut Nonunpin>) {
-        emplace! { let mut tmp = mov!(x.as_mut()); }
-        x.assign(mov!(y.as_mut()));
-        y.assign(mov!(tmp));
-    }
-    let mut c1 = Box::emplace(Nonunpin::ctor_new(1));
-    let mut c2 = Box::emplace(Nonunpin::ctor_new(2));
-    swap(c1.as_mut(), c2.as_mut());
-    assert_eq!(c1.value(), 2);
-    assert_eq!(c2.value(), 1);
-}
diff --git a/rs_bindings_from_cc/test/struct/operator_and/operator_and.golden.cc b/rs_bindings_from_cc/test/struct/operator_and/operator_and.golden.cc
index ad59b22..924223a 100644
--- a/rs_bindings_from_cc/test/struct/operator_and/operator_and.golden.cc
+++ b/rs_bindings_from_cc/test/struct/operator_and/operator_and.golden.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/struct/operator_and:operator_and
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/struct/operators/BUILD b/rs_bindings_from_cc/test/struct/operators/BUILD
index 01a2962..bc995ed 100644
--- a/rs_bindings_from_cc/test/struct/operators/BUILD
+++ b/rs_bindings_from_cc/test/struct/operators/BUILD
@@ -36,7 +36,7 @@
     deps = [
         "//support:ctor",
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
 
@@ -71,6 +71,6 @@
     deps = [
         "//support:ctor",
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
diff --git a/rs_bindings_from_cc/test/supported_feature_set/BUILD b/rs_bindings_from_cc/test/supported_feature_set/BUILD
index d0e3452..6b0b921 100644
--- a/rs_bindings_from_cc/test/supported_feature_set/BUILD
+++ b/rs_bindings_from_cc/test/supported_feature_set/BUILD
@@ -15,7 +15,10 @@
 crubit_test_cc_library(
     name = "no_bindings",
     hdrs = ["no_bindings.h"],
-    aspect_hints = ["//features:supported"],
+    aspect_hints = [
+        "//features:supported",
+        "//features:non_unpin_ctor",
+    ],
 )
 
 crubit_rust_test(
@@ -30,7 +33,7 @@
     deps = [
         "//support:oops",
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
 
diff --git a/rs_bindings_from_cc/test/supported_feature_set/no_bindings.h b/rs_bindings_from_cc/test/supported_feature_set/no_bindings.h
index 3305dfa..919c958 100644
--- a/rs_bindings_from_cc/test/supported_feature_set/no_bindings.h
+++ b/rs_bindings_from_cc/test/supported_feature_set/no_bindings.h
@@ -10,11 +10,6 @@
 #include <set>
 namespace crubit::no_bindings {
 
-struct Nontrivial {
-  ~Nontrivial() {}  // NOLINT(modernize-use-equals-default)
-};
-
-using NontrivialAlias = Nontrivial;
 using DeprecatedAlias [[deprecated]] = int;
 
 // This struct would receive bindings, if it weren't for the unrecognized
@@ -38,9 +33,6 @@
 
 using InstantiatedTemplatedStruct = TemplatedStruct<int>;
 
-inline void crubit_accepts_nontrivial_value(Nontrivial) {}
-inline Nontrivial crubit_returns_nontrivial_value() { return {}; }
-
 [[clang::vectorcall]] inline void crubit_vectorcall() {}
 
 [[noreturn]] inline void crubit_noreturn() {
diff --git a/rs_bindings_from_cc/test/supported_feature_set/no_bindings_test.rs b/rs_bindings_from_cc/test/supported_feature_set/no_bindings_test.rs
index c4e5f8c..848a894 100644
--- a/rs_bindings_from_cc/test/supported_feature_set/no_bindings_test.rs
+++ b/rs_bindings_from_cc/test/supported_feature_set/no_bindings_test.rs
@@ -11,16 +11,6 @@
     assert!(!type_exists!(no_bindings::DeprecatedAlias));
 }
 
-#[gtest]
-fn test_accepts_nontrivial_value() {
-    assert!(!value_exists!(no_bindings::crubit_accepts_nontrivial_value));
-}
-
-#[gtest]
-fn test_returns_nontrivial_value() {
-    assert!(!value_exists!(no_bindings::crubit_returns_nontrivial_value));
-}
-
 // vectorcall attribute is outright ignored on e.g. ARM -- so on that platform,
 // this isn't actually a different calling convention, and we'd expect bindings
 // to exist after all.
diff --git a/rs_bindings_from_cc/test/templates/regression_401857961/repro_rs_api.rs b/rs_bindings_from_cc/test/templates/regression_401857961/repro_rs_api.rs
index dc7c62f..2e45ce6 100644
--- a/rs_bindings_from_cc/test/templates/regression_401857961/repro_rs_api.rs
+++ b/rs_bindings_from_cc/test/templates/regression_401857961/repro_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/templates/regression_401857961:repro
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -38,18 +38,19 @@
 
     // Generated from: rs_bindings_from_cc/test/templates/regression_401857961/repro.h;l=15
     // Error while generating bindings for constructor 'Interval::Interval':
+    // Default constructors do yet receive bindings. See b/452726517.
     // Expected first constructor parameter to be a mutable reference, got: *mut crate::repro::Interval
     // Expected first reference parameter `__this` to have a lifetime, found *mut crate::repro::Interval
 
     // Generated from: rs_bindings_from_cc/test/templates/regression_401857961/repro.h;l=15
     // Error while generating bindings for constructor 'Interval::Interval':
-    // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+    // Move and copy constructors do yet receive bindings. See b/452726517.
     // Expected first constructor parameter to be a mutable reference, got: *mut crate::repro::Interval
     // Expected first reference parameter `__this` to have a lifetime, found *mut crate::repro::Interval
 
     // Generated from: rs_bindings_from_cc/test/templates/regression_401857961/repro.h;l=15
     // Error while generating bindings for constructor 'Interval::Interval':
-    // Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+    // Move and copy constructors do yet receive bindings. See b/452726517.
     // Expected first constructor parameter to be a mutable reference, got: *mut crate::repro::Interval
     // Expected first reference parameter `__this` to have a lifetime, found *mut crate::repro::Interval
 
diff --git a/rs_bindings_from_cc/test/templates/regression_401857961/repro_rs_api_impl.cc b/rs_bindings_from_cc/test/templates/regression_401857961/repro_rs_api_impl.cc
index 4338c5f..5476106 100644
--- a/rs_bindings_from_cc/test/templates/regression_401857961/repro_rs_api_impl.cc
+++ b/rs_bindings_from_cc/test/templates/regression_401857961/repro_rs_api_impl.cc
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/templates/regression_401857961:repro
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector, supported
 
 #include "support/internal/cxx20_backports.h"
 #include "support/internal/offsetof.h"
diff --git a/rs_bindings_from_cc/test/types/thread_safety/BUILD b/rs_bindings_from_cc/test/types/thread_safety/BUILD
index 53ef33c..09a9d49 100644
--- a/rs_bindings_from_cc/test/types/thread_safety/BUILD
+++ b/rs_bindings_from_cc/test/types/thread_safety/BUILD
@@ -22,6 +22,6 @@
     ],
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
diff --git a/rs_bindings_from_cc/test/wrapper/fallback_types/wrapper_library_rs_api.rs b/rs_bindings_from_cc/test/wrapper/fallback_types/wrapper_library_rs_api.rs
index 7ce3558..1f6a3f0 100644
--- a/rs_bindings_from_cc/test/wrapper/fallback_types/wrapper_library_rs_api.rs
+++ b/rs_bindings_from_cc/test/wrapper/fallback_types/wrapper_library_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/wrapper/fallback_types:wrapper_library
-// Features: std_unique_ptr, std_vector, supported, wrapper
+// Features: do_not_hardcode_status_bridge, non_unpin_ctor, std_unique_ptr, std_vector, supported, wrapper
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes)]
diff --git a/rs_bindings_from_cc/test/wrapper/impl_ctor/impl_ctor_rs_api.rs b/rs_bindings_from_cc/test/wrapper/impl_ctor/impl_ctor_rs_api.rs
index 4745bd3..1c1c565 100644
--- a/rs_bindings_from_cc/test/wrapper/impl_ctor/impl_ctor_rs_api.rs
+++ b/rs_bindings_from_cc/test/wrapper/impl_ctor/impl_ctor_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/wrapper/impl_ctor:impl_ctor
-// Features: std_unique_ptr, std_vector, supported, wrapper
+// Features: do_not_hardcode_status_bridge, non_unpin_ctor, std_unique_ptr, std_vector, supported, wrapper
 
 #![rustfmt::skip]
 #![feature(
@@ -44,6 +44,7 @@
 
 // Generated from: rs_bindings_from_cc/test/wrapper/impl_ctor/impl_ctor.h;l=11
 // Error while generating bindings for constructor 'Nontrivial::Nontrivial':
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::Nontrivial
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::Nontrivial
 
@@ -61,8 +62,7 @@
 
 /// Generated from: rs_bindings_from_cc/test/wrapper/impl_ctor/impl_ctor.h;l=16
 #[inline(always)]
-pub(crate) fn Create() -> impl ::ctor::Ctor<Output = crate::Nontrivial, Error = ::ctor::Infallible>
-{
+pub fn Create() -> impl ::ctor::Ctor<Output = crate::Nontrivial, Error = ::ctor::Infallible> {
     unsafe {
         ::ctor::FnCtor::new(move |dest: *mut crate::Nontrivial| {
             crate::detail::__rust_thunk___Z6Createv(dest as *mut ::core::ffi::c_void);
@@ -72,7 +72,7 @@
 
 /// Generated from: rs_bindings_from_cc/test/wrapper/impl_ctor/impl_ctor.h;l=18
 #[inline(always)]
-pub(crate) fn Read(
+pub fn Read(
     nontrivial: impl ::ctor::Ctor<Output = crate::Nontrivial, Error = ::ctor::Infallible>,
 ) -> ::core::ffi::c_int {
     unsafe {
diff --git a/rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types_rs_api.rs b/rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types_rs_api.rs
index ea4f2fd..95ad67f 100644
--- a/rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types_rs_api.rs
+++ b/rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types_rs_api.rs
@@ -4,7 +4,7 @@
 
 // Automatically @generated Rust bindings for the following C++ target:
 // //rs_bindings_from_cc/test/wrapper/pub_crate_types:pub_crate_types
-// Features: std_unique_ptr, std_vector, supported, wrapper
+// Features: do_not_hardcode_status_bridge, non_unpin_ctor, std_unique_ptr, std_vector, supported, wrapper
 
 #![rustfmt::skip]
 #![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
@@ -42,18 +42,19 @@
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types.h;l=23
 // Error while generating bindings for constructor 'CompoundDataType::CompoundDataType':
+// Default constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::CompoundDataType
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::CompoundDataType
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types.h;l=23
 // Error while generating bindings for constructor 'CompoundDataType::CompoundDataType':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::CompoundDataType
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::CompoundDataType
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types.h;l=23
 // Error while generating bindings for constructor 'CompoundDataType::CompoundDataType':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::CompoundDataType
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::CompoundDataType
 
@@ -140,18 +141,19 @@
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/other_pub_crate_types.h;l=11
 // Error while generating bindings for constructor 'Template2<int>::Template2<int>':
+// Default constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::__CcTemplateInst9Template2IiE
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::__CcTemplateInst9Template2IiE
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/other_pub_crate_types.h;l=11
 // Error while generating bindings for constructor 'Template2<int>::Template2<int>':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::__CcTemplateInst9Template2IiE
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::__CcTemplateInst9Template2IiE
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/other_pub_crate_types.h;l=11
 // Error while generating bindings for constructor 'Template2<int>::Template2<int>':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::__CcTemplateInst9Template2IiE
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::__CcTemplateInst9Template2IiE
 
@@ -179,18 +181,19 @@
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types.h;l=11
 // Error while generating bindings for constructor 'Template<int>::Template<int>':
+// Default constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::__CcTemplateInst8TemplateIiE
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::__CcTemplateInst8TemplateIiE
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types.h;l=11
 // Error while generating bindings for constructor 'Template<int>::Template<int>':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::__CcTemplateInst8TemplateIiE
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::__CcTemplateInst8TemplateIiE
 
 // Generated from: rs_bindings_from_cc/test/wrapper/pub_crate_types/pub_crate_types.h;l=11
 // Error while generating bindings for constructor 'Template<int>::Template<int>':
-// Unsafe constructors (e.g. with no elided or explicit lifetimes) are intentionally not supported. See b/216648347.
+// Move and copy constructors do yet receive bindings. See b/452726517.
 // Expected first constructor parameter to be a mutable reference, got: *mut crate::__CcTemplateInst8TemplateIiE
 // Expected first reference parameter `__this` to have a lifetime, found *mut crate::__CcTemplateInst8TemplateIiE
 
diff --git a/support/BUILD b/support/BUILD
index c0eca73..4961167 100644
--- a/support/BUILD
+++ b/support/BUILD
@@ -17,6 +17,7 @@
 rust_library(
     name = "ctor",
     srcs = ["ctor.rs"],
+    compatible_with = ["//buildenv/target:non_prod"],
     crate_features = ["unstable"],
     proc_macro_deps = [":ctor_proc_macros"],
     # LINT.IfChange
@@ -33,14 +34,15 @@
     compatible_with = ["//buildenv/target:non_prod"],
     deps = [
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
 cc_library(
     name = "annotations",
     hdrs = ["annotations.h"],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = [
         "//visibility:public",
     ],
@@ -52,6 +54,7 @@
 cc_library(
     name = "annotations_internal",
     hdrs = ["annotations_internal.h"],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = [
         "//visibility:public",
     ],
@@ -69,8 +72,8 @@
     ],
     deps = [
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -124,6 +127,7 @@
 rust_library(
     name = "forward_declare",
     srcs = ["forward_declare.rs"],
+    compatible_with = ["//buildenv/target:non_prod"],
     crate_features = ["unstable"],
     proc_macro_deps = [":forward_declare_proc_macros"],
     # LINT.IfChange
@@ -144,8 +148,8 @@
     ],
     deps = [
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -169,6 +173,7 @@
 rust_library(
     name = "oops",
     srcs = ["oops.rs"],
+    compatible_with = ["//buildenv/target:non_prod"],
     # LINT.IfChange
     rustc_flags = ["-Zallow-features=negative_impls"],
     # LINT.ThenChange(//docs/overview/unstable_features.md)
@@ -191,6 +196,7 @@
 rust_library(
     name = "bridge_rust",
     srcs = ["bridge.rs"],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = [
         "//visibility:public",
     ],
@@ -207,6 +213,7 @@
 cc_library(
     name = "bridge_cpp",
     hdrs = ["bridge.h"],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = [
         "//visibility:public",
     ],
@@ -217,34 +224,7 @@
     srcs = ["bridge_test.cc"],
     deps = [
         ":bridge_cpp",
-        "@googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "status_bridge_cpp",
-    srcs = ["status_bridge.cc"],
-    hdrs = ["status_bridge.h"],
-    visibility = [
-        "//visibility:public",
-    ],
-    deps = [
-        ":bridge_cpp",
-        "@abseil-cpp//absl/status",
-        "@abseil-cpp//absl/status:statusor",
-        "@abseil-cpp//absl/strings:string_view",
-    ],
-)
-
-cc_test(
-    name = "status_bridge_cpp_test",
-    srcs = ["status_bridge_test.cc"],
-    deps = [
-        ":bridge_cpp",
-        ":status_bridge_cpp",
-        "@abseil-cpp//absl/status",
-        "@abseil-cpp//absl/status:statusor",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
diff --git a/support/bridge.h b/support/bridge.h
index 0b75f55..5b2fc1a 100644
--- a/support/bridge.h
+++ b/support/bridge.h
@@ -5,6 +5,10 @@
 #ifndef THIRD_PARTY_CRUBIT_SUPPORT_BRIDGE_H_
 #define THIRD_PARTY_CRUBIT_SUPPORT_BRIDGE_H_
 
+// Allow others to check #ifdef CRUBIT_BRIDGE_ENABLED
+#if !defined(SWIG) && defined(__clang__) && __cplusplus >= 202002L
+#define CRUBIT_BRIDGE_ENABLED
+
 #include <concepts>
 #include <cstddef>
 #include <cstring>
@@ -376,4 +380,5 @@
 
 }  // namespace crubit
 
+#endif  // CRUBIT_BRIDGE_ENABLED
 #endif  // THIRD_PARTY_CRUBIT_SUPPORT_BRIDGE_H_
diff --git a/support/cc_import/BUILD b/support/cc_import/BUILD
index fd3ee0f..579ca56 100644
--- a/support/cc_import/BUILD
+++ b/support/cc_import/BUILD
@@ -19,7 +19,7 @@
     deps = [
         ":cc_import_internal",
         "@crate_index//:proc-macro2",
-        "@crate_index//:syn",
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -28,12 +28,13 @@
     srcs = [
         "cc_import_internal.rs",
     ],
+    compatible_with = ["//buildenv/target:non_prod"],
     deps = [
         ":merged_namespaces",
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:serde_json",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:serde_json",  # v1
+        "@crate_index//:syn",  # v1
         "@rules_rust//util/import:import_internal",
     ],
 )
@@ -41,12 +42,13 @@
 rust_library(
     name = "merged_namespaces",
     srcs = ["merged_namespaces.rs"],
+    compatible_with = ["//buildenv/target:non_prod"],
     deps = [
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:serde",
-        "@crate_index//:serde_json",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:serde",  # v1
+        "@crate_index//:serde_json",  # v1
+        "@crate_index//:syn",  # v1
         "@rules_rust//util/import:import_internal",
     ],
 )
diff --git a/support/cc_std_impl/test/cpp_std_string/BUILD b/support/cc_std_impl/test/cpp_std_string/BUILD
index f1d0a0a..ebd4e57 100644
--- a/support/cc_std_impl/test/cpp_std_string/BUILD
+++ b/support/cc_std_impl/test/cpp_std_string/BUILD
@@ -18,6 +18,6 @@
     ],
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:rstest",
+        "@crate_index//:rstest",  # v0_16
     ],
 )
diff --git a/support/cc_std_impl/test/string_view/BUILD b/support/cc_std_impl/test/string_view/BUILD
index 1c38f90..2e1f276 100644
--- a/support/cc_std_impl/test/string_view/BUILD
+++ b/support/cc_std_impl/test/string_view/BUILD
@@ -49,6 +49,6 @@
     srcs = ["test.cc"],
     deps = [
         ":string_view_rs_apis_cc",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/support/cc_std_impl/test/vector/BUILD b/support/cc_std_impl/test/vector/BUILD
index f7fbf33..0f376b3 100644
--- a/support/cc_std_impl/test/vector/BUILD
+++ b/support/cc_std_impl/test/vector/BUILD
@@ -24,6 +24,6 @@
     ],
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
diff --git a/support/cc_template/BUILD b/support/cc_template/BUILD
index 4c37364..323959a 100644
--- a/support/cc_template/BUILD
+++ b/support/cc_template/BUILD
@@ -16,19 +16,20 @@
     ],
     deps = [
         ":cc_template_impl",
-        "@crate_index//:syn",
+        "@crate_index//:syn",  # v1
     ],
 )
 
 rust_library(
     name = "cc_template_impl",
     srcs = ["cc_template_impl.rs"],
+    compatible_with = ["//buildenv/target:non_prod"],
     deps = [
-        "@crate_index//:anyhow",
+        "@crate_index//:anyhow",  # v1
         "@crate_index//:proc-macro2",
-        "@crate_index//:quote",
-        "@crate_index//:serde_json",
-        "@crate_index//:syn",
+        "@crate_index//:quote",  # v1
+        "@crate_index//:serde_json",  # v1
+        "@crate_index//:syn",  # v1
     ],
 )
 
@@ -42,6 +43,6 @@
     crate = ":cc_template_impl",
     deps = [
         "@crate_index//:googletest",
-        "@crate_index//:maplit",
+        "@crate_index//:maplit",  # v1
     ],
 )
diff --git a/support/ffi_11/BUILD b/support/ffi_11/BUILD
index 68e5147..dc37f34 100644
--- a/support/ffi_11/BUILD
+++ b/support/ffi_11/BUILD
@@ -5,6 +5,7 @@
 rust_library(
     name = "ffi_11",
     srcs = glob(["src/*.rs"]),
+    compatible_with = ["//buildenv/target:non_prod"],
     crate_features = ["crubit"],
     visibility = [
         "//:__subpackages__",
diff --git a/support/ffi_11/tests/BUILD b/support/ffi_11/tests/BUILD
index 2709492..8f8fc9c 100644
--- a/support/ffi_11/tests/BUILD
+++ b/support/ffi_11/tests/BUILD
@@ -7,6 +7,6 @@
     srcs = ["conversion_test.rs"],
     deps = [
         "//support/ffi_11",
-        "@crate_index//:static_assertions",
+        "@crate_index//:static_assertions",  # v1
     ],
 )
diff --git a/support/ffi_11/tests/type_identity/rust_api_cc_api.h b/support/ffi_11/tests/type_identity/rust_api_cc_api.h
index c8b6b8a..a47bf81 100644
--- a/support/ffi_11/tests/type_identity/rust_api_cc_api.h
+++ b/support/ffi_11/tests/type_identity/rust_api_cc_api.h
@@ -4,7 +4,8 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // rust_api_golden
-// Features: std_unique_ptr, std_vector, supported
+// Features: do_not_hardcode_status_bridge, std_unique_ptr, std_vector,
+// supported
 
 // clang-format off
 #ifndef THIRD_PARTY_CRUBIT_SUPPORT_FFI_11_TESTS_TYPE_IDENTITY_RUST_API_GOLDEN
diff --git a/support/internal/BUILD b/support/internal/BUILD
index 7be2ba1..15a9472 100644
--- a/support/internal/BUILD
+++ b/support/internal/BUILD
@@ -15,6 +15,7 @@
         "sizeof.h",
         "slot.h",
     ],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = [
         "//visibility:public",
     ],
@@ -35,7 +36,7 @@
     srcs = ["check_no_mutable_aliasing_test.cc"],
     deps = [
         ":bindings_support",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -44,7 +45,7 @@
     srcs = ["memswap_test.cc"],
     deps = [
         ":bindings_support",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -53,7 +54,7 @@
     srcs = ["offsetof_test.cc"],
     deps = [
         ":bindings_support",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
 
@@ -62,9 +63,9 @@
     srcs = ["slot_test.cc"],
     deps = [
         ":bindings_support",
+        "//testing/base/public:gunit_main",
         "@abseil-cpp//absl/base:core_headers",
         "@abseil-cpp//absl/log:check",
-        "@googletest//:gtest_main",
     ],
 )
 
@@ -73,6 +74,6 @@
     srcs = ["sizeof_test.cc"],
     deps = [
         ":bindings_support",
-        "@googletest//:gtest_main",
+        "//testing/base/public:gunit_main",
     ],
 )
diff --git a/support/internal/slot.h b/support/internal/slot.h
index 2707564..4374ed6 100644
--- a/support/internal/slot.h
+++ b/support/internal/slot.h
@@ -7,6 +7,8 @@
 #ifndef CRUBIT_SUPPORT_INTERNAL_RETURN_VALUE_SLOT_H_
 #define CRUBIT_SUPPORT_INTERNAL_RETURN_VALUE_SLOT_H_
 
+#include <array>
+#include <cstddef>
 #include <memory>
 #include <utility>
 
@@ -130,6 +132,49 @@
 template <typename T>
 Slot(T) -> Slot<T>;
 
+template <typename T, std::size_t N, std::size_t... I>
+constexpr std::array<T, N> unsafe_move_array(T* ptr,
+                                             std::index_sequence<I...>) {
+  return {{T(UnsafeRelocateTag{}, std::move(ptr[I]))...}};
+}
+
+// TODO: b/451981992 - This works for single-level arrays, but we'd like to
+// support arbitrary composite types that include arrays in Slot.
+template <typename UT, unsigned S>
+class Slot<std::array<UT, S>> {
+ public:
+  using T = std::array<UT, S>;
+  Slot() {}
+  explicit constexpr Slot(T&& x) {
+    if constexpr (requires(UT x) { UT(UnsafeRelocateTag{}, std::move(x)); }) {
+      memcpy(value_.data(), x.data(), sizeof(UT) * S);
+    } else {
+      value_ = std::move(x);
+    }
+  }
+  T* Get() { return &value_; }
+  T AssumeInitAndTakeValue() && {
+    if constexpr (requires(UT x) { UT(UnsafeRelocateTag{}, std::move(x)); }) {
+      return unsafe_move_array<UT, S>(value_.data(),
+                                      std::make_index_sequence<S>());
+    } else {
+      T return_value(std::move(value_));
+      std::destroy_at(&value_);
+      return return_value;
+    }
+  }
+  Slot(Slot&& other) { value_ = std::move(other.value_); }
+  ~Slot() {}
+
+  Slot(const Slot&) = delete;
+  Slot& operator=(const Slot&) = delete;
+  Slot& operator=(Slot&&) = delete;
+
+ private:
+  union {
+    T value_;
+  };
+};
 }  // namespace crubit
 
 #endif  // CRUBIT_SUPPORT_INTERNAL_RETURN_VALUE_SLOT_H_
diff --git a/support/rs_std/BUILD b/support/rs_std/BUILD
index a9e4a4d..2964c94 100644
--- a/support/rs_std/BUILD
+++ b/support/rs_std/BUILD
@@ -19,6 +19,7 @@
     hdrs = ["char.h"],
     # Enable bidirectional bindings (via crubit_internal_rust_type).
     aspect_hints = ["//features:experimental"],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = ["//visibility:public"],
 
     # It is important to be thoughtful when adding new dependencies for `char`
@@ -76,6 +77,7 @@
     cc_deps = [
         ":cpp_waker",
     ],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = ["//visibility:public"],
 )
 
@@ -91,6 +93,7 @@
     hdrs = ["slice_ref.h"],
     # Enable bidirectional bindings (via crubit_internal_rust_type).
     aspect_hints = ["//features:experimental"],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = [
         "//visibility:public",
     ],
@@ -124,6 +127,7 @@
     hdrs = ["str_ref.h"],
     # Enable bidirectional bindings (via crubit_internal_rust_type).
     aspect_hints = ["//features:experimental"],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = [
         "//visibility:public",
     ],
diff --git a/support/rs_std/internal/BUILD b/support/rs_std/internal/BUILD
index 719cfcc..cd15860 100644
--- a/support/rs_std/internal/BUILD
+++ b/support/rs_std/internal/BUILD
@@ -7,6 +7,7 @@
 cc_library(
     name = "is_utf8",
     hdrs = ["is_utf8.h"],
+    compatible_with = ["//buildenv/target:non_prod"],
     visibility = [
         "//visibility:public",
     ],
diff --git a/support/stable_fallback/BUILD b/support/stable_fallback/BUILD
index ade2dd5..d86f592 100644
--- a/support/stable_fallback/BUILD
+++ b/support/stable_fallback/BUILD
@@ -8,6 +8,7 @@
     name = "forward_declare",
     testonly = True,
     srcs = ["//support:forward_declare.rs"],
+    compatible_with = ["//buildenv/target:non_prod"],
     crate_features = [],
     proc_macro_deps = ["//support:forward_declare_proc_macros"],
     # LINT.IfChange
diff --git a/support/status_bridge.cc b/support/status_bridge.cc
deleted file mode 100644
index 50c2983..0000000
--- a/support/status_bridge.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-// Part of the Crubit project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#include "support/status_bridge.h"
-
-#include "support/bridge.h"
-
-#include <cstddef>
-#include <cstdint>
-#include <string>
-#include <utility>
-
-#include "absl/status/status.h"
-#include "absl/strings/string_view.h"
-
-namespace crubit {
-
-constexpr uintptr_t kRustOkStatusRep = 0;
-
-static_assert(
-    sizeof(absl::Status) == sizeof(uintptr_t) &&
-        alignof(absl::Status) == alignof(uintptr_t),
-    "Crubit invariant broken, please reach out to us at <internal link>");
-
-void StatusAbi::Encode(absl::Status value, Encoder& encoder) {
-  if (value.ok()) {
-    // No reference counting, okay to just drop.
-    encoder.EncodeTransmute<uintptr_t>(kRustOkStatusRep);
-    return;
-  }
-
-  // Ownership of the Status is transferred into the buffer.
-  alignas(absl::Status) char rep[sizeof(absl::Status)];
-  new (rep) absl::Status(std::move(value));
-  encoder.EncodeTransmute<uintptr_t>(*reinterpret_cast<uintptr_t*>(rep));
-}
-
-absl::Status StatusAbi::Decode(Decoder& decoder) {
-  uintptr_t rep = decoder.DecodeTransmute<uintptr_t>();
-  if (rep == kRustOkStatusRep) {
-    return absl::OkStatus();
-  }
-
-  return absl::Status(reinterpret_cast<absl::Status&&>(rep));
-}
-
-// These functions do not have prototypes because they are extern "C" functions
-// that are linked to Rust code, and never called from C++ code.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmissing-prototypes"
-
-// Increments the reference count of the `Status` with the provided `rep`.
-//
-// The caller must ensure that:
-//   * `rep` is a valid `rep_` from a `Status`,
-//   * if `rep` is the allocated variant, the underlying `StatusRep*` has not
-//   been deleted.
-//
-// The caller may assume that the reference count has been incremented.
-extern "C" void absl_status_internal_ref(uintptr_t rep) {
-  alignas(absl::Status) char erase[sizeof(absl::Status)];
-  new (erase) absl::Status(reinterpret_cast<const absl::Status&>(rep));
-}
-
-// Takes semantic overship over the `rep` by creating a `Status` from it, and
-// then immediately destroys it to decrement the reference count.
-//
-// The caller must ensure that:
-//   * `rep` is a valid `rep_` from a `Status`,
-//   * if `rep` is the allocated variant, the underlying `StatusRep*` has not
-//   been deleted prior to calling this function.
-extern "C" void absl_status_internal_unref(uintptr_t rep) {
-  (void)absl::Status(reinterpret_cast<absl::Status&&>(rep));
-}
-
-// A C-compatible representation of a `string_view` used to pass string views by
-// value across the C ABI.
-struct c_string_view {
-  size_t size;
-  const char* data;
-};
-
-// Creates a rep for a new `Status` with the provided code and message.
-//
-// The caller must ensure that `message` constitutes valid `absl::string_view`.
-//
-// The caller may assume that the returned rep is a valid `rep_` from a C++
-// `Status` meaning it is never 0, and that if it is the allocated variant, the
-// reference count is set to 1 which accounts for the ownership that the caller
-// is expected to take.
-extern "C" uintptr_t absl_status_internal_new(int code, c_string_view message) {
-  alignas(absl::Status) char rep[sizeof(absl::Status)];
-  new (rep) absl::Status(static_cast<absl::StatusCode>(code),
-                         absl::string_view(message.data, message.size));
-  return *reinterpret_cast<uintptr_t*>(rep);
-}
-
-// Returns the raw code of the `Status`.
-//
-// The caller must ensure that:
-//   * `rep` is a valid `rep_` from a `Status`,
-//   * if `rep` is the allocated variant, the underlying `StatusRep*` has not
-//   been deleted.
-//
-// The caller may not assume that the returned value is a valid `StatusCode`
-// value.
-extern "C" int absl_status_internal_raw_code(uintptr_t rep) {
-  return reinterpret_cast<const absl::Status&>(rep).raw_code();
-}
-
-// Returns the message of the `Status`.
-//
-// The caller must ensure that:
-//   * `rep` is a valid `rep_` from a `Status`,
-//   * if `rep` is the allocated variant, the underlying `StatusRep*` has not
-//   been deleted,
-//
-// The caller may assume that the returned `string_view` is valid until `rep`
-// is converted back to a `Status` and the `Status` is destroyed.
-extern "C" c_string_view absl_status_internal_message(uintptr_t rep) {
-  auto message = reinterpret_cast<const absl::Status&>(rep).message();
-  return {message.size(), message.data()};
-}
-
-// Returns true if two Status values are equal, false otherwise.
-//
-// The caller must ensure that for `rep` in (lhs, rhs):
-//   * `rep` is a valid `rep_` from a `Status`,
-//   * if `rep` is the allocated variant, the underlying `StatusRep*` has not
-//   been deleted,
-extern "C" bool absl_status_internal_operator_equals(uintptr_t lhs,
-                                                     uintptr_t rhs) {
-  return reinterpret_cast<const absl::Status&>(lhs) ==
-         reinterpret_cast<const absl::Status&>(rhs);
-}
-
-// Writes the stringified representation to a Rust `fmt::Formatter`.
-//
-// The caller must ensure that:
-//   * `rep` is a valid `rep_` from a `Status`,
-//   * if `rep` is the allocated variant, the underlying `StatusRep*` has not
-//   been deleted,
-//   * `formatter` can be safely casted to a `&mut fmt::Formatter`,
-//   * `cb` is a function that takes the underlying type of `formatter` and
-//     returns true if the string was successfully written.
-//
-// The caller may assume that if `cb` is called, then the void* is the
-// `formatter` passed into this function, and that the c_string_view is valid
-// for the duration of `cb`.
-extern "C" bool absl_status_internal_to_string(uintptr_t rep, void* formatter,
-                                               bool (*cb)(void*,
-                                                          c_string_view)) {
-  std::string s = reinterpret_cast<absl::Status&>(rep).ToString();
-  if (s.empty()) {
-    return true;
-  }
-
-  // Need to use a function pointer so that :status can compile without linking
-  // against :additional_status_src.
-  return cb(formatter, {s.size(), s.data()});
-}
-
-#pragma clang diagnostic pop
-
-}  // namespace crubit
diff --git a/support/status_bridge.h b/support/status_bridge.h
deleted file mode 100644
index faea975..0000000
--- a/support/status_bridge.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Part of the Crubit project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#ifndef THIRD_PARTY_CRUBIT_SUPPORT_STATUS_BRIDGE_H_
-#define THIRD_PARTY_CRUBIT_SUPPORT_STATUS_BRIDGE_H_
-
-#include "support/bridge.h"
-
-#include <cstddef>
-#include <cstdint>
-
-#include "absl/status/status.h"
-#include "absl/status/statusor.h"
-
-namespace crubit {
-
-struct StatusAbi {
-  using Value = absl::Status;
-  static constexpr size_t kSize = sizeof(uintptr_t);
-  static void Encode(Value value, Encoder& encoder);
-  static Value Decode(Decoder& decoder);
-};
-
-template <typename Abi>
-struct StatusOrAbi {
-  static_assert(is_crubit_abi<Abi>,
-                "StatusOrAbi requires Abi to be is_crubit_abi");
-  using Value = absl::StatusOr<typename Abi::Value>;
-  static constexpr size_t kSize = StatusAbi::kSize + Abi::kSize;
-  static void Encode(Value value, Encoder& encoder) {
-    encoder.Encode<StatusAbi>(value.status());
-    if (value.ok()) {
-      encoder.Encode<Abi>(*std::move(value));
-    }
-  }
-  static Value Decode(Decoder& decoder) {
-    absl::Status status(decoder.Decode<StatusAbi>());
-    if (status.ok()) {
-      return decoder.Decode<Abi>();
-    } else {
-      return status;
-    }
-  }
-};
-
-}  // namespace crubit
-
-#endif  // THIRD_PARTY_CRUBIT_SUPPORT_BRIDGE_REMOTE_STATUS_BRIDGE_H_
diff --git a/support/status_bridge_test.cc b/support/status_bridge_test.cc
deleted file mode 100644
index 9e8ad55..0000000
--- a/support/status_bridge_test.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Part of the Crubit project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#include "support/status_bridge.h"
-
-#include "crubit/support/bridge.h"
-#include "gtest/gtest.h"
-#include "absl/status/status.h"
-#include "absl/status/statusor.h"
-
-namespace crubit::bridge {
-namespace {
-
-TEST(BridgeTest, RoundtripOkStatus) {
-  using Abi = StatusAbi;
-
-  absl::Status original = absl::OkStatus();
-
-  unsigned char buf[Abi::kSize];
-  internal::Encode<Abi>(buf, original);
-  absl::Status value = internal::Decode<Abi>(buf);
-  EXPECT_EQ(value, original);
-}
-
-TEST(BridgeTest, RoundtripErrStatus) {
-  using Abi = StatusAbi;
-
-  absl::Status original = absl::InternalError("test");
-
-  unsigned char buf[Abi::kSize];
-  internal::Encode<Abi>(buf, original);
-  absl::Status value = internal::Decode<Abi>(buf);
-  EXPECT_EQ(value, original);
-}
-
-TEST(BridgeTest, RoundtripOkStatusOr) {
-  using Abi = StatusOrAbi<TransmuteAbi<int>>;
-
-  absl::StatusOr<int> original = 123;
-
-  unsigned char buf[Abi::kSize];
-  internal::Encode<Abi>(buf, original);
-  absl::StatusOr<int> value = internal::Decode<Abi>(buf);
-  EXPECT_EQ(value, original);
-}
-
-TEST(BridgeTest, RoundtripErrStatusOr) {
-  using Abi = StatusOrAbi<TransmuteAbi<int>>;
-
-  absl::StatusOr<int> original = absl::InternalError("test");
-
-  unsigned char buf[Abi::kSize];
-  internal::Encode<Abi>(buf, original);
-  absl::StatusOr<int> value = internal::Decode<Abi>(buf);
-  EXPECT_EQ(value, original);
-}
-
-}  // namespace
-}  // namespace crubit::bridge