Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1d6077d

Browse files
authoredAug 8, 2024
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 ee26ae8 + 8c14c3d commit 1d6077d

File tree

4 files changed

+125
-23
lines changed

4 files changed

+125
-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,27 @@
1+
//@ edition: 2021
2+
3+
#![feature(async_closure, target_feature_11)]
4+
// `target_feature_11` just to test safe functions w/ target features.
5+
6+
use std::pin::Pin;
7+
use std::future::Future;
8+
9+
unsafe extern "Rust" {
10+
pub unsafe fn unsafety() -> Pin<Box<dyn Future<Output = ()> + 'static>>;
11+
}
12+
13+
unsafe extern "C" {
14+
pub safe fn abi() -> Pin<Box<dyn Future<Output = ()> + 'static>>;
15+
}
16+
17+
18+
#[target_feature(enable = "sse2")]
19+
fn target_feature() -> Pin<Box<dyn Future<Output = ()> + 'static>> { todo!() }
20+
21+
fn test(f: impl async Fn()) {}
22+
23+
fn main() {
24+
test(unsafety); //~ ERROR the trait bound
25+
test(abi); //~ ERROR the trait bound
26+
test(target_feature); //~ ERROR the trait bound
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
error[E0277]: the trait bound `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {unsafety}: AsyncFn<()>` is not satisfied
2+
--> $DIR/fn-exception.rs:24: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:21: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:25: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:21:17
25+
|
26+
LL | fn test(f: impl async Fn()) {}
27+
| ^^^^^^^^^^ required by this bound in `test`
28+
29+
error[E0277]: the trait bound `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn<()>` is not satisfied
30+
--> $DIR/fn-exception.rs:26:10
31+
|
32+
LL | test(target_feature);
33+
| ---- ^^^^^^^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
34+
| |
35+
| required by a bound introduced by this call
36+
|
37+
note: required by a bound in `test`
38+
--> $DIR/fn-exception.rs:21:17
39+
|
40+
LL | fn test(f: impl async Fn()) {}
41+
| ^^^^^^^^^^ required by this bound in `test`
42+
43+
error: aborting due to 3 previous errors
44+
45+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)
Please sign in to comment.