Skip to content

Commit 8b5cf7c

Browse files
authored
Rollup merge of rust-lang#120103 - compiler-errors:concrete-afits, r=oli-obk
Make it so that async-fn-in-trait is compatible with a concrete future in implementation There's no technical reason why an AFIT like `async fn foo()` cannot be satisfied with an implementation signature like `fn foo() -> Pin<Box<dyn Future<Output = ()> + 'static>>`. We rejected this previously because we were uncertain about how AFITs worked with refinement, but I don't believe this needs to be a restriction any longer. r? oli-obk
2 parents 766182d + 1a3214b commit 8b5cf7c

9 files changed

+117
-70
lines changed

compiler/rustc_hir_analysis/messages.ftl

+3-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the as
3333
3434
hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes
3535
36-
hir_analysis_async_trait_impl_should_be_async =
37-
method `{$method_name}` should be async because the method from the trait is async
38-
.trait_item_label = required because the trait method is async
39-
4036
hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit while auto-dereferencing `{$ty}`
4137
.label = deref recursion limit reached
4238
.help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`)
@@ -210,6 +206,9 @@ hir_analysis_manual_implementation =
210206
.label = manual implementations of `{$trait_name}` are experimental
211207
.help = add `#![feature(unboxed_closures)]` to the crate attributes to enable
212208
209+
hir_analysis_method_should_return_future = method should be `async` or return a future, but it is synchronous
210+
.note = this method is `async` so it expects a future to be returned
211+
213212
hir_analysis_missing_one_of_trait_item = not all trait items implemented, missing one of: `{$missing_items_msg}`
214213
.label = missing one of `{$missing_items_msg}` in implementation
215214
.note = required because of this annotation

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+53-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::potentially_plural_count;
2-
use crate::errors::LifetimesOrBoundsMismatchOnTrait;
2+
use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture};
33
use hir::def_id::{DefId, DefIdMap, LocalDefId};
44
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
55
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed};
@@ -10,7 +10,7 @@ use rustc_hir::{GenericParamKind, ImplItemKind};
1010
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1111
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
1212
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
13-
use rustc_infer::traits::util;
13+
use rustc_infer::traits::{util, FulfillmentError};
1414
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1515
use rustc_middle::ty::fold::BottomUpFolder;
1616
use rustc_middle::ty::util::ExplicitSelf;
@@ -74,7 +74,6 @@ fn check_method_is_structurally_compatible<'tcx>(
7474
compare_generic_param_kinds(tcx, impl_m, trait_m, delay)?;
7575
compare_number_of_method_arguments(tcx, impl_m, trait_m, delay)?;
7676
compare_synthetic_generics(tcx, impl_m, trait_m, delay)?;
77-
compare_asyncness(tcx, impl_m, trait_m, delay)?;
7877
check_region_bounds_on_impl_item(tcx, impl_m, trait_m, delay)?;
7978
Ok(())
8079
}
@@ -414,36 +413,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> {
414413
}
415414
}
416415

417-
fn compare_asyncness<'tcx>(
418-
tcx: TyCtxt<'tcx>,
419-
impl_m: ty::AssocItem,
420-
trait_m: ty::AssocItem,
421-
delay: bool,
422-
) -> Result<(), ErrorGuaranteed> {
423-
if tcx.asyncness(trait_m.def_id).is_async() {
424-
match tcx.fn_sig(impl_m.def_id).skip_binder().skip_binder().output().kind() {
425-
ty::Alias(ty::Opaque, ..) => {
426-
// allow both `async fn foo()` and `fn foo() -> impl Future`
427-
}
428-
ty::Error(_) => {
429-
// We don't know if it's ok, but at least it's already an error.
430-
}
431-
_ => {
432-
return Err(tcx
433-
.dcx()
434-
.create_err(crate::errors::AsyncTraitImplShouldBeAsync {
435-
span: tcx.def_span(impl_m.def_id),
436-
method_name: trait_m.name,
437-
trait_item_span: tcx.hir().span_if_local(trait_m.def_id),
438-
})
439-
.emit_unless(delay));
440-
}
441-
};
442-
}
443-
444-
Ok(())
445-
}
446-
447416
/// Given a method def-id in an impl, compare the method signature of the impl
448417
/// against the trait that it's implementing. In doing so, infer the hidden types
449418
/// that this method's signature provides to satisfy each return-position `impl Trait`
@@ -695,8 +664,13 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
695664
// RPITs.
696665
let errors = ocx.select_all_or_error();
697666
if !errors.is_empty() {
698-
let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
699-
return Err(reported);
667+
if let Err(guar) = try_report_async_mismatch(tcx, infcx, &errors, trait_m, impl_m, impl_sig)
668+
{
669+
return Err(guar);
670+
}
671+
672+
let guar = infcx.err_ctxt().report_fulfillment_errors(errors);
673+
return Err(guar);
700674
}
701675

702676
// Finally, resolve all regions. This catches wily misuses of
@@ -2248,3 +2222,47 @@ fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
22482222
ty::AssocKind::Type => "type",
22492223
}
22502224
}
2225+
2226+
/// Manually check here that `async fn foo()` wasn't matched against `fn foo()`,
2227+
/// and extract a better error if so.
2228+
fn try_report_async_mismatch<'tcx>(
2229+
tcx: TyCtxt<'tcx>,
2230+
infcx: &InferCtxt<'tcx>,
2231+
errors: &[FulfillmentError<'tcx>],
2232+
trait_m: ty::AssocItem,
2233+
impl_m: ty::AssocItem,
2234+
impl_sig: ty::FnSig<'tcx>,
2235+
) -> Result<(), ErrorGuaranteed> {
2236+
if !tcx.asyncness(trait_m.def_id).is_async() {
2237+
return Ok(());
2238+
}
2239+
2240+
let ty::Alias(ty::Projection, ty::AliasTy { def_id: async_future_def_id, .. }) =
2241+
*tcx.fn_sig(trait_m.def_id).skip_binder().skip_binder().output().kind()
2242+
else {
2243+
bug!("expected `async fn` to return an RPITIT");
2244+
};
2245+
2246+
for error in errors {
2247+
if let traits::BindingObligation(def_id, _) = *error.root_obligation.cause.code()
2248+
&& def_id == async_future_def_id
2249+
&& let Some(proj) = error.root_obligation.predicate.to_opt_poly_projection_pred()
2250+
&& let Some(proj) = proj.no_bound_vars()
2251+
&& infcx.can_eq(
2252+
error.root_obligation.param_env,
2253+
proj.term.ty().unwrap(),
2254+
impl_sig.output(),
2255+
)
2256+
{
2257+
// FIXME: We should suggest making the fn `async`, but extracting
2258+
// the right span is a bit difficult.
2259+
return Err(tcx.sess.dcx().emit_err(MethodShouldReturnFuture {
2260+
span: tcx.def_span(impl_m.def_id),
2261+
method_name: trait_m.name,
2262+
trait_item_span: tcx.hir().span_if_local(trait_m.def_id),
2263+
}));
2264+
}
2265+
}
2266+
2267+
Ok(())
2268+
}

compiler/rustc_hir_analysis/src/errors.rs

+10-11
Original file line numberDiff line numberDiff line change
@@ -166,17 +166,6 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
166166
pub ident: Ident,
167167
}
168168

169-
#[derive(Diagnostic)]
170-
#[diag(hir_analysis_async_trait_impl_should_be_async)]
171-
pub struct AsyncTraitImplShouldBeAsync {
172-
#[primary_span]
173-
// #[label]
174-
pub span: Span,
175-
#[label(hir_analysis_trait_item_label)]
176-
pub trait_item_span: Option<Span>,
177-
pub method_name: Symbol,
178-
}
179-
180169
#[derive(Diagnostic)]
181170
#[diag(hir_analysis_drop_impl_on_wrong_item, code = E0120)]
182171
pub struct DropImplOnWrongItem {
@@ -1512,6 +1501,16 @@ pub struct NotSupportedDelegation<'a> {
15121501
pub callee_span: Span,
15131502
}
15141503

1504+
#[derive(Diagnostic)]
1505+
#[diag(hir_analysis_method_should_return_future)]
1506+
pub struct MethodShouldReturnFuture {
1507+
#[primary_span]
1508+
pub span: Span,
1509+
pub method_name: Symbol,
1510+
#[note]
1511+
pub trait_item_span: Option<Span>,
1512+
}
1513+
15151514
#[derive(Diagnostic)]
15161515
#[diag(hir_analysis_unused_generic_parameter)]
15171516
pub(crate) struct UnusedGenericParameter {

tests/ui/async-await/in-trait/async-example-desugared-boxed.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
// edition: 2021
2+
// check-pass
23

34
use std::future::Future;
45
use std::pin::Pin;
56

6-
trait MyTrait {
7+
#[allow(async_fn_in_trait)]
8+
pub trait MyTrait {
79
async fn foo(&self) -> i32;
810
}
911

1012
impl MyTrait for i32 {
13+
#[warn(refining_impl_trait)]
1114
fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
12-
//~^ ERROR method `foo` should be async
15+
//~^ WARN impl trait in impl method signature does not match trait method signature
1316
Box::pin(async { *self })
1417
}
1518
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
error: method `foo` should be async because the method from the trait is async
2-
--> $DIR/async-example-desugared-boxed.rs:11:5
1+
warning: impl trait in impl method signature does not match trait method signature
2+
--> $DIR/async-example-desugared-boxed.rs:14:22
33
|
44
LL | async fn foo(&self) -> i32;
5-
| --------------------------- required because the trait method is async
5+
| --------------------------- return type from trait method defined here
66
...
77
LL | fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
8-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9+
|
10+
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
11+
note: the lint level is defined here
12+
--> $DIR/async-example-desugared-boxed.rs:13:12
13+
|
14+
LL | #[warn(refining_impl_trait)]
15+
| ^^^^^^^^^^^^^^^^^^^
16+
help: replace the return type so that it matches the trait
17+
|
18+
LL | fn foo(&self) -> impl Future<Output = i32> {
19+
| ~~~~~~~~~~~~~~~~~~~~~~~~~
920

10-
error: aborting due to 1 previous error
21+
warning: 1 warning emitted
1122

tests/ui/async-await/in-trait/async-example-desugared-manual.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// edition: 2021
2+
// check-pass
23

34
use std::future::Future;
45
use std::task::Poll;
56

6-
trait MyTrait {
7+
#[allow(async_fn_in_trait)]
8+
pub trait MyTrait {
79
async fn foo(&self) -> i32;
810
}
911

10-
struct MyFuture;
12+
pub struct MyFuture;
1113
impl Future for MyFuture {
1214
type Output = i32;
1315
fn poll(self: std::pin::Pin<&mut Self>, _: &mut std::task::Context<'_>) -> Poll<Self::Output> {
@@ -16,8 +18,9 @@ impl Future for MyFuture {
1618
}
1719

1820
impl MyTrait for u32 {
21+
#[warn(refining_impl_trait)]
1922
fn foo(&self) -> MyFuture {
20-
//~^ ERROR method `foo` should be async
23+
//~^ WARN impl trait in impl method signature does not match trait method signature
2124
MyFuture
2225
}
2326
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
error: method `foo` should be async because the method from the trait is async
2-
--> $DIR/async-example-desugared-manual.rs:19:5
1+
warning: impl trait in impl method signature does not match trait method signature
2+
--> $DIR/async-example-desugared-manual.rs:22:22
33
|
44
LL | async fn foo(&self) -> i32;
5-
| --------------------------- required because the trait method is async
5+
| --------------------------- return type from trait method defined here
66
...
77
LL | fn foo(&self) -> MyFuture {
8-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
8+
| ^^^^^^^^
9+
|
10+
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
11+
note: the lint level is defined here
12+
--> $DIR/async-example-desugared-manual.rs:21:12
13+
|
14+
LL | #[warn(refining_impl_trait)]
15+
| ^^^^^^^^^^^^^^^^^^^
16+
help: replace the return type so that it matches the trait
17+
|
18+
LL | fn foo(&self) -> impl Future<Output = i32> {
19+
| ~~~~~~~~~~~~~~~~~~~~~~~~~
920

10-
error: aborting due to 1 previous error
21+
warning: 1 warning emitted
1122

tests/ui/async-await/in-trait/fn-not-async-err.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ trait MyTrait {
88

99
impl MyTrait for i32 {
1010
fn foo(&self) -> i32 {
11-
//~^ ERROR: method `foo` should be async
11+
//~^ ERROR: method should be `async` or return a future, but it is synchronous
1212
*self
1313
}
1414
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
error: method `foo` should be async because the method from the trait is async
1+
error: method should be `async` or return a future, but it is synchronous
22
--> $DIR/fn-not-async-err.rs:10:5
33
|
4-
LL | async fn foo(&self) -> i32;
5-
| --------------------------- required because the trait method is async
6-
...
74
LL | fn foo(&self) -> i32 {
85
| ^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: this method is `async` so it expects a future to be returned
8+
--> $DIR/fn-not-async-err.rs:6:5
9+
|
10+
LL | async fn foo(&self) -> i32;
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
912

1013
error: aborting due to 1 previous error
1114

0 commit comments

Comments
 (0)