about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-10-11 10:59:09 -0400
committerMichael Goulet <michael@errs.io>2024-10-12 06:14:45 -0400
commit5e8820caaa91f3d7740303e9e7047fbfb82281ce (patch)
tree947b1a31a2451d496436d44a800700dbda43a909
parentfb20e4d3b96d1de459d086980a8b99d5060ad9fe (diff)
downloadrust-5e8820caaa91f3d7740303e9e7047fbfb82281ce.tar.gz
rust-5e8820caaa91f3d7740303e9e7047fbfb82281ce.zip
Add a note for ? on future in sync function
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs98
-rw-r--r--tests/ui/async-await/try-in-sync.rs9
-rw-r--r--tests/ui/async-await/try-in-sync.stderr18
3 files changed, 82 insertions, 43 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 87834c329e1..8dcdee736ff 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3594,52 +3594,64 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
     ) {
-        if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
-            self.tcx.coroutine_kind(obligation.cause.body_id)
-        {
-            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+        let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
 
-            let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
-            let impls_future = self.type_implements_trait(
-                future_trait,
-                [self.tcx.instantiate_bound_regions_with_erased(self_ty)],
-                obligation.param_env,
-            );
-            if !impls_future.must_apply_modulo_regions() {
-                return;
-            }
+        let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
+        let impls_future = self.type_implements_trait(
+            future_trait,
+            [self.tcx.instantiate_bound_regions_with_erased(self_ty)],
+            obligation.param_env,
+        );
+        if !impls_future.must_apply_modulo_regions() {
+            return;
+        }
 
-            let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
-            // `<T as Future>::Output`
-            let projection_ty = trait_pred.map_bound(|trait_pred| {
-                Ty::new_projection(
-                    self.tcx,
-                    item_def_id,
-                    // Future::Output has no args
-                    [trait_pred.self_ty()],
-                )
-            });
-            let InferOk { value: projection_ty, .. } =
-                self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
+        let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
+        // `<T as Future>::Output`
+        let projection_ty = trait_pred.map_bound(|trait_pred| {
+            Ty::new_projection(
+                self.tcx,
+                item_def_id,
+                // Future::Output has no args
+                [trait_pred.self_ty()],
+            )
+        });
+        let InferOk { value: projection_ty, .. } =
+            self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
 
-            debug!(
-                normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
-            );
-            let try_obligation = self.mk_trait_obligation_with_new_self_ty(
-                obligation.param_env,
-                trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
-            );
-            debug!(try_trait_obligation = ?try_obligation);
-            if self.predicate_may_hold(&try_obligation)
-                && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
-                && snippet.ends_with('?')
-            {
-                err.span_suggestion_verbose(
-                    span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
-                    "consider `await`ing on the `Future`",
-                    ".await",
-                    Applicability::MaybeIncorrect,
-                );
+        debug!(
+            normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
+        );
+        let try_obligation = self.mk_trait_obligation_with_new_self_ty(
+            obligation.param_env,
+            trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
+        );
+        debug!(try_trait_obligation = ?try_obligation);
+        if self.predicate_may_hold(&try_obligation)
+            && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+            && snippet.ends_with('?')
+        {
+            match self.tcx.coroutine_kind(obligation.cause.body_id) {
+                Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
+                    err.span_suggestion_verbose(
+                        span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
+                        "consider `await`ing on the `Future`",
+                        ".await",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                _ => {
+                    let mut span: MultiSpan = span.with_lo(span.hi() - BytePos(1)).into();
+                    span.push_span_label(
+                        self.tcx.def_span(obligation.cause.body_id),
+                        "this is not `async`",
+                    );
+                    err.span_note(
+                        span,
+                        "this implements `Future` and its output type supports \
+                        `?`, but the future cannot be awaited in a synchronous function",
+                    );
+                }
             }
         }
     }
diff --git a/tests/ui/async-await/try-in-sync.rs b/tests/ui/async-await/try-in-sync.rs
new file mode 100644
index 00000000000..81d72c3fb9a
--- /dev/null
+++ b/tests/ui/async-await/try-in-sync.rs
@@ -0,0 +1,9 @@
+//@ edition: 2021
+
+async fn foo() -> Result<(), ()> { todo!() }
+
+fn main() -> Result<(), ()> {
+    foo()?;
+    //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+    Ok(())
+}
diff --git a/tests/ui/async-await/try-in-sync.stderr b/tests/ui/async-await/try-in-sync.stderr
new file mode 100644
index 00000000000..bc7a6bd0151
--- /dev/null
+++ b/tests/ui/async-await/try-in-sync.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the `?` operator can only be applied to values that implement `Try`
+  --> $DIR/try-in-sync.rs:6:5
+   |
+LL |     foo()?;
+   |     ^^^^^^ the `?` operator cannot be applied to type `impl Future<Output = Result<(), ()>>`
+   |
+   = help: the trait `Try` is not implemented for `impl Future<Output = Result<(), ()>>`
+note: this implements `Future` and its output type supports `?`, but the future cannot be awaited in a synchronous function
+  --> $DIR/try-in-sync.rs:6:10
+   |
+LL | fn main() -> Result<(), ()> {
+   | --------------------------- this is not `async`
+LL |     foo()?;
+   |          ^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.