Support unions in `#[recursively_pinned]`, albeit without supporting pin-projection.
The rationale for why unions don't receive a `pin_project` function is documented here -- tl;dr we can't project because we can't know which field is active.
Users can just do it by hand, if they do know which field is active. Alternatively, we can add -- and this is still open even for structs -- we can add field-specific accessors, like `union U { x: i32, y: i64}` could have `project_x()` and `project_y()` functions.
The rationale for why we *should* support unions isn't documented, it's somewhat taken as a given, right? But like -- when this was originally written, I don't think I fully appreciated that this would be applied to literally every C++ type. And some features will apply to some types, and others to other types. For another example, I will be adding *pointer* projection in a followup CL, and that *will* support unions (but won't support enums!) The only type that's going to have every single subfeature supported is likely going to be structs. :)
PiperOrigin-RevId: 450662659
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index e97b2c7..55f5d05 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -1206,8 +1206,7 @@
} else {
quote! { struct }
};
- // TODO(b/233603159): not all unions are unpin, remove `record.is_union` once
- // `ctor` supports unions.
+
let recursively_pinned_attribute = if record.is_unpin() {
quote! {}
} else {
@@ -2333,28 +2332,29 @@
let size = Literal::usize_unsuffixed(record.size);
let alignment = Literal::usize_unsuffixed(record.alignment);
let tag_kind = tag_kind(record);
- let field_assertions =
- record.fields.iter()
- .filter(|f| f.access == AccessSpecifier::Public && f.identifier.is_some())
- // https://en.cppreference.com/w/cpp/types/offsetof points out that "if member is [...]
- // a bit-field [...] the behavior [of `offsetof` macro] is undefined.". In such
- // scenario clang reports an error: cannot compute offset of bit-field 'field_name'.
- .filter(|f| !f.is_bitfield)
- .map(|field| {
- // The IR contains the offset in bits, while `CRUBIT_OFFSET_OF` returns the offset
- // in bytes, so we need to convert. We can assert that `field.offset` is always at
- // field boundaries, because the bitfields have been filtered out earlier.
- assert_eq!(field.offset % 8, 0);
- let expected_offset = Literal::usize_unsuffixed(field.offset / 8);
+ let field_assertions = record
+ .fields
+ .iter()
+ .filter(|f| f.access == AccessSpecifier::Public && f.identifier.is_some())
+ // https://en.cppreference.com/w/cpp/types/offsetof points out that "if member is [...]
+ // a bit-field [...] the behavior [of `offsetof` macro] is undefined.". In such
+ // scenario clang reports an error: cannot compute offset of bit-field 'field_name'.
+ .filter(|f| !f.is_bitfield)
+ .map(|field| {
+ // The IR contains the offset in bits, while `CRUBIT_OFFSET_OF` returns the
+ // offset in bytes, so we need to convert. We can assert that
+ // `field.offset` is always at field boundaries, because the
+ // bitfields have been filtered out earlier.
+ assert_eq!(field.offset % 8, 0);
+ let expected_offset = Literal::usize_unsuffixed(field.offset / 8);
- let field_ident = format_cc_ident(&field.identifier.as_ref().unwrap().identifier);
- let actual_offset = quote!{
- CRUBIT_OFFSET_OF(#field_ident, #tag_kind #namespace_qualifier #record_ident)
- };
+ let field_ident = format_cc_ident(&field.identifier.as_ref().unwrap().identifier);
+ let actual_offset = quote! {
+ CRUBIT_OFFSET_OF(#field_ident, #tag_kind #namespace_qualifier #record_ident)
+ };
- quote! { static_assert( #actual_offset == #expected_offset); }
- }
- );
+ quote! { static_assert( #actual_offset == #expected_offset); }
+ });
Ok(quote! {
static_assert(sizeof(#tag_kind #namespace_qualifier #record_ident) == #size);
static_assert(alignof(#tag_kind #namespace_qualifier #record_ident) == #alignment);
@@ -4270,9 +4270,14 @@
assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
- // TODO(b/233603159): not all unions are unpin, add a test verifying that unions
- // get recursively pinned correctly once `ctor` supports unions.
- assert_rs_not_matches!(rs_api, quote! {recursively_pinned});
+ assert_rs_matches!(
+ rs_api,
+ quote! {
+ #[ctor::recursively_pinned]
+ #[repr(C)]
+ pub union UnionWithNontrivialField { ... }
+ }
+ );
Ok(())
}