diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 8212958510a6f..d72ed72e3a8ac 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>, + 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 d6836d2ee3665..e05752f08f631 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> { + desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) } + } + query native_libraries(_: CrateNum) -> Lrc> { 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 40088bc069005..afd4413069ee1 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 8fb3d0b7d98fb..ad4418ddca74a 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 896bfc0795434..5cae66bc5dac6 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 1b472810ccf1d..7a3f7ec56a2ce 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 `::N` + DefId, + ), +) -> Option> { + 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 13b45df64eabe..e383a9126f7bc 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 { Poll::Pending } +} + +impl Future for Tuple { + type Output = Tuple; + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { 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 e71f4e7136dad..69b6e8c3cf569 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`.