Skip to content

Commit 60913c2

Browse files
authored
Rollup merge of rust-lang#104592 - ComputerDruid:async_check, r=compiler-errors
Ensure async trait impls are async (or otherwise return an opaque type) As a workaround for the full `#[refine]` semantics not being implemented yet, forbit returning a concrete future type like `Box<dyn Future>` or a manually implemented Future. `-> impl Future` is still permitted; while that can also cause accidental refinement, that's behind a different feature gate (`return_position_impl_trait_in_trait`) and that problem exists regardless of whether the trait method is async, so will have to be solved more generally. Fixes rust-lang#102745
2 parents 5261162 + da98ef9 commit 60913c2

12 files changed

+146
-25
lines changed

compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
2020
.where_label = this `where` clause might not match the one in the trait
2121
.bounds_label = this bound might be missing in the impl
2222
23+
hir_analysis_async_trait_impl_should_be_async =
24+
method `{$method_name}` should be async because the method from the trait is async
25+
.trait_item_label = required because the trait method is async
26+
2327
hir_analysis_drop_impl_on_wrong_item =
2428
the `Drop` trait may only be implemented for local structs, enums, and unions
2529
.label = must be a struct, enum, or union in the current crate

compiler/rustc_hir_analysis/src/check/compare_method.rs

+32
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ pub(crate) fn compare_impl_method<'tcx>(
6767
return;
6868
}
6969

70+
if let Err(_) = compare_asyncness(tcx, impl_m, impl_m_span, trait_m, trait_item_span) {
71+
return;
72+
}
73+
7074
if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
7175
{
7276
return;
@@ -323,6 +327,34 @@ fn compare_predicate_entailment<'tcx>(
323327
Ok(())
324328
}
325329

330+
fn compare_asyncness<'tcx>(
331+
tcx: TyCtxt<'tcx>,
332+
impl_m: &ty::AssocItem,
333+
impl_m_span: Span,
334+
trait_m: &ty::AssocItem,
335+
trait_item_span: Option<Span>,
336+
) -> Result<(), ErrorGuaranteed> {
337+
if tcx.asyncness(trait_m.def_id) == hir::IsAsync::Async {
338+
match tcx.fn_sig(impl_m.def_id).skip_binder().output().kind() {
339+
ty::Alias(ty::Opaque, ..) => {
340+
// allow both `async fn foo()` and `fn foo() -> impl Future`
341+
}
342+
ty::Error(rustc_errors::ErrorGuaranteed { .. }) => {
343+
// We don't know if it's ok, but at least it's already an error.
344+
}
345+
_ => {
346+
return Err(tcx.sess.emit_err(crate::errors::AsyncTraitImplShouldBeAsync {
347+
span: impl_m_span,
348+
method_name: trait_m.name,
349+
trait_item_span,
350+
}));
351+
}
352+
};
353+
}
354+
355+
Ok(())
356+
}
357+
326358
#[instrument(skip(tcx), level = "debug", ret)]
327359
pub fn collect_trait_impl_trait_tys<'tcx>(
328360
tcx: TyCtxt<'tcx>,

compiler/rustc_hir_analysis/src/errors.rs

+11
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
5151
pub ident: Ident,
5252
}
5353

54+
#[derive(Diagnostic)]
55+
#[diag(hir_analysis_async_trait_impl_should_be_async)]
56+
pub struct AsyncTraitImplShouldBeAsync {
57+
#[primary_span]
58+
// #[label]
59+
pub span: Span,
60+
#[label(trait_item_label)]
61+
pub trait_item_span: Option<Span>,
62+
pub method_name: Symbol,
63+
}
64+
5465
#[derive(Diagnostic)]
5566
#[diag(hir_analysis_drop_impl_on_wrong_item, code = "E0120")]
5667
pub struct DropImplOnWrongItem {

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

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// check-pass
21
// edition: 2021
32

43
#![feature(async_fn_in_trait)]
@@ -13,11 +12,9 @@ trait MyTrait {
1312
}
1413

1514
impl MyTrait for i32 {
16-
// This will break once a PR that implements #102745 is merged
1715
fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
18-
Box::pin(async {
19-
*self
20-
})
16+
//~^ ERROR method `foo` should be async
17+
Box::pin(async { *self })
2118
}
2219
}
2320

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: method `foo` should be async because the method from the trait is async
2+
--> $DIR/async-example-desugared-boxed.rs:15:5
3+
|
4+
LL | async fn foo(&self) -> i32;
5+
| --------------------------- required because the trait method is async
6+
...
7+
LL | fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9+
10+
error: aborting due to previous error
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// check-pass
2+
// edition: 2021
3+
4+
#![feature(async_fn_in_trait)]
5+
#![feature(return_position_impl_trait_in_trait)]
6+
#![allow(incomplete_features)]
7+
8+
use std::future::Future;
9+
use std::pin::Pin;
10+
use std::task::Poll;
11+
12+
trait MyTrait {
13+
async fn foo(&self) -> i32;
14+
}
15+
16+
#[derive(Clone)]
17+
struct MyFuture(i32);
18+
19+
impl Future for MyFuture {
20+
type Output = i32;
21+
fn poll(
22+
self: Pin<&mut Self>,
23+
_: &mut std::task::Context<'_>,
24+
) -> Poll<<Self as Future>::Output> {
25+
Poll::Ready(self.0)
26+
}
27+
}
28+
29+
impl MyTrait for i32 {
30+
// FIXME: this should eventually require `#[refine]` to compile, because it also provides
31+
// `Clone`.
32+
fn foo(&self) -> impl Future<Output = i32> + Clone {
33+
MyFuture(*self)
34+
}
35+
}
36+
37+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// edition: 2021
2+
3+
#![feature(async_fn_in_trait)]
4+
#![feature(return_position_impl_trait_in_trait)]
5+
#![allow(incomplete_features)]
6+
7+
use std::future::Future;
8+
use std::task::Poll;
9+
10+
trait MyTrait {
11+
async fn foo(&self) -> i32;
12+
}
13+
14+
struct MyFuture;
15+
impl Future for MyFuture {
16+
type Output = i32;
17+
fn poll(self: std::pin::Pin<&mut Self>, _: &mut std::task::Context<'_>) -> Poll<Self::Output> {
18+
Poll::Ready(0)
19+
}
20+
}
21+
22+
impl MyTrait for u32 {
23+
fn foo(&self) -> MyFuture {
24+
//~^ ERROR method `foo` should be async
25+
MyFuture
26+
}
27+
}
28+
29+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: method `foo` should be async because the method from the trait is async
2+
--> $DIR/async-example-desugared-manual.rs:23:5
3+
|
4+
LL | async fn foo(&self) -> i32;
5+
| --------------------------- required because the trait method is async
6+
...
7+
LL | fn foo(&self) -> MyFuture {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
9+
10+
error: aborting due to previous error
11+

src/test/ui/async-await/in-trait/async-example-desugared.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,8 @@ trait MyTrait {
1212
}
1313

1414
impl MyTrait for i32 {
15-
// This will break once a PR that implements #102745 is merged
1615
fn foo(&self) -> impl Future<Output = i32> + '_ {
17-
async {
18-
*self
19-
}
16+
async { *self }
2017
}
2118
}
2219

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ trait MyTrait {
99

1010
impl MyTrait for i32 {
1111
fn foo(&self) -> i32 {
12-
//~^ ERROR: `i32` is not a future [E0277]
12+
//~^ ERROR: method `foo` should be async
1313
*self
1414
}
1515
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
error[E0277]: `i32` is not a future
2-
--> $DIR/fn-not-async-err.rs:11:22
3-
|
4-
LL | fn foo(&self) -> i32 {
5-
| ^^^ `i32` is not a future
6-
|
7-
= help: the trait `Future` is not implemented for `i32`
8-
= note: i32 must be a future or must implement `IntoFuture` to be awaited
9-
note: required by a bound in `MyTrait::foo::{opaque#0}`
10-
--> $DIR/fn-not-async-err.rs:7:28
1+
error: method `foo` should be async because the method from the trait is async
2+
--> $DIR/fn-not-async-err.rs:11:5
113
|
124
LL | async fn foo(&self) -> i32;
13-
| ^^^ required by this bound in `MyTrait::foo::{opaque#0}`
5+
| --------------------------- required because the trait method is async
6+
...
7+
LL | fn foo(&self) -> i32 {
8+
| ^^^^^^^^^^^^^^^^^^^^
149

1510
error: aborting due to previous error
1611

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

src/test/ui/async-await/in-trait/fn-not-async-err2.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ trait MyTrait {
1212
impl MyTrait for i32 {
1313
fn foo(&self) -> impl Future<Output = i32> {
1414
//~^ ERROR `impl Trait` only allowed in function and inherent method return types, not in `impl` method return [E0562]
15-
async {
16-
*self
17-
}
15+
async { *self }
1816
}
1917
}
2018

0 commit comments

Comments
 (0)