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! {