Extract `format_thunk_decl` out of `format_fn`.
Having a separate `format_thunk_decl` enables generating a thunk without
generating a corresponding main api. This will be useful when generating
bindings for `Default` trait impl as a C++ constructor.
PiperOrigin-RevId: 521829165
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index b1a6e28..de92edd 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -559,6 +559,59 @@
}
}
+/// Formats a C++ function declaration of a thunk that wraps a Rust function
+/// identified by `fn_def_id`. `format_thunk_impl` may panic if `fn_def_id`
+/// doesn't identify a function.
+fn format_thunk_decl(input: &Input, fn_def_id: LocalDefId, thunk_name: &str) -> Result<CcSnippet> {
+ let tcx = input.tcx;
+ let thunk_name = format_cc_ident(thunk_name)?;
+
+ let mut prereqs = CcPrerequisites::default();
+ let sig = get_fn_sig(tcx, fn_def_id)?;
+ let main_api_ret_type = format_ret_ty_for_cc(input, sig.output())
+ .context("Error formatting function return type")?
+ .into_tokens(&mut prereqs);
+
+ let mut thunk_params = sig
+ .inputs()
+ .iter()
+ .map(|&ty| -> Result<TokenStream> {
+ let cc_type = format_ty_for_cc(input, ty)?.into_tokens(&mut prereqs);
+ if is_c_abi_compatible_by_value(ty) {
+ Ok(quote! { #cc_type })
+ } 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* })
+ }
+ })
+ .collect::<Result<Vec<_>>>()?;
+
+ let thunk_ret_type: TokenStream;
+ if is_c_abi_compatible_by_value(sig.output()) {
+ thunk_ret_type = main_api_ret_type.clone();
+ } else {
+ thunk_ret_type = quote! { void };
+ thunk_params.push(quote! { #main_api_ret_type* __ret_ptr });
+ prereqs.includes.insert(CcInclude::utility());
+ prereqs.includes.insert(input.support_header("internal/return_value_slot.h"));
+ };
+ Ok(CcSnippet {
+ prereqs,
+ tokens: quote! {
+ namespace __crubit_internal {
+ extern "C" #thunk_ret_type #thunk_name ( #( #thunk_params ),* );
+ }
+ },
+ })
+}
+
/// Formats a thunk implementation in Rust that provides an `extern "C"` ABI for
/// calling a Rust function identified by `fn_def_id`. `format_thunk_impl` may
/// panic if `fn_def_id` doesn't identify a function.
@@ -792,7 +845,7 @@
CcSnippet::default()
} else {
let thunk_name =
- format_cc_ident(symbol_name.name).context("Error formatting exported name")?;
+ format_cc_ident(symbol_name.name).context("Error formatting thunk name")?;
let struct_name = match struct_name.as_ref() {
None => quote! {},
Some(symbol) => {
@@ -803,24 +856,9 @@
};
let mut prereqs = main_api_prereqs;
- 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 thunk_decl = format_thunk_decl(input, local_def_id, symbol_name.name)?
+ .into_tokens(&mut prereqs);
+
let mut thunk_args = params
.iter()
.map(|Param { cc_name, ty, .. }| {
@@ -831,16 +869,12 @@
}
})
.collect_vec();
- let thunk_ret_type: TokenStream;
let impl_body: TokenStream;
if is_c_abi_compatible_by_value(sig.output()) {
- thunk_ret_type = main_api_ret_type.clone();
impl_body = quote! {
return __crubit_internal :: #thunk_name( #( #thunk_args ),* );
};
} else {
- thunk_ret_type = quote! { void };
- thunk_params.push(quote! { #main_api_ret_type* __ret_ptr });
thunk_args.push(quote! { __ret_slot.Get() });
impl_body = quote! {
crubit::ReturnValueSlot<#main_api_ret_type> __ret_slot;
@@ -854,9 +888,7 @@
prereqs,
tokens: quote! {
__NEWLINE__
- namespace __crubit_internal {
- extern "C" #thunk_ret_type #thunk_name ( #( #thunk_params ),* );
- }
+ #thunk_decl
inline #main_api_ret_type #struct_name #main_api_fn_name (
#( #main_api_params ),* ) {
#impl_body
@@ -1721,7 +1753,7 @@
...
inline double public_function(double x, double y);
namespace __crubit_internal {
- extern "C" double export_name(double x, double y);
+ extern "C" double export_name(double, double);
}
inline double public_function(double x, double y) {
return __crubit_internal::export_name(x, y);
@@ -2447,7 +2479,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" double ...(double x, double y);
+ extern "C" double ...(double, double);
}
...
inline double public_function(double x, double y) {
@@ -2485,7 +2517,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" double export_name(double x, double y);
+ extern "C" double export_name(double, double);
}
...
inline double public_function(double x, double y) {
@@ -2540,7 +2572,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" std::int32_t ...( std::int32_t i);
+ extern "C" std::int32_t ...( std::int32_t);
}
...
inline std::int32_t foo(std::int32_t i) {
@@ -2914,7 +2946,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" double ...(double x, double y);
+ extern "C" double ...(double, double);
}
...
inline double add(double x, double y) {
@@ -2954,7 +2986,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" std::int32_t ...(::rust_out::S* s);
+ extern "C" std::int32_t ...(::rust_out::S*);
}
...
inline std::int32_t into_i32(::rust_out::S s) {
@@ -2994,7 +3026,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" void ...(std::int32_t i, ::rust_out::S* __ret_ptr);
+ extern "C" void ...(std::int32_t, ::rust_out::S* __ret_ptr);
}
...
inline ::rust_out::S create(std::int32_t i) {
@@ -3056,7 +3088,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" double ...(double x, double y);
+ extern "C" double ...(double, double);
}
...
inline double add(double x, double y) {
@@ -3154,8 +3186,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" void ...(
- double __param_0, double __param_1);
+ extern "C" void ...(double, double);
}
...
inline void foo(double __param_0, double __param_1) {
@@ -3202,7 +3233,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" std::int32_t ...(::rust_out::S* __param_0);
+ extern "C" std::int32_t ...(::rust_out::S*);
}
...
inline std::int32_t func(::rust_out::S __param_0) {
@@ -3601,7 +3632,7 @@
result.cc_details.tokens,
quote! {
namespace __crubit_internal {
- extern "C" float ... (float x, float y);
+ extern "C" float ... (float, float);
}
inline float Math::add_i32(float x, float y) {
return __crubit_internal::...(x, y);