about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-08-27 07:26:32 +0000
committerbors <bors@rust-lang.org>2020-08-27 07:26:32 +0000
commitf7cbb7a594658099ebb9d0008779511fe2fbe9ab (patch)
treecec33661a7609e6d6e2d44428918a36dfa4f7b91 /src
parent4e701afd84e4500bb7a4800f187284c1e1bf01ee (diff)
parent7cfcefd1fbbbfefbdc88feb7359e6364d7c0bf8a (diff)
downloadrust-f7cbb7a594658099ebb9d0008779511fe2fbe9ab.tar.gz
rust-f7cbb7a594658099ebb9d0008779511fe2fbe9ab.zip
Auto merge of #72784 - csmoe:issue-61076, r=estebank
Await on mismatched future types

Closes #61076
This PR suggests to `await` on:
1. `async_fn().bar() => async_fn().await.bar()`
2. `async_fn().field => async_fn().await.field`
3. ` if let x = async() {} => if let x = async().await {}`

r? @tmandry @estebank
Diffstat (limited to 'src')
-rw-r--r--src/librustc_infer/infer/error_reporting/mod.rs60
-rw-r--r--src/librustc_middle/query/mod.rs4
-rw-r--r--src/librustc_typeck/check/_match.rs4
-rw-r--r--src/librustc_typeck/check/expr.rs57
-rw-r--r--src/librustc_typeck/check/method/suggest.rs59
-rw-r--r--src/librustc_typeck/collect.rs23
-rw-r--r--src/test/ui/async-await/issue-61076.rs41
-rw-r--r--src/test/ui/async-await/issue-61076.stderr58
8 files changed, 299 insertions, 7 deletions
diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs
index 8212958510a..d72ed72e3a8 100644
--- a/src/librustc_infer/infer/error_reporting/mod.rs
+++ b/src/librustc_infer/infer/error_reporting/mod.rs
@@ -50,6 +50,7 @@ use super::region_constraints::GenericKind;
 use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
 
 use crate::infer;
+use crate::infer::OriginalQueryValues;
 use crate::traits::error_reporting::report_object_safety_error;
 use crate::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -60,8 +61,10 @@ use rustc_errors::{pluralize, struct_span_err};
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{Item, ItemKind, Node};
 use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::ParamEnvAnd;
 use rustc_middle::ty::{
     self,
     subst::{Subst, SubstsRef},
@@ -1529,6 +1532,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         };
         if let Some(exp_found) = exp_found {
             self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
+            self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
         }
 
         // In some (most?) cases cause.body_id points to actual body, but in some cases
@@ -1547,6 +1551,62 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         self.note_error_origin(diag, cause, exp_found);
     }
 
+    fn suggest_await_on_expect_found(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut DiagnosticBuilder<'tcx>,
+    ) {
+        debug!(
+            "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
+            exp_span, exp_found.expected, exp_found.found
+        );
+
+        if let ty::Opaque(def_id, _) = exp_found.expected.kind {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+            // Future::Output
+            let item_def_id = self
+                .tcx
+                .associated_items(future_trait)
+                .in_definition_order()
+                .next()
+                .unwrap()
+                .def_id;
+
+            let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
+            if let Some(projection_ty) = projection_ty {
+                let projection_query = self.canonicalize_query(
+                    &ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty },
+                    &mut OriginalQueryValues::default(),
+                );
+                if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) {
+                    let normalized_ty = resp.value.value.normalized_ty;
+                    debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty);
+                    if ty::TyS::same_type(normalized_ty, exp_found.found) {
+                        let span = if let ObligationCauseCode::Pattern {
+                            span,
+                            origin_expr: _,
+                            root_ty: _,
+                        } = cause.code
+                        {
+                            // scrutinee's span
+                            span.unwrap_or(exp_span)
+                        } else {
+                            exp_span
+                        };
+                        diag.span_suggestion_verbose(
+                            span.shrink_to_hi(),
+                            "consider awaiting on the future",
+                            ".await".to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
     /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
     /// suggests it.
     fn suggest_as_ref_where_appropriate(
diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs
index d6836d2ee36..e05752f08f6 100644
--- a/src/librustc_middle/query/mod.rs
+++ b/src/librustc_middle/query/mod.rs
@@ -173,6 +173,10 @@ rustc_queries! {
             desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) }
         }
 
+        query projection_ty_from_predicates(key: (DefId, DefId)) -> Option<ty::ProjectionTy<'tcx>> {
+            desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) }
+        }
+
         query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLib>> {
             desc { "looking up the native libraries of a linked crate" }
         }
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 40088bc0690..afd4413069e 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -28,7 +28,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         // Type check the descriminant and get its type.
-        let scrut_ty = if force_scrutinee_bool {
+        let scrutinee_ty = if force_scrutinee_bool {
             // Here we want to ensure:
             //
             // 1. That default match bindings are *not* accepted in the condition of an
@@ -55,7 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // #55810: Type check patterns first so we get types for all bindings.
         for arm in arms {
-            self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true);
+            self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut.span), true);
         }
 
         // Now typecheck the blocks.
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 8fb3d0b7d98..ad4418ddca7 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -37,7 +37,7 @@ use rustc_middle::ty::{AdtKind, Visibility};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_trait_selection::traits::{self, ObligationCauseCode};
+use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
 
 use std::fmt::Display;
 
@@ -1509,6 +1509,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.tcx().ty_error()
     }
 
+    fn suggest_await_on_field_access(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        field_ident: Ident,
+        base: &'tcx hir::Expr<'tcx>,
+        expr: &'tcx hir::Expr<'tcx>,
+        def_id: DefId,
+    ) {
+        let param_env = self.tcx().param_env(def_id);
+        let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+        // Future::Output
+        let item_def_id =
+            self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id;
+
+        let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
+        debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty);
+
+        let cause = self.misc(expr.span);
+        let mut selcx = SelectionContext::new(&self.infcx);
+
+        let mut obligations = vec![];
+        if let Some(projection_ty) = projection_ty {
+            let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
+                &mut selcx,
+                param_env,
+                projection_ty,
+                cause,
+                0,
+                &mut obligations,
+            );
+            debug!(
+                "suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}",
+                self.resolve_vars_if_possible(&normalized_ty),
+                normalized_ty.kind,
+            );
+            if let ty::Adt(def, _) = normalized_ty.kind {
+                if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) {
+                    err.span_suggestion_verbose(
+                        base.span.shrink_to_hi(),
+                        "consider awaiting before field access",
+                        ".await".to_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+    }
+
     fn ban_nonexisting_field(
         &self,
         field: Ident,
@@ -1516,6 +1564,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
         expr_t: Ty<'tcx>,
     ) {
+        debug!(
+            "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
+            field, base, expr, expr_t
+        );
         let mut err = self.no_such_field_err(field.span, field, expr_t);
 
         match expr_t.peel_refs().kind {
@@ -1531,6 +1583,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty::Param(param_ty) => {
                 self.point_at_param_definition(&mut err, param_ty);
             }
+            ty::Opaque(def_id, _) => {
+                self.suggest_await_on_field_access(&mut err, field, base, expr, def_id);
+            }
             _ => {}
         }
 
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 896bfc07954..5cae66bc5da 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -21,6 +21,7 @@ use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{source_map, FileName, Span};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::Obligation;
+use rustc_trait_selection::traits::SelectionContext;
 
 use std::cmp::Ordering;
 
@@ -392,6 +393,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             actual.prefix_string(),
                             ty_str,
                         );
+                        if let Mode::MethodCall = mode {
+                            if let SelfSource::MethodCall(call) = source {
+                                self.suggest_await_before_method(
+                                    &mut err, item_name, actual, call, span,
+                                );
+                            }
+                        }
                         if let Some(span) =
                             tcx.sess.confused_type_with_std_module.borrow().get(&span)
                         {
@@ -854,6 +862,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    fn suggest_await_before_method(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        item_name: Ident,
+        ty: Ty<'tcx>,
+        call: &hir::Expr<'_>,
+        span: Span,
+    ) {
+        if let ty::Opaque(def_id, _) = ty.kind {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+            // Future::Output
+            let item_def_id = self
+                .tcx
+                .associated_items(future_trait)
+                .in_definition_order()
+                .next()
+                .unwrap()
+                .def_id;
+
+            let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
+            let cause = self.misc(span);
+            let mut selcx = SelectionContext::new(&self.infcx);
+            let mut obligations = vec![];
+            if let Some(projection_ty) = projection_ty {
+                let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
+                    &mut selcx,
+                    self.param_env,
+                    projection_ty,
+                    cause,
+                    0,
+                    &mut obligations,
+                );
+                debug!(
+                    "suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}",
+                    self.resolve_vars_if_possible(&normalized_ty),
+                    normalized_ty.kind,
+                );
+                let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true);
+                debug!("suggest_await_before_method: is_method_exist={}", method_exists);
+                if method_exists {
+                    err.span_suggestion_verbose(
+                        span.shrink_to_lo(),
+                        "consider awaiting before this method call",
+                        "await.".to_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+    }
+
     fn suggest_use_candidates(
         &self,
         err: &mut DiagnosticBuilder<'_>,
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 1b472810ccf..7a3f7ec56a2 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -70,6 +70,7 @@ pub fn provide(providers: &mut Providers) {
         generics_of,
         predicates_of,
         predicates_defined_on,
+        projection_ty_from_predicates,
         explicit_predicates_of,
         super_predicates_of,
         type_param_predicates,
@@ -2051,6 +2052,28 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
     result
 }
 
+fn projection_ty_from_predicates(
+    tcx: TyCtxt<'tcx>,
+    key: (
+        // ty_def_id
+        DefId,
+        // def_id of `N` in `<T as Trait>::N`
+        DefId,
+    ),
+) -> Option<ty::ProjectionTy<'tcx>> {
+    let (ty_def_id, item_def_id) = key;
+    let mut projection_ty = None;
+    for (predicate, _) in tcx.predicates_of(ty_def_id).predicates {
+        if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
+            if item_def_id == projection_predicate.projection_ty.item_def_id {
+                projection_ty = Some(projection_predicate.projection_ty);
+                break;
+            }
+        }
+    }
+    projection_ty
+}
+
 fn trait_associated_item_predicates(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs
index 13b45df64ea..e383a9126f7 100644
--- a/src/test/ui/async-await/issue-61076.rs
+++ b/src/test/ui/async-await/issue-61076.rs
@@ -6,6 +6,26 @@ use core::task::{Context, Poll};
 
 struct T;
 
+struct Tuple(i32);
+
+struct Struct {
+    a: i32
+}
+
+impl Struct {
+    fn method(&self) {}
+}
+
+impl Future for Struct {
+    type Output = Struct;
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
+}
+
+impl Future for Tuple {
+    type Output = Tuple;
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
+}
+
 impl Future for T {
     type Output = Result<(), ()>;
 
@@ -23,10 +43,31 @@ async fn bar() -> Result<(), ()> {
     Ok(())
 }
 
+async fn struct_() -> Struct {
+    Struct { a: 1 }
+}
+
+async fn tuple() -> Tuple {
+    Tuple(1i32)
+}
+
 async fn baz() -> Result<(), ()> {
     let t = T;
     t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+
+    let _: i32 = tuple().0; //~ ERROR no field `0`
+
+    let _: i32 = struct_().a; //~ ERROR no field `a`
+
+    struct_().method(); //~ ERROR no method named
+
     Ok(())
 }
 
+async fn match_() {
+    match tuple() {
+        Tuple(_) => {} //~ ERROR mismatched types
+    }
+}
+
 fn main() {}
diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr
index e71f4e7136d..69b6e8c3cf5 100644
--- a/src/test/ui/async-await/issue-61076.stderr
+++ b/src/test/ui/async-await/issue-61076.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
-  --> $DIR/issue-61076.rs:22:5
+  --> $DIR/issue-61076.rs:42:5
    |
 LL |     foo()?;
    |     ^^^^^^
@@ -11,7 +11,7 @@ LL |     foo()?;
    = note: required by `std::ops::Try::into_result`
 
 error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
-  --> $DIR/issue-61076.rs:28:5
+  --> $DIR/issue-61076.rs:56:5
    |
 LL |     t?;
    |     ^^
@@ -22,6 +22,56 @@ LL |     t?;
    = help: the trait `std::ops::Try` is not implemented for `T`
    = note: required by `std::ops::Try::into_result`
 
-error: aborting due to 2 previous errors
+error[E0609]: no field `0` on type `impl std::future::Future`
+  --> $DIR/issue-61076.rs:58:26
+   |
+LL |     let _: i32 = tuple().0;
+   |                          ^
+   |
+help: consider awaiting before field access
+   |
+LL |     let _: i32 = tuple().await.0;
+   |                         ^^^^^^
+
+error[E0609]: no field `a` on type `impl std::future::Future`
+  --> $DIR/issue-61076.rs:60:28
+   |
+LL |     let _: i32 = struct_().a;
+   |                            ^
+   |
+help: consider awaiting before field access
+   |
+LL |     let _: i32 = struct_().await.a;
+   |                           ^^^^^^
+
+error[E0599]: no method named `method` found for opaque type `impl std::future::Future` in the current scope
+  --> $DIR/issue-61076.rs:62:15
+   |
+LL |     struct_().method();
+   |               ^^^^^^ method not found in `impl std::future::Future`
+   |
+help: consider awaiting before this method call
+   |
+LL |     struct_().await.method();
+   |               ^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/issue-61076.rs:69:9
+   |
+LL | async fn tuple() -> Tuple {
+   |                     ----- the `Output` of this `async fn`'s expected opaque type
+...
+LL |         Tuple(_) => {}
+   |         ^^^^^^^^ expected opaque type, found struct `Tuple`
+   |
+   = note: expected opaque type `impl std::future::Future`
+                   found struct `Tuple`
+help: consider awaiting on the future
+   |
+LL |     match tuple().await {
+   |                  ^^^^^^
+
+error: aborting due to 6 previous errors
 
-For more information about this error, try `rustc --explain E0277`.
+Some errors have detailed explanations: E0277, E0308, E0599, E0609.
+For more information about an error, try `rustc --explain E0277`.