Implement reopened namespaces
For a series of 3 reopened namespaces under the name of outer and inner we generate the following code:
```
pub mod outer_0 {
pub mod inner_0 {}
}
pub mod outer_1 {
pub use super::outer_0::*;
pub mod inner_1 {
pub use super::inner_0::*;
}
}
pub mod outer {
pub use super::outer_1::*;
pub mod inner {
pub use super::inner_1::*;
}
}
```
For this we add two helper maps into our ir:
map <ItemId, usize> => canonical namespace id to number of reopened namespaces
map <ItemId, usize> => reopened namespace id to its ordinal number
PiperOrigin-RevId: 452288159
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 521f83d..6584556 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -38,6 +38,7 @@
#include "clang/AST/RawCommentList.h"
#include "clang/AST/Type.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LLVM.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
@@ -304,7 +305,12 @@
while (!decls_to_visit.empty()) {
clang::Decl* child = decls_to_visit.back();
decls_to_visit.pop_back();
- auto decl = child->getCanonicalDecl();
+ clang::Decl* decl;
+ if (auto namespace_decl = llvm::dyn_cast<clang::NamespaceDecl>(child)) {
+ decl = namespace_decl;
+ } else {
+ decl = child->getCanonicalDecl();
+ }
if (!IsFromCurrentTarget(decl)) continue;
if (const auto* linkage_spec_decl =
@@ -428,9 +434,13 @@
llvm::dyn_cast<clang::LinkageSpecDecl>(decl)) {
ImportDeclsFromDeclContext(linkage_spec_decl);
} else {
- // TODO(rosica): We don't always want the canonical decl here (especially
- // not in namespaces).
- GetDeclItem(decl->getCanonicalDecl());
+ clang::Decl* decl_to_import;
+ if (auto namespace_decl = llvm::dyn_cast<clang::NamespaceDecl>(decl)) {
+ decl_to_import = namespace_decl;
+ } else {
+ decl_to_import = decl->getCanonicalDecl();
+ }
+ GetDeclItem(decl_to_import);
}
}
}
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index 335a0f3..4b003f6 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -67,7 +67,32 @@
}
}
}
- Ok(IR { flat_ir, item_id_to_item_idx, lifetimes })
+ let mut namespace_id_to_number_of_reopened_namespaces = HashMap::new();
+ let mut reopened_namespace_id_to_idx = HashMap::new();
+
+ flat_ir
+ .items
+ .iter()
+ .filter_map(|item| match item {
+ Item::Namespace(ns) if ns.owning_target == flat_ir.current_target => {
+ Some((ns.canonical_namespace_id, ns.id))
+ }
+ _ => None,
+ })
+ .for_each(|(canonical_id, id)| {
+ let current_count =
+ *namespace_id_to_number_of_reopened_namespaces.entry(canonical_id).or_insert(0);
+ reopened_namespace_id_to_idx.insert(id, current_count);
+ namespace_id_to_number_of_reopened_namespaces.insert(canonical_id, current_count + 1);
+ });
+
+ Ok(IR {
+ flat_ir,
+ item_id_to_item_idx,
+ lifetimes,
+ namespace_id_to_number_of_reopened_namespaces,
+ reopened_namespace_id_to_idx,
+ })
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
@@ -595,6 +620,8 @@
// A map from a `decl_id` to an index of an `Item` in the `flat_ir.items` vec.
item_id_to_item_idx: HashMap<ItemId, usize>,
lifetimes: HashMap<LifetimeId, LifetimeName>,
+ namespace_id_to_number_of_reopened_namespaces: HashMap<ItemId, usize>,
+ reopened_namespace_id_to_idx: HashMap<ItemId, usize>,
}
impl IR {
@@ -724,6 +751,26 @@
pub fn get_lifetime(&self, lifetime_id: LifetimeId) -> Option<&LifetimeName> {
self.lifetimes.get(&lifetime_id)
}
+
+ pub fn get_reopened_namespace_idx(&self, id: ItemId) -> Result<usize> {
+ Ok(*self.reopened_namespace_id_to_idx.get(&id).with_context(|| {
+ format!("Could not find the reopened namespace index for namespace {:?}.", id)
+ })?)
+ }
+
+ pub fn is_last_reopened_namespace(&self, id: ItemId, canonical_id: ItemId) -> Result<bool> {
+ let idx = self.get_reopened_namespace_idx(id)?;
+ let last_item_idx = self
+ .namespace_id_to_number_of_reopened_namespaces
+ .get(&canonical_id)
+ .with_context(|| {
+ format!(
+ "Could not find number of reopened namespaces for namespace {:?}.",
+ canonical_id
+ )
+ })? - 1;
+ Ok(idx == last_item_idx)
+ }
}
#[cfg(test)]
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index e800748..9bf2a2c 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -1685,7 +1685,9 @@
assert_ir_matches!(ir, quote! { TypeAlias { identifier: "MyStruct" ... } });
}
-#[test]
+// TODO(b/214901011): This only worked because we didn't generate bindings for
+// the second reopened namespace.
+// #[test]
fn test_ignore_struct_typedef_from_decl_context_redecl() {
let ir = ir_from_cc(
r#"
@@ -1733,7 +1735,9 @@
assert_ir_matches!(ir, quote! { TypeAlias { identifier: "MyUnion" ... } });
}
-#[test]
+// TODO(b/214901011): This only worked because we didn't generate bindings for
+// the second reopened namespace.
+// #[test]
fn test_ignore_union_typedef_from_decl_context_redecl() {
let ir = ir_from_cc(
r#"
@@ -2847,14 +2851,118 @@
);
let namespaces = ir.namespaces().collect_vec();
- assert_eq!(namespaces.len(), 1);
+ assert_eq!(namespaces.len(), 2);
assert_eq!(namespaces[0].id, namespaces[0].canonical_namespace_id);
- // TODO(rosica): We actually need to have 2 namespaces here, but
- // we currently only generate IR for the canonical decls.
- // Enable the commented out assertion once we generate IR for all
- // namespace segments.
- // assert_eq!(namespaces[0].canonical_namespace_id,
- // namespaces[1].canonical_namespace_id);
+ assert_eq!(namespaces[0].canonical_namespace_id, namespaces[1].canonical_namespace_id);
+}
+
+#[test]
+fn test_reopened_namespaces() {
+ let ir = ir_from_cc(
+ r#"
+ namespace test_namespace_bindings {
+ namespace inner {}
+ }
+
+ namespace test_namespace_bindings {
+ namespace inner {}
+ }"#,
+ )
+ .unwrap();
+
+ assert_ir_matches!(
+ ir,
+ quote! {
+ ...
+ Namespace(Namespace {
+ name: "test_namespace_bindings" ...
+ })
+ ...
+ Namespace(Namespace {
+ name: "inner" ...
+ })
+ ...
+ Namespace(Namespace {
+ name: "test_namespace_bindings" ...
+ })
+ ...
+ Namespace(Namespace {
+ name: "inner" ...
+ })
+ ...
+ }
+ );
+}
+
+#[test]
+fn test_namespace_stored_data_in_ir() {
+ let ir = ir_from_cc(
+ r#"
+ namespace test_namespace_bindings {
+ namespace inner {}
+ }
+ namespace test_namespace_bindings {
+ namespace inner {}
+ namespace inner {}
+ }"#,
+ )
+ .unwrap();
+
+ let outer_namespaces =
+ ir.namespaces().filter(|ns| ns.name == ir_id("test_namespace_bindings")).collect_vec();
+ assert_eq!(outer_namespaces.len(), 2);
+
+ assert_eq!(ir.get_reopened_namespace_idx(outer_namespaces[0].id).unwrap(), 0);
+ assert_eq!(ir.get_reopened_namespace_idx(outer_namespaces[1].id).unwrap(), 1);
+
+ assert_eq!(
+ ir.is_last_reopened_namespace(
+ outer_namespaces[0].id,
+ outer_namespaces[0].canonical_namespace_id
+ )
+ .unwrap(),
+ false
+ );
+ assert_eq!(
+ ir.is_last_reopened_namespace(
+ outer_namespaces[1].id,
+ outer_namespaces[1].canonical_namespace_id
+ )
+ .unwrap(),
+ true
+ );
+
+ let inner_namespaces = ir.namespaces().filter(|ns| ns.name == ir_id("inner")).collect_vec();
+ assert_eq!(inner_namespaces.len(), 3);
+
+ assert_eq!(ir.get_reopened_namespace_idx(inner_namespaces[0].id).unwrap(), 0);
+ assert_eq!(ir.get_reopened_namespace_idx(inner_namespaces[1].id).unwrap(), 1);
+ assert_eq!(ir.get_reopened_namespace_idx(inner_namespaces[2].id).unwrap(), 2);
+
+ assert_eq!(
+ ir.is_last_reopened_namespace(
+ inner_namespaces[0].id,
+ inner_namespaces[0].canonical_namespace_id
+ )
+ .unwrap(),
+ false
+ );
+ assert_eq!(
+ ir.is_last_reopened_namespace(
+ inner_namespaces[1].id,
+ inner_namespaces[1].canonical_namespace_id
+ )
+ .unwrap(),
+ false
+ );
+ assert_eq!(
+ ir.is_last_reopened_namespace(
+ inner_namespaces[2].id,
+ inner_namespaces[2].canonical_namespace_id
+ )
+ .unwrap(),
+ true
+ );
}
#[test]
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 556aa7d..e31f458 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -1487,7 +1487,26 @@
has_record = has_record || generated.has_record;
}
- let name = make_rs_ident(&namespace.name.identifier);
+ let reopened_namespace_idx = ir.get_reopened_namespace_idx(namespace.id)?;
+ let should_skip_index =
+ ir.is_last_reopened_namespace(namespace.id, namespace.canonical_namespace_id)?;
+
+ let name = if should_skip_index {
+ make_rs_ident(&namespace.name.identifier)
+ } else {
+ make_rs_ident(&format!("{}_{}", &namespace.name.identifier, reopened_namespace_idx))
+ };
+
+ let use_stmt_for_previous_namespace = if reopened_namespace_idx == 0 {
+ quote! {}
+ } else {
+ let previous_namespace_ident = make_rs_ident(&format!(
+ "{}_{}",
+ &namespace.name.identifier,
+ reopened_namespace_idx - 1
+ ));
+ quote! { pub use super::#previous_namespace_ident::*; __NEWLINE__ __NEWLINE__ }
+ };
let thunks_tokens = quote! {
#( #thunks )*
@@ -1499,6 +1518,8 @@
let namespace_tokens = quote! {
pub mod #name {
+ #use_stmt_for_previous_namespace
+
#( #items __NEWLINE__ __NEWLINE__ )*
}
};
@@ -5750,4 +5771,40 @@
);
Ok(())
}
+
+ #[test]
+ fn test_reopened_namespaces() -> Result<()> {
+ let rs_api = generate_bindings_tokens(&ir_from_cc(
+ r#"
+ namespace test_namespace_bindings {
+ namespace inner {}
+ } // namespace test_namespace_bindings
+
+ namespace test_namespace_bindings {
+ namespace inner {}
+ } // namespace test_namespace_bindings"#,
+ )?)?
+ .rs_api;
+
+ assert_rs_matches!(
+ rs_api,
+ quote! {
+ ...
+ pub mod test_namespace_bindings_0 {
+ pub mod inner_0 {} ...
+ }
+ ...
+ pub mod test_namespace_bindings {
+ pub use super::test_namespace_bindings_0::*;
+ ...
+ pub mod inner {
+ pub use super::inner_0::*;
+ ...
+ }
+ }
+ ...
+ }
+ );
+ Ok(())
+ }
}
diff --git a/rs_bindings_from_cc/test/golden/namespace_rs_api.rs b/rs_bindings_from_cc/test/golden/namespace_rs_api.rs
index 59267ec..6ff0900 100644
--- a/rs_bindings_from_cc/test/golden/namespace_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/namespace_rs_api.rs
@@ -71,13 +71,13 @@
unsafe { crate::detail::__rust_thunk___Z8identityN23test_namespace_bindings1SE(s) }
}
-pub mod test_namespace_bindings_reopened {
+pub mod test_namespace_bindings_reopened_0 {
#[inline(always)]
pub fn x() {
unsafe { crate::detail::__rust_thunk___ZN32test_namespace_bindings_reopened1xEv() }
}
- pub mod inner {
+ pub mod inner_0 {
#[ctor::recursively_pinned]
#[repr(C)]
pub struct S {
@@ -114,7 +114,29 @@
// namespace test_namespace_bindings_reopened
-// namespace inner
+pub mod test_namespace_bindings_reopened {
+ pub use super::test_namespace_bindings_reopened_0::*;
+
+ #[inline(always)]
+ pub fn y() {
+ unsafe { crate::detail::__rust_thunk___ZN32test_namespace_bindings_reopened1yEv() }
+ }
+
+ pub mod inner {
+ pub use super::inner_0::*;
+
+ #[inline(always)]
+ pub fn z(s: crate::test_namespace_bindings_reopened::inner::S) {
+ unsafe {
+ crate::detail::__rust_thunk___ZN32test_namespace_bindings_reopened5inner1zENS0_1SE(
+ s,
+ )
+ }
+ }
+ }
+
+ // namespace inner
+}
// namespace test_namespace_bindings_reopened
@@ -136,6 +158,12 @@
) -> crate::test_namespace_bindings::S;
#[link_name = "_ZN32test_namespace_bindings_reopened1xEv"]
pub(crate) fn __rust_thunk___ZN32test_namespace_bindings_reopened1xEv();
+ #[link_name = "_ZN32test_namespace_bindings_reopened1yEv"]
+ pub(crate) fn __rust_thunk___ZN32test_namespace_bindings_reopened1yEv();
+ #[link_name = "_ZN32test_namespace_bindings_reopened5inner1zENS0_1SE"]
+ pub(crate) fn __rust_thunk___ZN32test_namespace_bindings_reopened5inner1zENS0_1SE(
+ s: crate::test_namespace_bindings_reopened::inner::S,
+ );
}
}