Skip to content

Commit

Permalink
Rollup merge of rust-lang#128791 - compiler-errors:async-fn-unsafe, r…
Browse files Browse the repository at this point in the history
…=lcnr

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

Due to unsafety, ABI, or the presence of target features, some `FnDef`/`FnPtr` types don't implement `Fn*`. Do the same for `AsyncFn*`.

Noticed this due to rust-lang#128764, but this isn't really related to that ICE, which is fixed in rust-lang#128792.
  • Loading branch information
matthiaskrgr authored Aug 8, 2024
2 parents ee26ae8 + 8c14c3d commit 1d6077d
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
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);
}
}
_ => {}
}
Expand Down
27 changes: 27 additions & 0 deletions tests/ui/async-await/async-closures/fn-exception.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//@ edition: 2021

#![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;

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>>;
}


#[target_feature(enable = "sse2")]
fn target_feature() -> Pin<Box<dyn Future<Output = ()> + 'static>> { todo!() }

fn test(f: impl async Fn()) {}

fn main() {
test(unsafety); //~ ERROR the trait bound
test(abi); //~ ERROR the trait bound
test(target_feature); //~ ERROR the trait bound
}
45 changes: 45 additions & 0 deletions tests/ui/async-await/async-closures/fn-exception.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
error[E0277]: the trait bound `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {unsafety}: AsyncFn<()>` is not satisfied
--> $DIR/fn-exception.rs:24: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:21: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:25: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:21:17
|
LL | fn test(f: impl async Fn()) {}
| ^^^^^^^^^^ required by this bound in `test`

error[E0277]: the trait bound `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn<()>` is not satisfied
--> $DIR/fn-exception.rs:26: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.rs:21:17
|
LL | fn test(f: impl async Fn()) {}
| ^^^^^^^^^^ required by this bound in `test`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.

0 comments on commit 1d6077d

Please sign in to comment.