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(())
     }