Don't implement AsyncFn for FnDef/FnPtr that wouldnt implement Fn

This commit is contained in:
Michael Goulet 2024-08-07 12:36:41 -04:00
parent 9bad7ba324
commit ec1c424293
6 changed files with 139 additions and 23 deletions

View File

@ -458,28 +458,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
))
}
ty::FnDef(..) | ty::FnPtr(..) => {
let bound_sig = self_ty.fn_sig(cx);
let sig = bound_sig.skip_binder();
let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future);
// `FnDef` and `FnPtr` only implement `AsyncFn*` when their
// return type implements `Future`.
let nested = vec![
bound_sig
.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()]))
.upcast(cx),
];
let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput);
let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
Ok((
bound_sig.rebind(AsyncCallableRelevantTypes {
tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()),
output_coroutine_ty: sig.output(),
coroutine_return_ty: future_output_ty,
}),
nested,
))
ty::FnDef(def_id, _) => {
let sig = self_ty.fn_sig(cx);
if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) {
fn_item_to_async_callable(cx, sig)
} else {
Err(NoSolution)
}
}
ty::FnPtr(..) => {
let sig = self_ty.fn_sig(cx);
if sig.skip_binder().is_fn_trait_compatible() {
fn_item_to_async_callable(cx, sig)
} else {
Err(NoSolution)
}
}
ty::Closure(_, args) => {
let args = args.as_closure();
let bound_sig = args.sig();
@ -563,6 +558,29 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
}
}
fn fn_item_to_async_callable<I: Interner>(
cx: I,
bound_sig: ty::Binder<I, ty::FnSig<I>>,
) -> Result<(ty::Binder<I, AsyncCallableRelevantTypes<I>>, Vec<I::Predicate>), NoSolution> {
let sig = bound_sig.skip_binder();
let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future);
// `FnDef` and `FnPtr` only implement `AsyncFn*` when their
// return type implements `Future`.
let nested = vec![
bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx),
];
let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput);
let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
Ok((
bound_sig.rebind(AsyncCallableRelevantTypes {
tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()),
output_coroutine_ty: sig.output(),
coroutine_return_ty: future_output_ty,
}),
nested,
))
}
/// Given a coroutine-closure, project to its returned coroutine when we are *certain*
/// that the closure's kind is compatible with the goal.
fn coroutine_closure_to_certain_coroutine<I: Interner>(

View File

@ -467,8 +467,20 @@ fn assemble_async_closure_candidates(
}
candidates.vec.push(AsyncClosureCandidate);
}
ty::FnDef(..) | ty::FnPtr(..) => {
candidates.vec.push(AsyncClosureCandidate);
// Provide an impl, but only for suitable `fn` pointers.
ty::FnPtr(sig) => {
if sig.is_fn_trait_compatible() {
candidates.vec.push(AsyncClosureCandidate);
}
}
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
ty::FnDef(def_id, _) => {
let tcx = self.tcx();
if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
&& tcx.codegen_fn_attrs(def_id).target_features.is_empty()
{
candidates.vec.push(AsyncClosureCandidate);
}
}
_ => {}
}

View File

@ -0,0 +1,17 @@
//@ edition: 2021
//@ only-x86_64
#![feature(async_closure, target_feature_11)]
// `target_feature_11` just to test safe functions w/ target features.
use std::pin::Pin;
use std::future::Future;
#[target_feature(enable = "sse2")]
fn target_feature() -> Pin<Box<dyn Future<Output = ()> + 'static>> { todo!() }
fn test(f: impl async Fn()) {}
fn main() {
test(target_feature); //~ ERROR the trait bound
}

View File

@ -0,0 +1,17 @@
error[E0277]: the trait bound `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn<()>` is not satisfied
--> $DIR/fn-exception-target-features.rs:16:10
|
LL | test(target_feature);
| ---- ^^^^^^^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
| |
| required by a bound introduced by this call
|
note: required by a bound in `test`
--> $DIR/fn-exception-target-features.rs:13:17
|
LL | fn test(f: impl async Fn()) {}
| ^^^^^^^^^^ required by this bound in `test`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,21 @@
//@ edition: 2021
#![feature(async_closure)]
use std::pin::Pin;
use std::future::Future;
unsafe extern "Rust" {
pub unsafe fn unsafety() -> Pin<Box<dyn Future<Output = ()> + 'static>>;
}
unsafe extern "C" {
pub safe fn abi() -> Pin<Box<dyn Future<Output = ()> + 'static>>;
}
fn test(f: impl async Fn()) {}
fn main() {
test(unsafety); //~ ERROR the trait bound
test(abi); //~ ERROR the trait bound
}

View File

@ -0,0 +1,31 @@
error[E0277]: the trait bound `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {unsafety}: AsyncFn<()>` is not satisfied
--> $DIR/fn-exception.rs:19:10
|
LL | test(unsafety);
| ---- ^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {unsafety}`
| |
| required by a bound introduced by this call
|
note: required by a bound in `test`
--> $DIR/fn-exception.rs:16:17
|
LL | fn test(f: impl async Fn()) {}
| ^^^^^^^^^^ required by this bound in `test`
error[E0277]: the trait bound `extern "C" fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {abi}: AsyncFn<()>` is not satisfied
--> $DIR/fn-exception.rs:20:10
|
LL | test(abi);
| ---- ^^^ the trait `AsyncFn<()>` is not implemented for fn item `extern "C" fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {abi}`
| |
| required by a bound introduced by this call
|
note: required by a bound in `test`
--> $DIR/fn-exception.rs:16:17
|
LL | fn test(f: impl async Fn()) {}
| ^^^^^^^^^^ required by this bound in `test`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.