Assume bindings are generated in `__cc_template_instantiations_rs_api`
crate submodule when instantiating templates

PiperOrigin-RevId: 488891130
diff --git a/rs_bindings_from_cc/generate_bindings_and_metadata.cc b/rs_bindings_from_cc/generate_bindings_and_metadata.cc
index b351b0f..13d5dee 100644
--- a/rs_bindings_from_cc/generate_bindings_and_metadata.cc
+++ b/rs_bindings_from_cc/generate_bindings_and_metadata.cc
@@ -74,6 +74,10 @@
           cmdline.headers_to_targets(), cmdline.extra_rs_srcs(),
           clang_args_view, requested_instantiations));
 
+  if (!cmdline.instantiations_out().empty()) {
+    ir.crate_root_path_ = "__cc_template_instantiations_rs_api";
+  }
+
   bool generate_error_report = !cmdline.error_report_out().empty();
   CRUBIT_ASSIGN_OR_RETURN(
       Bindings bindings,
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index f86cf41..59c5300 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -549,12 +549,16 @@
     top_level_ids.push_back(id.value());
   }
 
-  return llvm::json::Object{
+  llvm::json::Object result{
       {"public_headers", public_headers},
       {"current_target", current_target},
       {"items", std::move(json_items)},
       {"top_level_item_ids", std::move(top_level_ids)},
   };
+  if (!crate_root_path_.empty()) {
+    result["crate_root_path"] = crate_root_path_;
+  }
+  return std::move(result);
 }
 
 std::string ItemToString(const IR::Item& item) {
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index 1e18f78..cbf762c 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -796,6 +796,19 @@
                             UnsupportedItem, Comment, Namespace, UseMod>;
   std::vector<Item> items;
   std::vector<ItemId> top_level_item_ids;
+  // Empty string signals that the bindings should be generated in the crate
+  // root. This is the default state.
+  //
+  // Non-empty value represents the name of the first-level submodule inside of
+  // which bindings should be generated. This is how we generate bindings for
+  // class template instantiations - we put all generated bindings into a hidden
+  // module of the user crate so everything can be compiled in one rustc
+  // invocation (this enables us to access types introduced in the user crate
+  // for template instantiations in the future).
+  //
+  // TODO(hlopko): Replace empty strings with std::optional<std::string>
+  // throughout the codebase
+  std::string crate_root_path_;
 };
 
 inline std::string IrToJson(const IR& ir) {
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index 916e951..d60ef34 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -31,8 +31,9 @@
     public_headers: Vec<HeaderName>,
     current_target: BazelLabel,
     top_level_item_ids: Vec<ItemId>,
+    crate_root_path: Option<String>,
 ) -> Result<IR> {
-    make_ir(FlatIR { public_headers, current_target, items, top_level_item_ids })
+    make_ir(FlatIR { public_headers, current_target, items, top_level_item_ids, crate_root_path })
 }
 
 fn make_ir(flat_ir: FlatIR) -> Result<IR> {
@@ -690,6 +691,8 @@
     items: Vec<Item>,
     #[serde(default)]
     top_level_item_ids: Vec<ItemId>,
+    #[serde(default)]
+    crate_root_path: Option<String>,
 }
 
 /// Struct providing the necessary information about the API of a C++ target to
@@ -863,6 +866,10 @@
             Ok(None)
         }
     }
+
+    pub fn crate_root_path(&self) -> Option<&str> {
+        self.flat_ir.crate_root_path.as_deref()
+    }
 }
 
 #[cfg(test)]
@@ -901,7 +908,27 @@
             current_target: "//foo:bar".into(),
             top_level_item_ids: vec![],
             items: vec![],
+            crate_root_path: None,
         };
         assert_eq!(ir.flat_ir, expected);
     }
+
+    #[test]
+    fn test_empty_crate_root_path() {
+        let input = "{ \"current_target\": \"//foo:bar\" }";
+        let ir = deserialize_ir(input.as_bytes()).unwrap();
+        assert_eq!(ir.crate_root_path(), None);
+    }
+
+    #[test]
+    fn test_crate_root_path() {
+        let input = r#"
+        {
+            "crate_root_path": "__cc_template_instantiations_rs_api",
+            "current_target": "//foo:bar"
+        }
+        "#;
+        let ir = deserialize_ir(input.as_bytes()).unwrap();
+        assert_eq!(ir.crate_root_path(), Some("__cc_template_instantiations_rs_api"));
+    }
 }
diff --git a/rs_bindings_from_cc/ir_from_cc.cc b/rs_bindings_from_cc/ir_from_cc.cc
index fc823e3..76660d4 100644
--- a/rs_bindings_from_cc/ir_from_cc.cc
+++ b/rs_bindings_from_cc/ir_from_cc.cc
@@ -43,8 +43,6 @@
   // Caller should verify that the inputs are not empty.
   CHECK(!extra_source_code_for_testing.empty() || !public_headers.empty() ||
         !extra_instantiations.empty());
-  CHECK(!extra_source_code_for_testing.empty() || !headers_to_targets.empty() ||
-        !extra_instantiations.empty());
 
   clang::tooling::FileContentMappings file_contents;
 
diff --git a/rs_bindings_from_cc/ir_matchers.rs b/rs_bindings_from_cc/ir_matchers.rs
index 19c6751..e26cab2 100644
--- a/rs_bindings_from_cc/ir_matchers.rs
+++ b/rs_bindings_from_cc/ir_matchers.rs
@@ -145,7 +145,7 @@
     fn test_assert_ir_matches_assumes_trailing_commas_in_groups() {
         assert_ir_matches!(
             ir_from_cc("").unwrap(),
-            quote! {{... items: [...], top_level_item_ids: [...], }}
+            quote! {{... crate_root_path: None, }}
         );
     }
 
diff --git a/rs_bindings_from_cc/ir_testing.rs b/rs_bindings_from_cc/ir_testing.rs
index 072f677..e9b3ef5 100644
--- a/rs_bindings_from_cc/ir_testing.rs
+++ b/rs_bindings_from_cc/ir_testing.rs
@@ -40,6 +40,7 @@
         /* public_headers= */ vec![],
         /* current_target= */ TESTING_TARGET.into(),
         /* top_level_item_ids= */ vec![],
+        /* crate_root_path= */ None,
     )
 }
 
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index f25d295..59b45b9 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -1232,6 +1232,7 @@
     func: Rc<Func>,
 ) -> Result<Option<Rc<(RsSnippet, RsSnippet, Rc<FunctionId>)>>> {
     let ir = db.ir();
+    let crate_root_path = crate_root_path_tokens(&ir);
     let mut features = BTreeSet::new();
     let mut param_types = func
         .params
@@ -1334,7 +1335,7 @@
                 quote! {
                     let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
                     unsafe {
-                        crate::detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
+                        #crate_root_path::detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
                         tmp.assume_init()
                     }
                 }
@@ -1346,11 +1347,11 @@
                 //
                 // TODO(jeanpierreda): separately handle non-Unpin and non-trivial types.
                 let mut body = if return_type.is_unpin() {
-                    quote! { crate::detail::#thunk_ident( #( #thunk_args #clone_suffixes ),* ) }
+                    quote! { #crate_root_path::detail::#thunk_ident( #( #thunk_args #clone_suffixes ),* ) }
                 } else {
                     quote! {
                         ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<#return_type>>| {
-                            crate::detail::#thunk_ident(::std::pin::Pin::into_inner_unchecked(dest) #( , #thunk_args )*);
+                            #crate_root_path::detail::#thunk_ident(::std::pin::Pin::into_inner_unchecked(dest) #( , #thunk_args )*);
                         })
                     }
                 };
@@ -1965,10 +1966,11 @@
     errors: &mut dyn ErrorReporting,
 ) -> Result<GeneratedItem> {
     let ir = db.ir();
+    let crate_root_path = crate_root_path_tokens(&ir);
     let ident = make_rs_ident(&record.rs_name);
     let namespace_qualifier = NamespaceQualifier::new(record.id, &ir)?.format_for_rs();
     let qualified_ident = {
-        quote! { crate:: #namespace_qualifier #ident }
+        quote! { #crate_root_path:: #namespace_qualifier #ident }
     };
     let doc_comment = generate_doc_comment(&record.doc_comment);
 
@@ -2858,14 +2860,20 @@
 }
 
 impl CratePath {
-    fn new(namespace_qualifier: &NamespaceQualifier, crate_ident: Option<Ident>) -> CratePath {
+    fn new(
+        ir: &IR,
+        namespace_qualifier: &NamespaceQualifier,
+        crate_ident: Option<Ident>,
+    ) -> CratePath {
         let namespace_qualifiers = &namespace_qualifier.0;
         let crate_ident = match crate_ident {
             Some(ci) => ci.to_string(),
             None => "crate".to_string(),
         };
-        let idents =
-            std::iter::once(crate_ident).chain(namespace_qualifiers.iter().cloned()).collect();
+        let idents = std::iter::once(crate_ident)
+            .chain(ir.crate_root_path().into_iter().map(ToOwned::to_owned))
+            .chain(namespace_qualifiers.iter().cloned())
+            .collect();
         CratePath { idents }
     }
 }
@@ -2937,6 +2945,7 @@
 impl RsTypeKind {
     pub fn new_record(record: Rc<Record>, ir: &IR) -> Result<Self> {
         let crate_path = Rc::new(CratePath::new(
+            ir,
             &NamespaceQualifier::new(record.id, ir)?,
             rs_imported_crate_name(&record.owning_target, ir),
         ));
@@ -3167,10 +3176,7 @@
                 let return_frag = return_type.format_as_return_type_fragment();
                 quote! { extern #abi fn( #( #param_types ),* ) #return_frag }
             }
-            RsTypeKind::IncompleteRecord {
-                incomplete_record,
-                crate_path
-            } => {
+            RsTypeKind::IncompleteRecord { incomplete_record, crate_path } => {
                 let record_ident = make_rs_ident(&incomplete_record.rs_name);
                 quote! { #crate_path :: #record_ident }
             }
@@ -3274,11 +3280,11 @@
             match ir.item_for_type(&ty)? {
                 Item::IncompleteRecord(incomplete_record) => RsTypeKind::IncompleteRecord {
                     incomplete_record: incomplete_record.clone(),
-                   crate_path: Rc::new(CratePath::new(&NamespaceQualifier::new(
-                        incomplete_record.id,
+                    crate_path: Rc::new(CratePath::new(
                         &ir,
-                    )?,
-                    rs_imported_crate_name(&incomplete_record.owning_target, &ir)))
+                        &NamespaceQualifier::new(incomplete_record.id, &ir)?,
+                        rs_imported_crate_name(&incomplete_record.owning_target, &ir),
+                    )),
                 },
                 Item::Record(record) => RsTypeKind::new_record(record.clone(), &ir)?,
                 Item::TypeAlias(type_alias) => {
@@ -3289,11 +3295,11 @@
                     } else {
                         RsTypeKind::TypeAlias {
                             type_alias: type_alias.clone(),
-                           crate_path: Rc::new(CratePath::new(&NamespaceQualifier::new(
-                                type_alias.id,
+                            crate_path: Rc::new(CratePath::new(
                                 &ir,
-                            )?,
-                            rs_imported_crate_name(&type_alias.owning_target, &ir))),
+                                &NamespaceQualifier::new(type_alias.id, &ir)?,
+                                rs_imported_crate_name(&type_alias.owning_target, &ir),
+                            )),
                             underlying_type: Rc::new(
                                 db.rs_type_kind(type_alias.underlying_type.rs_type.clone())?,
                             ),
@@ -3569,6 +3575,15 @@
     })
 }
 
+fn crate_root_path_tokens(ir: &IR) -> TokenStream {
+    if let Some(crate_root_path) = ir.crate_root_path() {
+        let crate_root_path = make_rs_ident(crate_root_path);
+        quote! {crate::#crate_root_path}
+    } else {
+        quote! {crate}
+    }
+}
+
 /// Returns the implementation of base class conversions, for converting a type
 /// to its unambiguous public base classes.
 fn cc_struct_upcast_impl(record: &Rc<Record>, ir: &IR) -> Result<GeneratedItem> {
@@ -3600,8 +3615,9 @@
             thunks.push(quote! {
                 pub fn #cast_fn_name (from: *const #derived_name) -> *const #base_name;
             });
+            let crate_root_path = crate_root_path_tokens(ir);
             body = quote! {
-                crate::detail::#cast_fn_name(derived)
+                #crate_root_path::detail::#cast_fn_name(derived)
             };
         }
         impls.push(quote! {