From 7ee6cebd8afbb7f6085e5980121ba53f9304885e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 8 Jan 2023 22:15:22 +0000 Subject: [PATCH 1/4] Unconditionally normalize xform_ret_ty in probe --- compiler/rustc_hir_typeck/src/method/probe.rs | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 2daf1979ee5e6..aa458748a7fa8 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1521,11 +1521,30 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let mut result = ProbeResult::Match; - let mut xform_ret_ty = probe.xform_ret_ty; - debug!(?xform_ret_ty); - let cause = traits::ObligationCause::misc(self.span, self.body_id); + let xform_ret_ty = if let Some(xform_ret_ty) = probe.xform_ret_ty { + // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, + // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` + // for why this is necessary + let InferOk { + value: normalized_xform_ret_ty, + obligations: normalization_obligations, + } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty); + debug!("xform_ret_ty after normalization: {:?}", normalized_xform_ret_ty); + + for o in normalization_obligations { + if !self.predicate_may_hold(&o) { + possibly_unsatisfied_predicates.push((o.predicate, None, Some(o.cause))); + result = ProbeResult::NoMatch; + } + } + + Some(normalized_xform_ret_ty) + } else { + None + }; + let mut parent_pred = None; // If so, impls may carry other conditions (e.g., where @@ -1534,16 +1553,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // don't have enough information to fully evaluate). match probe.kind { InherentImplCandidate(ref substs, ref ref_obligations) => { - // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, - // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` - // for why this is necessary - let InferOk { - value: normalized_xform_ret_ty, - obligations: normalization_obligations, - } = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty); - xform_ret_ty = normalized_xform_ret_ty; - debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); - // Check whether the impl imposes obligations we have to worry about. let impl_def_id = probe.item.container_id(self.tcx); let impl_bounds = self.tcx.predicates_of(impl_def_id); @@ -1554,15 +1563,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Convert the bounds into obligations. let impl_obligations = traits::predicates_for_generics( - move |_, _| cause.clone(), + |_, _| cause.clone(), self.param_env, impl_bounds, ); let candidate_obligations = impl_obligations .chain(norm_obligations.into_iter()) - .chain(ref_obligations.iter().cloned()) - .chain(normalization_obligations.into_iter()); + .chain(ref_obligations.iter().cloned()); // Evaluate those obligations to see if they might possibly hold. for o in candidate_obligations { @@ -1597,7 +1605,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx); parent_pred = Some(predicate); let obligation = - traits::Obligation::new(self.tcx, cause, self.param_env, predicate); + traits::Obligation::new(self.tcx, cause.clone(), self.param_env, predicate); if !self.predicate_may_hold(&obligation) { result = ProbeResult::NoMatch; if self.probe(|_| { @@ -1656,21 +1664,22 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - if let ProbeResult::Match = result { - if let (Some(return_ty), Some(xform_ret_ty)) = (self.return_type, xform_ret_ty) { - let xform_ret_ty = self.resolve_vars_if_possible(xform_ret_ty); - debug!( - "comparing return_ty {:?} with xform ret ty {:?}", - return_ty, probe.xform_ret_ty - ); - if self - .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) - .sup(return_ty, xform_ret_ty) - .is_err() - { - return ProbeResult::BadReturnType; - } + if let ProbeResult::Match = result + && let Some(return_ty) = self.return_type + && let Some(xform_ret_ty) = xform_ret_ty + { + debug!( + "comparing return_ty {:?} with xform ret ty {:?}", + return_ty, xform_ret_ty + ); + if let ProbeResult::Match = result + && self + .at(&ObligationCause::dummy(), self.param_env) + .define_opaque_types(false) + .sup(return_ty, xform_ret_ty) + .is_err() + { + return ProbeResult::BadReturnType; } } From 38491c65790e393915616ccc3822a752272a4a92 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 8 Jan 2023 22:31:47 +0000 Subject: [PATCH 2/4] Consider method return type for various method suggestions --- compiler/rustc_hir_typeck/src/callee.rs | 1 + compiler/rustc_hir_typeck/src/demand.rs | 8 +++ compiler/rustc_hir_typeck/src/expr.rs | 18 +++++-- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 1 + .../src/fn_ctxt/suggestions.rs | 1 + compiler/rustc_hir_typeck/src/method/mod.rs | 7 ++- compiler/rustc_hir_typeck/src/method/probe.rs | 6 ++- .../rustc_hir_typeck/src/method/suggest.rs | 51 +++++++++++++++---- ...suggest-await-on-method-return-mismatch.rs | 24 +++++++++ ...est-await-on-method-return-mismatch.stderr | 9 ++++ ...field-method-suggestion-using-return-ty.rs | 18 +++++++ ...d-method-suggestion-using-return-ty.stderr | 27 ++++++++++ .../ui/privacy/private-field-ty-err.stderr | 5 -- .../method-access-to-range-literal-typo.fixed | 34 +++++++++++++ .../method-access-to-range-literal-typo.rs | 14 +++-- ...method-access-to-range-literal-typo.stderr | 24 ++++----- ...impl-derived-implicit-sized-bound-2.stderr | 7 +++ .../impl-derived-implicit-sized-bound.stderr | 7 +++ 18 files changed, 224 insertions(+), 38 deletions(-) create mode 100644 src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.rs create mode 100644 src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr create mode 100644 src/test/ui/methods/field-method-suggestion-using-return-ty.rs create mode 100644 src/test/ui/methods/field-method-suggestion-using-return-ty.stderr create mode 100644 src/test/ui/suggestions/method-access-to-range-literal-typo.fixed diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 2cb976f718c22..e299a65ba6fc2 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -499,6 +499,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .probe_for_name( Mode::MethodCall, segment.ident, + expected.only_has_type(self), IsSuggestion(true), callee_ty, call_expr.hir_id, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index f68a428d09ae3..1f31a45cfec0a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -560,6 +560,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .probe_for_name( probe::Mode::MethodCall, path.ident, + None, probe::IsSuggestion(true), self_ty, deref.hir_id, @@ -570,6 +571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let in_scope_methods = self.probe_for_name_many( probe::Mode::MethodCall, path.ident, + Some(expected), probe::IsSuggestion(true), self_ty, deref.hir_id, @@ -581,6 +583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let all_methods = self.probe_for_name_many( probe::Mode::MethodCall, path.ident, + Some(expected), probe::IsSuggestion(true), self_ty, deref.hir_id, @@ -1850,10 +1853,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } let mut expr = end.expr; + let mut expectation = Some(expected_ty); while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind { // Getting to the root receiver and asserting it is a fn call let's us ignore cases in // `src/test/ui/methods/issues/issue-90315.stderr`. expr = rcvr; + // If we have more than one layer of calls, then the expected ty + // cannot guide the method probe. + expectation = None; } let hir::ExprKind::Call(method_name, _) = expr.kind else { return; }; let ty::Adt(adt, _) = checked_ty.kind() else { return; }; @@ -1872,6 +1879,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Ok(_pick) = self.probe_for_name( probe::Mode::MethodCall, *ident, + expectation, probe::IsSuggestion(true), self_ty, expr.hir_id, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index b08b22108c8ce..46aabe7d5c3b0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -351,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Struct(qpath, fields, ref base_expr) => { self.check_expr_struct(expr, expected, qpath, fields, base_expr) } - ExprKind::Field(base, field) => self.check_field(expr, &base, field), + ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected), ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr), ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src), hir::ExprKind::Err => tcx.ty_error(), @@ -1244,6 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { SelfSource::MethodCall(rcvr), error, Some((rcvr, args)), + expected, ) { err.emit(); } @@ -2186,6 +2187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, base: &'tcx hir::Expr<'tcx>, field: Ident, + expected: Expectation<'tcx>, ) -> Ty<'tcx> { debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); let base_ty = self.check_expr(base); @@ -2244,12 +2246,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (#90483) apply adjustments to avoid ExprUseVisitor from // creating erroneous projection. self.apply_adjustments(base, adjustments); - self.ban_private_field_access(expr, base_ty, field, did); + self.ban_private_field_access(expr, base_ty, field, did, expected.only_has_type(self)); return self.tcx().ty_error(); } if field.name == kw::Empty { - } else if self.method_exists(field, base_ty, expr.hir_id, true) { + } else if self.method_exists( + field, + base_ty, + expr.hir_id, + true, + expected.only_has_type(self), + ) { self.ban_take_value_of_method(expr, base_ty, field); } else if !base_ty.is_primitive_ty() { self.ban_nonexisting_field(field, base, expr, base_ty); @@ -2427,6 +2435,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_t: Ty<'tcx>, field: Ident, base_did: DefId, + return_ty: Option>, ) { let struct_path = self.tcx().def_path_str(base_did); let kind_name = self.tcx().def_kind(base_did).descr(base_did); @@ -2438,7 +2447,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(field.span, "private field"); // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id) + if self.method_exists(field, expr_t, expr.hir_id, false, return_ty) + && !self.expr_in_place(expr.hir_id) { self.suggest_method_call( &mut err, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 634688de01a65..ebbd64eba37b7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -841,6 +841,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { SelfSource::QPath(qself), error, None, + Expectation::NoExpectation, ) { e.emit(); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 236bdc60e677d..91498265259de 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1343,6 +1343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Ok(pick) = self.probe_for_name( Mode::Path, Ident::new(capitalized_name, segment.ident.span), + Some(expected_ty), IsSuggestion(true), self_ty, expr.hir_id, diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index d276bcdb81e3e..76c62d6fdc49a 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -97,10 +97,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, call_expr_id: hir::HirId, allow_private: bool, + return_type: Option>, ) -> bool { match self.probe_for_name( probe::Mode::MethodCall, method_name, + return_type, IsSuggestion(false), self_ty, call_expr_id, @@ -118,7 +120,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(Ambiguity(..)) => true, Err(PrivateMatch(..)) => allow_private, Err(IllegalSizedBound { .. }) => true, - Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"), + Err(BadReturnType) => false, } } @@ -137,6 +139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .probe_for_name( probe::Mode::MethodCall, method_name, + None, IsSuggestion(true), self_ty, call_expr.hir_id, @@ -258,6 +261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pick = self.probe_for_name( probe::Mode::MethodCall, method_name, + None, IsSuggestion(false), self_ty, call_expr.hir_id, @@ -484,6 +488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pick = self.probe_for_name( probe::Mode::Path, method_name, + None, IsSuggestion(false), self_ty, expr_id, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index aa458748a7fa8..47c072831e3a6 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -304,6 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, mode: Mode, item_name: Ident, + return_type: Option>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, @@ -313,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name.span, mode, Some(item_name), - None, + return_type, is_suggestion, self_ty, scope_expr_id, @@ -327,6 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, mode: Mode, item_name: Ident, + return_type: Option>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, @@ -336,7 +338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name.span, mode, Some(item_name), - None, + return_type, is_suggestion, self_ty, scope_expr_id, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 536c427065958..bd2b66a72d508 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2,6 +2,7 @@ //! found or is otherwise invalid. use crate::errors; +use crate::Expectation; use crate::FnCtxt; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -108,6 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source: SelfSource<'tcx>, error: MethodError<'tcx>, args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + expected: Expectation<'tcx>, ) -> Option> { // Avoid suggestions when we don't know what's going on. if rcvr_ty.references_error() { @@ -131,6 +133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args, sugg_span, &mut no_match_data, + expected, ); } @@ -250,6 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, sugg_span: Span, no_match_data: &mut NoMatchData<'tcx>, + expected: Expectation<'tcx>, ) -> Option> { let mode = no_match_data.mode; let tcx = self.tcx; @@ -320,7 +324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { self.suggest_await_before_method( - &mut err, item_name, rcvr_ty, cal, span, + &mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self), ); } if let Some(span) = @@ -898,7 +902,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` // can't be called due to `typeof(expr): Clone` not holding. if unsatisfied_predicates.is_empty() { - self.suggest_calling_method_on_field(&mut err, source, span, rcvr_ty, item_name); + self.suggest_calling_method_on_field( + &mut err, + source, + span, + rcvr_ty, + item_name, + expected.only_has_type(self), + ); } self.check_for_inner_self(&mut err, source, rcvr_ty, item_name); @@ -922,6 +933,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &unsatisfied_predicates, &static_candidates, unsatisfied_bounds, + expected.only_has_type(self), ); } @@ -987,7 +999,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.check_for_deref_method(&mut err, source, rcvr_ty, item_name); + self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected); return Some(err); } @@ -1377,6 +1389,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pick = self.probe_for_name( Mode::MethodCall, item_name, + None, IsSuggestion(true), range_ty, expr.hir_id, @@ -1587,6 +1600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, actual: Ty<'tcx>, item_name: Ident, + return_type: Option>, ) { if let SelfSource::MethodCall(expr) = source && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() @@ -1610,10 +1624,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_for_nested_field_satisfying( span, &|_, field_ty| { - self.lookup_probe( + self.probe_for_name( + Mode::MethodCall, item_name, + return_type, + IsSuggestion(true), field_ty, - call_expr, + call_expr.hir_id, ProbeScope::TraitsInScope, ) .map_or(false, |pick| { @@ -2010,12 +2027,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_source: SelfSource<'tcx>, rcvr_ty: Ty<'tcx>, item_name: Ident, + expected: Expectation<'tcx>, ) { let SelfSource::QPath(ty) = self_source else { return; }; for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { if let Ok(pick) = self.probe_for_name( Mode::Path, item_name, + expected.only_has_type(self), IsSuggestion(true), deref_ty, ty.hir_id, @@ -2080,12 +2099,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, call: &hir::Expr<'_>, span: Span, + return_type: Option>, ) { let output_ty = match self.get_impl_future_output_ty(ty) { Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; - let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); + let method_exists = + self.method_exists(item_name, output_ty, call.hir_id, true, return_type); debug!("suggest_await_before_method: is_method_exist={}", method_exists); if method_exists { err.span_suggestion_verbose( @@ -2199,6 +2220,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )], static_candidates: &[CandidateSource], unsatisfied_bounds: bool, + return_type: Option>, ) { let mut alt_rcvr_sugg = false; if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { @@ -2221,7 +2243,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), ] { - match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { + match self.probe_for_name( + Mode::MethodCall, + item_name, + return_type, + IsSuggestion(true), + *rcvr_ty, + rcvr.hir_id, + ProbeScope::AllTraits, + ) { Ok(pick) => { // If the method is defined for the receiver we have, it likely wasn't `use`d. // We point at the method, but we just skip the rest of the check for arbitrary @@ -2254,10 +2284,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), ] { if let Some(new_rcvr_t) = *rcvr_ty - && let Ok(pick) = self.lookup_probe( + && let Ok(pick) = self.probe_for_name( + Mode::MethodCall, item_name, + return_type, + IsSuggestion(true), new_rcvr_t, - rcvr, + rcvr.hir_id, ProbeScope::AllTraits, ) { diff --git a/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.rs b/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.rs new file mode 100644 index 0000000000000..f2f87a908178c --- /dev/null +++ b/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.rs @@ -0,0 +1,24 @@ +// edition:2021 + +// Test that we do not suggest `.await` when it doesn't make sense. + +struct A; + +impl A { + fn test(&self) -> i32 { + 1 + } +} + +async fn foo() -> A { + A +} + +async fn async_main() { + let x: u32 = foo().test(); + //~^ ERROR no method named `test` found for opaque type `impl Future` in the current scope +} + +fn main() { + let _ = async_main(); +} diff --git a/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr b/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr new file mode 100644 index 0000000000000..e65d9d0e5d35f --- /dev/null +++ b/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr @@ -0,0 +1,9 @@ +error[E0599]: no method named `test` found for opaque type `impl Future` in the current scope + --> $DIR/dont-suggest-await-on-method-return-mismatch.rs:18:24 + | +LL | let x: u32 = foo().test(); + | ^^^^ method not found in `impl Future` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/methods/field-method-suggestion-using-return-ty.rs b/src/test/ui/methods/field-method-suggestion-using-return-ty.rs new file mode 100644 index 0000000000000..07b975c44c905 --- /dev/null +++ b/src/test/ui/methods/field-method-suggestion-using-return-ty.rs @@ -0,0 +1,18 @@ +struct Wrapper(T); + +impl Wrapper> { + fn inner_mut(&self) -> Option<&mut i32> { + self.as_mut() + //~^ ERROR no method named `as_mut` found for reference `&Wrapper>` in the current scope + //~| HELP one of the expressions' fields has a method of the same name + //~| HELP items from traits can only be used if + } + + fn inner_mut_bad(&self) -> Option<&mut u32> { + self.as_mut() + //~^ ERROR no method named `as_mut` found for reference `&Wrapper>` in the current scope + //~| HELP items from traits can only be used if + } +} + +fn main() {} diff --git a/src/test/ui/methods/field-method-suggestion-using-return-ty.stderr b/src/test/ui/methods/field-method-suggestion-using-return-ty.stderr new file mode 100644 index 0000000000000..51c52a07e10f6 --- /dev/null +++ b/src/test/ui/methods/field-method-suggestion-using-return-ty.stderr @@ -0,0 +1,27 @@ +error[E0599]: no method named `as_mut` found for reference `&Wrapper>` in the current scope + --> $DIR/field-method-suggestion-using-return-ty.rs:5:14 + | +LL | self.as_mut() + | ^^^^^^ method not found in `&Wrapper>` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_mut`, perhaps you need to implement it: + candidate #1: `AsMut` +help: one of the expressions' fields has a method of the same name + | +LL | self.0.as_mut() + | ++ + +error[E0599]: no method named `as_mut` found for reference `&Wrapper>` in the current scope + --> $DIR/field-method-suggestion-using-return-ty.rs:12:14 + | +LL | self.as_mut() + | ^^^^^^ method not found in `&Wrapper>` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_mut`, perhaps you need to implement it: + candidate #1: `AsMut` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/privacy/private-field-ty-err.stderr b/src/test/ui/privacy/private-field-ty-err.stderr index e583a25fd8fde..98ba7856e57e3 100644 --- a/src/test/ui/privacy/private-field-ty-err.stderr +++ b/src/test/ui/privacy/private-field-ty-err.stderr @@ -3,11 +3,6 @@ error[E0616]: field `len` of struct `Foo` is private | LL | if x.len { | ^^^ private field - | -help: a method `len` also exists, call it with parentheses - | -LL | if x.len() { - | ++ error: aborting due to previous error diff --git a/src/test/ui/suggestions/method-access-to-range-literal-typo.fixed b/src/test/ui/suggestions/method-access-to-range-literal-typo.fixed new file mode 100644 index 0000000000000..13601eef6c25b --- /dev/null +++ b/src/test/ui/suggestions/method-access-to-range-literal-typo.fixed @@ -0,0 +1,34 @@ +// run-rustfix + +#![allow(unused)] + +fn as_ref() -> Option> { + None +} +struct Type { + option: Option> +} +trait Trait { + fn foo(&self) -> &Vec; +} +impl Trait for Option> { + fn foo(&self) -> &Vec { + self.as_ref().unwrap() + } +} + +impl Type { + fn method(&self) -> Option<&Vec> { + self.option.as_ref().map(|x| x) + //~^ ERROR E0308 + } + fn method2(&self) -> Option<&u8> { + self.option.foo().get(0) + //~^ ERROR E0425 + //~| ERROR E0308 + } +} + +fn main() { + let _ = Type { option: None }.method(); +} diff --git a/src/test/ui/suggestions/method-access-to-range-literal-typo.rs b/src/test/ui/suggestions/method-access-to-range-literal-typo.rs index ac662edafe6b0..fdcd6425d32de 100644 --- a/src/test/ui/suggestions/method-access-to-range-literal-typo.rs +++ b/src/test/ui/suggestions/method-access-to-range-literal-typo.rs @@ -1,3 +1,7 @@ +// run-rustfix + +#![allow(unused)] + fn as_ref() -> Option> { None } @@ -5,20 +9,20 @@ struct Type { option: Option> } trait Trait { - fn foo(&self) -> Vec; + fn foo(&self) -> &Vec; } impl Trait for Option> { - fn foo(&self) -> Vec { - vec![1, 2, 3] + fn foo(&self) -> &Vec { + self.as_ref().unwrap() } } impl Type { - fn method(&self) -> Option> { + fn method(&self) -> Option<&Vec> { self.option..as_ref().map(|x| x) //~^ ERROR E0308 } - fn method2(&self) -> &u8 { + fn method2(&self) -> Option<&u8> { self.option..foo().get(0) //~^ ERROR E0425 //~| ERROR E0308 diff --git a/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr b/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr index c84f94678914f..f421408944bb3 100644 --- a/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr +++ b/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find function `foo` in this scope - --> $DIR/method-access-to-range-literal-typo.rs:22:22 + --> $DIR/method-access-to-range-literal-typo.rs:26:22 | LL | self.option..foo().get(0) | ^^^ not found in this scope @@ -11,15 +11,15 @@ LL + self.option.foo().get(0) | error[E0308]: mismatched types - --> $DIR/method-access-to-range-literal-typo.rs:18:9 + --> $DIR/method-access-to-range-literal-typo.rs:22:9 | -LL | fn method(&self) -> Option> { - | --------------- expected `Option>` because of return type +LL | fn method(&self) -> Option<&Vec> { + | ---------------- expected `Option<&Vec>` because of return type LL | self.option..as_ref().map(|x| x) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range` | - = note: expected enum `Option<_>` - found struct `std::ops::Range>` + = note: expected enum `Option<&Vec>` + found struct `std::ops::Range>>` help: you likely meant to write a method call instead of a range | LL - self.option..as_ref().map(|x| x) @@ -27,15 +27,15 @@ LL + self.option.as_ref().map(|x| x) | error[E0308]: mismatched types - --> $DIR/method-access-to-range-literal-typo.rs:22:9 + --> $DIR/method-access-to-range-literal-typo.rs:26:9 | -LL | fn method2(&self) -> &u8 { - | --- expected `&u8` because of return type +LL | fn method2(&self) -> Option<&u8> { + | ----------- expected `Option<&u8>` because of return type LL | self.option..foo().get(0) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found struct `Range` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range` | - = note: expected reference `&u8` - found struct `std::ops::Range>>` + = note: expected enum `Option<&u8>` + found struct `std::ops::Range>>` help: you likely meant to write a method call instead of a range | LL - self.option..foo().get(0) diff --git a/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound-2.stderr b/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound-2.stderr index 543ceac8e9178..7cd9788a7d348 100644 --- a/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound-2.stderr +++ b/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound-2.stderr @@ -17,6 +17,13 @@ LL | impl<'a, T: Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { | ^ ----------- ------------- | | | unsatisfied trait bound introduced here + = note: the following trait bounds were not satisfied: + `&Victim<'_, Self>: VictimTrait` + `&mut Victim<'_, Self>: VictimTrait` +help: consider relaxing the type parameter's implicit `Sized` bound + | +LL | impl<'a, T: ?Sized + Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { + | ++++++++ help: consider relaxing the type parameter's implicit `Sized` bound | LL | impl<'a, T: ?Sized + Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { diff --git a/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound.stderr b/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound.stderr index f08d685836ec1..96345df73b4b3 100644 --- a/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound.stderr +++ b/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound.stderr @@ -17,6 +17,13 @@ LL | impl<'a, T: Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { | ^ ----------- ------------- | | | unsatisfied trait bound introduced here + = note: the following trait bounds were not satisfied: + `&Victim<'_, Self>: VictimTrait` + `&mut Victim<'_, Self>: VictimTrait` +help: consider relaxing the type parameter's implicit `Sized` bound + | +LL | impl<'a, T: ?Sized + Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { + | ++++++++ help: consider relaxing the type parameter's implicit `Sized` bound | LL | impl<'a, T: ?Sized + Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { From 2600d6245bbc0d04e57e4ce561a6c07bf6b55951 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 8 Jan 2023 22:54:05 +0000 Subject: [PATCH 3/4] Add lookup_probe_for_diagnostic --- compiler/rustc_hir_typeck/src/callee.rs | 10 +-- compiler/rustc_hir_typeck/src/demand.rs | 21 +++--- compiler/rustc_hir_typeck/src/expr.rs | 4 +- compiler/rustc_hir_typeck/src/method/mod.rs | 75 ++++++++++++------- .../rustc_hir_typeck/src/method/suggest.rs | 62 ++++++++------- 5 files changed, 98 insertions(+), 74 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index e299a65ba6fc2..3b664363d232f 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -1,4 +1,4 @@ -use super::method::probe::{IsSuggestion, Mode, ProbeScope}; +use super::method::probe::ProbeScope; use super::method::MethodCallee; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; @@ -496,16 +496,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // any strange errors. If it's successful, then we'll do a true // method lookup. let Ok(pick) = self - .probe_for_name( - Mode::MethodCall, + .lookup_probe_for_diagnostic( segment.ident, - expected.only_has_type(self), - IsSuggestion(true), callee_ty, - call_expr.hir_id, + call_expr, // We didn't record the in scope traits during late resolution // so we need to probe AllTraits unfortunately ProbeScope::AllTraits, + expected.only_has_type(self), ) else { return None; }; diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 1f31a45cfec0a..33fc7413a679c 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -303,11 +303,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Get the evaluated type *after* calling the method call, so that the influence // of the arguments can be reflected in the receiver type. The receiver // expression has the type *before* theis analysis is done. - let ty = match self.lookup_probe( + let ty = match self.lookup_probe_for_diagnostic( segment.ident, rcvr_ty, expr, probe::ProbeScope::TraitsInScope, + None, ) { Ok(pick) => pick.self_ty, Err(_) => rcvr_ty, @@ -557,14 +558,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else { return; }; let Ok(pick) = self - .probe_for_name( - probe::Mode::MethodCall, + .lookup_probe_for_diagnostic( path.ident, - None, - probe::IsSuggestion(true), self_ty, - deref.hir_id, + deref, probe::ProbeScope::TraitsInScope, + None, ) else { return; }; @@ -1835,7 +1834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_for_range_as_method_call( &self, err: &mut Diagnostic, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) { @@ -1876,14 +1875,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; }; let [hir::PathSegment { ident, .. }] = p.segments else { return; }; let self_ty = self.typeck_results.borrow().expr_ty(start.expr); - let Ok(_pick) = self.probe_for_name( - probe::Mode::MethodCall, + let Ok(_pick) = self.lookup_probe_for_diagnostic( *ident, - expectation, - probe::IsSuggestion(true), self_ty, - expr.hir_id, + expr, probe::ProbeScope::AllTraits, + expectation, ) else { return; }; let mut sugg = "."; let mut span = start.expr.span.between(end.expr.span); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 46aabe7d5c3b0..ba1a5a0cb03e1 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2431,7 +2431,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn ban_private_field_access( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expr_t: Ty<'tcx>, field: Ident, base_did: DefId, @@ -2462,7 +2462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) { + fn ban_take_value_of_method(&self, expr: &hir::Expr<'tcx>, expr_t: Ty<'tcx>, field: Ident) { let mut err = type_error_struct!( self.tcx().sess, field.span, diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 76c62d6fdc49a..146d5e60c2f38 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -132,18 +132,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { msg: &str, method_name: Ident, self_ty: Ty<'tcx>, - call_expr: &hir::Expr<'_>, + call_expr: &hir::Expr<'tcx>, span: Option, ) { let params = self - .probe_for_name( - probe::Mode::MethodCall, + .lookup_probe_for_diagnostic( method_name, - None, - IsSuggestion(true), self_ty, - call_expr.hir_id, + call_expr, ProbeScope::TraitsInScope, + None, ) .map(|pick| { let sig = self.tcx.fn_sig(pick.item.def_id); @@ -224,25 +222,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // We probe again, taking all traits into account (not only those in scope). - let candidates = - match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) { - // If we find a different result the caller probably forgot to import a trait. - Ok(ref new_pick) if pick.differs_from(new_pick) => { - vec![new_pick.item.container_id(self.tcx)] - } - Err(Ambiguity(ref sources)) => sources - .iter() - .filter_map(|source| { - match *source { - // Note: this cannot come from an inherent impl, - // because the first probing succeeded. - CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), - CandidateSource::Trait(_) => None, - } - }) - .collect(), - _ => Vec::new(), - }; + let candidates = match self.lookup_probe_for_diagnostic( + segment.ident, + self_ty, + call_expr, + ProbeScope::AllTraits, + None, + ) { + // If we find a different result the caller probably forgot to import a trait. + Ok(ref new_pick) if pick.differs_from(new_pick) => { + vec![new_pick.item.container_id(self.tcx)] + } + Err(Ambiguity(ref sources)) => sources + .iter() + .filter_map(|source| { + match *source { + // Note: this cannot come from an inherent impl, + // because the first probing succeeded. + CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), + CandidateSource::Trait(_) => None, + } + }) + .collect(), + _ => Vec::new(), + }; return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr }); } @@ -255,7 +258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, method_name: Ident, self_ty: Ty<'tcx>, - call_expr: &'tcx hir::Expr<'tcx>, + call_expr: &hir::Expr<'_>, scope: ProbeScope, ) -> probe::PickResult<'tcx> { let pick = self.probe_for_name( @@ -271,6 +274,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(pick) } + pub fn lookup_probe_for_diagnostic( + &self, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr: &hir::Expr<'_>, + scope: ProbeScope, + return_type: Option>, + ) -> probe::PickResult<'tcx> { + let pick = self.probe_for_name( + probe::Mode::MethodCall, + method_name, + return_type, + IsSuggestion(true), + self_ty, + call_expr.hir_id, + scope, + )?; + Ok(pick) + } + pub(super) fn obligation_for_method( &self, cause: ObligationCause<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index bd2b66a72d508..62e80659486a8 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -370,8 +370,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(rcvr_expr.hir_id)); - let probe = - self.lookup_probe(item_name, output_ty, call_expr, ProbeScope::AllTraits); + let probe = self.lookup_probe_for_diagnostic( + item_name, + output_ty, + call_expr, + ProbeScope::AllTraits, + expected.only_has_type(self), + ); probe.is_ok() }); } @@ -1386,14 +1391,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let range_ty = self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); - let pick = self.probe_for_name( - Mode::MethodCall, + let pick = self.lookup_probe_for_diagnostic( item_name, - None, - IsSuggestion(true), range_ty, - expr.hir_id, + expr, ProbeScope::AllTraits, + None, ); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); @@ -1573,11 +1576,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Some(expr) = visitor.result && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { - let probe = self.lookup_probe( + let probe = self.lookup_probe_for_diagnostic( seg2.ident, self_ty, call_expr, ProbeScope::TraitsInScope, + None, ); if probe.is_ok() { let sm = self.infcx.tcx.sess.source_map(); @@ -1624,14 +1628,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_for_nested_field_satisfying( span, &|_, field_ty| { - self.probe_for_name( - Mode::MethodCall, + self.lookup_probe_for_diagnostic( item_name, - return_type, - IsSuggestion(true), field_ty, - call_expr.hir_id, + call_expr, ProbeScope::TraitsInScope, + return_type, ) .map_or(false, |pick| { !never_mention_traits @@ -1697,9 +1699,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope) - .ok() - .map(|pick| (variant, field, pick)) + self.lookup_probe_for_diagnostic( + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + None, + ) + .ok() + .map(|pick| (variant, field, pick)) }) .collect(); @@ -1763,11 +1771,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::AdtKind::Struct | ty::AdtKind::Union => { let [first] = ***substs else { return; }; let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; - let Ok(pick) = self.lookup_probe( + let Ok(pick) = self.lookup_probe_for_diagnostic( item_name, ty, call_expr, ProbeScope::TraitsInScope, + None, ) else { return; }; let name = self.ty_to_value_string(actual); @@ -2243,14 +2252,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), ] { - match self.probe_for_name( - Mode::MethodCall, + match self.lookup_probe_for_diagnostic( item_name, - return_type, - IsSuggestion(true), *rcvr_ty, - rcvr.hir_id, + rcvr, ProbeScope::AllTraits, + return_type, ) { Ok(pick) => { // If the method is defined for the receiver we have, it likely wasn't `use`d. @@ -2284,14 +2291,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), ] { if let Some(new_rcvr_t) = *rcvr_ty - && let Ok(pick) = self.probe_for_name( - Mode::MethodCall, + && let Ok(pick) = self.lookup_probe_for_diagnostic( item_name, - return_type, - IsSuggestion(true), new_rcvr_t, - rcvr.hir_id, + rcvr, ProbeScope::AllTraits, + return_type, ) { debug!("try_alt_rcvr: pick candidate {:?}", pick); @@ -2670,11 +2675,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { name: Symbol::intern(&format!("{}_else", method_name.as_str())), span: method_name.span, }; - let probe = self.lookup_probe( + let probe = self.lookup_probe_for_diagnostic( new_name, self_ty, self_expr, ProbeScope::TraitsInScope, + Some(expected), ); // check the method arguments number From 4f15034b55f6c9e134f7644c4571faa5ddb5facb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 9 Jan 2023 06:53:11 +0000 Subject: [PATCH 4/4] hack: don't normalize xform_ret_ty for trait/object candidates unless needed --- compiler/rustc_hir_typeck/src/method/probe.rs | 67 ++++++++++++------- ...impl-derived-implicit-sized-bound-2.stderr | 7 -- .../impl-derived-implicit-sized-bound.stderr | 7 -- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 47c072831e3a6..02b4d5bb2fbe6 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1523,29 +1523,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let mut result = ProbeResult::Match; - let cause = traits::ObligationCause::misc(self.span, self.body_id); - - let xform_ret_ty = if let Some(xform_ret_ty) = probe.xform_ret_ty { - // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, - // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` - // for why this is necessary - let InferOk { - value: normalized_xform_ret_ty, - obligations: normalization_obligations, - } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty); - debug!("xform_ret_ty after normalization: {:?}", normalized_xform_ret_ty); - - for o in normalization_obligations { - if !self.predicate_may_hold(&o) { - possibly_unsatisfied_predicates.push((o.predicate, None, Some(o.cause))); - result = ProbeResult::NoMatch; - } - } + let mut xform_ret_ty = probe.xform_ret_ty; + debug!(?xform_ret_ty); - Some(normalized_xform_ret_ty) - } else { - None - }; + let cause = traits::ObligationCause::misc(self.span, self.body_id); let mut parent_pred = None; @@ -1555,6 +1536,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // don't have enough information to fully evaluate). match probe.kind { InherentImplCandidate(ref substs, ref ref_obligations) => { + // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, + // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` + // for why this is necessary + let InferOk { + value: normalized_xform_ret_ty, + obligations: normalization_obligations, + } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty); + xform_ret_ty = normalized_xform_ret_ty; + debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); + // Check whether the impl imposes obligations we have to worry about. let impl_def_id = probe.item.container_id(self.tcx); let impl_bounds = self.tcx.predicates_of(impl_def_id); @@ -1572,7 +1563,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let candidate_obligations = impl_obligations .chain(norm_obligations.into_iter()) - .chain(ref_obligations.iter().cloned()); + .chain(ref_obligations.iter().cloned()) + .chain(normalization_obligations.into_iter()); // Evaluate those obligations to see if they might possibly hold. for o in candidate_obligations { @@ -1668,8 +1660,33 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let ProbeResult::Match = result && let Some(return_ty) = self.return_type - && let Some(xform_ret_ty) = xform_ret_ty + && let Some(mut xform_ret_ty) = xform_ret_ty { + // `xform_ret_ty` has only been normalized for `InherentImplCandidate`. + // We don't normalize the other candidates for perf/backwards-compat reasons... + // but `self.return_type` is only set on the diagnostic-path, so we + // should be okay doing it here. + if !matches!(probe.kind, InherentImplCandidate(..)) { + let InferOk { + value: normalized_xform_ret_ty, + obligations: normalization_obligations, + } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty); + xform_ret_ty = normalized_xform_ret_ty; + debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); + // Evaluate those obligations to see if they might possibly hold. + for o in normalization_obligations { + let o = self.resolve_vars_if_possible(o); + if !self.predicate_may_hold(&o) { + result = ProbeResult::NoMatch; + possibly_unsatisfied_predicates.push(( + o.predicate, + None, + Some(o.cause), + )); + } + } + } + debug!( "comparing return_ty {:?} with xform ret ty {:?}", return_ty, xform_ret_ty @@ -1681,7 +1698,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .sup(return_ty, xform_ret_ty) .is_err() { - return ProbeResult::BadReturnType; + result = ProbeResult::BadReturnType; } } diff --git a/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound-2.stderr b/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound-2.stderr index 7cd9788a7d348..543ceac8e9178 100644 --- a/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound-2.stderr +++ b/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound-2.stderr @@ -17,13 +17,6 @@ LL | impl<'a, T: Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { | ^ ----------- ------------- | | | unsatisfied trait bound introduced here - = note: the following trait bounds were not satisfied: - `&Victim<'_, Self>: VictimTrait` - `&mut Victim<'_, Self>: VictimTrait` -help: consider relaxing the type parameter's implicit `Sized` bound - | -LL | impl<'a, T: ?Sized + Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { - | ++++++++ help: consider relaxing the type parameter's implicit `Sized` bound | LL | impl<'a, T: ?Sized + Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { diff --git a/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound.stderr b/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound.stderr index 96345df73b4b3..f08d685836ec1 100644 --- a/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound.stderr +++ b/src/test/ui/trait-bounds/impl-derived-implicit-sized-bound.stderr @@ -17,13 +17,6 @@ LL | impl<'a, T: Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { | ^ ----------- ------------- | | | unsatisfied trait bound introduced here - = note: the following trait bounds were not satisfied: - `&Victim<'_, Self>: VictimTrait` - `&mut Victim<'_, Self>: VictimTrait` -help: consider relaxing the type parameter's implicit `Sized` bound - | -LL | impl<'a, T: ?Sized + Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> { - | ++++++++ help: consider relaxing the type parameter's implicit `Sized` bound | LL | impl<'a, T: ?Sized + Perpetrator /*+ ?Sized*/> VictimTrait for Victim<'a, T> {