From 32a46e91155de70eeca062a1ece74cea56574649 Mon Sep 17 00:00:00 2001 From: csmoe Date: Wed, 6 May 2020 18:43:56 +0800 Subject: [PATCH 1/7] add test case for issue-61076 --- src/test/ui/async-await/issue-61076.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/test/ui/async-await/issue-61076.rs diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs new file mode 100644 index 0000000000000..0c6c531199773 --- /dev/null +++ b/src/test/ui/async-await/issue-61076.rs @@ -0,0 +1,12 @@ +// edition:2018 + +async fn foo() -> Result<(), ()> { + Ok(()) +} + +async fn bar() -> Result<(), ()> { + foo()?; + Ok(()) +} + +fn main() {} From 2e2aac4f57b6bd4ca8e17b9143d49c14371e071f Mon Sep 17 00:00:00 2001 From: csmoe Date: Wed, 6 May 2020 18:44:14 +0800 Subject: [PATCH 2/7] add try trait as lang item --- src/libcore/ops/try.rs | 1 + src/librustc_hir/lang_items.rs | 1 + src/librustc_typeck/check/mod.rs | 32 ++++++++++++++++++++++---------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index 996a01d413cbc..ed8c614b10979 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs @@ -25,6 +25,7 @@ ) )] #[doc(alias = "?")] + #[cfg_attr(not(bootstrap), lang = "try_trait")] pub trait Try { /// The type of this value when viewed as successful. #[unstable(feature = "try_trait", issue = "42327")] diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 53f72804a848d..2ffa77031f33b 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -194,6 +194,7 @@ language_item_table! { ShrAssignTraitLangItem, "shr_assign", shr_assign_trait, Target::Trait; IndexTraitLangItem, "index", index_trait, Target::Trait; IndexMutTraitLangItem, "index_mut", index_mut_trait, Target::Trait; + TryTraitLangItem, "try_trait", try_trait, Target::Trait; UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type, Target::Struct; VaListTypeLangItem, "va_list", va_list, Target::Struct; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 956e09ec52b4a..53891a4fd9fd6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5289,6 +5289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) { + debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found); // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the // body isn't `async`. let item_id = self.tcx().hir().get_parent_node(self.body_id); @@ -5306,22 +5307,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .next() .unwrap() .def_id; + // `::Output` + let projection_ty = ty::ProjectionTy { + // `T` + substs: self + .tcx + .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)), + // `Future::Output` + item_def_id, + }; + let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate { - // `::Output` - projection_ty: ty::ProjectionTy { - // `T` - substs: self.tcx.mk_substs_trait( - found, - self.fresh_substs_for_item(sp, item_def_id), - ), - // `Future::Output` - item_def_id, - }, + projection_ty, ty: expected, })); let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); debug!("suggest_missing_await: trying obligation {:?}", obligation); + + //let try_trait_def_id = self.tcx.require_lang_item(lang_items::TryTraitLangItem, None); + //let try_trait_ref = ty::TraitRef { + // def_id: try_trait_def_id, + // substs: self.tcx.mk_substs_trait(self.tcx.type_of(item_def_id), &[]), + //}; + //let try_obligation = traits::Obligation::new(self.misc(sp), self.param_env, try_trait_ref.without_const().to_predicate()); + //let try_trait_is_implemented = self.predicate_must_hold_modulo_regions(&try_obligation); + //debug!("suggest_missing_await: try trait is implemented {}", try_trait_is_implemented); + if self.infcx.predicate_may_hold(&obligation) { debug!("suggest_missing_await: obligation held: {:?}", obligation); if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { From 114cd006f52272618bd64382213cd08eaa313136 Mon Sep 17 00:00:00 2001 From: csmoe Date: Sat, 9 May 2020 22:04:02 +0800 Subject: [PATCH 3/7] normalize Future::Ouput --- src/librustc_typeck/check/mod.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 53891a4fd9fd6..bd5049446e615 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5317,22 +5317,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_def_id, }; + let cause = traits::ObligationCause::misc(sp, self.body_id); + let normalized_ty = self.fulfillment_cx.borrow_mut().normalize_projection_type( + &self.infcx, + self.param_env, + projection_ty, + cause, + ); + debug!("suggest_missing_await: projection_type {:?}", normalized_ty); + let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate { projection_ty, ty: expected, })); let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); - debug!("suggest_missing_await: trying obligation {:?}", obligation); - //let try_trait_def_id = self.tcx.require_lang_item(lang_items::TryTraitLangItem, None); - //let try_trait_ref = ty::TraitRef { - // def_id: try_trait_def_id, - // substs: self.tcx.mk_substs_trait(self.tcx.type_of(item_def_id), &[]), - //}; - //let try_obligation = traits::Obligation::new(self.misc(sp), self.param_env, try_trait_ref.without_const().to_predicate()); - //let try_trait_is_implemented = self.predicate_must_hold_modulo_regions(&try_obligation); - //debug!("suggest_missing_await: try trait is implemented {}", try_trait_is_implemented); + debug!("suggest_missing_await: trying obligation {:?}", obligation); if self.infcx.predicate_may_hold(&obligation) { debug!("suggest_missing_await: obligation held: {:?}", obligation); From 627f473dd426497972cce58ba64e8b0ff2409078 Mon Sep 17 00:00:00 2001 From: csmoe Date: Sun, 10 May 2020 20:07:42 +0800 Subject: [PATCH 4/7] suggest await before try when performing trait selection --- src/libcore/ops/try.rs | 2 +- .../traits/error_reporting/mod.rs | 11 +++ .../traits/error_reporting/suggestions.rs | 80 +++++++++++++++++++ src/librustc_typeck/check/mod.rs | 9 --- 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index ed8c614b10979..ef748bcee6e2d 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs @@ -25,7 +25,7 @@ ) )] #[doc(alias = "?")] - #[cfg_attr(not(bootstrap), lang = "try_trait")] +#[cfg_attr(not(bootstrap), lang = "try_trait")] pub trait Try { /// The type of this value when viewed as successful. #[unstable(feature = "try_trait", issue = "42327")] diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs index 405c656bad562..272827cfef830 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -400,6 +400,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.suggest_remove_reference(&obligation, &mut err, &trait_ref); self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); self.note_version_mismatch(&mut err, &trait_ref); + //self.sugggest_await_before_try(&mut err, &obligation, &trait_ref); + debug!( + "suggest_await_befor_try: trait_predicate={:?} obligation={:?}, trait_ref={:?}", + trait_predicate, obligation, trait_ref + ); + self.suggest_await_befor_try( + &mut err, + &obligation, + trait_ref.self_ty(), + span, + ); if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { err.emit(); return; diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 74dd47a91c279..d0b39d6016af2 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -1,8 +1,10 @@ use super::{ EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + SelectionContext, }; use crate::infer::InferCtxt; +use crate::traits::normalize_projection_type; use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style}; use rustc_hir as hir; @@ -150,6 +152,15 @@ pub trait InferCtxtExt<'tcx> { T: fmt::Display; fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>); + + /// Suggest to await before try: future? => future.await? + fn suggest_await_befor_try( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ty: Ty<'tcx>, + span: Span, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { @@ -1765,6 +1776,75 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { suggested_limit, self.tcx.crate_name, )); } + + fn suggest_await_befor_try( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ty: Ty<'tcx>, + span: Span, + ) { + debug!("suggest_await_befor_try: obligation={:?}, span={:?}", obligation, span); + let body_hir_id = obligation.cause.body_id; + let item_id = self.tcx.hir().get_parent_node(body_hir_id); + if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) { + let body = self.tcx.hir().body(body_id); + if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { + // Check for `Future` implementations by constructing a predicate to + // prove: `::Output == U` + let future_trait = self.tcx.lang_items().future_trait().unwrap(); + let item_def_id = self + .tcx + .associated_items(future_trait) + .in_definition_order() + .next() + .unwrap() + .def_id; + // `::Output` + let projection_ty = ty::ProjectionTy { + // `T` + substs: self + .tcx + .mk_substs_trait(ty, self.fresh_substs_for_item(span, item_def_id)), + // `Future::Output` + item_def_id, + }; + + let cause = ObligationCause::misc(span, body_hir_id); + let mut selcx = SelectionContext::new(self); + + let mut obligations = vec![]; + let normalized_ty = normalize_projection_type( + &mut selcx, + obligation.param_env, + projection_ty, + obligation.cause.clone(), + 0, + &mut obligations, + ); + + debug!("suggest_await_befor_try: normalized_projection_type {:?}", normalized_ty); + let try_trait_ref_id = self.tcx.lang_items().try_trait().unwrap(); + if let Some(try_trait_ref) = self.tcx.impl_trait_ref(try_trait_ref_id) { + let try_predicate = try_trait_ref.without_const().to_predicate(); + let try_obligation = + Obligation::new(cause, obligation.param_env, try_predicate); + debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation); + if self.predicate_may_hold(&try_obligation) { + debug!("try_obligation holds"); + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + err.span_suggestion( + span, + "consider using `.await` here", + format!("{}.await", snippet), + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + } } /// Collect all the returned expressions within the input expression. diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index bd5049446e615..c142e88b7de55 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5317,15 +5317,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_def_id, }; - let cause = traits::ObligationCause::misc(sp, self.body_id); - let normalized_ty = self.fulfillment_cx.borrow_mut().normalize_projection_type( - &self.infcx, - self.param_env, - projection_ty, - cause, - ); - debug!("suggest_missing_await: projection_type {:?}", normalized_ty); - let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate { projection_ty, From c7e64f54c86a05ddd09fc3da4e98a8d748658337 Mon Sep 17 00:00:00 2001 From: csmoe Date: Sun, 10 May 2020 22:34:01 +0800 Subject: [PATCH 5/7] remove try_trait lang item --- src/libcore/ops/try.rs | 1 - src/librustc_hir/lang_items.rs | 2 - .../traits/error_reporting/mod.rs | 12 +---- .../traits/error_reporting/suggestions.rs | 46 ++++++++++--------- 4 files changed, 26 insertions(+), 35 deletions(-) diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index ef748bcee6e2d..996a01d413cbc 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs @@ -25,7 +25,6 @@ ) )] #[doc(alias = "?")] -#[cfg_attr(not(bootstrap), lang = "try_trait")] pub trait Try { /// The type of this value when viewed as successful. #[unstable(feature = "try_trait", issue = "42327")] diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 2ffa77031f33b..a503e3534e3bd 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -194,8 +194,6 @@ language_item_table! { ShrAssignTraitLangItem, "shr_assign", shr_assign_trait, Target::Trait; IndexTraitLangItem, "index", index_trait, Target::Trait; IndexMutTraitLangItem, "index_mut", index_mut_trait, Target::Trait; - TryTraitLangItem, "try_trait", try_trait, Target::Trait; - UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type, Target::Struct; VaListTypeLangItem, "va_list", va_list, Target::Struct; diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs index 272827cfef830..c74870306869b 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -400,17 +400,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.suggest_remove_reference(&obligation, &mut err, &trait_ref); self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); self.note_version_mismatch(&mut err, &trait_ref); - //self.sugggest_await_before_try(&mut err, &obligation, &trait_ref); - debug!( - "suggest_await_befor_try: trait_predicate={:?} obligation={:?}, trait_ref={:?}", - trait_predicate, obligation, trait_ref - ); - self.suggest_await_befor_try( - &mut err, - &obligation, - trait_ref.self_ty(), - span, - ); + self.suggest_await_before_try(&mut err, &obligation, &trait_ref, span); if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { err.emit(); return; diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index d0b39d6016af2..b28f0001cd974 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -154,11 +154,11 @@ pub trait InferCtxtExt<'tcx> { fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>); /// Suggest to await before try: future? => future.await? - fn suggest_await_befor_try( + fn suggest_await_before_try( &self, err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, - ty: Ty<'tcx>, + trait_ref: &ty::Binder>, span: Span, ); } @@ -1777,21 +1777,23 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { )); } - fn suggest_await_befor_try( + fn suggest_await_before_try( &self, err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, - ty: Ty<'tcx>, + trait_ref: &ty::Binder>, span: Span, ) { - debug!("suggest_await_befor_try: obligation={:?}, span={:?}", obligation, span); + debug!( + "suggest_await_befor_try: obligation={:?}, span={:?}, trait_ref={:?}", + obligation, span, trait_ref + ); let body_hir_id = obligation.cause.body_id; let item_id = self.tcx.hir().get_parent_node(body_hir_id); + if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) { let body = self.tcx.hir().body(body_id); if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { - // Check for `Future` implementations by constructing a predicate to - // prove: `::Output == U` let future_trait = self.tcx.lang_items().future_trait().unwrap(); let item_def_id = self .tcx @@ -1803,14 +1805,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // `::Output` let projection_ty = ty::ProjectionTy { // `T` - substs: self - .tcx - .mk_substs_trait(ty, self.fresh_substs_for_item(span, item_def_id)), + substs: self.tcx.mk_substs_trait( + trait_ref.self_ty(), + self.fresh_substs_for_item(span, item_def_id), + ), // `Future::Output` item_def_id, }; - let cause = ObligationCause::misc(span, body_hir_id); + //let cause = ObligationCause::misc(span, body_hir_id); let mut selcx = SelectionContext::new(self); let mut obligations = vec![]; @@ -1824,19 +1827,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); debug!("suggest_await_befor_try: normalized_projection_type {:?}", normalized_ty); - let try_trait_ref_id = self.tcx.lang_items().try_trait().unwrap(); - if let Some(try_trait_ref) = self.tcx.impl_trait_ref(try_trait_ref_id) { - let try_predicate = try_trait_ref.without_const().to_predicate(); - let try_obligation = - Obligation::new(cause, obligation.param_env, try_predicate); - debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation); - if self.predicate_may_hold(&try_obligation) { - debug!("try_obligation holds"); - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let try_obligation = self.mk_obligation_for_def_id( + trait_ref.def_id(), + normalized_ty, + obligation.cause.clone(), + obligation.param_env, + ); + debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation); + if self.predicate_may_hold(&try_obligation) { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + if snippet.ends_with('?') { err.span_suggestion( span, "consider using `.await` here", - format!("{}.await", snippet), + format!("{}.await?", snippet.trim_end_matches('?')), Applicability::MaybeIncorrect, ); } From a1104b4dea0e0d3b35fa73c7f7c186f76b23db78 Mon Sep 17 00:00:00 2001 From: csmoe Date: Sun, 10 May 2020 22:34:20 +0800 Subject: [PATCH 6/7] bless ui tests --- src/librustc_hir/lang_items.rs | 1 + src/librustc_middle/hir/map/mod.rs | 6 +---- .../traits/error_reporting/suggestions.rs | 22 ++++++++++++++++--- .../incorrect-syntax-suggestions.stderr | 5 ++++- src/test/ui/async-await/issue-61076.rs | 2 +- src/test/ui/async-await/issue-61076.stderr | 15 +++++++++++++ 6 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/async-await/issue-61076.stderr diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index a503e3534e3bd..53f72804a848d 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -194,6 +194,7 @@ language_item_table! { ShrAssignTraitLangItem, "shr_assign", shr_assign_trait, Target::Trait; IndexTraitLangItem, "index", index_trait, Target::Trait; IndexMutTraitLangItem, "index_mut", index_mut_trait, Target::Trait; + UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type, Target::Struct; VaListTypeLangItem, "va_list", va_list, Target::Struct; diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs index de0373c138497..a3f8cd1ee36fe 100644 --- a/src/librustc_middle/hir/map/mod.rs +++ b/src/librustc_middle/hir/map/mod.rs @@ -390,11 +390,7 @@ impl<'hir> Map<'hir> { /// Given a `HirId`, returns the `BodyId` associated with it, /// if the node is a body owner, otherwise returns `None`. pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option { - if let Some(node) = self.find(hir_id) { - associated_body(node) - } else { - bug!("no entry for id `{}`", hir_id) - } + if let Some(node) = self.find(hir_id) { associated_body(node) } else { None } } /// Given a body owner's id, returns the `BodyId` associated with it. diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index b28f0001cd974..5e3b383ff2580 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -1791,6 +1791,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let body_hir_id = obligation.cause.body_id; let item_id = self.tcx.hir().get_parent_node(body_hir_id); + let mut is_future = false; + if let ty::Opaque(def_id, substs) = trait_ref.self_ty().kind { + let preds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); + for p in preds.predicates { + if let Some(trait_ref) = p.to_opt_poly_trait_ref() { + if Some(trait_ref.def_id()) == self.tcx.lang_items().future_trait() { + is_future = true; + break; + } + } + } + } + if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) { let body = self.tcx.hir().body(body_id); if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { @@ -1802,6 +1815,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .next() .unwrap() .def_id; + debug!("trait_ref_self_ty: {:?}", trait_ref.self_ty()); // `::Output` let projection_ty = ty::ProjectionTy { // `T` @@ -1813,7 +1827,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { item_def_id, }; - //let cause = ObligationCause::misc(span, body_hir_id); let mut selcx = SelectionContext::new(self); let mut obligations = vec![]; @@ -1826,7 +1839,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &mut obligations, ); - debug!("suggest_await_befor_try: normalized_projection_type {:?}", normalized_ty); + debug!( + "suggest_await_befor_try: normalized_projection_type {:?}", + self.resolve_vars_if_possible(&normalized_ty) + ); let try_obligation = self.mk_obligation_for_def_id( trait_ref.def_id(), normalized_ty, @@ -1834,7 +1850,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation.param_env, ); debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation); - if self.predicate_may_hold(&try_obligation) { + if self.predicate_may_hold(&try_obligation) && is_future { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { if snippet.ends_with('?') { err.span_suggestion( diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr index 61f2570b2ff93..96158fc0e0496 100644 --- a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr +++ b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr @@ -237,7 +237,10 @@ error[E0277]: the `?` operator can only be applied to values that implement `std --> $DIR/incorrect-syntax-suggestions.rs:16:19 | LL | let _ = await bar()?; - | ^^^^^^ the `?` operator cannot be applied to type `impl std::future::Future` + | ^^^^^^ + | | + | the `?` operator cannot be applied to type `impl std::future::Future` + | help: consider using `.await` here: `bar().await?` | = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` = note: required by `std::ops::Try::into_result` diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs index 0c6c531199773..04a9148ae2413 100644 --- a/src/test/ui/async-await/issue-61076.rs +++ b/src/test/ui/async-await/issue-61076.rs @@ -5,7 +5,7 @@ async fn foo() -> Result<(), ()> { } async fn bar() -> Result<(), ()> { - foo()?; + foo()?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` Ok(()) } diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr new file mode 100644 index 0000000000000..fb1eead04e6bd --- /dev/null +++ b/src/test/ui/async-await/issue-61076.stderr @@ -0,0 +1,15 @@ +error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` + --> $DIR/issue-61076.rs:8:5 + | +LL | foo()?; + | ^^^^^^ + | | + | the `?` operator cannot be applied to type `impl std::future::Future` + | help: consider using `.await` here: `foo().await?` + | + = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` + = note: required by `std::ops::Try::into_result` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 10d7da4e0b07f469c9d2b8215b563528adfc7e99 Mon Sep 17 00:00:00 2001 From: csmoe Date: Thu, 14 May 2020 23:07:46 +0800 Subject: [PATCH 7/7] implement type_implments_trait query --- src/librustc_middle/hir/map/mod.rs | 2 +- src/librustc_middle/query/mod.rs | 6 +++ src/librustc_middle/ty/query/keys.rs | 12 ++++++ .../traits/error_reporting/suggestions.rs | 37 ++++++++-------- src/librustc_trait_selection/traits/mod.rs | 42 ++++++++++++++++++- src/test/ui/async-await/issue-61076.rs | 20 +++++++++ src/test/ui/async-await/issue-61076.stderr | 16 ++++++- .../ui/async-await/try-on-option-in-async.rs | 3 +- .../async-await/try-on-option-in-async.stderr | 6 +-- .../clippy/clippy_lints/src/utils/mod.rs | 18 +------- 10 files changed, 120 insertions(+), 42 deletions(-) diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs index a3f8cd1ee36fe..b823516d64f3b 100644 --- a/src/librustc_middle/hir/map/mod.rs +++ b/src/librustc_middle/hir/map/mod.rs @@ -390,7 +390,7 @@ impl<'hir> Map<'hir> { /// Given a `HirId`, returns the `BodyId` associated with it, /// if the node is a body owner, otherwise returns `None`. pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option { - if let Some(node) = self.find(hir_id) { associated_body(node) } else { None } + self.find(hir_id).map(associated_body).flatten() } /// Given a body owner's id, returns the `BodyId` associated with it. diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index 2ceba51949420..13cf9a934b72c 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -1164,6 +1164,12 @@ rustc_queries! { desc { "evaluating trait selection obligation `{}`", goal.value } } + query type_implements_trait( + key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, ) + ) -> bool { + desc { "evaluating `type_implements_trait` `{:?}`", key } + } + /// Do not call this query directly: part of the `Eq` type-op query type_op_ascribe_user_type( goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> diff --git a/src/librustc_middle/ty/query/keys.rs b/src/librustc_middle/ty/query/keys.rs index 239691dbd17ac..4acf766f033d8 100644 --- a/src/librustc_middle/ty/query/keys.rs +++ b/src/librustc_middle/ty/query/keys.rs @@ -295,3 +295,15 @@ impl Key for (Symbol, u32, u32) { DUMMY_SP } } + +impl<'tcx> Key for (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>) { + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + LOCAL_CRATE + } + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 5e3b383ff2580..d5b2b765e939f 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -11,6 +11,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; +use rustc_hir::lang_items; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; use rustc_middle::ty::TypeckTables; use rustc_middle::ty::{ @@ -1785,29 +1786,30 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span: Span, ) { debug!( - "suggest_await_befor_try: obligation={:?}, span={:?}, trait_ref={:?}", - obligation, span, trait_ref + "suggest_await_befor_try: obligation={:?}, span={:?}, trait_ref={:?}, trait_ref_self_ty={:?}", + obligation, + span, + trait_ref, + trait_ref.self_ty() ); let body_hir_id = obligation.cause.body_id; let item_id = self.tcx.hir().get_parent_node(body_hir_id); - let mut is_future = false; - if let ty::Opaque(def_id, substs) = trait_ref.self_ty().kind { - let preds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - for p in preds.predicates { - if let Some(trait_ref) = p.to_opt_poly_trait_ref() { - if Some(trait_ref.def_id()) == self.tcx.lang_items().future_trait() { - is_future = true; - break; - } - } - } - } - if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) { let body = self.tcx.hir().body(body_id); if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { - let future_trait = self.tcx.lang_items().future_trait().unwrap(); + let future_trait = + self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None); + + let self_ty = self.resolve_vars_if_possible(&trait_ref.self_ty()); + + let impls_future = self.tcx.type_implements_trait(( + future_trait, + self_ty, + ty::List::empty(), + obligation.param_env, + )); + let item_def_id = self .tcx .associated_items(future_trait) @@ -1815,7 +1817,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .next() .unwrap() .def_id; - debug!("trait_ref_self_ty: {:?}", trait_ref.self_ty()); // `::Output` let projection_ty = ty::ProjectionTy { // `T` @@ -1850,7 +1851,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation.param_env, ); debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation); - if self.predicate_may_hold(&try_obligation) && is_future { + if self.predicate_may_hold(&try_obligation) && impls_future { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { if snippet.ends_with('?') { err.span_suggestion( diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index 778430fc2ca9c..9592f93ce2e76 100644 --- a/src/librustc_trait_selection/traits/mod.rs +++ b/src/librustc_trait_selection/traits/mod.rs @@ -31,7 +31,9 @@ use rustc_hir::def_id::DefId; use rustc_middle::middle::region; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_middle::ty::{ + self, GenericParamDefKind, ParamEnv, ToPredicate, Ty, TyCtxt, WithConstness, +}; use rustc_span::Span; use std::fmt::Debug; @@ -523,6 +525,43 @@ fn vtable_methods<'tcx>( })) } +/// Check whether a `ty` implements given trait(trait_def_id). +/// +/// NOTE: Always return `false` for a type which needs inference. +fn type_implements_trait<'tcx>( + tcx: TyCtxt<'tcx>, + key: ( + DefId, // trait_def_id, + Ty<'tcx>, // type + SubstsRef<'tcx>, + ParamEnv<'tcx>, + ), +) -> bool { + let (trait_def_id, ty, params, param_env) = key; + + debug!( + "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}", + trait_def_id, ty, params, param_env + ); + + // Do not check on infer_types to avoid panic in evaluate_obligation. + if ty.has_infer_types() { + return false; + } + + let ty = tcx.erase_regions(&ty); + + let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) }; + + let obligation = Obligation { + cause: ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: trait_ref.without_const().to_predicate(), + }; + tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) +} + pub fn provide(providers: &mut ty::query::Providers<'_>) { object_safety::provide(providers); *providers = ty::query::Providers { @@ -531,6 +570,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, vtable_methods, substitute_normalize_and_test_predicates, + type_implements_trait, ..*providers }; } diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs index 04a9148ae2413..13b45df64eabe 100644 --- a/src/test/ui/async-await/issue-61076.rs +++ b/src/test/ui/async-await/issue-61076.rs @@ -1,5 +1,19 @@ // edition:2018 +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct T; + +impl Future for T { + type Output = Result<(), ()>; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + async fn foo() -> Result<(), ()> { Ok(()) } @@ -9,4 +23,10 @@ async fn bar() -> Result<(), ()> { Ok(()) } +async fn baz() -> Result<(), ()> { + let t = T; + t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + Ok(()) +} + fn main() {} diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr index fb1eead04e6bd..e71f4e7136dad 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:8:5 + --> $DIR/issue-61076.rs:22:5 | LL | foo()?; | ^^^^^^ @@ -10,6 +10,18 @@ LL | foo()?; = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` = note: required by `std::ops::Try::into_result` -error: aborting due to previous error +error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` + --> $DIR/issue-61076.rs:28:5 + | +LL | t?; + | ^^ + | | + | the `?` operator cannot be applied to type `T` + | help: consider using `.await` here: `t.await?` + | + = 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 For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/try-on-option-in-async.rs b/src/test/ui/async-await/try-on-option-in-async.rs index 51ac522017cb3..c520a07abc172 100644 --- a/src/test/ui/async-await/try-on-option-in-async.rs +++ b/src/test/ui/async-await/try-on-option-in-async.rs @@ -7,7 +7,8 @@ async fn an_async_block() -> u32 { let x: Option = None; x?; //~ ERROR the `?` operator 22 - }.await + } + .await } async fn async_closure_containing_fn() -> u32 { diff --git a/src/test/ui/async-await/try-on-option-in-async.stderr b/src/test/ui/async-await/try-on-option-in-async.stderr index 46f8f41076bf5..700296d674784 100644 --- a/src/test/ui/async-await/try-on-option-in-async.stderr +++ b/src/test/ui/async-await/try-on-option-in-async.stderr @@ -7,14 +7,14 @@ LL | | let x: Option = None; LL | | x?; | | ^^ cannot use the `?` operator in an async block that returns `{integer}` LL | | 22 -LL | | }.await +LL | | } | |_____- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `{integer}` = note: required by `std::ops::Try::from_error` error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`) - --> $DIR/try-on-option-in-async.rs:16:9 + --> $DIR/try-on-option-in-async.rs:17:9 | LL | let async_closure = async || { | __________________________________- @@ -29,7 +29,7 @@ LL | | }; = note: required by `std::ops::Try::from_error` error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) - --> $DIR/try-on-option-in-async.rs:25:5 + --> $DIR/try-on-option-in-async.rs:26:5 | LL | async fn an_async_function() -> u32 { | _____________________________________- diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 2fd080e9ef0f8..84a324f76c5b6 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -40,15 +40,12 @@ use rustc_hir::{ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::traits; use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; -use rustc_trait_selection::traits::predicate_for_trait_def; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; use smallvec::SmallVec; @@ -326,19 +323,8 @@ pub fn implements_trait<'a, 'tcx>( trait_id: DefId, ty_params: &[GenericArg<'tcx>], ) -> bool { - let ty = cx.tcx.erase_regions(&ty); - let obligation = predicate_for_trait_def( - cx.tcx, - cx.param_env, - traits::ObligationCause::dummy(), - trait_id, - 0, - ty, - ty_params, - ); - cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + let ty_params = cx.tcx.mk_substs(ty_params.iter()); + cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env)) } /// Gets the `hir::TraitRef` of the trait the given method is implemented for.