Skip to content

Commit 03b5112

Browse files
authored
Rollup merge of rust-lang#128791 - compiler-errors:async-fn-unsafe, r=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.
2 parents d7e5622 + ec1c424 commit 03b5112

File tree

6 files changed

+139
-23
lines changed

6 files changed

+139
-23
lines changed

compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs

+39-21
Original file line numberDiff line numberDiff line change
@@ -458,28 +458,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
458458
))
459459
}
460460

461-
ty::FnDef(..) | ty::FnPtr(..) => {
462-
let bound_sig = self_ty.fn_sig(cx);
463-
let sig = bound_sig.skip_binder();
464-
let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future);
465-
// `FnDef` and `FnPtr` only implement `AsyncFn*` when their
466-
// return type implements `Future`.
467-
let nested = vec![
468-
bound_sig
469-
.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()]))
470-
.upcast(cx),
471-
];
472-
let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput);
473-
let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
474-
Ok((
475-
bound_sig.rebind(AsyncCallableRelevantTypes {
476-
tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()),
477-
output_coroutine_ty: sig.output(),
478-
coroutine_return_ty: future_output_ty,
479-
}),
480-
nested,
481-
))
461+
ty::FnDef(def_id, _) => {
462+
let sig = self_ty.fn_sig(cx);
463+
if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) {
464+
fn_item_to_async_callable(cx, sig)
465+
} else {
466+
Err(NoSolution)
467+
}
468+
}
469+
ty::FnPtr(..) => {
470+
let sig = self_ty.fn_sig(cx);
471+
if sig.skip_binder().is_fn_trait_compatible() {
472+
fn_item_to_async_callable(cx, sig)
473+
} else {
474+
Err(NoSolution)
475+
}
482476
}
477+
483478
ty::Closure(_, args) => {
484479
let args = args.as_closure();
485480
let bound_sig = args.sig();
@@ -563,6 +558,29 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
563558
}
564559
}
565560

561+
fn fn_item_to_async_callable<I: Interner>(
562+
cx: I,
563+
bound_sig: ty::Binder<I, ty::FnSig<I>>,
564+
) -> Result<(ty::Binder<I, AsyncCallableRelevantTypes<I>>, Vec<I::Predicate>), NoSolution> {
565+
let sig = bound_sig.skip_binder();
566+
let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future);
567+
// `FnDef` and `FnPtr` only implement `AsyncFn*` when their
568+
// return type implements `Future`.
569+
let nested = vec![
570+
bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx),
571+
];
572+
let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput);
573+
let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
574+
Ok((
575+
bound_sig.rebind(AsyncCallableRelevantTypes {
576+
tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()),
577+
output_coroutine_ty: sig.output(),
578+
coroutine_return_ty: future_output_ty,
579+
}),
580+
nested,
581+
))
582+
}
583+
566584
/// Given a coroutine-closure, project to its returned coroutine when we are *certain*
567585
/// that the closure's kind is compatible with the goal.
568586
fn coroutine_closure_to_certain_coroutine<I: Interner>(

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
467467
}
468468
candidates.vec.push(AsyncClosureCandidate);
469469
}
470-
ty::FnDef(..) | ty::FnPtr(..) => {
471-
candidates.vec.push(AsyncClosureCandidate);
470+
// Provide an impl, but only for suitable `fn` pointers.
471+
ty::FnPtr(sig) => {
472+
if sig.is_fn_trait_compatible() {
473+
candidates.vec.push(AsyncClosureCandidate);
474+
}
475+
}
476+
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
477+
ty::FnDef(def_id, _) => {
478+
let tcx = self.tcx();
479+
if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
480+
&& tcx.codegen_fn_attrs(def_id).target_features.is_empty()
481+
{
482+
candidates.vec.push(AsyncClosureCandidate);
483+
}
472484
}
473485
_ => {}
474486
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ edition: 2021
2+
//@ only-x86_64
3+
4+
#![feature(async_closure, target_feature_11)]
5+
// `target_feature_11` just to test safe functions w/ target features.
6+
7+
use std::pin::Pin;
8+
use std::future::Future;
9+
10+
#[target_feature(enable = "sse2")]
11+
fn target_feature() -> Pin<Box<dyn Future<Output = ()> + 'static>> { todo!() }
12+
13+
fn test(f: impl async Fn()) {}
14+
15+
fn main() {
16+
test(target_feature); //~ ERROR the trait bound
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0277]: the trait bound `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn<()>` is not satisfied
2+
--> $DIR/fn-exception-target-features.rs:16:10
3+
|
4+
LL | test(target_feature);
5+
| ---- ^^^^^^^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `test`
10+
--> $DIR/fn-exception-target-features.rs:13:17
11+
|
12+
LL | fn test(f: impl async Fn()) {}
13+
| ^^^^^^^^^^ required by this bound in `test`
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ edition: 2021
2+
3+
#![feature(async_closure)]
4+
5+
use std::pin::Pin;
6+
use std::future::Future;
7+
8+
unsafe extern "Rust" {
9+
pub unsafe fn unsafety() -> Pin<Box<dyn Future<Output = ()> + 'static>>;
10+
}
11+
12+
unsafe extern "C" {
13+
pub safe fn abi() -> Pin<Box<dyn Future<Output = ()> + 'static>>;
14+
}
15+
16+
fn test(f: impl async Fn()) {}
17+
18+
fn main() {
19+
test(unsafety); //~ ERROR the trait bound
20+
test(abi); //~ ERROR the trait bound
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0277]: the trait bound `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {unsafety}: AsyncFn<()>` is not satisfied
2+
--> $DIR/fn-exception.rs:19:10
3+
|
4+
LL | test(unsafety);
5+
| ---- ^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {unsafety}`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `test`
10+
--> $DIR/fn-exception.rs:16:17
11+
|
12+
LL | fn test(f: impl async Fn()) {}
13+
| ^^^^^^^^^^ required by this bound in `test`
14+
15+
error[E0277]: the trait bound `extern "C" fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {abi}: AsyncFn<()>` is not satisfied
16+
--> $DIR/fn-exception.rs:20:10
17+
|
18+
LL | test(abi);
19+
| ---- ^^^ the trait `AsyncFn<()>` is not implemented for fn item `extern "C" fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {abi}`
20+
| |
21+
| required by a bound introduced by this call
22+
|
23+
note: required by a bound in `test`
24+
--> $DIR/fn-exception.rs:16:17
25+
|
26+
LL | fn test(f: impl async Fn()) {}
27+
| ^^^^^^^^^^ required by this bound in `test`
28+
29+
error: aborting due to 2 previous errors
30+
31+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)