mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
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 #102745
This commit is contained in:
parent
b70baa4f92
commit
da98ef9a5d
@ -20,6 +20,10 @@ hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
|
||||
.where_label = this `where` clause might not match the one in the trait
|
||||
.bounds_label = this bound might be missing in the impl
|
||||
|
||||
hir_analysis_async_trait_impl_should_be_async =
|
||||
method `{$method_name}` should be async because the method from the trait is async
|
||||
.trait_item_label = required because the trait method is async
|
||||
|
||||
hir_analysis_drop_impl_on_wrong_item =
|
||||
the `Drop` trait may only be implemented for local structs, enums, and unions
|
||||
.label = must be a struct, enum, or union in the current crate
|
||||
|
@ -67,6 +67,10 @@ pub(crate) fn compare_impl_method<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(_) = compare_asyncness(tcx, impl_m, impl_m_span, trait_m, trait_item_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
|
||||
{
|
||||
return;
|
||||
@ -323,6 +327,34 @@ fn compare_predicate_entailment<'tcx>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compare_asyncness<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_m: &ty::AssocItem,
|
||||
impl_m_span: Span,
|
||||
trait_m: &ty::AssocItem,
|
||||
trait_item_span: Option<Span>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if tcx.asyncness(trait_m.def_id) == hir::IsAsync::Async {
|
||||
match tcx.fn_sig(impl_m.def_id).skip_binder().output().kind() {
|
||||
ty::Alias(ty::Opaque, ..) => {
|
||||
// allow both `async fn foo()` and `fn foo() -> impl Future`
|
||||
}
|
||||
ty::Error(rustc_errors::ErrorGuaranteed { .. }) => {
|
||||
// We don't know if it's ok, but at least it's already an error.
|
||||
}
|
||||
_ => {
|
||||
return Err(tcx.sess.emit_err(crate::errors::AsyncTraitImplShouldBeAsync {
|
||||
span: impl_m_span,
|
||||
method_name: trait_m.name,
|
||||
trait_item_span,
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug", ret)]
|
||||
pub fn collect_trait_impl_trait_tys<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
@ -51,6 +51,17 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
|
||||
pub ident: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_async_trait_impl_should_be_async)]
|
||||
pub struct AsyncTraitImplShouldBeAsync {
|
||||
#[primary_span]
|
||||
// #[label]
|
||||
pub span: Span,
|
||||
#[label(trait_item_label)]
|
||||
pub trait_item_span: Option<Span>,
|
||||
pub method_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_drop_impl_on_wrong_item, code = "E0120")]
|
||||
pub struct DropImplOnWrongItem {
|
||||
|
@ -1,4 +1,3 @@
|
||||
// check-pass
|
||||
// edition: 2021
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
@ -13,11 +12,9 @@ trait MyTrait {
|
||||
}
|
||||
|
||||
impl MyTrait for i32 {
|
||||
// This will break once a PR that implements #102745 is merged
|
||||
fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
|
||||
Box::pin(async {
|
||||
*self
|
||||
})
|
||||
//~^ ERROR method `foo` should be async
|
||||
Box::pin(async { *self })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
error: method `foo` should be async because the method from the trait is async
|
||||
--> $DIR/async-example-desugared-boxed.rs:15:5
|
||||
|
|
||||
LL | async fn foo(&self) -> i32;
|
||||
| --------------------------- required because the trait method is async
|
||||
...
|
||||
LL | fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,37 @@
|
||||
// check-pass
|
||||
// edition: 2021
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
#![feature(return_position_impl_trait_in_trait)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::Poll;
|
||||
|
||||
trait MyTrait {
|
||||
async fn foo(&self) -> i32;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MyFuture(i32);
|
||||
|
||||
impl Future for MyFuture {
|
||||
type Output = i32;
|
||||
fn poll(
|
||||
self: Pin<&mut Self>,
|
||||
_: &mut std::task::Context<'_>,
|
||||
) -> Poll<<Self as Future>::Output> {
|
||||
Poll::Ready(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MyTrait for i32 {
|
||||
// FIXME: this should eventually require `#[refine]` to compile, because it also provides
|
||||
// `Clone`.
|
||||
fn foo(&self) -> impl Future<Output = i32> + Clone {
|
||||
MyFuture(*self)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,29 @@
|
||||
// edition: 2021
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
#![feature(return_position_impl_trait_in_trait)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::future::Future;
|
||||
use std::task::Poll;
|
||||
|
||||
trait MyTrait {
|
||||
async fn foo(&self) -> i32;
|
||||
}
|
||||
|
||||
struct MyFuture;
|
||||
impl Future for MyFuture {
|
||||
type Output = i32;
|
||||
fn poll(self: std::pin::Pin<&mut Self>, _: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||
Poll::Ready(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MyTrait for u32 {
|
||||
fn foo(&self) -> MyFuture {
|
||||
//~^ ERROR method `foo` should be async
|
||||
MyFuture
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,11 @@
|
||||
error: method `foo` should be async because the method from the trait is async
|
||||
--> $DIR/async-example-desugared-manual.rs:23:5
|
||||
|
|
||||
LL | async fn foo(&self) -> i32;
|
||||
| --------------------------- required because the trait method is async
|
||||
...
|
||||
LL | fn foo(&self) -> MyFuture {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -12,11 +12,8 @@ trait MyTrait {
|
||||
}
|
||||
|
||||
impl MyTrait for i32 {
|
||||
// This will break once a PR that implements #102745 is merged
|
||||
fn foo(&self) -> impl Future<Output = i32> + '_ {
|
||||
async {
|
||||
*self
|
||||
}
|
||||
async { *self }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ trait MyTrait {
|
||||
|
||||
impl MyTrait for i32 {
|
||||
fn foo(&self) -> i32 {
|
||||
//~^ ERROR: `i32` is not a future [E0277]
|
||||
//~^ ERROR: method `foo` should be async
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,11 @@
|
||||
error[E0277]: `i32` is not a future
|
||||
--> $DIR/fn-not-async-err.rs:11:22
|
||||
|
|
||||
LL | fn foo(&self) -> i32 {
|
||||
| ^^^ `i32` is not a future
|
||||
|
|
||||
= help: the trait `Future` is not implemented for `i32`
|
||||
= note: i32 must be a future or must implement `IntoFuture` to be awaited
|
||||
note: required by a bound in `MyTrait::foo::{opaque#0}`
|
||||
--> $DIR/fn-not-async-err.rs:7:28
|
||||
error: method `foo` should be async because the method from the trait is async
|
||||
--> $DIR/fn-not-async-err.rs:11:5
|
||||
|
|
||||
LL | async fn foo(&self) -> i32;
|
||||
| ^^^ required by this bound in `MyTrait::foo::{opaque#0}`
|
||||
| --------------------------- required because the trait method is async
|
||||
...
|
||||
LL | fn foo(&self) -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
@ -12,9 +12,7 @@ trait MyTrait {
|
||||
impl MyTrait for i32 {
|
||||
fn foo(&self) -> impl Future<Output = i32> {
|
||||
//~^ ERROR `impl Trait` only allowed in function and inherent method return types, not in `impl` method return [E0562]
|
||||
async {
|
||||
*self
|
||||
}
|
||||
async { *self }
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user