Inline `format_cc_thunk_arg` and add tests for passing struct by value.
This CL inlines `format_cc_thunk_arg` into `format_fn` and adds
`format_item`-level tests of passing struct be value as 1) function
argument and 2) function return value. The first of the new tests
replaces the test coverage of the removed `test_format_cc_thunk_arg`
test.
PiperOrigin-RevId: 515110419
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 8d2ed43..ffdf6c5 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -251,21 +251,6 @@
}
}
-/// Formats an argument of a thunk. For example:
-/// - most primitive types are passed as-is - e.g. `123`
-/// - structs need to be moved: `std::move(value)`
-/// - in the future additional processing may be needed for other types (this is
-/// speculative so please take these examples with a grain of salt):
-/// - `&str`: utf-8 verification (see b/262580415)
-/// - `&T`: calling into `crubit::MutRef::unsafe_get_ptr` (see b/258235219)
-fn format_cc_thunk_arg<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, value: TokenStream) -> CcSnippet {
- if ty.is_copy_modulo_regions(tcx, ty::ParamEnv::empty()) {
- CcSnippet::new(value)
- } else {
- CcSnippet::with_include(quote! { std::move(#value) }, CcInclude::utility())
- }
-}
-
/// Formats `ty` into a `CcSnippet` that represents how the type should be
/// spelled in a C++ declaration of a function parameter or field.
//
@@ -707,8 +692,13 @@
let thunk_params = &main_api_params;
let thunk_args = params
.iter()
- .map(|Param{ cc_name, ty, ..}| format_cc_thunk_arg(tcx, *ty, cc_name.clone())
- .into_tokens(&mut prereqs))
+ .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) }
+ })
.collect_vec();
let thunk_ret_type = &main_api_ret_type;
CcSnippet {
@@ -1302,7 +1292,7 @@
use rustc_span::def_id::LocalDefId;
use crate::run_compiler::tests::run_compiler_for_testing;
- use code_gen_utils::{format_cc_ident, format_cc_includes};
+ use code_gen_utils::format_cc_includes;
use token_stream_matchers::{
assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches,
};
@@ -2599,6 +2589,88 @@
});
}
+ #[test]
+ fn test_format_item_fn_rust_abi_with_param_taking_struct_by_value() {
+ let test_src = r#"
+ pub struct S(i32);
+ pub fn into_i32(s: S) -> i32 { s.0 }
+ "#;
+ test_format_item(test_src, "into_i32", |result| {
+ let result = result.unwrap();
+ let main_api = get_main_api_snippet(&result);
+ let impl_details = get_impl_details_snippet(&result);
+ assert_cc_matches!(
+ main_api.tokens,
+ quote! {
+ inline std::int32_t into_i32(::rust_out::S s);
+ }
+ );
+ assert_cc_matches!(
+ impl_details.cc.tokens,
+ quote! {
+ namespace __crubit_internal {
+ 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));
+ }
+ }
+ );
+ assert_rs_matches!(
+ impl_details.rs,
+ quote! {
+ #[no_mangle]
+ extern "C"
+ fn ...(s: ::rust_out::S) -> i32 {
+ ::rust_out::into_i32(s)
+ }
+ }
+ );
+ });
+ }
+
+ #[test]
+ fn test_format_item_fn_rust_abi_returning_struct_by_value() {
+ let test_src = r#"
+ pub struct S(i32);
+ pub fn create(i: i32) -> S { S(i) }
+ "#;
+ test_format_item(test_src, "create", |result| {
+ let result = result.unwrap();
+ let main_api = get_main_api_snippet(&result);
+ let impl_details = get_impl_details_snippet(&result);
+ assert_cc_matches!(
+ main_api.tokens,
+ quote! {
+ inline ::rust_out::S create(std::int32_t i);
+ }
+ );
+ assert_cc_matches!(
+ impl_details.cc.tokens,
+ quote! {
+ namespace __crubit_internal {
+ extern "C" ::rust_out::S ...(std::int32_t i);
+ }
+ ...
+ inline ::rust_out::S create(std::int32_t i) {
+ return __crubit_internal::...(i);
+ }
+ }
+ );
+ assert_rs_matches!(
+ impl_details.rs,
+ quote! {
+ #[no_mangle]
+ extern "C"
+ fn ...(i: i32) -> ::rust_out::S {
+ ::rust_out::create(i)
+ }
+ }
+ );
+ });
+ }
+
/// `test_format_item_fn_rust_abi` tests a function call that is not a
/// C-ABI, and is not the default Rust ABI. It can't use `"stdcall"`,
/// because it is not supported on the targets where Crubit's tests run.
@@ -4016,40 +4088,6 @@
});
}
- #[test]
- fn test_format_cc_thunk_arg() {
- let testcases = [
- // ( <Rust type>, (<expected C++ type>, <expected #include>) )
- ("i32", ("value", "")),
- ("SomeStruct", ("std::move(value)", "utility")),
- ];
- let preamble = quote! {
- pub struct SomeStruct {
- pub x: i32,
- pub y: i32,
- }
- };
- test_ty(&testcases, preamble, |desc, tcx, ty, (expected_tokens, expected_include)| {
- let (actual_tokens, actual_includes) = {
- let cc_snippet = format_cc_thunk_arg(tcx, ty, quote! { value });
- (cc_snippet.tokens.to_string(), cc_snippet.prereqs.includes)
- };
-
- let expected_tokens = expected_tokens.parse::<TokenStream>().unwrap().to_string();
- assert_eq!(actual_tokens, expected_tokens, "{desc}");
-
- if expected_include.is_empty() {
- assert!(actual_includes.is_empty());
- } else {
- let expected_header = format_cc_ident(expected_include).unwrap();
- assert_cc_matches!(
- format_cc_includes(&actual_includes),
- quote! { include <#expected_header> }
- );
- }
- });
- }
-
fn test_ty<TestFn, Expectation>(
testcases: &[(&str, Expectation)],
preamble: TokenStream,