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.