mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 02:03:53 +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
|
.where_label = this `where` clause might not match the one in the trait
|
||||||
.bounds_label = this bound might be missing in the impl
|
.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 =
|
hir_analysis_drop_impl_on_wrong_item =
|
||||||
the `Drop` trait may only be implemented for local structs, enums, and unions
|
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
|
.label = must be a struct, enum, or union in the current crate
|
||||||
|
@ -67,6 +67,10 @@ pub(crate) fn compare_impl_method<'tcx>(
|
|||||||
return;
|
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)
|
if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -323,6 +327,34 @@ fn compare_predicate_entailment<'tcx>(
|
|||||||
Ok(())
|
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)]
|
#[instrument(skip(tcx), level = "debug", ret)]
|
||||||
pub fn collect_trait_impl_trait_tys<'tcx>(
|
pub fn collect_trait_impl_trait_tys<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
@ -51,6 +51,17 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
|
|||||||
pub ident: Ident,
|
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)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(hir_analysis_drop_impl_on_wrong_item, code = "E0120")]
|
#[diag(hir_analysis_drop_impl_on_wrong_item, code = "E0120")]
|
||||||
pub struct DropImplOnWrongItem {
|
pub struct DropImplOnWrongItem {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// check-pass
|
|
||||||
// edition: 2021
|
// edition: 2021
|
||||||
|
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
@ -13,11 +12,9 @@ trait MyTrait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MyTrait for i32 {
|
impl MyTrait for i32 {
|
||||||
// This will break once a PR that implements #102745 is merged
|
|
||||||
fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
|
fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
|
||||||
Box::pin(async {
|
//~^ ERROR method `foo` should be async
|
||||||
*self
|
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 {
|
impl MyTrait for i32 {
|
||||||
// This will break once a PR that implements #102745 is merged
|
|
||||||
fn foo(&self) -> impl Future<Output = i32> + '_ {
|
fn foo(&self) -> impl Future<Output = i32> + '_ {
|
||||||
async {
|
async { *self }
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ trait MyTrait {
|
|||||||
|
|
||||||
impl MyTrait for i32 {
|
impl MyTrait for i32 {
|
||||||
fn foo(&self) -> i32 {
|
fn foo(&self) -> i32 {
|
||||||
//~^ ERROR: `i32` is not a future [E0277]
|
//~^ ERROR: method `foo` should be async
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
error[E0277]: `i32` is not a future
|
error: method `foo` should be async because the method from the trait is async
|
||||||
--> $DIR/fn-not-async-err.rs:11:22
|
--> $DIR/fn-not-async-err.rs:11:5
|
||||||
|
|
|
||||||
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
|
|
||||||
|
|
|
|
||||||
LL | async fn foo(&self) -> i32;
|
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
|
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 {
|
impl MyTrait for i32 {
|
||||||
fn foo(&self) -> impl Future<Output = 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]
|
//~^ ERROR `impl Trait` only allowed in function and inherent method return types, not in `impl` method return [E0562]
|
||||||
async {
|
async { *self }
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user