Use the dedicated TypeMapOverride item as the decl_id for MappedType.
This partially undoes the original CL that even added type map overrides, but I think it's for good cause. Now that we _have_ a decl, it makes sense to hang the type off of it in general. And in specific, this gives us a place to configure the mapped type. A followup CL will allow marking these as not `c_abi_compatible`, so that we don't get UB from round-tripping structs with blob fields.
However, this is not to say it's necessarily a clean CL. Note that we now need to _explicitly_ decide what to do with methods. Before this CL, they'd all get suppressed because their Self type wasn't present. Now that we generate an item ID for the self type, but it isn't a Record, this breaks how methods work a bit.
Actually, methods are mostly not visited (since we don't recursively visit the members of the record), but there's at least one circumstance where they are which I don't fully understand (it doesn't show up in the final product either way) - at any rate, the most flexible/reasonable thing to do is gracefully handle the incorrectly typed `this`.
(Rather than try anything fancy, I suppress them by replacing a panic with a result.)
This also ended up requiring a predecessor CL to get working with our existing tests.
BTW, as a final note, note that I straight up serialize the whole C++ type name as a string, the way we do for MappedType, but not the way we do for records etc. I wonder if that's wise? It made the logic a lot simpler!
PiperOrigin-RevId: 535736013
diff --git a/rs_bindings_from_cc/BUILD b/rs_bindings_from_cc/BUILD
index ba2cc96..65cdd99 100644
--- a/rs_bindings_from_cc/BUILD
+++ b/rs_bindings_from_cc/BUILD
@@ -165,7 +165,6 @@
":cc_ir",
"//common:status_macros",
"@absl//absl/container:flat_hash_map",
- "@absl//absl/status",
"@absl//absl/status:statusor",
"@absl//absl/strings",
"@llvm-project//clang:ast",
@@ -302,6 +301,7 @@
name = "cc_ir",
srcs = ["ir.cc"],
hdrs = ["ir.h"],
+ visibility = ["//rs_bindings_from_cc:__subpackages__"],
deps = [
":bazel_types",
"//common:strong_int",
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 4c6fa23..df5004a 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -763,8 +763,8 @@
// Qualifiers are handled separately in ConvertQualType().
std::string type_string = clang::QualType(type, 0).getAsString();
- CRUBIT_ASSIGN_OR_RETURN(auto override_type, GetTypeMapOverride(*type));
- if (override_type.has_value()) {
+ if (auto override_type = GetTypeMapOverride(*type);
+ override_type.has_value()) {
return *std::move(override_type);
} else if (type->isPointerType() || type->isLValueReferenceType() ||
type->isRValueReferenceType()) {
diff --git a/rs_bindings_from_cc/importers/BUILD b/rs_bindings_from_cc/importers/BUILD
index 61b8035..3e1b962 100644
--- a/rs_bindings_from_cc/importers/BUILD
+++ b/rs_bindings_from_cc/importers/BUILD
@@ -108,10 +108,12 @@
srcs = ["type_map_override.cc"],
hdrs = ["type_map_override.h"],
deps = [
+ "@absl//absl/status",
+ "//common:status_macros",
+ "//rs_bindings_from_cc:cc_ir",
"//rs_bindings_from_cc:decl_importer",
"//rs_bindings_from_cc:type_map",
"@llvm-project//clang:ast",
- "@llvm-project//llvm:Support",
],
)
diff --git a/rs_bindings_from_cc/importers/type_map_override.cc b/rs_bindings_from_cc/importers/type_map_override.cc
index 0c80f3c..f44eb29 100644
--- a/rs_bindings_from_cc/importers/type_map_override.cc
+++ b/rs_bindings_from_cc/importers/type_map_override.cc
@@ -5,37 +5,122 @@
#include "rs_bindings_from_cc/importers/type_map_override.h"
#include <optional>
+#include <string>
#include <utility>
-#include "rs_bindings_from_cc/type_map.h"
+#include "absl/status/status.h"
+#include "common/status_macros.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/Type.h"
-#include "llvm/Support/raw_ostream.h"
namespace crubit {
+namespace {
+
+// Copied from lifetime_annotations/type_lifetimes.cc, which is expected to move
+// into ClangTidy. See:
+// https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377
+absl::StatusOr<absl::string_view> EvaluateAsStringLiteral(
+ const clang::Expr& expr, const clang::ASTContext& ast_context) {
+ auto error = []() {
+ return absl::InvalidArgumentError(
+ "cannot evaluate argument as a string literal");
+ };
+
+ clang::Expr::EvalResult eval_result;
+ if (!expr.EvaluateAsConstantExpr(eval_result, ast_context) ||
+ !eval_result.Val.isLValue()) {
+ return error();
+ }
+
+ const auto* eval_result_expr =
+ eval_result.Val.getLValueBase().dyn_cast<const clang::Expr*>();
+ if (!eval_result_expr) {
+ return error();
+ }
+
+ const auto* string_literal =
+ clang::dyn_cast<clang::StringLiteral>(eval_result_expr);
+ if (!string_literal) {
+ return error();
+ }
+
+ return {string_literal->getString()};
+}
+
+absl::StatusOr<std::optional<absl::string_view>> GetRustTypeAttribute(
+ const clang::Type& cc_type) {
+ std::optional<absl::string_view> rust_type;
+ const clang::Decl* decl = nullptr;
+ if (const auto* alias_type = cc_type.getAs<clang::TypedefType>();
+ alias_type != nullptr) {
+ decl = alias_type->getDecl();
+ } else if (const clang::TagDecl* tag_decl = cc_type.getAsTagDecl();
+ tag_decl != nullptr) {
+ decl = tag_decl;
+ }
+ if (decl != nullptr) {
+ for (clang::AnnotateAttr* attr :
+ decl->specific_attrs<clang::AnnotateAttr>()) {
+ if (attr->getAnnotation() != "crubit_internal_rust_type") continue;
+
+ if (rust_type.has_value())
+ return absl::InvalidArgumentError(
+ "Only one `crubit_internal_rust_type` attribute may be placed on a "
+ "type.");
+ if (attr->args_size() != 1)
+ return absl::InvalidArgumentError(
+ "The `crubit_internal_rust_type` attribute requires a single "
+ "string literal "
+ "argument, the Rust type.");
+ const clang::Expr& arg = **attr->args_begin();
+ CRUBIT_ASSIGN_OR_RETURN(
+ rust_type, EvaluateAsStringLiteral(arg, decl->getASTContext()));
+ }
+ }
+ return rust_type;
+}
+} // namespace
std::optional<IR::Item> TypeMapOverrideImporter::Import(
clang::TypeDecl* type_decl) {
clang::ASTContext& context = type_decl->getASTContext();
- const clang::Type* type = context.getTypeDeclType(type_decl).getTypePtr();
- if (type == nullptr) return std::nullopt;
- if (auto override_type = GetTypeMapOverride(*type);
- override_type.ok() && override_type->has_value()) {
- std::optional<SizeAlign> size_align;
- if (!type->isIncompleteType()) {
- size_align = SizeAlign{
- .size = context.getTypeSizeInChars(type).getQuantity(),
- .alignment = context.getTypeAlignInChars(type).getQuantity(),
- };
- }
- return TypeMapOverride{
- .type = **std::move(override_type),
- .owning_target = ictx_.GetOwningTarget(type_decl),
- .size_align = std::move(size_align),
- .id = GenerateItemId(type_decl),
+ clang::QualType cc_qualtype = context.getTypeDeclType(type_decl);
+ const clang::Type* cc_type = cc_qualtype.getTypePtr();
+ if (cc_type == nullptr) return std::nullopt;
+
+ absl::StatusOr<std::optional<absl::string_view>> rust_type =
+ GetRustTypeAttribute(*cc_type);
+ if (!rust_type.ok()) {
+ return ictx_.ImportUnsupportedItem(
+ type_decl, absl::StrCat("Invalid crubit_internal_rust_type attribute: ",
+ rust_type.status().message()));
+ }
+ if (!rust_type->has_value()) {
+ return std::nullopt;
+ }
+ auto rs_name = std::string(**rust_type);
+ std::string cc_name = cc_qualtype.getAsString();
+
+ ictx_.MarkAsSuccessfullyImported(type_decl);
+
+ std::optional<SizeAlign> size_align;
+ if (!cc_type->isIncompleteType()) {
+ size_align = SizeAlign{
+ .size = context.getTypeSizeInChars(cc_type).getQuantity(),
+ .alignment = context.getTypeAlignInChars(cc_type).getQuantity(),
};
}
- return std::nullopt;
+ return TypeMapOverride{
+ .rs_name = std::move(rs_name),
+ .cc_name = std::move(cc_name),
+ .owning_target = ictx_.GetOwningTarget(type_decl),
+ .size_align = std::move(size_align),
+ .id = GenerateItemId(type_decl),
+ };
}
} // namespace crubit
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index 90ebdf3..8b3632d 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -296,7 +296,8 @@
llvm::json::Value TypeMapOverride::ToJson() const {
llvm::json::Object override{
- {"type", type},
+ {"rs_name", rs_name},
+ {"cc_name", cc_name},
{"owning_target", owning_target},
{"id", id},
};
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index a24c3b7..4ef02e0 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -753,7 +753,9 @@
struct TypeMapOverride {
llvm::json::Value ToJson() const;
- MappedType type;
+ std::string rs_name;
+ std::string cc_name;
+
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 083b036..a079aaf 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -801,8 +801,8 @@
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TypeMapOverride {
- #[serde(rename(deserialize = "type"))]
- pub type_: MappedType,
+ pub rs_name: Rc<str>,
+ pub cc_name: Rc<str>,
pub owning_target: BazelLabel,
pub size_align: Option<SizeAlign>,
pub id: ItemId,
@@ -813,7 +813,7 @@
self.id
}
fn debug_name(&self, _: &IR) -> Rc<str> {
- self.type_.cc_type.name.clone().unwrap_or_else(|| Rc::from("<anonymous C++ type>"))
+ self.cc_name.clone()
}
fn source_loc(&self) -> Option<Rc<str>> {
None
@@ -1243,20 +1243,15 @@
Ok(idx == last_item_idx)
}
- /// Returns the `Record` defining `func`, or `None` if `func` is not a
+ /// Returns the `Item` defining `func`, or `None` if `func` is not a
/// member function.
///
- /// Panics if `Func` is a member function, but its `Record` is somehow not
- /// in `self`.
- pub fn record_for_member_func(&self, func: &Func) -> Option<&Rc<Record>> {
+ /// Note that even if `func` is a member function, the associated record
+ /// might not be a Record IR Item (e.g. it has its type changed via
+ /// crubit_internal_rust_type).
+ pub fn record_for_member_func(&self, func: &Func) -> Option<&Item> {
if let Some(meta) = func.member_func_metadata.as_ref() {
- Some(
- self.find_decl(meta.record_id)
- .with_context(|| {
- format!("Failed to retrieve Record for MemberFuncMetadata of {:?}", func)
- })
- .unwrap(),
- )
+ Some(self.find_untyped_decl(meta.record_id))
} else {
None
}
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index 6320e14..706ee7e 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -11,6 +11,7 @@
use quote::quote;
use std::collections::{HashMap, HashSet};
use std::iter::Iterator;
+use std::rc::Rc;
fn ir_from_cc(header: &str) -> Result<IR> {
ir_testing::ir_from_cc(multiplatform_testing::test_platform(), header)
@@ -3050,9 +3051,11 @@
.functions()
.filter(|f| {
// Only SomeStruct member functions (excluding stddef.h stuff).
- ir.record_for_member_func(f)
- .map(|r| r.rs_name.as_ref() == "SomeStruct")
- .unwrap_or_default()
+ if let Some(Ok(r)) = ir.record_for_member_func(f).map(<&Rc<Record>>::try_from) {
+ r.rs_name.as_ref() == "SomeStruct"
+ } else {
+ false
+ }
})
.flat_map(|f| match &f.name {
UnqualifiedIdentifier::Operator(op) => Some(op.name.as_ref()),
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 0f7c3a7..dbbc6a5 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -696,7 +696,16 @@
let ir = db.ir();
let op_meta = &*OPERATOR_METADATA;
- let maybe_record: Option<&Rc<Record>> = ir.record_for_member_func(func);
+ let maybe_record = match ir.record_for_member_func(func).map(<&Rc<Record>>::try_from) {
+ None => None,
+ Some(Ok(record)) => Some(record),
+ // Functions whose record was replaced with some other IR Item type are ignored.
+ // This occurs for instance if you use crubit_internal_rust_type: member functions defined
+ // out-of-line, such as implicitly generated constructors, will still be present in the IR,
+ // but should be ignored.
+ Some(Err(_)) => return Ok(None),
+ };
+
let has_pointer_params = param_types.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
let impl_kind: ImplKind;
let func_name: syn::Ident;
@@ -2785,7 +2794,7 @@
}
Item::TypeMapOverride(type_override) => {
// (This shouldn't fail, since we replace with known Rust types via a string.)
- let rs_type = db.rs_type_kind(type_override.type_.rs_type.clone())?;
+ let rs_type = RsTypeKind::new_type_map_override(type_override);
let disable_comment = format!(
"Type bindings for {cc_type} suppressed due to being mapped to \
an existing Rust type ({rs_type})",
@@ -3216,6 +3225,10 @@
Ok(RsTypeKind::Record { record, crate_path })
}
+ pub fn new_type_map_override(type_map_override: &TypeMapOverride) -> Self {
+ RsTypeKind::Other { name: type_map_override.rs_name.clone(), type_args: Rc::from([]) }
+ }
+
/// Returns true if the type is known to be `Unpin`, false otherwise.
pub fn is_unpin(&self) -> bool {
match self {
@@ -3696,6 +3709,9 @@
}
}
}
+ Item::TypeMapOverride(type_map_override) => {
+ RsTypeKind::new_type_map_override(type_map_override)
+ }
other_item => bail!("Item does not define a type: {:?}", other_item),
}
}
@@ -3798,6 +3814,10 @@
Ok(quote! { #namespace_qualifier #ident })
}
}
+ Item::TypeMapOverride(type_map_override) => type_map_override
+ .cc_name
+ .parse::<TokenStream>()
+ .map_err(|_| anyhow!("malformed type name: {:?}", type_map_override.cc_name)),
_ => bail!("Item does not define a type: {:?}", item),
}
}
diff --git a/rs_bindings_from_cc/test/golden/crubit_internal_rust_type.h b/rs_bindings_from_cc/test/golden/crubit_internal_rust_type.h
new file mode 100644
index 0000000..d7c6fd1
--- /dev/null
+++ b/rs_bindings_from_cc/test/golden/crubit_internal_rust_type.h
@@ -0,0 +1,53 @@
+// 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
+
+#ifndef THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_CRUBIT_INTERNAL_RS_TYPE_H_
+#define THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_CRUBIT_INTERNAL_RS_TYPE_H_
+
+#pragma clang lifetime_elision
+
+// These types should be suppressed due to the rust type override, as should
+// any methods they have.
+
+struct [[clang::annotate("crubit_internal_rust_type", "i8")]] MyI8Struct final {
+ signed char x;
+
+ void Method();
+};
+
+inline void MyI8Struct::Method() {
+ // Note that this is potentially visited, even if the original declaration is
+ // skipped due to crubit_internal_rust_type.
+}
+
+struct [[clang::annotate("crubit_internal_rust_type", "i8")]] MyI8Class final {
+ signed char x;
+};
+
+enum [[clang::annotate("crubit_internal_rust_type",
+ "i8")]] MyI8Enum : unsigned char{kX};
+
+using MyI8Alias [[clang::annotate("crubit_internal_rust_type", "i8")]] =
+ unsigned char;
+
+// Invalid annotations cause bindings to fail to be generated.
+// (It's important not to fall back to the underlying type, since the user
+// intent was to override it.)
+using TooFewArgs [[clang::annotate("crubit_internal_rust_type")]] =
+ unsigned char;
+using TooManyArgs [[clang::annotate("crubit_internal_rust_type", "i8", "i8")]] =
+ unsigned char;
+using NonStringArg [[clang::annotate("crubit_internal_rust_type", 8)]] =
+ unsigned char;
+
+struct TypeMapOverrideFieldTypes final {
+ MyI8Struct my_i8_struct;
+ MyI8Class my_i8_class;
+ MyI8Enum my_i8_enum;
+ MyI8Alias my_i8_alias;
+
+ TooFewArgs error;
+};
+
+#endif // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_CRUBIT_INTERNAL_RS_TYPE_H_
diff --git a/rs_bindings_from_cc/test/golden/crubit_internal_rust_type_rs_api.rs b/rs_bindings_from_cc/test/golden/crubit_internal_rust_type_rs_api.rs
new file mode 100644
index 0000000..0b1dbd8
--- /dev/null
+++ b/rs_bindings_from_cc/test/golden/crubit_internal_rust_type_rs_api.rs
@@ -0,0 +1,148 @@
+// 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 Rust bindings for the following C++ target:
+// //rs_bindings_from_cc/test/golden:crubit_internal_rust_type_cc
+// Features: experimental, supported
+
+#![rustfmt::skip]
+#![feature(custom_inner_attributes)]
+#![allow(stable_features)]
+#![no_std]
+#![allow(improper_ctypes)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![deny(warnings)]
+
+// 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
+
+// Type bindings for struct MyI8Struct suppressed due to being mapped to an existing Rust type (i8)
+const _: () = assert!(::core::mem::size_of::<i8>() == 1);
+const _: () = assert!(::core::mem::align_of::<i8>() == 1);
+
+// Note that this is potentially visited, even if the original declaration is
+// skipped due to crubit_internal_rust_type.
+
+// Type bindings for struct MyI8Class suppressed due to being mapped to an existing Rust type (i8)
+const _: () = assert!(::core::mem::size_of::<i8>() == 1);
+const _: () = assert!(::core::mem::align_of::<i8>() == 1);
+
+// Type bindings for enum MyI8Enum suppressed due to being mapped to an existing Rust type (i8)
+const _: () = assert!(::core::mem::size_of::<i8>() == 1);
+const _: () = assert!(::core::mem::align_of::<i8>() == 1);
+
+// Type bindings for MyI8Alias suppressed due to being mapped to an existing Rust type (i8)
+const _: () = assert!(::core::mem::size_of::<i8>() == 1);
+const _: () = assert!(::core::mem::align_of::<i8>() == 1);
+
+// Error while generating bindings for item 'TooFewArgs':
+// Invalid crubit_internal_rust_type attribute: The `crubit_internal_rust_type` attribute requires a single string literal argument, the Rust type.
+
+// Error while generating bindings for item 'TooManyArgs':
+// Invalid crubit_internal_rust_type attribute: The `crubit_internal_rust_type` attribute requires a single string literal argument, the Rust type.
+
+// Error while generating bindings for item 'NonStringArg':
+// Invalid crubit_internal_rust_type attribute: cannot evaluate argument as a string literal
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct TypeMapOverrideFieldTypes {
+ pub my_i8_struct: i8,
+ pub my_i8_class: i8,
+ pub my_i8_enum: i8,
+ pub my_i8_alias: i8,
+ /// Reason for representing this field as a blob of bytes:
+ /// Unsupported type 'TooFewArgs': No generated bindings found for 'TooFewArgs'
+ pub(crate) error: [::core::mem::MaybeUninit<u8>; 1],
+}
+forward_declare::unsafe_define!(
+ forward_declare::symbol!("TypeMapOverrideFieldTypes"),
+ crate::TypeMapOverrideFieldTypes
+);
+
+impl Default for TypeMapOverrideFieldTypes {
+ #[inline(always)]
+ fn default() -> Self {
+ let mut tmp = ::core::mem::MaybeUninit::<Self>::zeroed();
+ unsafe {
+ crate::detail::__rust_thunk___ZN25TypeMapOverrideFieldTypesC1Ev(&mut tmp);
+ tmp.assume_init()
+ }
+ }
+}
+
+impl<'b> From<::ctor::RvalueReference<'b, Self>> for TypeMapOverrideFieldTypes {
+ #[inline(always)]
+ fn from(__param_0: ::ctor::RvalueReference<'b, Self>) -> Self {
+ let mut tmp = ::core::mem::MaybeUninit::<Self>::zeroed();
+ unsafe {
+ crate::detail::__rust_thunk___ZN25TypeMapOverrideFieldTypesC1EOS_(&mut tmp, __param_0);
+ tmp.assume_init()
+ }
+ }
+}
+
+impl<'b> ::ctor::UnpinAssign<&'b Self> for TypeMapOverrideFieldTypes {
+ #[inline(always)]
+ fn unpin_assign<'a>(&'a mut self, __param_0: &'b Self) {
+ unsafe {
+ crate::detail::__rust_thunk___ZN25TypeMapOverrideFieldTypesaSERKS_(self, __param_0);
+ }
+ }
+}
+
+impl<'b> ::ctor::UnpinAssign<::ctor::RvalueReference<'b, Self>> for TypeMapOverrideFieldTypes {
+ #[inline(always)]
+ fn unpin_assign<'a>(&'a mut self, __param_0: ::ctor::RvalueReference<'b, Self>) {
+ unsafe {
+ crate::detail::__rust_thunk___ZN25TypeMapOverrideFieldTypesaSEOS_(self, __param_0);
+ }
+ }
+}
+
+// THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_CRUBIT_INTERNAL_RS_TYPE_H_
+
+mod detail {
+ #[allow(unused_imports)]
+ use super::*;
+ extern "C" {
+ pub(crate) fn __rust_thunk___ZN25TypeMapOverrideFieldTypesC1Ev<'a>(
+ __this: &'a mut ::core::mem::MaybeUninit<crate::TypeMapOverrideFieldTypes>,
+ );
+ pub(crate) fn __rust_thunk___ZN25TypeMapOverrideFieldTypesC1EOS_<'a, 'b>(
+ __this: &'a mut ::core::mem::MaybeUninit<crate::TypeMapOverrideFieldTypes>,
+ __param_0: ::ctor::RvalueReference<'b, crate::TypeMapOverrideFieldTypes>,
+ );
+ pub(crate) fn __rust_thunk___ZN25TypeMapOverrideFieldTypesaSERKS_<'a, 'b>(
+ __this: &'a mut crate::TypeMapOverrideFieldTypes,
+ __param_0: &'b crate::TypeMapOverrideFieldTypes,
+ ) -> &'a mut crate::TypeMapOverrideFieldTypes;
+ pub(crate) fn __rust_thunk___ZN25TypeMapOverrideFieldTypesaSEOS_<'a, 'b>(
+ __this: &'a mut crate::TypeMapOverrideFieldTypes,
+ __param_0: ::ctor::RvalueReference<'b, crate::TypeMapOverrideFieldTypes>,
+ ) -> &'a mut crate::TypeMapOverrideFieldTypes;
+ }
+}
+
+const _: () = assert!(::core::mem::size_of::<Option<&i32>>() == ::core::mem::size_of::<&i32>());
+
+const _: () = assert!(::core::mem::size_of::<crate::TypeMapOverrideFieldTypes>() == 5);
+const _: () = assert!(::core::mem::align_of::<crate::TypeMapOverrideFieldTypes>() == 1);
+const _: () = {
+ static_assertions::assert_impl_all!(crate::TypeMapOverrideFieldTypes:Clone);
+};
+const _: () = {
+ static_assertions::assert_impl_all!(crate::TypeMapOverrideFieldTypes:Copy);
+};
+const _: () = {
+ static_assertions::assert_not_impl_any!(crate::TypeMapOverrideFieldTypes:Drop);
+};
+const _: () = assert!(memoffset::offset_of!(crate::TypeMapOverrideFieldTypes, my_i8_struct) == 0);
+const _: () = assert!(memoffset::offset_of!(crate::TypeMapOverrideFieldTypes, my_i8_class) == 1);
+const _: () = assert!(memoffset::offset_of!(crate::TypeMapOverrideFieldTypes, my_i8_enum) == 2);
+const _: () = assert!(memoffset::offset_of!(crate::TypeMapOverrideFieldTypes, my_i8_alias) == 3);
+const _: () = assert!(memoffset::offset_of!(crate::TypeMapOverrideFieldTypes, error) == 4);
diff --git a/rs_bindings_from_cc/test/golden/crubit_internal_rust_type_rs_api_impl.cc b/rs_bindings_from_cc/test/golden/crubit_internal_rust_type_rs_api_impl.cc
new file mode 100644
index 0000000..eb4a91a
--- /dev/null
+++ b/rs_bindings_from_cc/test/golden/crubit_internal_rust_type_rs_api_impl.cc
@@ -0,0 +1,59 @@
+// 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 Rust bindings for the following C++ target:
+// //rs_bindings_from_cc/test/golden:crubit_internal_rust_type_cc
+// Features: experimental, supported
+
+#include <cstddef>
+#include <memory>
+
+#include "support/internal/cxx20_backports.h"
+#include "support/internal/offsetof.h"
+#include "support/internal/sizeof.h"
+
+// Public headers of the C++ library being wrapped.
+#include "rs_bindings_from_cc/test/golden/crubit_internal_rust_type.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wthread-safety-analysis"
+
+static_assert(sizeof(struct TypeMapOverrideFieldTypes) == 5);
+static_assert(alignof(struct TypeMapOverrideFieldTypes) == 1);
+static_assert(CRUBIT_OFFSET_OF(my_i8_struct,
+ struct TypeMapOverrideFieldTypes) == 0);
+static_assert(CRUBIT_OFFSET_OF(my_i8_class, struct TypeMapOverrideFieldTypes) ==
+ 1);
+static_assert(CRUBIT_OFFSET_OF(my_i8_enum, struct TypeMapOverrideFieldTypes) ==
+ 2);
+static_assert(CRUBIT_OFFSET_OF(my_i8_alias, struct TypeMapOverrideFieldTypes) ==
+ 3);
+static_assert(CRUBIT_OFFSET_OF(error, struct TypeMapOverrideFieldTypes) == 4);
+
+extern "C" void __rust_thunk___ZN25TypeMapOverrideFieldTypesC1Ev(
+ struct TypeMapOverrideFieldTypes* __this) {
+ crubit::construct_at(__this);
+}
+
+extern "C" void __rust_thunk___ZN25TypeMapOverrideFieldTypesC1EOS_(
+ struct TypeMapOverrideFieldTypes* __this,
+ struct TypeMapOverrideFieldTypes* __param_0) {
+ crubit::construct_at(__this, std::move(*__param_0));
+}
+
+extern "C" struct TypeMapOverrideFieldTypes*
+__rust_thunk___ZN25TypeMapOverrideFieldTypesaSERKS_(
+ struct TypeMapOverrideFieldTypes* __this,
+ const struct TypeMapOverrideFieldTypes* __param_0) {
+ return &__this->operator=(*__param_0);
+}
+
+extern "C" struct TypeMapOverrideFieldTypes*
+__rust_thunk___ZN25TypeMapOverrideFieldTypesaSEOS_(
+ struct TypeMapOverrideFieldTypes* __this,
+ struct TypeMapOverrideFieldTypes* __param_0) {
+ return &__this->operator=(std::move(*__param_0));
+}
+
+#pragma clang diagnostic pop
diff --git a/rs_bindings_from_cc/test/golden/types.h b/rs_bindings_from_cc/test/golden/types.h
index 914cc3f..81e314f 100644
--- a/rs_bindings_from_cc/test/golden/types.h
+++ b/rs_bindings_from_cc/test/golden/types.h
@@ -76,20 +76,4 @@
inline void* FunctionWithVoidPointers(void*, const void*) { return nullptr; }
-// These types should be suppressed due to the rust type override
-
-struct [[clang::annotate("crubit_internal_rust_type", "i8")]] MyI8Struct final {
- signed char x;
-};
-
-struct [[clang::annotate("crubit_internal_rust_type", "i8")]] MyI8Class final {
- signed char x;
-};
-
-enum [[clang::annotate("crubit_internal_rust_type",
- "i8")]] MyI8Enum : unsigned char{kX};
-
-using MyI8Alias [[clang::annotate("crubit_internal_rust_type", "i8")]] =
- unsigned char;
-
#endif // CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_TYPES_H_
diff --git a/rs_bindings_from_cc/test/golden/types_rs_api.rs b/rs_bindings_from_cc/test/golden/types_rs_api.rs
index 00b927b..1f9839d 100644
--- a/rs_bindings_from_cc/test/golden/types_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/types_rs_api.rs
@@ -161,22 +161,6 @@
crate::detail::__rust_thunk___Z24FunctionWithVoidPointersPvPKv(__param_0, __param_1)
}
-// Type bindings for struct MyI8Struct suppressed due to being mapped to an existing Rust type (i8)
-const _: () = assert!(::core::mem::size_of::<i8>() == 1);
-const _: () = assert!(::core::mem::align_of::<i8>() == 1);
-
-// Type bindings for struct MyI8Class suppressed due to being mapped to an existing Rust type (i8)
-const _: () = assert!(::core::mem::size_of::<i8>() == 1);
-const _: () = assert!(::core::mem::align_of::<i8>() == 1);
-
-// Type bindings for enum MyI8Enum suppressed due to being mapped to an existing Rust type (i8)
-const _: () = assert!(::core::mem::size_of::<i8>() == 1);
-const _: () = assert!(::core::mem::align_of::<i8>() == 1);
-
-// Type bindings for MyI8Alias suppressed due to being mapped to an existing Rust type (i8)
-const _: () = assert!(::core::mem::size_of::<i8>() == 1);
-const _: () = assert!(::core::mem::align_of::<i8>() == 1);
-
// CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_TYPES_H_
mod detail {
diff --git a/rs_bindings_from_cc/type_map.cc b/rs_bindings_from_cc/type_map.cc
index f8665a8..d02853f 100644
--- a/rs_bindings_from_cc/type_map.cc
+++ b/rs_bindings_from_cc/type_map.cc
@@ -5,15 +5,12 @@
#include "rs_bindings_from_cc/type_map.h"
#include <optional>
+#include <string>
#include "absl/container/flat_hash_map.h"
-#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "common/status_macros.h"
#include "rs_bindings_from_cc/ir.h"
-#include "clang/AST/Attr.h"
-#include "clang/AST/Attrs.inc"
-#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"
namespace crubit {
@@ -67,80 +64,12 @@
return it->second;
}
-// Copied from lifetime_annotations/type_lifetimes.cc, which is expected to move
-// into ClangTidy. See:
-// https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377
-absl::StatusOr<absl::string_view> EvaluateAsStringLiteral(
- const clang::Expr& expr, const clang::ASTContext& ast_context) {
- auto error = []() {
- return absl::InvalidArgumentError(
- "cannot evaluate argument as a string literal");
- };
-
- clang::Expr::EvalResult eval_result;
- if (!expr.EvaluateAsConstantExpr(eval_result, ast_context) ||
- !eval_result.Val.isLValue()) {
- return error();
- }
-
- const auto* eval_result_expr =
- eval_result.Val.getLValueBase().dyn_cast<const clang::Expr*>();
- if (!eval_result_expr) {
- return error();
- }
-
- const auto* string_literal =
- clang::dyn_cast<clang::StringLiteral>(eval_result_expr);
- if (!string_literal) {
- return error();
- }
-
- return {string_literal->getString()};
-}
-
-absl::StatusOr<std::optional<absl::string_view>> GetRustTypeAttribute(
- const clang::Type& cc_type) {
- std::optional<absl::string_view> rust_type;
- const clang::Decl* decl = nullptr;
- if (const auto* alias_type = cc_type.getAs<clang::TypedefType>();
- alias_type != nullptr) {
- decl = alias_type->getDecl();
- } else if (const clang::TagDecl* tag_decl = cc_type.getAsTagDecl();
- tag_decl != nullptr) {
- decl = tag_decl;
- }
- if (decl != nullptr) {
- for (clang::AnnotateAttr* attr :
- decl->specific_attrs<clang::AnnotateAttr>()) {
- if (attr->getAnnotation() != "crubit_internal_rust_type") continue;
-
- if (rust_type.has_value())
- return absl::InvalidArgumentError(
- "Only one `crubit_internal_rust_type` attribute may be placed on a "
- "type.");
- if (attr->args_size() != 1)
- return absl::InvalidArgumentError(
- "The `crubit_internal_rust_type` attribute requires a single "
- "string literal "
- "argument, the Rust type.");
- const clang::Expr& arg = **attr->args_begin();
- CRUBIT_ASSIGN_OR_RETURN(
- rust_type, EvaluateAsStringLiteral(arg, decl->getASTContext()));
- }
- }
- return rust_type;
-}
-
} // namespace
-absl::StatusOr<std::optional<MappedType>> GetTypeMapOverride(
- const clang::Type& cc_type) {
+std::optional<MappedType> GetTypeMapOverride(const clang::Type& cc_type) {
std::string type_string = clang::QualType(&cc_type, 0).getAsString();
- std::optional<absl::string_view> rust_type;
- CRUBIT_ASSIGN_OR_RETURN(rust_type, GetRustTypeAttribute(cc_type));
- if (!rust_type.has_value()) {
- rust_type = MapKnownCcTypeToRsType(type_string);
- }
+ std::optional<absl::string_view> rust_type =
+ MapKnownCcTypeToRsType(type_string);
if (rust_type.has_value()) {
return MappedType::Simple(std::string(*rust_type), type_string);
}
diff --git a/rs_bindings_from_cc/type_map.h b/rs_bindings_from_cc/type_map.h
index 22dcf4b..f3eb992 100644
--- a/rs_bindings_from_cc/type_map.h
+++ b/rs_bindings_from_cc/type_map.h
@@ -21,10 +21,9 @@
//
// For example, C++ `int64_t` becomes Rust `i64`.
//
-// To create a new type mapping, either add the type to the hardcoded list
-// of types, or else add the `crubit_rust_type` attribute.
-absl::StatusOr<std::optional<MappedType>> GetTypeMapOverride(
- const clang::Type& cc_type);
+// To create a new type mapping, add the type to the hardcoded list
+// of types.
+std::optional<MappedType> GetTypeMapOverride(const clang::Type& cc_type);
} // namespace crubit