Begin memoizing queries in cc_bindings_from_rs.

PiperOrigin-RevId: 645306299
Change-Id: Ic4155f847bd45b29582ef8bd8ac0f5bbc9699df7
diff --git a/cc_bindings_from_rs/BUILD b/cc_bindings_from_rs/BUILD
index 7e8878f..2e36bff 100644
--- a/cc_bindings_from_rs/BUILD
+++ b/cc_bindings_from_rs/BUILD
@@ -21,8 +21,9 @@
         ":crubit_attr",
         ":run_compiler",
         ":toposort",
+        "//common:arc_anyhow",
         "//common:code_gen_utils",
-        "@crate_index//:anyhow",
+        "//common:memoized",
         "@crate_index//:itertools",
         "@crate_index//:proc-macro2",
         "@crate_index//:quote",
@@ -54,9 +55,9 @@
         ":bindings",
         ":cmdline",
         ":run_compiler",
+        "//common:arc_anyhow",
         "//common:code_gen_utils",
         "//common:token_stream_printer",
-        "@crate_index//:anyhow",
         "@crate_index//:clap",
         "@crate_index//:itertools",
     ],
@@ -132,7 +133,7 @@
     ],
     rustc_flags = ["-Zallow-features=rustc_private"],
     deps = [
-        "@crate_index//:anyhow",
+        "//common:arc_anyhow",
         "@crate_index//:either",
         "@crate_index//:once_cell",
     ],
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 9690c2e..7020ee0 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -13,7 +13,7 @@
 extern crate rustc_trait_selection;
 extern crate rustc_type_ir;
 
-use anyhow::{anyhow, bail, ensure, Context, Result};
+use arc_anyhow::{anyhow, bail, ensure, Context, Error, Result};
 use code_gen_utils::{
     escape_non_identifier_chars, format_cc_ident, format_cc_includes, make_rs_ident, CcInclude,
     NamespaceQualifier,
@@ -34,40 +34,70 @@
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_type_ir::RegionKind;
 use std::collections::{BTreeSet, HashMap, HashSet};
+use std::hash::{Hash, Hasher};
 use std::iter::once;
 use std::ops::AddAssign;
 use std::rc::Rc;
 use std::slice;
 
-pub struct Input<'tcx> {
-    /// Compilation context for the crate that the bindings should be generated
-    /// for.
-    pub tcx: TyCtxt<'tcx>,
+memoized::query_group! {
+    trait BindingsGenerator<'tcx> {
+        /// Compilation context for the crate that the bindings should be generated
+        /// for.
+        #[input]
+        fn tcx(&self) -> TyCtxt<'tcx>;
 
-    /// Format specifier for `#include` Crubit C++ support library headers,
-    /// using `{header}` as the place holder.  Example:
-    /// `<crubit/support/{header}>` results in `#include
-    /// <crubit/support/hdr.h>`.
-    pub crubit_support_path_format: Rc<str>,
+        /// Format specifier for `#include` Crubit C++ support library headers,
+        /// using `{header}` as the place holder.  Example:
+        /// `<crubit/support/{header}>` results in `#include
+        /// <crubit/support/hdr.h>`.
+        #[input]
+        fn crubit_support_path_format(&self) -> Rc<str>;
 
-    /// A map from a crate name to the include paths of the corresponding C++
-    /// headers This is used when formatting a type exported from another
-    /// crate.
-    // TODO(b/271857814): A crate name might not be globally unique - the key needs to also cover
-    // a "hash" of the crate version and compilation flags.
-    pub crate_name_to_include_paths: HashMap<Rc<str>, Vec<CcInclude>>,
+        /// A map from a crate name to the include paths of the corresponding C++
+        /// headers This is used when formatting a type exported from another
+        /// crate.
+        // TODO(b/271857814): A crate name might not be globally unique - the key needs to also cover
+        // a "hash" of the crate version and compilation flags.
+        #[input]
+        fn crate_name_to_include_paths(&self) -> Rc<HashMap<Rc<str>, Vec<CcInclude>>>;
 
-    // TODO(b/262878759): Provide a set of enabled/disabled Crubit features.
-    pub _features: (),
+        // TODO(b/262878759): Provide a set of enabled/disabled Crubit features.
+        #[input]
+        fn _features(&self) -> ();
+
+        fn support_header(&self, suffix: &'tcx str) -> CcInclude;
+
+        fn repr_attrs(&self, did: DefId) -> Rc<[rustc_attr::ReprAttr]>;
+
+        fn format_ty_for_cc(
+            &self,
+            ty: Ty<'tcx>,
+            location: TypeLocation,
+        ) -> Result<CcSnippet>;
+
+        fn format_default_ctor(
+            &self,
+            core: Rc<AdtCoreBindings<'tcx>>,
+        ) -> Result<ApiSnippets, ApiSnippets>;
+        fn format_copy_ctor_and_assignment_operator(
+            &self,
+            core: Rc<AdtCoreBindings<'tcx>>,
+        ) -> Result<ApiSnippets, ApiSnippets>;
+        fn format_move_ctor_and_assignment_operator(
+            &self,
+            core: Rc<AdtCoreBindings<'tcx>>,
+        ) -> Result<ApiSnippets, ApiSnippets>;
+
+        fn format_item(&self, def_id: LocalDefId) -> Result<Option<ApiSnippets>>;
+        fn format_fn(&self, local_def_id: LocalDefId) -> Result<ApiSnippets>;
+        fn format_adt_core(&self, def_id: DefId) -> Result<Rc<AdtCoreBindings<'tcx>>>;
+    }
+    pub struct Database;
 }
 
-impl<'tcx> Input<'tcx> {
-    // TODO(b/259724276): This function's results should be memoized.  It may be
-    // easier if separate functions are provided for each support header - e.g.
-    // `rs_char()`, `return_value_slot()`, etc.
-    fn support_header(&self, suffix: &str) -> CcInclude {
-        CcInclude::support_lib_header(self.crubit_support_path_format.clone(), suffix.into())
-    }
+fn support_header<'tcx>(db: &dyn BindingsGenerator<'tcx>, suffix: &'tcx str) -> CcInclude {
+    CcInclude::support_lib_header(db.crubit_support_path_format(), suffix.into())
 }
 
 pub struct Output {
@@ -75,14 +105,15 @@
     pub rs_body: TokenStream,
 }
 
-pub fn generate_bindings(input: &Input) -> Result<Output> {
-    match input.tcx.sess().panic_strategy() {
+pub fn generate_bindings(db: &Database) -> Result<Output> {
+    let tcx = db.tcx();
+    match tcx.sess().panic_strategy() {
         PanicStrategy::Unwind => bail!("No support for panic=unwind strategy (b/254049425)"),
         PanicStrategy::Abort => (),
     };
 
     let top_comment = {
-        let crate_name = input.tcx.crate_name(LOCAL_CRATE);
+        let crate_name = tcx.crate_name(LOCAL_CRATE);
         let txt = format!(
             "Automatically @generated C++ bindings for the following Rust crate:\n\
              {crate_name}"
@@ -90,7 +121,7 @@
         quote! { __COMMENT__ #txt __NEWLINE__ }
     };
 
-    let Output { h_body, rs_body } = format_crate(input).unwrap_or_else(|err| {
+    let Output { h_body, rs_body } = format_crate(db).unwrap_or_else(|err| {
         let txt = format!("Failed to generate bindings for the crate: {err}");
         let src = quote! { __COMMENT__ #txt };
         Output { h_body: src.clone(), rs_body: src }
@@ -184,7 +215,7 @@
     }
 }
 
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
 struct CcSnippet {
     tokens: TokenStream,
     prereqs: CcPrerequisites,
@@ -355,6 +386,7 @@
 }
 
 /// Location where a type is used.
+#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
 enum TypeLocation {
     /// The top-level return type.
     ///
@@ -380,32 +412,32 @@
 }
 
 fn format_pointer_or_reference_ty_for_cc<'tcx>(
-    input: &Input<'tcx>,
+    db: &dyn BindingsGenerator<'tcx>,
     pointee: Ty<'tcx>,
     mutability: rustc_middle::mir::Mutability,
     pointer_sigil: TokenStream,
 ) -> Result<CcSnippet> {
+    let tcx = db.tcx();
     let const_qualifier = match mutability {
         Mutability::Mut => quote! {},
         Mutability::Not => quote! { const },
     };
-    if pointee.is_c_void(input.tcx) {
+    if pointee.is_c_void(tcx) {
         return Ok(CcSnippet { tokens: quote! { #const_qualifier void* }, ..Default::default() });
     }
-    let CcSnippet { tokens, mut prereqs } = format_ty_for_cc(input, pointee, TypeLocation::Other)?;
+    let CcSnippet { tokens, mut prereqs } = db.format_ty_for_cc(pointee, TypeLocation::Other)?;
     prereqs.move_defs_to_fwd_decls();
     Ok(CcSnippet { prereqs, tokens: quote! { #tokens #const_qualifier #pointer_sigil } })
 }
 
 /// Formats `ty` into a `CcSnippet` that represents how the type should be
 /// spelled in a C++ declaration of a function parameter or field.
-//
-// TODO(b/259724276): This function's results should be memoized.
 fn format_ty_for_cc<'tcx>(
-    input: &Input<'tcx>,
+    db: &dyn BindingsGenerator<'tcx>,
     ty: Ty<'tcx>,
     location: TypeLocation,
 ) -> Result<CcSnippet> {
+    let tcx = db.tcx();
     fn cstdint(tokens: TokenStream) -> CcSnippet {
         CcSnippet::with_include(tokens, CcInclude::cstdint())
     }
@@ -457,8 +489,7 @@
             // Asserting that the target architecture meets the assumption from Crubit's
             // `rust_builtin_type_abi_assumptions.md` - we assume that Rust's `char` has the
             // same ABI as `u32`.
-            let layout = input
-                .tcx
+            let layout = tcx
                 .layout_of(ty::ParamEnv::empty().and(ty))
                 .expect("`layout_of` is expected to succeed for the builtin `char` type")
                 .layout;
@@ -474,7 +505,7 @@
 
             CcSnippet::with_include(
                 quote! { rs_std::rs_char },
-                input.support_header("rs_std/rs_char.h"),
+                db.support_header("rs_std/rs_char.h"),
             )
         }
 
@@ -516,7 +547,7 @@
         ty::TyKind::Adt(adt, substs) => {
             ensure!(substs.len() == 0, "Generic types are not supported yet (b/259749095)");
             ensure!(
-                is_directly_public(input.tcx, adt.did()),
+                is_directly_public(tcx, adt.did()),
                 "Not directly public type (re-exports are not supported yet - b/262052635)"
             );
 
@@ -525,9 +556,9 @@
             if def_id.krate == LOCAL_CRATE {
                 prereqs.defs.insert(def_id.expect_local());
             } else {
-                let other_crate_name = input.tcx.crate_name(def_id.krate);
-                let includes = input
-                    .crate_name_to_include_paths
+                let other_crate_name = tcx.crate_name(def_id.krate);
+                let crate_name_to_include_paths = db.crate_name_to_include_paths();
+                let includes = crate_name_to_include_paths
                     .get(other_crate_name.as_str())
                     .ok_or_else(|| {
                         anyhow!(
@@ -539,18 +570,15 @@
             }
 
             // Verify if definition of `ty` can be succesfully imported and bail otherwise.
-            format_adt_core(input.tcx, def_id).with_context(|| {
+            db.format_adt_core(def_id).with_context(|| {
                 format!("Failed to generate bindings for the definition of `{ty}`")
             })?;
 
-            CcSnippet {
-                tokens: FullyQualifiedName::new(input.tcx, def_id).format_for_cc()?,
-                prereqs,
-            }
+            CcSnippet { tokens: FullyQualifiedName::new(tcx, def_id).format_for_cc()?, prereqs }
         }
 
         ty::TyKind::RawPtr(pointee_ty, mutbl) => {
-            format_pointer_or_reference_ty_for_cc(input, *pointee_ty, *mutbl, quote! { * })
+            format_pointer_or_reference_ty_for_cc(db, *pointee_ty, *mutbl, quote! { * })
                 .with_context(|| {
                     format!("Failed to format the pointee of the pointer type `{ty}`")
                 })?
@@ -566,7 +594,7 @@
             };
             let lifetime = format_region_as_cc_lifetime(region);
             format_pointer_or_reference_ty_for_cc(
-                input,
+                db,
                 *referent_ty,
                 *mutability,
                 quote! { & #lifetime },
@@ -600,9 +628,9 @@
             };
 
             let mut prereqs = CcPrerequisites::default();
-            prereqs.includes.insert(input.support_header("internal/cxx20_backports.h"));
-            let ret_type = format_ret_ty_for_cc(input, &sig)?.into_tokens(&mut prereqs);
-            let param_types = format_param_types_for_cc(input, &sig)?
+            prereqs.includes.insert(db.support_header("internal/cxx20_backports.h"));
+            let ret_type = format_ret_ty_for_cc(db, &sig)?.into_tokens(&mut prereqs);
+            let param_types = format_param_types_for_cc(db, &sig)?
                 .into_iter()
                 .map(|snippet| snippet.into_tokens(&mut prereqs));
             let tokens = quote! {
@@ -623,20 +651,23 @@
     })
 }
 
-fn format_ret_ty_for_cc<'tcx>(input: &Input<'tcx>, sig: &ty::FnSig<'tcx>) -> Result<CcSnippet> {
-    format_ty_for_cc(input, sig.output(), TypeLocation::FnReturn)
+fn format_ret_ty_for_cc<'tcx>(
+    db: &dyn BindingsGenerator<'tcx>,
+    sig: &ty::FnSig<'tcx>,
+) -> Result<CcSnippet> {
+    db.format_ty_for_cc(sig.output(), TypeLocation::FnReturn)
         .context("Error formatting function return type")
 }
 
 fn format_param_types_for_cc<'tcx>(
-    input: &Input<'tcx>,
+    db: &dyn BindingsGenerator<'tcx>,
     sig: &ty::FnSig<'tcx>,
 ) -> Result<Vec<CcSnippet>> {
     sig.inputs()
         .iter()
         .enumerate()
         .map(|(i, &ty)| {
-            format_ty_for_cc(input, ty, TypeLocation::FnParam)
+            db.format_ty_for_cc(ty, TypeLocation::FnParam)
                 .with_context(|| format!("Error handling parameter #{i}"))
         })
         .collect()
@@ -717,7 +748,7 @@
     quote! { #lifetime }
 }
 
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
 struct ApiSnippets {
     /// Main API - for example:
     /// - A C++ declaration of a function (with a doc comment),
@@ -780,18 +811,18 @@
 /// identified by `fn_def_id`.  `format_thunk_impl` may panic if `fn_def_id`
 /// doesn't identify a function.
 fn format_thunk_decl<'tcx>(
-    input: &Input<'tcx>,
+    db: &dyn BindingsGenerator<'tcx>,
     fn_def_id: DefId,
     sig: &ty::FnSig<'tcx>,
     thunk_name: &TokenStream,
 ) -> Result<CcSnippet> {
-    let tcx = input.tcx;
+    let tcx = db.tcx();
 
     let mut prereqs = CcPrerequisites::default();
-    let main_api_ret_type = format_ret_ty_for_cc(input, sig)?.into_tokens(&mut prereqs);
+    let main_api_ret_type = format_ret_ty_for_cc(db, sig)?.into_tokens(&mut prereqs);
 
     let mut thunk_params = {
-        let cc_types = format_param_types_for_cc(input, sig)?;
+        let cc_types = format_param_types_for_cc(db, sig)?;
         sig.inputs()
             .iter()
             .zip(cc_types.into_iter())
@@ -1047,8 +1078,8 @@
 /// Will panic if `local_def_id`
 /// - is invalid
 /// - doesn't identify a function,
-fn format_fn(input: &Input, local_def_id: LocalDefId) -> Result<ApiSnippets> {
-    let tcx = input.tcx;
+fn format_fn(db: &dyn BindingsGenerator<'_>, local_def_id: LocalDefId) -> Result<ApiSnippets> {
+    let tcx = db.tcx();
     let def_id: DefId = local_def_id.to_def_id(); // Convert LocalDefId to DefId.
 
     ensure!(
@@ -1082,7 +1113,7 @@
         format_cc_ident(short_fn_name.as_str()).context("Error formatting function name")?;
 
     let mut main_api_prereqs = CcPrerequisites::default();
-    let main_api_ret_type = format_ret_ty_for_cc(input, &sig)?.into_tokens(&mut main_api_prereqs);
+    let main_api_ret_type = format_ret_ty_for_cc(db, &sig)?.into_tokens(&mut main_api_prereqs);
 
     struct Param<'tcx> {
         cc_name: TokenStream,
@@ -1091,7 +1122,7 @@
     }
     let params = {
         let names = tcx.fn_arg_names(def_id).iter();
-        let cc_types = format_param_types_for_cc(input, &sig)?;
+        let cc_types = format_param_types_for_cc(db, &sig)?;
         names
             .enumerate()
             .zip(sig.inputs().iter())
@@ -1238,7 +1269,7 @@
 
         let mut prereqs = main_api_prereqs;
         let thunk_decl =
-            format_thunk_decl(input, def_id, &sig, &thunk_name)?.into_tokens(&mut prereqs);
+            format_thunk_decl(db, def_id, &sig, &thunk_name)?.into_tokens(&mut prereqs);
 
         let mut thunk_args = params
             .iter()
@@ -1264,8 +1295,8 @@
             };
         } else {
             if let Some(adt_def) = sig.output().ty_adt_def() {
-                let core = format_adt_core(tcx, adt_def.did())?;
-                format_move_ctor_and_assignment_operator(input, &core).map_err(|_| {
+                let core = db.format_adt_core(adt_def.did())?;
+                db.format_move_ctor_and_assignment_operator(core).map_err(|_| {
                     anyhow!("Can't pass the return type by value without a move constructor")
                 })?;
             }
@@ -1276,7 +1307,7 @@
                 return std::move(__ret_slot).AssumeInitAndTakeValue();
             };
             prereqs.includes.insert(CcInclude::utility()); // for `std::move`
-            prereqs.includes.insert(input.support_header("internal/return_value_slot.h"));
+            prereqs.includes.insert(db.support_header("internal/return_value_slot.h"));
         };
         CcSnippet {
             prereqs,
@@ -1323,13 +1354,11 @@
 ///
 /// `keyword`, `name` are stored separately, to support formatting them as a
 /// forward declaration - e.g. `struct SomeStruct`.
+#[derive(Clone)]
 struct AdtCoreBindings<'tcx> {
     /// DefId of the ADT.
     def_id: DefId,
 
-    /// The repr attributes, e.g. `repr(C)`.
-    repr_attrs: Vec<rustc_attr::ReprAttr>,
-
     /// C++ tag - e.g. `struct`, `class`, `enum`, or `union`.  This isn't always
     /// a direct mapping from Rust (e.g. a Rust `enum` might end up being
     /// represented as an opaque C++ `struct`).
@@ -1352,6 +1381,20 @@
     size_in_bytes: u64,
 }
 
+// AdtCoreBindings are a pure (and memoized...) function of the def_id.
+impl<'tcx> PartialEq for AdtCoreBindings<'tcx> {
+    fn eq(&self, other: &Self) -> bool {
+        self.def_id == other.def_id
+    }
+}
+
+impl<'tcx> Eq for AdtCoreBindings<'tcx> {}
+impl<'tcx> Hash for AdtCoreBindings<'tcx> {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.def_id.hash(state);
+    }
+}
+
 impl<'tcx> AdtCoreBindings<'tcx> {
     fn needs_drop(&self, tcx: TyCtxt<'tcx>) -> bool {
         self.self_ty.needs_drop(tcx, tcx.param_env(self.def_id))
@@ -1405,9 +1448,11 @@
 /// and 2) check if formatting would have succeeded (e.g. when called from
 /// `format_ty`).  The 2nd case is needed for ADTs defined in any crate - this
 /// is why the `def_id` parameter is a DefId rather than LocalDefId.
-//
-// TODO(b/259724276): This function's results should be memoized.
-fn format_adt_core(tcx: TyCtxt<'_>, def_id: DefId) -> Result<AdtCoreBindings<'_>> {
+fn format_adt_core<'tcx>(
+    db: &dyn BindingsGenerator<'tcx>,
+    def_id: DefId,
+) -> Result<Rc<AdtCoreBindings<'tcx>>> {
+    let tcx = db.tcx();
     let self_ty = tcx.type_of(def_id).instantiate_identity();
     assert!(self_ty.is_adt());
     assert!(is_directly_public(tcx, def_id), "Caller should verify");
@@ -1446,23 +1491,31 @@
     let size_in_bytes = layout.size().bytes();
     ensure!(size_in_bytes != 0, "Zero-sized types (ZSTs) are not supported (b/258259459)");
 
-    Ok(AdtCoreBindings {
+    Ok(Rc::new(AdtCoreBindings {
         def_id,
-        repr_attrs: tcx
-            .get_attrs(def_id, sym::repr)
-            .flat_map(|attr| rustc_attr::parse_repr_attr(tcx.sess(), attr))
-            .collect(),
         keyword,
         cc_short_name,
         rs_fully_qualified_name,
         self_ty,
         alignment_in_bytes,
         size_in_bytes,
-    })
+    }))
 }
 
-fn format_fields<'tcx>(input: &Input<'tcx>, core: &AdtCoreBindings<'tcx>) -> ApiSnippets {
-    let tcx = input.tcx;
+fn repr_attrs(db: &dyn BindingsGenerator<'_>, def_id: DefId) -> Rc<[rustc_attr::ReprAttr]> {
+    let tcx = db.tcx();
+    let attrs: Vec<_> = tcx
+        .get_attrs(def_id, sym::repr)
+        .flat_map(|attr| rustc_attr::parse_repr_attr(tcx.sess(), attr))
+        .collect();
+    attrs.into()
+}
+
+fn format_fields<'tcx>(
+    db: &dyn BindingsGenerator<'tcx>,
+    core: &AdtCoreBindings<'tcx>,
+) -> ApiSnippets {
+    let tcx = db.tcx();
 
     // TODO(b/259749095): Support non-empty set of generic parameters.
     let substs_ref = ty::List::empty();
@@ -1518,7 +1571,7 @@
                 let type_info = size.and_then(|size| {
                     Ok(FieldTypeInfo {
                         size,
-                        cc_type: format_ty_for_cc(input, field_ty, TypeLocation::Other)?,
+                        cc_type: db.format_ty_for_cc(field_ty, TypeLocation::Other)?,
                     })
                 });
                 let name = field_def.ident(tcx);
@@ -1654,7 +1707,8 @@
         // Foo(i8);` there are four different places the `i8` could be.
         // If it was placed in the second byte, for any reason, then we would need
         // explicit padding bytes.
-        let always_omit_padding = core.repr_attrs.contains(&rustc_attr::ReprC)
+        let repr_attrs = db.repr_attrs(core.def_id);
+        let always_omit_padding = repr_attrs.contains(&rustc_attr::ReprC)
             && fields.iter().all(|field| field.type_info.is_ok());
 
         let mut prereqs = CcPrerequisites::default();
@@ -1729,7 +1783,7 @@
                                 #padding
                             },
                             ty::AdtKind::Union => {
-                                if core.repr_attrs.contains(&rustc_attr::ReprC) {
+                                if repr_attrs.contains(&rustc_attr::ReprC) {
                                     quote! {
                                         __NEWLINE__
                                         #doc_comment
@@ -1796,11 +1850,11 @@
 }
 
 fn format_trait_thunks<'tcx>(
-    input: &Input<'tcx>,
+    db: &dyn BindingsGenerator<'tcx>,
     trait_id: DefId,
     adt: &AdtCoreBindings<'tcx>,
 ) -> Result<TraitThunks> {
-    let tcx = input.tcx;
+    let tcx = db.tcx();
     assert!(tcx.is_trait(trait_id));
 
     let self_ty = adt.self_ty;
@@ -1851,7 +1905,7 @@
 
         cc_thunk_decls.add_assign({
             let thunk_name = format_cc_ident(&thunk_name)?;
-            format_thunk_decl(input, method.def_id, &sig, &thunk_name)?
+            format_thunk_decl(db, method.def_id, &sig, &thunk_name)?
         });
 
         rs_thunk_impls.extend({
@@ -1887,17 +1941,15 @@
 /// trait is implemented for the ADT).  Returns an error otherwise (e.g. if
 /// there is no `Default` impl, then the default constructor will be
 /// `=delete`d in the returned snippet).
-//
-// TODO(b/259724276): This function's results should be memoized.
 fn format_default_ctor<'tcx>(
-    input: &Input<'tcx>,
-    core: &AdtCoreBindings<'tcx>,
+    db: &dyn BindingsGenerator<'tcx>,
+    core: Rc<AdtCoreBindings<'tcx>>,
 ) -> Result<ApiSnippets, ApiSnippets> {
     fn fallible_format_default_ctor<'tcx>(
-        input: &Input<'tcx>,
-        core: &AdtCoreBindings<'tcx>,
+        db: &dyn BindingsGenerator<'tcx>,
+        core: Rc<AdtCoreBindings<'tcx>>,
     ) -> Result<ApiSnippets> {
-        let tcx = input.tcx;
+        let tcx = db.tcx();
         let trait_id = tcx
             .get_diagnostic_item(sym::Default)
             .ok_or(anyhow!("Couldn't find `core::default::Default`"))?;
@@ -1905,7 +1957,7 @@
             method_name_to_cc_thunk_name,
             cc_thunk_decls,
             rs_thunk_impls: rs_details,
-        } = format_trait_thunks(input, trait_id, core)?;
+        } = format_trait_thunks(db, trait_id, &core)?;
 
         let cc_struct_name = &core.cc_short_name;
         let main_api = CcSnippet::new(quote! {
@@ -1931,7 +1983,7 @@
         };
         Ok(ApiSnippets { main_api, cc_details, rs_details })
     }
-    fallible_format_default_ctor(input, core).map_err(|err| {
+    fallible_format_default_ctor(db, core.clone()).map_err(|err| {
         let msg = format!("{err:#}");
         let adt_cc_name = &core.cc_short_name;
         ApiSnippets {
@@ -1948,17 +2000,15 @@
 /// possible (i.e. if the `Clone` trait is implemented for the ADT).  Returns an
 /// error otherwise (e.g. if there is no `Clone` impl, then the copy constructor
 /// and assignment operator will be `=delete`d in the returned snippet).
-//
-// TODO(b/259724276): This function's results should be memoized.
 fn format_copy_ctor_and_assignment_operator<'tcx>(
-    input: &Input<'tcx>,
-    core: &AdtCoreBindings<'tcx>,
+    db: &dyn BindingsGenerator<'tcx>,
+    core: Rc<AdtCoreBindings<'tcx>>,
 ) -> Result<ApiSnippets, ApiSnippets> {
     fn fallible_format_copy_ctor_and_assignment_operator<'tcx>(
-        input: &Input<'tcx>,
-        core: &AdtCoreBindings<'tcx>,
+        db: &dyn BindingsGenerator<'tcx>,
+        core: Rc<AdtCoreBindings<'tcx>>,
     ) -> Result<ApiSnippets> {
-        let tcx = input.tcx;
+        let tcx = db.tcx();
         let cc_struct_name = &core.cc_short_name;
 
         let is_copy = {
@@ -1994,7 +2044,7 @@
             method_name_to_cc_thunk_name,
             cc_thunk_decls,
             rs_thunk_impls: rs_details,
-        } = format_trait_thunks(input, trait_id, core)?;
+        } = format_trait_thunks(db, trait_id, &core)?;
         let main_api = CcSnippet::new(quote! {
             __NEWLINE__ __COMMENT__ "Clone::clone"
             #cc_struct_name(const #cc_struct_name&); __NEWLINE__
@@ -2025,7 +2075,7 @@
         };
         Ok(ApiSnippets { main_api, cc_details, rs_details })
     }
-    fallible_format_copy_ctor_and_assignment_operator(input, core).map_err(|err| {
+    fallible_format_copy_ctor_and_assignment_operator(db, core.clone()).map_err(|err| {
         let msg = format!("{err:#}");
         let adt_cc_name = &core.cc_short_name;
         ApiSnippets {
@@ -2043,20 +2093,18 @@
 /// possible (it depends on various factors like `needs_drop`, `is_unpin` and
 /// implementations of `Default` and/or `Clone` traits).  Returns an error
 /// otherwise (the error's `ApiSnippets` contain a `=delete`d declaration).
-//
-// TODO(b/259724276): This function's results should be memoized.
 fn format_move_ctor_and_assignment_operator<'tcx>(
-    input: &Input<'tcx>,
-    core: &AdtCoreBindings<'tcx>,
+    db: &dyn BindingsGenerator<'tcx>,
+    core: Rc<AdtCoreBindings<'tcx>>,
 ) -> Result<ApiSnippets, ApiSnippets> {
     fn fallible_format_move_ctor_and_assignment_operator<'tcx>(
-        input: &Input<'tcx>,
-        core: &AdtCoreBindings<'tcx>,
+        db: &dyn BindingsGenerator<'tcx>,
+        core: Rc<AdtCoreBindings<'tcx>>,
     ) -> Result<ApiSnippets> {
-        let tcx = input.tcx;
+        let tcx = db.tcx();
         let adt_cc_name = &core.cc_short_name;
         if core.needs_drop(tcx) {
-            let has_default_ctor = format_default_ctor(input, core).is_ok();
+            let has_default_ctor = db.format_default_ctor(core.clone()).is_ok();
             let is_unpin = core.self_ty.is_unpin(tcx, tcx.param_env(core.def_id));
             if has_default_ctor && is_unpin {
                 let main_api = CcSnippet::new(quote! {
@@ -2064,7 +2112,7 @@
                     #adt_cc_name& operator=(#adt_cc_name&&); __NEWLINE__
                 });
                 let mut prereqs = CcPrerequisites::default();
-                prereqs.includes.insert(input.support_header("internal/memswap.h"));
+                prereqs.includes.insert(db.support_header("internal/memswap.h"));
                 prereqs.includes.insert(CcInclude::utility()); // for `std::move`
                 let tokens = quote! {
                     inline #adt_cc_name::#adt_cc_name(#adt_cc_name&& other)
@@ -2078,7 +2126,7 @@
                 };
                 let cc_details = CcSnippet { tokens, prereqs };
                 Ok(ApiSnippets { main_api, cc_details, ..Default::default() })
-            } else if format_copy_ctor_and_assignment_operator(input, core).is_ok() {
+            } else if db.format_copy_ctor_and_assignment_operator(core).is_ok() {
                 // The class will have a custom copy constructor and copy assignment operator
                 // and *no* move constructor nor move assignment operator. This
                 // way, when a move is requested, a copy is performed instead
@@ -2140,7 +2188,7 @@
             Ok(ApiSnippets { main_api, cc_details, ..Default::default() })
         }
     }
-    fallible_format_move_ctor_and_assignment_operator(input, core).map_err(|err| {
+    fallible_format_move_ctor_and_assignment_operator(db, core.clone()).map_err(|err| {
         let msg = format!("{err:#}");
         let adt_cc_name = &core.cc_short_name;
         ApiSnippets {
@@ -2158,14 +2206,17 @@
 /// represented by `core`.  This function is infallible - after
 /// `format_adt_core` returns success we have committed to emitting C++ bindings
 /// for the ADT.
-fn format_adt<'tcx>(input: &Input<'tcx>, core: &AdtCoreBindings<'tcx>) -> ApiSnippets {
-    let tcx = input.tcx;
+fn format_adt<'tcx>(
+    db: &dyn BindingsGenerator<'tcx>,
+    core: Rc<AdtCoreBindings<'tcx>>,
+) -> ApiSnippets {
+    let tcx = db.tcx();
     let adt_cc_name = &core.cc_short_name;
 
     // `format_adt` should only be called for local ADTs.
     let local_def_id = core.def_id.expect_local();
 
-    let default_ctor_snippets = format_default_ctor(input, core).unwrap_or_else(|err| err);
+    let default_ctor_snippets = db.format_default_ctor(core.clone()).unwrap_or_else(|err| err);
 
     let destructor_snippets = if core.needs_drop(tcx) {
         let drop_trait_id =
@@ -2174,7 +2225,7 @@
             method_name_to_cc_thunk_name,
             cc_thunk_decls,
             rs_thunk_impls: rs_details,
-        } = format_trait_thunks(input, drop_trait_id, core)
+        } = format_trait_thunks(db, drop_trait_id, &core)
             .expect("`format_adt_core` should have already validated `Drop` support");
         let drop_thunk_name = method_name_to_cc_thunk_name
             .into_values()
@@ -2210,10 +2261,10 @@
     };
 
     let copy_ctor_and_assignment_snippets =
-        format_copy_ctor_and_assignment_operator(input, core).unwrap_or_else(|err| err);
+        db.format_copy_ctor_and_assignment_operator(core.clone()).unwrap_or_else(|err| err);
 
     let move_ctor_and_assignment_snippets =
-        format_move_ctor_and_assignment_operator(input, core).unwrap_or_else(|err| err);
+        db.format_move_ctor_and_assignment_operator(core.clone()).unwrap_or_else(|err| err);
 
     let impl_items_snippets = tcx
         .inherent_impls(core.def_id)
@@ -2234,7 +2285,7 @@
                 return None;
             }
             let result = match impl_item_ref.kind {
-                AssocItemKind::Fn { .. } => format_fn(input, def_id).map(Some),
+                AssocItemKind::Fn { .. } => db.format_fn(def_id).map(Some),
                 other => Err(anyhow!("Unsupported `impl` item kind: {other:?}")),
             };
             result.unwrap_or_else(|err| Some(format_unsupported_def(tcx, def_id, err)))
@@ -2259,7 +2310,7 @@
         main_api: fields_main_api,
         cc_details: fields_cc_details,
         rs_details: fields_rs_details,
-    } = format_fields(input, core);
+    } = format_fields(db, &core);
 
     let alignment = Literal::u64_unsuffixed(core.alignment_in_bytes);
     let size = Literal::u64_unsuffixed(core.size_in_bytes);
@@ -2270,7 +2321,11 @@
             quote! {alignas(#alignment)},
             quote! {[[clang::trivial_abi]]},
         ];
-        if core.repr_attrs.iter().any(|repr| matches!(repr, rustc_attr::ReprPacked { .. })) {
+        if db
+            .repr_attrs(core.def_id)
+            .iter()
+            .any(|repr| matches!(repr, rustc_attr::ReprPacked { .. }))
+        {
             attributes.push(quote! { __attribute__((packed)) })
         }
 
@@ -2294,7 +2349,7 @@
         let keyword = &core.keyword;
 
         let mut prereqs = CcPrerequisites::default();
-        prereqs.includes.insert(input.support_header("internal/attribute_macros.h"));
+        prereqs.includes.insert(db.support_header("internal/attribute_macros.h"));
         let public_functions_main_api = public_functions_main_api.into_tokens(&mut prereqs);
         let fields_main_api = fields_main_api.into_tokens(&mut prereqs);
         prereqs.fwd_decls.remove(&local_def_id);
@@ -2351,14 +2406,16 @@
 ///
 /// Will panic if `def_id` doesn't identify an ADT that can be successfully
 /// handled by `format_adt_core`.
-fn format_fwd_decl(tcx: TyCtxt, def_id: LocalDefId) -> TokenStream {
+fn format_fwd_decl(db: &Database<'_>, def_id: LocalDefId) -> TokenStream {
     let def_id = def_id.to_def_id(); // LocalDefId -> DefId conversion.
 
     // `format_fwd_decl` should only be called for items from
     // `CcPrerequisites::fwd_decls` and `fwd_decls` should only contain ADTs
     // that `format_adt_core` succeeds for.
-    let AdtCoreBindings { keyword, cc_short_name, .. } = format_adt_core(tcx, def_id)
+    let core_bindings = db
+        .format_adt_core(def_id)
         .expect("`format_fwd_decl` should only be called if `format_adt_core` succeeded");
+    let AdtCoreBindings { keyword, cc_short_name, .. } = &*core_bindings;
 
     quote! { #keyword #cc_short_name; }
 }
@@ -2401,28 +2458,29 @@
 /// can be ignored. Returns an `Err` if the definition couldn't be formatted.
 ///
 /// Will panic if `def_id` is invalid (i.e. doesn't identify a HIR item).
-fn format_item(input: &Input, def_id: LocalDefId) -> Result<Option<ApiSnippets>> {
+fn format_item(db: &dyn BindingsGenerator<'_>, def_id: LocalDefId) -> Result<Option<ApiSnippets>> {
+    let tcx = db.tcx();
     // TODO(b/262052635): When adding support for re-exports we may need to change
     // `is_directly_public` below into `is_exported`.  (OTOH such change *alone* is
     // undesirable, because it would mean exposing items from a private module.
     // Exposing a private module is undesirable, because it would mean that
     // changes of private implementation details of the crate could become
     // breaking changes for users of the generated C++ bindings.)
-    if !input.tcx.effective_visibilities(()).is_directly_public(def_id) {
+    if !tcx.effective_visibilities(()).is_directly_public(def_id) {
         return Ok(None);
     }
 
-    match input.tcx.hir().expect_item(def_id) {
+    match tcx.hir().expect_item(def_id) {
         Item { kind: ItemKind::Struct(_, generics) |
                      ItemKind::Enum(_, generics) |
                      ItemKind::Union(_, generics),
                .. } if !generics.params.is_empty() => {
             bail!("Generic types are not supported yet (b/259749095)");
         },
-        Item { kind: ItemKind::Fn(..), .. } => format_fn(input, def_id).map(Some),
+        Item { kind: ItemKind::Fn(..), .. } => db.format_fn(def_id).map(Some),
         Item { kind: ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..), .. } =>
-            format_adt_core(input.tcx, def_id.to_def_id())
-                .map(|core| Some(format_adt(input, &core))),
+            db.format_adt_core(def_id.to_def_id())
+                .map(|core| Some(format_adt(db, core))),
         Item { kind: ItemKind::Impl(_), .. } |  // Handled by `format_adt`
         Item { kind: ItemKind::Mod(_), .. } =>  // Handled by `format_crate`
             Ok(None),
@@ -2432,11 +2490,7 @@
 
 /// Formats a C++ comment explaining why no bindings have been generated for
 /// `local_def_id`.
-fn format_unsupported_def(
-    tcx: TyCtxt,
-    local_def_id: LocalDefId,
-    err: anyhow::Error,
-) -> ApiSnippets {
+fn format_unsupported_def(tcx: TyCtxt, local_def_id: LocalDefId, err: Error) -> ApiSnippets {
     let source_loc = format_source_location(tcx, local_def_id);
     let name = tcx.def_path_str(local_def_id.to_def_id());
 
@@ -2522,8 +2576,8 @@
 }
 
 /// Formats all public items from the Rust crate being compiled.
-fn format_crate(input: &Input) -> Result<Output> {
-    let tcx = input.tcx;
+fn format_crate(db: &Database) -> Result<Output> {
+    let tcx = db.tcx();
     let mut cc_details_prereqs = CcPrerequisites::default();
     let mut cc_details: Vec<(LocalDefId, TokenStream)> = vec![];
     let mut rs_body = TokenStream::default();
@@ -2533,7 +2587,7 @@
         .items()
         .filter_map(|item_id| {
             let def_id: LocalDefId = item_id.owner_id.def_id;
-            format_item(input, def_id)
+            db.format_item(def_id)
                 .unwrap_or_else(|err| Some(format_unsupported_def(tcx, def_id, err)))
                 .map(|api_snippets| (def_id, api_snippets))
         })
@@ -2602,7 +2656,7 @@
         let fwd_decls = fwd_decls
             .into_iter()
             .sorted_by_key(|def_id| tcx.def_span(*def_id))
-            .map(|local_def_id| (local_def_id, format_fwd_decl(tcx, local_def_id)));
+            .map(|local_def_id| (local_def_id, format_fwd_decl(db, local_def_id)));
 
         // The first item of the tuple here is the DefId of the namespace.
         let ordered_cc: Vec<(Option<DefId>, NamespaceQualifier, TokenStream)> = fwd_decls
@@ -6722,8 +6776,8 @@
         ];
         test_ty(TypeLocation::FnReturn, &testcases, quote! {}, |desc, tcx, ty, expected| {
             let actual = {
-                let input = bindings_input_for_tests(tcx);
-                let cc_snippet = format_ty_for_cc(&input, ty, TypeLocation::FnReturn).unwrap();
+                let db = bindings_db_for_tests(tcx);
+                let cc_snippet = format_ty_for_cc(&db, ty, TypeLocation::FnReturn).unwrap();
                 cc_snippet.tokens.to_string()
             };
             let expected = expected.parse::<TokenStream>().unwrap().to_string();
@@ -6860,8 +6914,8 @@
             |desc, tcx, ty,
              (expected_tokens, expected_include, expected_prereq_def, expected_prereq_fwd_decl)| {
                 let (actual_tokens, actual_prereqs) = {
-                    let input = bindings_input_for_tests(tcx);
-                    let s = format_ty_for_cc(&input, ty, TypeLocation::FnParam).unwrap();
+                    let db = bindings_db_for_tests(tcx);
+                    let s = format_ty_for_cc(&db, ty, TypeLocation::FnParam).unwrap();
                     (s.tokens.to_string(), s.prereqs)
                 };
                 let (actual_includes, actual_prereq_defs, actual_prereq_fwd_decls) =
@@ -7059,8 +7113,8 @@
                 as PublicReexportOfStruct;
         };
         test_ty(TypeLocation::FnParam, &testcases, preamble, |desc, tcx, ty, expected_msg| {
-            let input = bindings_input_for_tests(tcx);
-            let anyhow_err = format_ty_for_cc(&input, ty, TypeLocation::FnParam)
+            let db = bindings_db_for_tests(tcx);
+            let anyhow_err = format_ty_for_cc(&db, ty, TypeLocation::FnParam)
                 .expect_err(&format!("Expecting error for: {desc}"));
             let actual_msg = format!("{anyhow_err:#}");
             assert_eq!(&actual_msg, *expected_msg, "{desc}");
@@ -8070,7 +8124,7 @@
     {
         run_compiler_for_testing(source, |tcx| {
             let def_id = find_def_id_by_name(tcx, name);
-            let result = format_item(&bindings_input_for_tests(tcx), def_id);
+            let result = bindings_db_for_tests(tcx).format_item(def_id);
 
             // https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations says:
             // To print causes as well [...], use the alternate selector “{:#}”.
@@ -8080,13 +8134,13 @@
         })
     }
 
-    fn bindings_input_for_tests(tcx: TyCtxt) -> Input {
-        Input {
+    fn bindings_db_for_tests(tcx: TyCtxt) -> Database {
+        Database::new(
             tcx,
-            crubit_support_path_format: "<crubit/support/for/tests/{header}>".into(),
-            crate_name_to_include_paths: Default::default(),
-            _features: (),
-        }
+            /* crubit_support_path_format= */ "<crubit/support/for/tests/{header}>".into(),
+            /* crate_name_to_include_paths= */ Default::default(),
+            /* _features= */ (),
+        )
     }
 
     /// Tests invoking `generate_bindings` on the given Rust `source`.
@@ -8099,7 +8153,7 @@
         T: Send,
     {
         run_compiler_for_testing(source, |tcx| {
-            test_function(generate_bindings(&bindings_input_for_tests(tcx)))
+            test_function(generate_bindings(&bindings_db_for_tests(tcx)))
         })
     }
 }
diff --git a/cc_bindings_from_rs/cc_bindings_from_rs.rs b/cc_bindings_from_rs/cc_bindings_from_rs.rs
index 74d7d3c..9aad969 100644
--- a/cc_bindings_from_rs/cc_bindings_from_rs.rs
+++ b/cc_bindings_from_rs/cc_bindings_from_rs.rs
@@ -8,14 +8,14 @@
 
 extern crate rustc_middle;
 
-use anyhow::Context;
+use arc_anyhow::{Context, Result};
 use itertools::Itertools;
 use rustc_middle::ty::TyCtxt; // See also <internal link>/ty.html#import-conventions
 use std::collections::HashMap;
 use std::path::Path;
 use std::rc::Rc;
 
-use bindings::Input;
+use bindings::Database;
 use cmdline::Cmdline;
 use code_gen_utils::CcInclude;
 use run_compiler::run_compiler;
@@ -23,12 +23,12 @@
     cc_tokens_to_formatted_string, rs_tokens_to_formatted_string, RustfmtConfig,
 };
 
-fn write_file(path: &Path, content: &str) -> anyhow::Result<()> {
+fn write_file(path: &Path, content: &str) -> Result<()> {
     std::fs::write(path, content)
         .with_context(|| format!("Error when writing to {}", path.display()))
 }
 
-fn new_input<'tcx>(cmdline: &Cmdline, tcx: TyCtxt<'tcx>) -> Input<'tcx> {
+fn new_db<'tcx>(cmdline: &Cmdline, tcx: TyCtxt<'tcx>) -> Database<'tcx> {
     let crubit_support_path_format = cmdline.crubit_support_path_format.as_str().into();
 
     let mut crate_name_to_include_paths = <HashMap<Rc<str>, Vec<CcInclude>>>::new();
@@ -37,14 +37,19 @@
         paths.push(CcInclude::user_header(include_path.as_str().into()));
     }
 
-    Input { tcx, crubit_support_path_format, crate_name_to_include_paths, _features: () }
+    Database::new(
+        tcx,
+        crubit_support_path_format,
+        crate_name_to_include_paths.into(),
+        /* _features= */ (),
+    )
 }
 
-fn run_with_tcx(cmdline: &Cmdline, tcx: TyCtxt) -> anyhow::Result<()> {
+fn run_with_tcx(cmdline: &Cmdline, tcx: TyCtxt) -> Result<()> {
     use bindings::{generate_bindings, Output};
     let Output { h_body, rs_body } = {
-        let input = new_input(cmdline, tcx);
-        generate_bindings(&input)?
+        let db = new_db(cmdline, tcx);
+        generate_bindings(&db)?
     };
 
     {
@@ -65,12 +70,12 @@
 /// Main entrypoint that (unlike `main`) doesn't do any intitializations that
 /// should only happen once for the binary (e.g. it doesn't call
 /// `init_env_logger`) and therefore can be used from the tests module below.
-fn run_with_cmdline_args(args: &[String]) -> anyhow::Result<()> {
+fn run_with_cmdline_args(args: &[String]) -> Result<()> {
     let cmdline = Cmdline::new(args)?;
     run_compiler(&cmdline.rustc_args, |tcx| run_with_tcx(&cmdline, tcx))
 }
 
-fn main() -> anyhow::Result<()> {
+fn main() -> Result<()> {
     // TODO: Investigate if we should install a signal handler here.  See also how
     // compiler/rustc_driver/src/lib.rs calls `signal_handler::install()`.
 
@@ -82,22 +87,22 @@
     // Unicode.  This seems okay.
     let args = std::env::args().collect_vec();
 
-    run_with_cmdline_args(&args).map_err(|anyhow_err| match anyhow_err.downcast::<clap::Error>() {
+    run_with_cmdline_args(&args).map_err(|err| match err.downcast_ref::<clap::Error>() {
         // Explicitly call `clap::Error::exit`, because 1) it results in *colored* output and
         // 2) it uses a zero exit code for specific "errors" (e.g. for `--help` output).
-        Ok(clap_err) => {
+        Some(clap_err) => {
             let _: ! = clap_err.exit();
         }
 
-        // Return `other_err` from `main`.  This will print the error message (no color codes
+        // Return `err` from `main`.  This will print the error message (no color codes
         // though) and terminate the process with a non-zero exit code.
-        Err(other_err) => other_err,
+        None => err,
     })
 }
 
 #[cfg(test)]
 mod tests {
-    use super::run_with_cmdline_args;
+    use super::*;
 
     use itertools::Itertools;
     use regex::{Regex, RegexBuilder};
@@ -130,7 +135,7 @@
     }
 
     impl TestArgs {
-        fn default_args() -> anyhow::Result<Self> {
+        fn default_args() -> Result<Self> {
             Ok(Self {
                 h_path: None,
                 extra_crubit_args: vec![],
@@ -174,7 +179,7 @@
         ///
         /// Returns the path to the `h_out` file.  The file's lifetime is the
         /// same as `&self`.
-        fn run(&self) -> anyhow::Result<TestResult> {
+        fn run(&self) -> Result<TestResult> {
             let h_path = match self.h_path.as_ref() {
                 None => self.tempdir.path().join("test_crate_cc_api.h"),
                 Some(s) => PathBuf::from(s),
@@ -264,7 +269,7 @@
     }
 
     #[test]
-    fn test_happy_path() -> anyhow::Result<()> {
+    fn test_happy_path() -> Result<()> {
         let test_args = TestArgs::default_args()?;
         let test_result = test_args.run().expect("Default args should succeed");
 
@@ -328,7 +333,7 @@
     /// get propagated. More detailed test coverage of various specific
     /// error types can be found in tests in `cmdline.rs`.
     #[test]
-    fn test_cmdline_error_propagation() -> anyhow::Result<()> {
+    fn test_cmdline_error_propagation() -> Result<()> {
         let err = TestArgs::default_args()?
             .with_extra_crubit_args(&["--unrecognized-crubit-flag"])
             .run()
@@ -344,7 +349,7 @@
     /// various specific error types can be found in tests in `run_compiler.
     /// rs`.
     #[test]
-    fn test_run_compiler_error_propagation() -> anyhow::Result<()> {
+    fn test_run_compiler_error_propagation() -> Result<()> {
         let err = TestArgs::default_args()?
             .with_extra_rustc_args(&["--unrecognized-rustc-flag"])
             .run()
@@ -362,7 +367,7 @@
     /// `bindings.rs` level, because `run_compiler_test_support` doesn't
     /// support specifying a custom panic mechanism.
     #[test]
-    fn test_rustc_unsupported_panic_mechanism() -> anyhow::Result<()> {
+    fn test_rustc_unsupported_panic_mechanism() -> Result<()> {
         let err = TestArgs::default_args()?
             .with_panic_mechanism("unwind")
             .run()
@@ -377,7 +382,7 @@
     /// invalid `--h-out` argument, but also tests that errors from
     /// `run_with_tcx` are propagated.
     #[test]
-    fn test_invalid_h_out_path() -> anyhow::Result<()> {
+    fn test_invalid_h_out_path() -> Result<()> {
         let err = TestArgs::default_args()?
             .with_h_path("../..")
             .run()
diff --git a/cc_bindings_from_rs/run_compiler.rs b/cc_bindings_from_rs/run_compiler.rs
index ed26892..fd21283 100644
--- a/cc_bindings_from_rs/run_compiler.rs
+++ b/cc_bindings_from_rs/run_compiler.rs
@@ -15,7 +15,7 @@
 extern crate rustc_session;
 extern crate rustc_span;
 
-use anyhow::bail;
+use arc_anyhow::{anyhow, bail, Result};
 use either::Either;
 use rustc_interface::interface::Compiler;
 use rustc_interface::Queries;
@@ -32,9 +32,9 @@
 /// - Returns the combined results from the Rust compiler *and* the `callback`.
 /// - Is safe to run from unit tests (which may run in parallel / on multiple
 ///   threads).
-pub fn run_compiler<F, R>(rustc_args: &[String], callback: F) -> anyhow::Result<R>
+pub fn run_compiler<F, R>(rustc_args: &[String], callback: F) -> Result<R>
 where
-    F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
+    F: FnOnce(TyCtxt) -> Result<R> + Send,
     R: Send,
 {
     // Calling `init_logger` 1) here and 2) via `sync::Lazy` helps to ensure
@@ -60,16 +60,16 @@
 
 struct AfterAnalysisCallback<'a, F, R>
 where
-    F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
+    F: FnOnce(TyCtxt) -> Result<R> + Send,
     R: Send,
 {
     args: &'a [String],
-    callback_or_result: Either<F, anyhow::Result<R>>,
+    callback_or_result: Either<F, Result<R>>,
 }
 
 impl<'a, F, R> AfterAnalysisCallback<'a, F, R>
 where
-    F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
+    F: FnOnce(TyCtxt) -> Result<R> + Send,
     R: Send,
 {
     fn new(args: &'a [String], callback: F) -> Self {
@@ -80,7 +80,7 @@
     /// `TyCtxt` of the parsed+analyzed Rust crate as the callback's
     /// argument), and then finally returns the combined results
     /// from Rust compiler *and* the callback.
-    fn run(mut self) -> anyhow::Result<R> {
+    fn run(mut self) -> Result<R> {
         // Rust compiler unwinds with a special sentinel value to abort compilation on
         // fatal errors. We use `catch_fatal_errors` to 1) catch such panics and
         // translate them into a Result, and 2) resume and propagate other panics.
@@ -108,7 +108,7 @@
 
 impl<'a, F, R> rustc_driver::Callbacks for AfterAnalysisCallback<'a, F, R>
 where
-    F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
+    F: FnOnce(TyCtxt) -> Result<R> + Send,
     R: Send,
 {
     /// Configures how `rustc` internals work when invoked via `run_compiler`.
@@ -133,7 +133,7 @@
             .expect("Expecting no compile errors inside `after_analysis` callback.");
         query_context.enter(|tcx| {
             let callback = {
-                let temporary_placeholder = Either::Right(Err(anyhow::anyhow!("unused")));
+                let temporary_placeholder = Either::Right(Err(anyhow!("unused")));
                 std::mem::replace(&mut self.callback_or_result, temporary_placeholder)
                     .left_or_else(|_| panic!("`after_analysis` should only run once"))
             };
@@ -160,7 +160,7 @@
             "#;
 
     #[test]
-    fn test_run_compiler_rustc_error_propagation() -> anyhow::Result<()> {
+    fn test_run_compiler_rustc_error_propagation() -> Result<()> {
         let rustc_args = vec![
             "run_compiler_unittest_executable".to_string(),
             "--unrecognized-rustc-flag".to_string(),
@@ -177,7 +177,7 @@
     /// where `rustc` doesn't compile anything (e.g. when there are no
     /// cmdline args).
     #[test]
-    fn test_run_compiler_no_args_except_argv0() -> anyhow::Result<()> {
+    fn test_run_compiler_no_args_except_argv0() -> Result<()> {
         let rustc_args = vec!["run_compiler_unittest_executable".to_string()];
         let err = run_compiler(&rustc_args, |_tcx| Ok(()))
             .expect_err("Empty `rustc_args` should trigger an error");
@@ -190,7 +190,7 @@
     /// `test_run_compiler_help` tests that we gracefully handle scenarios where
     /// `rustc` doesn't compile anything (e.g. when passing `--help`).
     #[test]
-    fn test_run_compiler_help() -> anyhow::Result<()> {
+    fn test_run_compiler_help() -> Result<()> {
         let rustc_args = vec!["run_compiler_unittest_executable".to_string(), "--help".to_string()];
         let err = run_compiler(&rustc_args, |_tcx| Ok(()))
             .expect_err("--help passed to rustc should trigger an error");
@@ -203,7 +203,7 @@
     /// `test_run_compiler_no_output_file` tests that we stop the compilation
     /// midway (i.e. that we return `Stop` from `after_analysis`).
     #[test]
-    fn test_run_compiler_no_output_file() -> anyhow::Result<()> {
+    fn test_run_compiler_no_output_file() -> Result<()> {
         let tmpdir = tempdir()?;
 
         let rs_path = tmpdir.path().join("input_crate.rs");
diff --git a/common/BUILD b/common/BUILD
index 7e3b6bd..71aa63c 100644
--- a/common/BUILD
+++ b/common/BUILD
@@ -30,7 +30,7 @@
     name = "code_gen_utils",
     srcs = ["code_gen_utils.rs"],
     deps = [
-        "@crate_index//:anyhow",
+        ":arc_anyhow",
         "@crate_index//:itertools",
         "@crate_index//:once_cell",
         "@crate_index//:proc-macro2",
diff --git a/common/code_gen_utils.rs b/common/code_gen_utils.rs
index edd7736..b27ab8d 100644
--- a/common/code_gen_utils.rs
+++ b/common/code_gen_utils.rs
@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-use anyhow::{anyhow, ensure, Result};
+use arc_anyhow::{anyhow, ensure, Result};
 use itertools::Itertools;
 use once_cell::sync::Lazy;
 use proc_macro2::{Ident, TokenStream};