Support nontrivial structs and destructors in `extern_c`.
PiperOrigin-RevId: 602638268
Change-Id: I54c26e81075026c76f102f4e338d894578860425
diff --git a/rs_bindings_from_cc/importers/cxx_record.cc b/rs_bindings_from_cc/importers/cxx_record.cc
index a724116..35567a0 100644
--- a/rs_bindings_from_cc/importers/cxx_record.cc
+++ b/rs_bindings_from_cc/importers/cxx_record.cc
@@ -208,6 +208,8 @@
return true;
} else if (clang::isa<clang::FinalAttr>(attr)) {
return true;
+ } else if (clang::isa<clang::TrivialABIAttr>(attr)) {
+ return true;
} else if (auto* visibility =
clang::dyn_cast<clang::VisibilityAttr>(&attr);
visibility && record_decl->isInStdNamespace()) {
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 5cf358c..f8bd356 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -2929,26 +2929,42 @@
match item {
Item::UnsupportedItem(..) => {}
Item::Func(func) => {
- for t in func.types() {
- let t = db.rs_type_kind(t.rs_type.clone())?;
- crubit_features |= t.required_crubit_features(&db.ir())?
- }
- if func.is_extern_c {
+ if func.name == UnqualifiedIdentifier::Destructor {
+ // We support destructors in extern_c even though they use some features we
+ // don't generally support with that feature set, because in this
+ // particular case, it's safe.
crubit_features |= ir::CrubitFeature::ExternC;
+ // Rather than walking the parameters -- which are not supported in extern_c
+ // (`&mut self`) -- we just look up the single `this` parameter's feature
+ // requirements.
+ let Some(meta) = &func.member_func_metadata else {
+ bail!("Destructor without `this`");
+ };
+ let ir = db.ir();
+ let record: &Item = ir.find_decl(meta.record_id)?;
+ crubit_features |= crubit_features_for_item(db, record)?;
} else {
- crubit_features |= ir::CrubitFeature::Experimental;
- }
- if !func.has_c_calling_convention
- || func.is_noreturn
- || func.nodiscard.is_some()
- || func.deprecated.is_some()
- {
- crubit_features |= ir::CrubitFeature::Experimental;
- }
- for param in &func.params {
- if param.unknown_attr.is_some() {
+ for t in func.types() {
+ let t = db.rs_type_kind(t.rs_type.clone())?;
+ crubit_features |= t.required_crubit_features(&db.ir())?
+ }
+ if func.is_extern_c {
+ crubit_features |= ir::CrubitFeature::ExternC;
+ } else {
crubit_features |= ir::CrubitFeature::Experimental;
}
+ if !func.has_c_calling_convention
+ || func.is_noreturn
+ || func.nodiscard.is_some()
+ || func.deprecated.is_some()
+ {
+ crubit_features |= ir::CrubitFeature::Experimental;
+ }
+ for param in &func.params {
+ if param.unknown_attr.is_some() {
+ crubit_features |= ir::CrubitFeature::Experimental;
+ }
+ }
}
}
Item::Record(record) => {
diff --git a/rs_bindings_from_cc/test/extern_c/BUILD b/rs_bindings_from_cc/test/extern_c/BUILD
index c83a31a..82e5b8e 100644
--- a/rs_bindings_from_cc/test/extern_c/BUILD
+++ b/rs_bindings_from_cc/test/extern_c/BUILD
@@ -7,6 +7,7 @@
name = "has_bindings",
hdrs = ["has_bindings.h"],
aspect_hints = ["//features:extern_c"],
+ deps = ["@absl//absl/base:core_headers"],
)
crubit_test_cc_library(
diff --git a/rs_bindings_from_cc/test/extern_c/has_bindings.h b/rs_bindings_from_cc/test/extern_c/has_bindings.h
index e288982..ea2cd1f 100644
--- a/rs_bindings_from_cc/test/extern_c/has_bindings.h
+++ b/rs_bindings_from_cc/test/extern_c/has_bindings.h
@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_EXTERN_C_ALLOWED_H_
#define THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_EXTERN_C_ALLOWED_H_
+#include "absl/base/attributes.h"
namespace crubit::has_bindings {
extern "C" {
@@ -16,6 +17,17 @@
using StructAlias = Struct;
+struct ABSL_ATTRIBUTE_TRIVIAL_ABI NontrivialStruct {
+ int* x;
+ ~NontrivialStruct() {
+ // this can do anything, but we'll do something silly for the sake of
+ // example.
+ if (x != nullptr) {
+ *x = 42;
+ }
+ }
+};
+
enum Enum {
kEnumerator = 0,
// This doesn't receive bindings, because the enumerator has an unrecognized
diff --git a/rs_bindings_from_cc/test/extern_c/has_bindings_test.rs b/rs_bindings_from_cc/test/extern_c/has_bindings_test.rs
index 5554093..20c6c56 100644
--- a/rs_bindings_from_cc/test/extern_c/has_bindings_test.rs
+++ b/rs_bindings_from_cc/test/extern_c/has_bindings_test.rs
@@ -26,6 +26,17 @@
}
#[test]
+fn test_nontrivial_struct() {
+ let mut i = 0;
+ {
+ let s = has_bindings::NontrivialStruct { x: &mut i };
+ let _s2 = s; // can still treat it like a normal Rust value!
+ }
+ // and the destructor gets invoked!
+ assert_eq!(i, 42);
+}
+
+#[test]
fn test_user_enum() {
let _: has_bindings::Enum = has_bindings::Enum::kEnumerator;
// Can't really assert this due to how value_exists works, sadly.