crubit: Add support for detecting slices via `crubit_internal_rust_type`
PiperOrigin-RevId: 662827806
Change-Id: Idd599bc6222de56b96700ac5fa3cd6dba6f60d77
diff --git a/rs_bindings_from_cc/generate_bindings/lib.rs b/rs_bindings_from_cc/generate_bindings/lib.rs
index d03d556..5b6a21c 100644
--- a/rs_bindings_from_cc/generate_bindings/lib.rs
+++ b/rs_bindings_from_cc/generate_bindings/lib.rs
@@ -543,8 +543,7 @@
.into()
}
Item::TypeMapOverride(type_override) => {
- // (This shouldn't fail, since we replace with known Rust types via a string.)
- let rs_type = RsTypeKind::new_type_map_override(type_override);
+ let rs_type = RsTypeKind::new_type_map_override(db, type_override)?;
let disable_comment = format!(
"Type bindings for {cpp_type} suppressed due to being mapped to \
an existing Rust type ({rs_type})",
@@ -1148,7 +1147,7 @@
Item::Enum(enum_) => RsTypeKind::new_enum(enum_.clone(), &ir)?,
Item::TypeAlias(type_alias) => new_type_alias(db, type_alias.clone())?,
Item::TypeMapOverride(type_map_override) => {
- RsTypeKind::new_type_map_override(type_map_override)
+ RsTypeKind::new_type_map_override(db, type_map_override)?
}
other_item => bail!("Item does not define a type: {other_item:?}"),
}
diff --git a/rs_bindings_from_cc/generate_bindings/rs_snippet.rs b/rs_bindings_from_cc/generate_bindings/rs_snippet.rs
index c1eb70b..3f5ad7a 100644
--- a/rs_bindings_from_cc/generate_bindings/rs_snippet.rs
+++ b/rs_bindings_from_cc/generate_bindings/rs_snippet.rs
@@ -4,6 +4,7 @@
#![allow(clippy::collapsible_else_if)]
//! Vocabulary types and code generation functions for generating Rust code.
+use crate::BindingsGenerator;
use arc_anyhow::Result;
use code_gen_utils::make_rs_ident;
use code_gen_utils::NamespaceQualifier;
@@ -16,6 +17,8 @@
use std::rc::Rc;
use token_stream_printer::write_unformatted_tokens;
+const SLICE_REF_NAME_RS: &str = "&[]";
+
/// A struct with information associated with the formatted Rust code snippet.
#[derive(Clone, Debug)]
pub struct RsSnippet {
@@ -334,6 +337,7 @@
crate_path: Rc<CratePath>,
},
Primitive(PrimitiveType),
+ Slice(Rc<RsTypeKind>),
/// Nullable T, using the rust Option type.
Option(Rc<RsTypeKind>),
Other {
@@ -362,12 +366,35 @@
Ok(RsTypeKind::Enum { enum_, crate_path })
}
- pub fn new_type_map_override(type_map_override: &TypeMapOverride) -> Self {
- RsTypeKind::Other {
+ pub fn new_type_map_override(
+ db: &dyn BindingsGenerator,
+ type_map_override: &TypeMapOverride,
+ ) -> Result<Self> {
+ if type_map_override.rs_name.as_ref() == SLICE_REF_NAME_RS {
+ if type_map_override.type_parameters.len() != 1 {
+ bail!(
+ "SliceRef has {} type parameters, expected 1",
+ type_map_override.type_parameters.len()
+ );
+ }
+ let mapped_slice_type = type_map_override.type_parameters.first().unwrap();
+ let mapped_slice_type_rs = db.rs_type_kind(mapped_slice_type.rs_type.clone())?;
+
+ return Ok(RsTypeKind::Pointer {
+ pointee: Rc::new(RsTypeKind::Slice(Rc::new(mapped_slice_type_rs))),
+ mutability: if mapped_slice_type.cpp_type.is_const {
+ Mutability::Const
+ } else {
+ Mutability::Mut
+ },
+ });
+ }
+
+ Ok(RsTypeKind::Other {
name: type_map_override.rs_name.clone(),
type_args: Rc::from([]),
is_same_abi: type_map_override.is_same_abi,
- }
+ })
}
/// Returns true if the type is known to be `Unpin`, false otherwise.
@@ -475,6 +502,7 @@
// aliased type, which is also visited by dfs_iter.
RsTypeKind::TypeAlias { .. } => require_feature(CrubitFeature::Supported, None),
RsTypeKind::Primitive { .. } => require_feature(CrubitFeature::Supported, None),
+ RsTypeKind::Slice { .. } => require_feature(CrubitFeature::Supported, None),
RsTypeKind::Option { .. } => require_feature(CrubitFeature::Supported, None),
// Fallback case, we can't really give a good error message here.
RsTypeKind::Other { .. } => require_feature(CrubitFeature::Experimental, None),
@@ -627,6 +655,7 @@
RsTypeKind::Enum { .. } => true,
RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
RsTypeKind::Option(t) => t.implements_copy(),
+ RsTypeKind::Slice(t) => t.implements_copy(),
RsTypeKind::Other { type_args, .. } => {
// All types that may appear here without `type_args` (e.g.
// primitive types like `i32`) implement `Copy`. Generic types
@@ -757,6 +786,10 @@
quote! { #crate_path #ident }
}
}
+ RsTypeKind::Slice(t) => {
+ let type_arg = t.to_token_stream_replacing_by_self(self_record);
+ quote! {[#type_arg]}
+ }
RsTypeKind::Option(t) => {
let type_arg = t.to_token_stream_replacing_by_self(self_record);
// TODO(jeanpierreda): This should likely be `::core::option::Option`.
@@ -847,6 +880,10 @@
quote! { #crate_path #ident }
}
RsTypeKind::Primitive(primitive) => quote! {#primitive},
+ RsTypeKind::Slice(t) => {
+ let type_arg = t.to_token_stream();
+ quote! {[#type_arg]}
+ }
RsTypeKind::Option(t) => {
// TODO(jeanpierreda): This should likely be `::core::option::Option`.
quote! {Option<#t>}
@@ -891,6 +928,7 @@
self.todo.push(return_type);
self.todo.extend(param_types.iter().rev());
}
+ RsTypeKind::Slice(t) => self.todo.push(t),
RsTypeKind::Option(t) => self.todo.push(t),
RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
};
diff --git a/rs_bindings_from_cc/importers/type_map_override.cc b/rs_bindings_from_cc/importers/type_map_override.cc
index 97d4e38..1f26ed6 100644
--- a/rs_bindings_from_cc/importers/type_map_override.cc
+++ b/rs_bindings_from_cc/importers/type_map_override.cc
@@ -7,6 +7,7 @@
#include <optional>
#include <string>
#include <utility>
+#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
@@ -14,15 +15,16 @@
#include "absl/strings/string_view.h"
#include "common/annotation_reader.h"
#include "common/status_macros.h"
+#include "rs_bindings_from_cc/decl_importer.h"
#include "rs_bindings_from_cc/ir.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Type.h"
-#include "clang/Basic/LLVM.h"
-#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
namespace crubit {
namespace {
@@ -55,6 +57,33 @@
"The `crubit_internal_same_abi` attribute takes no arguments.");
return attr != nullptr;
}
+
+// Gathers all instantiated template parameters for `decl` (if any) and converts
+// them to `MappedType`s.
+//
+// `decl` must not be null.
+absl::StatusOr<std::optional<std::vector<MappedType>>> GetTemplateParameters(
+ ImportContext& ictx, const clang::Decl* decl) {
+ const auto* specialization_decl =
+ llvm::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(decl);
+ if (!specialization_decl) {
+ return std::nullopt;
+ }
+
+ std::vector<MappedType> result;
+ for (const auto& arg : specialization_decl->getTemplateArgs().asArray()) {
+ auto mapped_type =
+ ictx.ConvertQualType(arg.getAsType(), /*lifetimes=*/nullptr,
+ /*ref_qualifier_kind=*/std::nullopt,
+ /*nullable=*/false);
+ if (!mapped_type.ok()) return mapped_type.status();
+
+ result.push_back(*mapped_type);
+ }
+
+ return result;
+}
+
} // namespace
std::optional<IR::Item> TypeMapOverrideImporter::Import(
@@ -85,6 +114,14 @@
if (cpp_type == nullptr) return std::nullopt;
std::string cc_name = cc_qualtype.getAsString();
+ absl::StatusOr<std::optional<std::vector<MappedType>>> type_parameters =
+ GetTemplateParameters(ictx_, type_decl);
+ if (!type_parameters.ok()) {
+ return ictx_.ImportUnsupportedItem(
+ type_decl, absl::StrCat("Error fetching template parameters: ",
+ type_parameters.status().message()));
+ }
+
ictx_.MarkAsSuccessfullyImported(type_decl);
std::optional<SizeAlign> size_align;
@@ -97,6 +134,7 @@
return TypeMapOverride{
.rs_name = std::move(rs_name),
.cc_name = std::move(cc_name),
+ .type_parameters = type_parameters->value_or(std::vector<MappedType>()),
.owning_target = ictx_.GetOwningTarget(type_decl),
.size_align = std::move(size_align),
.is_same_abi = *is_same_abi,
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index b566da4..499030c 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -299,6 +299,7 @@
llvm::json::Object override{
{"rs_name", rs_name},
{"cc_name", cc_name},
+ {"type_parameters", type_parameters},
{"owning_target", owning_target},
{"is_same_abi", is_same_abi},
{"id", id},
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index c5b6abb..407fe08 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -779,6 +779,9 @@
std::string rs_name;
std::string cc_name;
+ // The generic/template type parameters to the C++/Rust type.
+ std::vector<MappedType> type_parameters;
+
BazelLabel owning_target;
// Size and alignment, if known.
// (These will not be known for a forward declaration, for example.)
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index df2e1b4..9dab3fd 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -954,6 +954,7 @@
pub struct TypeMapOverride {
pub rs_name: Rc<str>,
pub cc_name: Rc<str>,
+ pub type_parameters: Vec<MappedType>,
pub owning_target: BazelLabel,
pub size_align: Option<SizeAlign>,
pub is_same_abi: bool,
diff --git a/rs_bindings_from_cc/test/types/types_nonptr.h b/rs_bindings_from_cc/test/types/types_nonptr.h
index 92f7206..b5137f5 100644
--- a/rs_bindings_from_cc/test/types/types_nonptr.h
+++ b/rs_bindings_from_cc/test/types/types_nonptr.h
@@ -115,4 +115,39 @@
TEST(TypeMapOverrideEnum, MyI8Enum);
TEST(TypeMapOverrideAlias, MyI8Alias);
+template <typename T>
+struct CRUBIT_INTERNAL_RUST_TYPE("&[]") SliceRef final {
+ size_t size;
+ T* data;
+};
+
+// Test all numerical types...
+TEST(TypeMapOverrideSliceRefConstUint8, SliceRef<const uint8_t>);
+TEST(TypeMapOverrideSliceRefUint8, SliceRef<uint8_t>);
+TEST(TypeMapOverrideSliceRefConstUint16, SliceRef<const uint16_t>);
+TEST(TypeMapOverrideSliceRefUint16, SliceRef<uint16_t>);
+TEST(TypeMapOverrideSliceRefConstUint32, SliceRef<const uint32_t>);
+TEST(TypeMapOverrideSliceRefUint32, SliceRef<uint32_t>);
+TEST(TypeMapOverrideSliceRefConstUint64, SliceRef<const uint64_t>);
+TEST(TypeMapOverrideSliceRefUint64, SliceRef<uint64_t>);
+
+TEST(TypeMapOverrideSliceRefConstInt8, SliceRef<const int8_t>);
+TEST(TypeMapOverrideSliceRefInt8, SliceRef<int8_t>);
+TEST(TypeMapOverrideSliceRefConstInt16, SliceRef<const int16_t>);
+TEST(TypeMapOverrideSliceRefInt16, SliceRef<int16_t>);
+TEST(TypeMapOverrideSliceRefConstInt32, SliceRef<const int32_t>);
+TEST(TypeMapOverrideSliceRefInt32, SliceRef<int32_t>);
+TEST(TypeMapOverrideSliceRefConstInt64, SliceRef<const int64_t>);
+TEST(TypeMapOverrideSliceRefInt64, SliceRef<int64_t>);
+
+TEST(TypeMapOverrideSliceRefConstFloat, SliceRef<const float>);
+TEST(TypeMapOverrideSliceRefFloat, SliceRef<float>);
+TEST(TypeMapOverrideSliceRefConstDouble, SliceRef<const double>);
+TEST(TypeMapOverrideSliceRefDouble, SliceRef<double>);
+
+// ... and arbitrary structs/enums.
+TEST(TypeMapOverrideSliceRefArbitraryStruct, SliceRef<ns::ExampleStruct>);
+TEST(TypeMapOverrideSliceRefArbitraryEnum, SliceRef<const ns::ExampleEnum>);
+TEST(TypeMapOverrideSliceRefArbitraryAliasEnum, SliceRef<AliasEnum>);
+
#endif // CRUBIT_RS_BINDINGS_FROM_CC_TEST_TYPES_TYPES_NONPTR_H_
diff --git a/rs_bindings_from_cc/test/types/types_test.rs b/rs_bindings_from_cc/test/types/types_test.rs
index 083e9e5..5fe335f 100644
--- a/rs_bindings_from_cc/test/types/types_test.rs
+++ b/rs_bindings_from_cc/test/types/types_test.rs
@@ -123,6 +123,33 @@
TypeMapOverrideClass => i8,
TypeMapOverrideEnum => i8,
TypeMapOverrideAlias => i8,
+
+ TypeMapOverrideSliceRefConstUint8 => *const [u8],
+ TypeMapOverrideSliceRefUint8 => *mut [u8],
+ TypeMapOverrideSliceRefConstUint16 => *const [u16],
+ TypeMapOverrideSliceRefUint16 => *mut [u16],
+ TypeMapOverrideSliceRefConstUint32 => *const [u32],
+ TypeMapOverrideSliceRefUint32 => *mut [u32],
+ TypeMapOverrideSliceRefConstUint64 => *const [u64],
+ TypeMapOverrideSliceRefUint64 => *mut [u64],
+
+ TypeMapOverrideSliceRefConstInt8 => *const [i8],
+ TypeMapOverrideSliceRefInt8 => *mut [i8],
+ TypeMapOverrideSliceRefConstInt16 => *const [i16],
+ TypeMapOverrideSliceRefInt16 => *mut [i16],
+ TypeMapOverrideSliceRefConstInt32 => *const [i32],
+ TypeMapOverrideSliceRefInt32 => *mut [i32],
+ TypeMapOverrideSliceRefConstInt64 => *const [i64],
+ TypeMapOverrideSliceRefInt64 => *mut [i64],
+
+ TypeMapOverrideSliceRefConstFloat => *const [f32],
+ TypeMapOverrideSliceRefFloat => *mut [f32],
+ TypeMapOverrideSliceRefConstDouble => *const [f64],
+ TypeMapOverrideSliceRefDouble => *mut [f64],
+
+ TypeMapOverrideSliceRefArbitraryStruct => *mut [types_nonptr::ns::ExampleStruct],
+ TypeMapOverrideSliceRefArbitraryEnum => *const [types_nonptr::ns::ExampleEnum],
+ TypeMapOverrideSliceRefArbitraryAliasEnum => *mut [types_nonptr::AliasEnum],
);
// TODO(b/228569417): These should all generate bindings and be & (mut) 'static.