diff --git a/cc_bindings_from_rs/generate_bindings/generate_function.rs b/cc_bindings_from_rs/generate_bindings/generate_function.rs index 46d7d88ea..f4d0c2216 100644 --- a/cc_bindings_from_rs/generate_bindings/generate_function.rs +++ b/cc_bindings_from_rs/generate_bindings/generate_function.rs @@ -794,6 +794,13 @@ pub fn generate_function<'tcx>( }; check_fn_sig(&sig_mid)?; + let rs_return_type = sig_mid.output(); + if tcx.asyncness(def_id).is_async() { + let return_ty = get_async_future_output_ty(tcx, rs_return_type)?; + // TODO(b/453897096): Support async functions. + bail!("async functions are not yet supported, consider manually wrapping with `DynFuture` instead and writing to an output `*mut {return_ty}` parameter instead."); + } + let trait_ref = tcx .impl_of_assoc(def_id) .and_then(|impl_id| tcx.impl_opt_trait_ref(impl_id)) @@ -929,7 +936,6 @@ pub fn generate_function<'tcx>( quote! { #cpp_type #cc_name #annotation } }) .collect_vec(); - let rs_return_type = sig_mid.output(); let thunk_name_cc = format_cc_ident(db, &thunk_name).context("Error formatting thunk name")?; let impl_body = generate_thunk_call( db, @@ -1117,3 +1123,33 @@ pub fn check_fn_sig(sig: &ty::FnSig) -> Result<()> { Ok(()) } + +/// If `rs_return_type` represents an async future desugared type, extracts and returns its `Output` type. +pub fn get_async_future_output_ty<'tcx>( + tcx: TyCtxt<'tcx>, + rs_return_type: Ty<'tcx>, +) -> Result> { + let ty::TyKind::Alias(alias_ty) = rs_return_type.kind() else { + bail!("async functions should always return a TyKind::Alias, this should never happen."); + }; + let future_output = tcx + .lang_items() + .future_output() + .ok_or_else(|| anyhow!("crubit.rs-bug: Future::Output lang item not found"))?; + tcx.explicit_item_bounds(alias_ty.kind.def_id()) + .iter_instantiated_copied(tcx, alias_ty.args) + .find_map(|(predicate, _span)| { + if let ty::ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() + && Some(projection_predicate.def_id()) == Some(future_output) + { + return projection_predicate.term.as_type(); + } + None + }) + .ok_or_else(|| { + anyhow!( + "Failed to find Future::Output associated type in bounds of {:?}", + rs_return_type + ) + }) +} diff --git a/cc_bindings_from_rs/generate_bindings/generate_function_test.rs b/cc_bindings_from_rs/generate_bindings/generate_function_test.rs index b23c030db..a57fe823e 100644 --- a/cc_bindings_from_rs/generate_bindings/generate_function_test.rs +++ b/cc_bindings_from_rs/generate_bindings/generate_function_test.rs @@ -1030,12 +1030,18 @@ fn test_format_item_unsupported_fn_async() { "#; test_format_item(test_src, "async_function", |result| { let err = result.unwrap_err(); - assert_eq!( - err, - "Error formatting function return type `impl std::future::Future`: \ - The following Rust type is not supported yet: \ - impl std::future::Future" - ); + assert_eq!(err, "async functions are not yet supported, consider manually wrapping with `DynFuture` instead and writing to an output `*mut ()` parameter instead."); + }); +} + +#[test] +fn test_format_item_unsupported_fn_async_returning_type() { + let test_src = r#" + pub async fn async_function() -> i32 { 42 } + "#; + test_format_item(test_src, "async_function", |result| { + let err = result.unwrap_err(); + assert_eq!(err, "async functions are not yet supported, consider manually wrapping with `DynFuture` instead and writing to an output `*mut i32` parameter instead."); }); } diff --git a/cc_bindings_from_rs/test/golden/async_fn.rs b/cc_bindings_from_rs/test/golden/async_fn.rs new file mode 100644 index 000000000..ac3f98943 --- /dev/null +++ b/cc_bindings_from_rs/test/golden/async_fn.rs @@ -0,0 +1,7 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +pub async fn add(x: i32, y: i32) -> i32 { + x + y +} diff --git a/cc_bindings_from_rs/test/golden/async_fn_cc_api.h b/cc_bindings_from_rs/test/golden/async_fn_cc_api.h new file mode 100644 index 000000000..9593f4913 --- /dev/null +++ b/cc_bindings_from_rs/test/golden/async_fn_cc_api.h @@ -0,0 +1,31 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Automatically @generated C++ bindings for the following Rust crate: +// async_fn_rust_golden +// Features: assume_lifetimes, assume_this_lifetimes, callables, +// check_default_initialized, experimental, fmt, leading_colons_for_cpp_type, +// supported, template_instantiation, types, unsafe_view, wrapper + +// clang-format off +#ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_GOLDEN_ASYNC_FN_RUST_GOLDEN +#define THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_GOLDEN_ASYNC_FN_RUST_GOLDEN + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#pragma clang diagnostic ignored "-Wunused-private-field" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wignored-attributes" + +namespace async_fn_rust { + +// Error generating bindings for function `async_fn_rust_golden::add` defined at +// cc_bindings_from_rs/test/golden/async_fn.rs;l=5: +// async functions are not yet supported, consider manually wrapping with +// `DynFuture` instead and writing to an output `*mut i32` parameter instead. + +} + +#pragma clang diagnostic pop +#endif // THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_GOLDEN_ASYNC_FN_RUST_GOLDEN diff --git a/cc_bindings_from_rs/test/golden/async_fn_cc_api_impl.rs b/cc_bindings_from_rs/test/golden/async_fn_cc_api_impl.rs new file mode 100644 index 000000000..df1f43c34 --- /dev/null +++ b/cc_bindings_from_rs/test/golden/async_fn_cc_api_impl.rs @@ -0,0 +1,14 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Automatically @generated C++ bindings for the following Rust crate: +// async_fn_rust_golden +// Features: assume_lifetimes, assume_this_lifetimes, callables, check_default_initialized, experimental, fmt, leading_colons_for_cpp_type, supported, template_instantiation, types, unsafe_view, wrapper + +#![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)] +#![allow(improper_ctypes_definitions)] +#![deny(warnings)] + +extern crate alloc; +extern crate core;