crubit: Add support for slice pointers in cc_bindings_from_rs
PiperOrigin-RevId: 663655996
Change-Id: Id6a97b7fefcfacddf17afaa1bbfcae14afc198a8
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 9423043..117626f 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -408,6 +408,14 @@
// See `rust_builtin_type_abi_assumptions.md` for more details.
ty::TyKind::Char => true,
+ // TODO(b/271016831): When launching `&[T]` (not just `*const T`), consider returning
+ // `true` for `TyKind::Ref` and document the rationale for such decision - maybe
+ // something like this will be sufficient:
+ // - In general `TyKind::Ref` should have the same ABI as `TyKind::RawPtr`
+ // - References to slices (`&[T]`) or strings (`&str`) rely on assumptions
+ // spelled out in `rust_builtin_type_abi_assumptions.md`.
+ ty::TyKind::Slice{..} => false,
+
// Crubit's C++ bindings for tuples, structs, and other ADTs may not preserve
// their ABI (even if they *do* preserve their memory layout). For example:
// - In System V ABI replacing a field with a fixed-length array of bytes may affect
@@ -428,17 +436,8 @@
// These kinds of reference-related types are not implemented yet - `is_c_abi_compatible_by_value`
// should never need to handle them, because `format_ty_for_cc` fails for such types.
- //
- // TODO(b/258235219): When implementing support for references we should
- // consider returning `true` for `TyKind::Ref` and document the rationale
- // for such decision - maybe something like this will be sufficient:
- // - In general `TyKind::Ref` should have the same ABI as `TyKind::RawPtr`
- // - References to slices (`&[T]`) or strings (`&str`) rely on assumptions
- // spelled out in `rust_builtin_type_abi_assumptions.md`..
ty::TyKind::Str |
- ty::TyKind::Array{..} |
- ty::TyKind::Slice{..} =>
- unimplemented!(),
+ ty::TyKind::Array{..} => unimplemented!(),
// `format_ty_for_cc` is expected to fail for other kinds of types
// and therefore `is_c_abi_compatible_by_value` should never be called for
@@ -492,6 +491,32 @@
Ok(CcSnippet { prereqs, tokens: quote! { #tokens #const_qualifier #pointer_sigil } })
}
+fn format_slice_pointer_for_cc<'tcx>(
+ db: &dyn BindingsGenerator<'tcx>,
+ slice_ty: SugaredTy<'tcx>,
+ mutability: rustc_middle::mir::Mutability,
+) -> Result<CcSnippet> {
+ let const_qualifier = match mutability {
+ Mutability::Mut => quote! {},
+ Mutability::Not => quote! { const },
+ };
+
+ let CcSnippet { tokens, mut prereqs } =
+ db.format_ty_for_cc(slice_ty, TypeLocation::Other).with_context(|| {
+ format!("Failed to format the inner type of the slice type `{slice_ty}`")
+ })?;
+ prereqs.includes.insert(db.support_header("rs_std/slice_ref.h"));
+
+ Ok(CcSnippet {
+ prereqs,
+ tokens: quote! {
+ rs_std::SliceRef<
+ #const_qualifier #tokens
+ >
+ },
+ })
+}
+
/// Checks that `ty` has the same ABI as `rs_std::SliceRef`.
fn check_slice_layout<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) {
// Check the assumption from `rust_builtin_type_abi_assumptions.md` that Rust's
@@ -667,8 +692,21 @@
}
ty::TyKind::RawPtr(pointee_mid, mutbl) => {
- if let ty::TyKind::Slice(_) = pointee_mid.kind() {
+ if let ty::TyKind::Slice(slice_ty) = pointee_mid.kind() {
check_slice_layout(db.tcx(), ty.mid());
+ let mut slice_hir_ty = None;
+ if let Some(hir) = ty.hir(db) {
+ if let rustc_hir::TyKind::Ptr(pointee) = &hir.kind {
+ if let rustc_hir::TyKind::Slice(slice_ty) = &pointee.ty.kind {
+ slice_hir_ty = Some(*slice_ty);
+ }
+ }
+ }
+ return format_slice_pointer_for_cc(
+ db,
+ SugaredTy::new(*slice_ty, slice_hir_ty),
+ *mutbl,
+ );
}
let mut pointee_hir = None;
if let Some(hir) = ty.hir(db) {
@@ -907,6 +945,12 @@
let lifetime = format_region_as_rs_lifetime(region);
quote! { & #lifetime #mutability #ty }
}
+ ty::TyKind::Slice(slice_ty) => {
+ let ty = format_ty_for_rs(tcx, *slice_ty).with_context(|| {
+ format!("Failed to format the element type of the slice type `{ty}`")
+ })?;
+ quote! { [#ty] }
+ }
_ => bail!("The following Rust type is not supported yet: {ty}"),
})
}
@@ -4550,6 +4594,29 @@
});
}
+ #[test]
+ fn test_format_item_slice() {
+ let test_src = r#"
+ pub fn foo(_a: *const [u32], _b: *const [u8], _c: *mut [i16], _d: *mut [bool]) { todo!() }
+ "#;
+ test_format_item(test_src, "foo", |result| {
+ let result = result.unwrap().unwrap();
+ let main_api = &result.main_api;
+ assert_cc_matches!(
+ main_api.tokens,
+ quote! {
+ void
+ foo(
+ rs_std::SliceRef<const std::uint32_t> _a,
+ rs_std::SliceRef<const std::uint8_t> _b,
+ rs_std::SliceRef<std::int16_t> _c,
+ rs_std::SliceRef<bool> _d
+ );
+ }
+ );
+ });
+ }
+
/// Test of lifetime-generic function with a `where` clause.
///
/// The `where` constraint below is a bit silly (why not just use `'static`
@@ -7538,6 +7605,29 @@
cc: "std :: int32_t & [[clang :: annotate_type (\"lifetime\" , \"static\")]]",
includes: ["<cstdint>"]
),
+ // Slice pointers:
+ case!(
+ rs: "*const [i8]",
+ cc: "rs_std::SliceRef<const std::int8_t>",
+ includes: ["<cstdint>", "<crubit/support/for/tests/rs_std/slice_ref.h>"]
+ ),
+ case!(
+ rs: "*mut [i64]",
+ cc: "rs_std::SliceRef<std::int64_t>",
+ includes: ["<cstdint>", "<crubit/support/for/tests/rs_std/slice_ref.h>"]
+ ),
+ case!(
+ rs: "*const [c_char]",
+ cc: "rs_std::SliceRef<const char>",
+ includes: ["<crubit/support/for/tests/rs_std/slice_ref.h>"]
+ ),
+ case!(
+ rs: "*mut [SomeStruct]",
+ cc: "rs_std::SliceRef< ::rust_out::SomeStruct>",
+ includes: [ "<crubit/support/for/tests/rs_std/slice_ref.h>"],
+ prereq_def: "SomeStruct"
+
+ ),
// `SomeStruct` is a `fwd_decls` prerequisite (not `defs` prerequisite):
case!(
rs: "*mut SomeStruct",
@@ -7878,6 +7968,8 @@
// Pointer to an ADT:
("*mut SomeStruct", "* mut :: rust_out :: SomeStruct"),
("extern \"C\" fn(i32) -> i32", "extern \"C\" fn(i32) -> i32"),
+ // Pointer to a Slice:
+ ("*mut [i32]", "*mut [i32]"),
];
let preamble = quote! {
#![feature(never_type)]
@@ -7917,11 +8009,6 @@
"The following Rust type is not supported yet: [i32; 42]",
),
(
- "&'static [i32]", // TyKind::Slice (nested underneath TyKind::Ref)
- "Failed to format the referent of the reference type `&'static [i32]`: \
- The following Rust type is not supported yet: [i32]",
- ),
- (
"&'static str", // TyKind::Str (nested underneath TyKind::Ref)
"Failed to format the referent of the reference type `&'static str`: \
The following Rust type is not supported yet: str",
diff --git a/docs/overview/rust_builtin_type_abi_assumptions.md b/docs/overview/rust_builtin_type_abi_assumptions.md
index a6c94d6..ca3a67b 100644
--- a/docs/overview/rust_builtin_type_abi_assumptions.md
+++ b/docs/overview/rust_builtin_type_abi_assumptions.md
@@ -39,13 +39,14 @@
if they do, then hopefully `rs_char` can just be tweaked to wrap another of the
C++ integer types.
-## Rust built-in `&[T]` slice reference type
+## Rust built-in `[T]` slice type
-In the *future* `extern “C”` thunks generated in `..._cc_api_impl.rs` may take
-`&[i32]` and similar arguments (or return them).
+`extern “C”` thunks generated in `..._cc_api_impl.rs` can take `*const [i32]`
+and similar arguments (or return them). At the moment references to slices (like
+`&[u8]`) are not supported.
[Rust documentation describes](https://rust-lang.github.io/unsafe-code-guidelines/layout/arrays-and-slices.html)
-the layout of arrays and slices and
+the layout of references and pointers to arrays and slices and
[also documents](https://doc.rust-lang.org/std/primitive.slice.html) that slice
references are “represented as a pointer and a length”.