Stop passing structs by value into `extern "C"` thunks.

PiperOrigin-RevId: 515490560
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index b355ea6..afed0e7 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -753,19 +753,30 @@
                 }
             };
 
-            // TODO(b/270454629): Tweak `thunk_params`, and `thunk_args` as needed to pass some
-            // arguments by pointer, rather than by value.
             let mut prereqs = main_api_prereqs;
-            let mut thunk_params = main_api_params.clone();
+            let mut thunk_params = params
+                .iter()
+                .map(|Param { cc_name, cc_type, ty, .. }| -> Result<TokenStream> {
+                     if is_c_abi_compatible_by_value(*ty) {
+                         Ok(quote! { #cc_type #cc_name })
+                     } else {
+                         // Rust thunk will move a value via memcpy - we need to `ensure` that
+                         // invoking the C++ destructor (on the moved-away value) is safe.
+                         // TODO(b/259749095): Support generic structs (with non-empty ParamEnv).
+                         ensure!(!ty.needs_drop(tcx, ty::ParamEnv::empty()),
+                                 "Only trivially-movable and trivially-destructible types \
+                                  may be passed by value over the FFI boundary");
+                         Ok(quote! { #cc_type* #cc_name })
+                     }})
+                .collect::<Result<Vec<_>>>()?;
             let mut thunk_args = params
                 .iter()
                 .map(|Param{ cc_name, ty, ..}|
-                     if ty.is_copy_modulo_regions(tcx, ty::ParamEnv::empty()) {
-                        quote!{ #cc_name }
-                    } else {
-                        prereqs.includes.insert(CcInclude::utility());
-                        quote!{ std::move(#cc_name) }
-                    })
+                     if is_c_abi_compatible_by_value(*ty) {
+                         quote!{ #cc_name }
+                     } else {
+                         quote!{ & #cc_name }
+                     })
                 .collect_vec();
             let thunk_ret_type: TokenStream;
             let impl_body: TokenStream;
@@ -808,7 +819,12 @@
             let thunk_name = make_rs_ident(symbol_name.name);
             let mut thunk_params = params
                 .iter()
-                .map(|Param{ rs_name, rs_type, ..}| quote!{ #rs_name: #rs_type })
+                .map(|Param{ rs_name, rs_type, ty, ..}|
+                    if is_c_abi_compatible_by_value(*ty) {
+                        quote!{ #rs_name: #rs_type }
+                    } else {
+                        quote!{ #rs_name: &mut ::core::mem::MaybeUninit<#rs_type> }
+                    })
                 .collect_vec();
             let mut thunk_ret_type = format_ty_for_rs(tcx, sig.output())?;
             let mut thunk_body = {
@@ -822,7 +838,12 @@
                         quote! { #name :: }
                     }
                 };
-                let fn_args = params.iter().map(|Param{ rs_name, .. }| rs_name);
+                let fn_args = params.iter().map(|Param{ rs_name, ty, .. }|
+                    if is_c_abi_compatible_by_value(*ty) {
+                        quote!{ #rs_name }
+                    } else {
+                        quote!{ unsafe { #rs_name.assume_init_read() } }
+                    });
                 quote!{
                     :: #crate_name :: #mod_path #struct_name #fn_name( #( #fn_args ),* )
                 }
@@ -1632,7 +1653,7 @@
                         };
                         ...
                         namespace __crubit_internal {
-                            extern "C" bool ...(::rust_out::S s);
+                            extern "C" bool ...(::rust_out::S* s);
                         }
                         ...
                         inline bool f(::rust_out::S s) { ... }
@@ -1648,7 +1669,7 @@
                 quote! {
                     #[no_mangle]
                     extern "C"
-                    fn ...(s: ::rust_out::S) -> bool { ...  }
+                    fn ...(s: &mut ::core::mem::MaybeUninit<::rust_out::S>) -> bool { ...  }
                     ...
                     const _: () = assert!(::std::mem::size_of::<::rust_out::S>() == ...);
                     const _: () = assert!(::std::mem::align_of::<::rust_out::S>() == ...);
@@ -2708,11 +2729,11 @@
                 impl_details.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
-                        extern "C" std::int32_t ...(::rust_out::S s);
+                        extern "C" std::int32_t ...(::rust_out::S* s);
                     }
                     ...
                     inline std::int32_t into_i32(::rust_out::S s) {
-                        return __crubit_internal::...(std::move(s));
+                        return __crubit_internal::...(&s);
                     }
                 }
             );
@@ -2721,8 +2742,8 @@
                 quote! {
                     #[no_mangle]
                     extern "C"
-                    fn ...(s: ::rust_out::S) -> i32 {
-                        ::rust_out::into_i32(s)
+                    fn ...(s: &mut ::core::mem::MaybeUninit<::rust_out::S>) -> i32 {
+                        ::rust_out::into_i32(unsafe { s.assume_init_read() })
                     }
                 }
             );
@@ -2960,11 +2981,11 @@
                 impl_details.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
-                        extern "C" std::int32_t ...(::rust_out::S __param_0);
+                        extern "C" std::int32_t ...(::rust_out::S* __param_0);
                     }
                     ...
                     inline std::int32_t func(::rust_out::S __param_0) {
-                        return __crubit_internal::...(std::move(__param_0));
+                        return __crubit_internal::...(&__param_0);
                     }
                 }
             );
@@ -2972,8 +2993,10 @@
                 impl_details.rs,
                 quote! {
                     #[no_mangle]
-                    extern "C" fn ...(__param_0: ::rust_out::S) -> i32 {
-                        ::rust_out::func(__param_0)
+                    extern "C" fn ...(
+                        __param_0: &mut ::core::mem::MaybeUninit<::rust_out::S>
+                    ) -> i32 {
+                        ::rust_out::func(unsafe {__param_0.assume_init_read() })
                     }
                 }
             );
diff --git a/cc_bindings_from_rs/test/structs/structs_test.cc b/cc_bindings_from_rs/test/structs/structs_test.cc
index 403cbae..ea92507 100644
--- a/cc_bindings_from_rs/test/structs/structs_test.cc
+++ b/cc_bindings_from_rs/test/structs/structs_test.cc
@@ -47,8 +47,7 @@
   EXPECT_EQ(123 * 456, test::StructInteger::inspect(std::move(product)));
 }
 
-// TODO(b/270454629): Re-enable the test after fixing the bug.
-TEST(StructsTest, DISABLED_StructFloat) {
+TEST(StructsTest, StructFloat) {
   namespace test = structs::abi_classification;
   test::StructFloat x = test::StructFloat::create(456.0);
   test::StructFloat y = test::StructFloat::create(789.0);
@@ -57,8 +56,7 @@
   EXPECT_EQ(456.0 * 789.0, test::StructFloat::inspect(std::move(product)));
 }
 
-// TODO(b/270454629): Re-enable the test after fixing the bug.
-TEST(StructsTest, DISABLED_StructMemory) {
+TEST(StructsTest, StructMemory) {
   namespace test = structs::abi_classification;
   test::StructMemory x = test::StructMemory::create(321);
   test::StructMemory y = test::StructMemory::create(654);