about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDan Johnson <computerdruid@google.com>2022-11-02 17:45:08 -0700
committerDan Johnson <computerdruid@google.com>2022-12-14 16:38:21 -0800
commitda98ef9a5d0b8a4fb90e1d845506880fae9e7352 (patch)
tree255ffbbb7f63e882d7d1173c08f9ad81b7f9134c
parentb70baa4f922a1809d79caeaeb902800c3be283b9 (diff)
downloadrust-da98ef9a5d0b8a4fb90e1d845506880fae9e7352.tar.gz
rust-da98ef9a5d0b8a4fb90e1d845506880fae9e7352.zip
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
-rw-r--r--compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl4
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs32
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs11
-rw-r--r--src/test/ui/async-await/in-trait/async-example-desugared-boxed.rs7
-rw-r--r--src/test/ui/async-await/in-trait/async-example-desugared-boxed.stderr11
-rw-r--r--src/test/ui/async-await/in-trait/async-example-desugared-extra.rs37
-rw-r--r--src/test/ui/async-await/in-trait/async-example-desugared-manual.rs29
-rw-r--r--src/test/ui/async-await/in-trait/async-example-desugared-manual.stderr11
-rw-r--r--src/test/ui/async-await/in-trait/async-example-desugared.rs5
-rw-r--r--src/test/ui/async-await/in-trait/fn-not-async-err.rs2
-rw-r--r--src/test/ui/async-await/in-trait/fn-not-async-err.stderr18
-rw-r--r--src/test/ui/async-await/in-trait/fn-not-async-err2.rs4
12 files changed, 146 insertions, 25 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
index e33323a7795..26cdf8a58f3 100644
--- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
@@ -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
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
index ba7d31cea2e..bfa37c05a19 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_method.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -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>,
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index d9697c63c56..d383fcacb3a 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -52,6 +52,17 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
 }
 
 #[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 {
     #[primary_span]
diff --git a/src/test/ui/async-await/in-trait/async-example-desugared-boxed.rs b/src/test/ui/async-await/in-trait/async-example-desugared-boxed.rs
index 61d7e2520ea..1b1b3cffd58 100644
--- a/src/test/ui/async-await/in-trait/async-example-desugared-boxed.rs
+++ b/src/test/ui/async-await/in-trait/async-example-desugared-boxed.rs
@@ -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 })
     }
 }
 
diff --git a/src/test/ui/async-await/in-trait/async-example-desugared-boxed.stderr b/src/test/ui/async-await/in-trait/async-example-desugared-boxed.stderr
new file mode 100644
index 00000000000..60fa534a64f
--- /dev/null
+++ b/src/test/ui/async-await/in-trait/async-example-desugared-boxed.stderr
@@ -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
+
diff --git a/src/test/ui/async-await/in-trait/async-example-desugared-extra.rs b/src/test/ui/async-await/in-trait/async-example-desugared-extra.rs
new file mode 100644
index 00000000000..81e1e59a362
--- /dev/null
+++ b/src/test/ui/async-await/in-trait/async-example-desugared-extra.rs
@@ -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() {}
diff --git a/src/test/ui/async-await/in-trait/async-example-desugared-manual.rs b/src/test/ui/async-await/in-trait/async-example-desugared-manual.rs
new file mode 100644
index 00000000000..71473e7455f
--- /dev/null
+++ b/src/test/ui/async-await/in-trait/async-example-desugared-manual.rs
@@ -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() {}
diff --git a/src/test/ui/async-await/in-trait/async-example-desugared-manual.stderr b/src/test/ui/async-await/in-trait/async-example-desugared-manual.stderr
new file mode 100644
index 00000000000..567a36a86d1
--- /dev/null
+++ b/src/test/ui/async-await/in-trait/async-example-desugared-manual.stderr
@@ -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
+
diff --git a/src/test/ui/async-await/in-trait/async-example-desugared.rs b/src/test/ui/async-await/in-trait/async-example-desugared.rs
index 1313c9edd86..fb92ec78674 100644
--- a/src/test/ui/async-await/in-trait/async-example-desugared.rs
+++ b/src/test/ui/async-await/in-trait/async-example-desugared.rs
@@ -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 }
     }
 }
 
diff --git a/src/test/ui/async-await/in-trait/fn-not-async-err.rs b/src/test/ui/async-await/in-trait/fn-not-async-err.rs
index f94d32145a2..9598d53bce8 100644
--- a/src/test/ui/async-await/in-trait/fn-not-async-err.rs
+++ b/src/test/ui/async-await/in-trait/fn-not-async-err.rs
@@ -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
     }
 }
diff --git a/src/test/ui/async-await/in-trait/fn-not-async-err.stderr b/src/test/ui/async-await/in-trait/fn-not-async-err.stderr
index 03321dc5b5a..579801d0f39 100644
--- a/src/test/ui/async-await/in-trait/fn-not-async-err.stderr
+++ b/src/test/ui/async-await/in-trait/fn-not-async-err.stderr
@@ -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`.
diff --git a/src/test/ui/async-await/in-trait/fn-not-async-err2.rs b/src/test/ui/async-await/in-trait/fn-not-async-err2.rs
index 594baa91ad8..2c4ed553580 100644
--- a/src/test/ui/async-await/in-trait/fn-not-async-err2.rs
+++ b/src/test/ui/async-await/in-trait/fn-not-async-err2.rs
@@ -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 }
     }
 }