Support `use` glob.
PiperOrigin-RevId: 670742832
Change-Id: Idd540ae60707b88dd912c87e8000a4b97f2e6191
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 6a38375..aecb12e 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -30,7 +30,7 @@
use rustc_middle::mir::ConstValue;
use rustc_middle::mir::Mutability;
use rustc_middle::ty::{self, Ty, TyCtxt}; // See <internal link>/ty.html#import-conventions
-use rustc_span::def_id::{DefId, LocalDefId, LOCAL_CRATE};
+use rustc_span::def_id::{DefId, LocalDefId, LocalModDefId, LOCAL_CRATE};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::abi::{
Abi, AddressSpace, FieldsShape, Integer, Layout, Pointer, Primitive, Scalar,
@@ -1336,44 +1336,13 @@
None
}
-fn format_use(
+fn generate_using_statement(
db: &dyn BindingsGenerator<'_>,
using_name: &str,
- use_path: &UsePath,
- use_kind: &UseKind,
+ def_id: DefId,
+ def_kind: DefKind,
) -> Result<ApiSnippets> {
let tcx = db.tcx();
-
- // TODO(b/350772554): Support multiple items with the same name in `use`
- // statements.`
- if use_path.res.len() != 1 {
- bail!(
- "use statements which resolve to multiple items with the same name are not supported yet"
- );
- }
-
- match use_kind {
- UseKind::Single | UseKind::ListStem => {}
- // TODO(b/350772554): Implement `pub use foo::*`
- UseKind::Glob => {
- bail!("Unsupported use kind: {use_kind:?}");
- }
- };
- let (def_kind, def_id) = match use_path.res[0] {
- // TODO(b/350772554): Support PrimTy.
- Res::Def(def_kind, def_id) => (def_kind, def_id),
- _ => {
- bail!(
- "Unsupported use statement that refers to this type of the entity: {:#?}",
- use_path.res[0]
- );
- }
- };
- ensure!(
- is_directly_public(tcx, def_id),
- "Not directly public type (re-exports are not supported yet - b/262052635)"
- );
-
match def_kind {
DefKind::Fn => {
let mut prereqs;
@@ -1416,13 +1385,96 @@
let use_type = SugaredTy::new(tcx.type_of(def_id).instantiate_identity(), None);
create_type_alias(db, def_id, using_name, use_type)
}
- _ => bail!(
- "Unsupported use statement that refers to this type of the entity: {:#?}",
- use_path.res
- ),
+ _ => {
+ bail!("Unsupported use statement that refers to this type of the entity: {:#?}", def_id)
+ }
}
}
+fn public_free_items_in_mod(
+ db: &dyn BindingsGenerator<'_>,
+ def_id: DefId,
+) -> Vec<(DefId, DefKind)> {
+ let mut items = vec![];
+ if def_id.as_local().is_none() {
+ return items;
+ }
+ let tcx = db.tcx();
+ let local_mod_def_id = LocalModDefId::new_unchecked(def_id.as_local().unwrap());
+ for item_id in tcx.hir_module_items(local_mod_def_id).free_items() {
+ let item_local_def_id: LocalDefId = item_id.owner_id.def_id;
+ let item_def_id = item_local_def_id.to_def_id();
+ let item_def_kind = tcx.def_kind(item_def_id);
+
+ if !is_directly_public(tcx, item_def_id) {
+ continue;
+ }
+ match item_def_kind {
+ DefKind::Fn | DefKind::Struct | DefKind::Enum => {
+ items.push((item_def_id, item_def_kind));
+ }
+ _ => {}
+ }
+ }
+ items
+}
+
+fn format_use(
+ db: &dyn BindingsGenerator<'_>,
+ using_name: &str,
+ use_path: &UsePath,
+ use_kind: &UseKind,
+) -> Result<ApiSnippets> {
+ let tcx = db.tcx();
+
+ // TODO(b/350772554): Support multiple items with the same name in `use`
+ // statements.`
+ if use_path.res.len() != 1 {
+ bail!(
+ "use statements which resolve to multiple items with the same name are not supported yet"
+ );
+ }
+
+ let (def_kind, def_id) = match use_path.res[0] {
+ // TODO(b/350772554): Support PrimTy.
+ // TODO(b/350772554): Support `use some_module`.
+ Res::Def(def_kind, def_id) if def_kind != DefKind::Mod || use_kind == &UseKind::Glob => {
+ (def_kind, def_id)
+ }
+ _ => {
+ bail!(
+ "Unsupported use statement that refers to this type of the entity: {:#?}",
+ use_path.res[0]
+ );
+ }
+ };
+ ensure!(
+ is_directly_public(tcx, def_id),
+ "Not directly public type (re-exports are not supported yet - b/262052635)"
+ );
+
+ let mut aliases = vec![];
+ if def_kind == DefKind::Mod {
+ for (item_def_id, item_def_kind) in public_free_items_in_mod(db, def_id) {
+ let item_using_name = format_cc_ident(tcx.item_name(item_def_id).as_str())
+ .context("Error formatting using name")?;
+ aliases.push((item_using_name.to_string(), item_def_id, item_def_kind));
+ }
+ } else {
+ aliases.push((using_name.to_string(), def_id, def_kind));
+ }
+ // TODO(b/350772554): Expose the errors. If any of the types in the `use`
+ // statement is not supported, we currently ignore it and discard the
+ // errors.
+ Ok(aliases
+ .into_iter()
+ .map(|(using_name, def_id, def_kind)| {
+ generate_using_statement(db, &using_name, def_id, def_kind)
+ })
+ .filter_map(Result::ok)
+ .collect())
+}
+
fn format_const(db: &dyn BindingsGenerator<'_>, local_def_id: LocalDefId) -> Result<ApiSnippets> {
let tcx = db.tcx();
let def_id: DefId = local_def_id.to_def_id();
@@ -7567,6 +7619,33 @@
}
#[test]
+ fn test_generate_bindings_use_glob() {
+ let test_src = r#"
+ pub mod test_mod {
+ pub struct X{
+ pub field: i32
+ }
+ pub struct Y{
+ pub field: i32
+ }
+ }
+
+ pub use test_mod::*;
+ "#;
+
+ test_generated_bindings(test_src, |bindings| {
+ let bindings = bindings.unwrap();
+ assert_cc_matches!(
+ bindings.h_body,
+ quote! {
+ using X = ::rust_out::test_mod::X;
+ using Y = ::rust_out::test_mod::Y;
+ }
+ );
+ });
+ }
+
+ #[test]
fn test_format_item_type_alias() {
let test_src = r#"
pub type TypeAlias = i32;
diff --git a/cc_bindings_from_rs/cc_bindings_from_rs.rs b/cc_bindings_from_rs/cc_bindings_from_rs.rs
index 82598d6..dc228d0 100644
--- a/cc_bindings_from_rs/cc_bindings_from_rs.rs
+++ b/cc_bindings_from_rs/cc_bindings_from_rs.rs
@@ -347,7 +347,7 @@
let expected_error_report = r#"{
"Unsupported use statement that refers to this type of the entity: {:#?}": {
"count": 2,
- "sample_message": "Unsupported use statement that refers to this type of the entity: [\n Def(\n Mod,\n DefId(std[46ff]::collections),\n ),\n]"
+ "sample_message": "Unsupported use statement that refers to this type of the entity: Def(\n Mod,\n DefId(std[46ff]::collections),\n)"
}
}"#;
assert_eq!(expected_error_report, error_report);
diff --git a/cc_bindings_from_rs/test/golden/uses.rs b/cc_bindings_from_rs/test/golden/uses.rs
new file mode 100644
index 0000000..2920f66
--- /dev/null
+++ b/cc_bindings_from_rs/test/golden/uses.rs
@@ -0,0 +1,29 @@
+// 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
+
+#![allow(dead_code)]
+
+pub mod test_use_glob {
+ pub fn f1() -> i32 {
+ 42
+ }
+
+ pub fn f2() -> i32 {
+ 43
+ }
+
+ fn f3() -> i32 {
+ 44
+ }
+
+ pub struct X1 {
+ x: i32,
+ }
+
+ struct X2 {
+ x: i32,
+ }
+}
+
+pub use test_use_glob::*;
diff --git a/cc_bindings_from_rs/test/golden/uses_cc_api.h b/cc_bindings_from_rs/test/golden/uses_cc_api.h
new file mode 100644
index 0000000..450e4b5
--- /dev/null
+++ b/cc_bindings_from_rs/test/golden/uses_cc_api.h
@@ -0,0 +1,88 @@
+// 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
+
+// Automatically @generated C++ bindings for the following Rust crate:
+// uses_rust
+
+#pragma once
+
+#include "support/internal/attribute_macros.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+namespace uses_rust {
+
+namespace test_use_glob {
+
+// Generated from:
+// cc_bindings_from_rs/test/golden/uses.rs;l=8
+std::int32_t f1();
+
+// Generated from:
+// cc_bindings_from_rs/test/golden/uses.rs;l=12
+std::int32_t f2();
+
+// Generated from:
+// cc_bindings_from_rs/test/golden/uses.rs;l=20
+struct CRUBIT_INTERNAL_RUST_TYPE(":: uses_rust :: test_use_glob :: X1") alignas(
+ 4) [[clang::trivial_abi]] X1 final {
+ public:
+ // `test_use_glob::X1` doesn't implement the `Default` trait
+ X1() = delete;
+
+ // No custom `Drop` impl and no custom \"drop glue\" required
+ ~X1() = default;
+ X1(X1&&) = default;
+ X1& operator=(X1&&) = default;
+
+ // `test_use_glob::X1` doesn't implement the `Clone` trait
+ X1(const X1&) = delete;
+ X1& operator=(const X1&) = delete;
+
+ private:
+ union {
+ // Generated from:
+ // cc_bindings_from_rs/test/golden/uses.rs;l=21
+ std::int32_t x;
+ };
+
+ private:
+ static void __crubit_field_offset_assertions();
+};
+
+} // namespace test_use_glob
+
+using ::uses_rust::test_use_glob::f1;
+using ::uses_rust::test_use_glob::f2;
+using X1 = ::uses_rust::test_use_glob::X1;
+
+namespace test_use_glob {
+
+namespace __crubit_internal {
+extern "C" std::int32_t __crubit_thunk_f1();
+}
+inline std::int32_t f1() { return __crubit_internal::__crubit_thunk_f1(); }
+
+namespace __crubit_internal {
+extern "C" std::int32_t __crubit_thunk_f2();
+}
+inline std::int32_t f2() { return __crubit_internal::__crubit_thunk_f2(); }
+
+static_assert(
+ sizeof(X1) == 4,
+ "Verify that ADT layout didn't change since this header got generated");
+static_assert(
+ alignof(X1) == 4,
+ "Verify that ADT layout didn't change since this header got generated");
+static_assert(std::is_trivially_destructible_v<X1>);
+static_assert(std::is_trivially_move_constructible_v<X1>);
+static_assert(std::is_trivially_move_assignable_v<X1>);
+inline void X1::__crubit_field_offset_assertions() {
+ static_assert(0 == offsetof(X1, x));
+}
+} // namespace test_use_glob
+
+} // namespace uses_rust
diff --git a/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs b/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs
new file mode 100644
index 0000000..21f4845
--- /dev/null
+++ b/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs
@@ -0,0 +1,19 @@
+// 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
+
+// Automatically @generated C++ bindings for the following Rust crate:
+// uses_rust
+
+#![allow(improper_ctypes_definitions)]
+
+#[no_mangle]
+extern "C" fn __crubit_thunk_f1() -> i32 {
+ ::uses_rust::test_use_glob::f1()
+}
+#[no_mangle]
+extern "C" fn __crubit_thunk_f2() -> i32 {
+ ::uses_rust::test_use_glob::f2()
+}
+const _: () = assert!(::std::mem::size_of::<::uses_rust::test_use_glob::X1>() == 4);
+const _: () = assert!(::std::mem::align_of::<::uses_rust::test_use_glob::X1>() == 4);