diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/error_reporting')
3 files changed, 355 insertions, 211 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index e442c5c91..efdb1ace1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -22,6 +22,7 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; +use rustc_infer::infer::TypeTrace; use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; @@ -348,7 +349,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { message, label, note, - enclosing_scope, + parent_label, append_const_msg, } = self.on_unimplemented_note(trait_ref, &obligation); let have_alt_message = message.is_some() || label.is_some(); @@ -449,12 +450,27 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { { "consider using `()`, or a `Result`".to_owned() } else { - format!( - "{}the trait `{}` is not implemented for `{}`", - pre_message, - trait_predicate.print_modifiers_and_trait_path(), - trait_ref.skip_binder().self_ty(), - ) + let ty_desc = match trait_ref.skip_binder().self_ty().kind() { + ty::FnDef(_, _) => Some("fn item"), + ty::Closure(_, _) => Some("closure"), + _ => None, + }; + + match ty_desc { + Some(desc) => format!( + "{}the trait `{}` is not implemented for {} `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + desc, + trait_ref.skip_binder().self_ty(), + ), + None => format!( + "{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + trait_ref.skip_binder().self_ty(), + ), + } }; if self.suggest_add_reference_to_arg( @@ -514,7 +530,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // If it has a custom `#[rustc_on_unimplemented]` note, let's display it err.note(s.as_str()); } - if let Some(ref s) = enclosing_scope { + if let Some(ref s) = parent_label { let body = tcx .hir() .opt_local_def_id(obligation.cause.body_id) @@ -523,11 +539,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { hir_id: obligation.cause.body_id, }) }); - - let enclosing_scope_span = - tcx.hir().span_with_body(tcx.hir().local_def_id_to_hir_id(body)); - - err.span_label(enclosing_scope_span, s); + err.span_label(tcx.def_span(body), s); } self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); @@ -859,8 +871,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } - err.emit(); - return; + err } ty::PredicateKind::WellFormed(ty) => { @@ -941,9 +952,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); + let mut not_tupled = false; + let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() { ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], - _ => vec![ArgKind::empty()], + _ => { + not_tupled = true; + vec![ArgKind::empty()] + } }; let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); @@ -951,10 +967,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Tuple(ref tys) => { tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect() } - _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], + _ => { + not_tupled = true; + vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())] + } }; - if found.len() == expected.len() { + // If this is a `Fn` family trait and either the expected or found + // is not tupled, then fall back to just a regular mismatch error. + // This shouldn't be common unless manually implementing one of the + // traits manually, but don't make it more confusing when it does + // happen. + if Some(expected_trait_ref.def_id()) != tcx.lang_items().gen_trait() && not_tupled { + self.report_and_explain_type_error( + TypeTrace::poly_trait_refs( + &obligation.cause, + true, + expected_trait_ref, + found_trait_ref, + ), + ty::error::TypeError::Mismatch, + ) + } else if found.len() == expected.len() { self.report_closure_arg_mismatch( span, found_span, @@ -1315,6 +1349,13 @@ trait InferCtxtPrivExt<'hir, 'tcx> { error: &MismatchedProjectionTypes<'tcx>, ); + fn maybe_detailed_projection_msg( + &self, + pred: ty::ProjectionPredicate<'tcx>, + normalized_ty: ty::Term<'tcx>, + expected_ty: ty::Term<'tcx>, + ) -> Option<String>; + fn fuzzy_match_tys( &self, a: Ty<'tcx>, @@ -1476,13 +1517,28 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { .emit(); } FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { - self.report_mismatched_consts( + let mut diag = self.report_mismatched_consts( &error.obligation.cause, expected_found.expected, expected_found.found, err.clone(), - ) - .emit(); + ); + let code = error.obligation.cause.code().peel_derives().peel_match_impls(); + if let ObligationCauseCode::BindingObligation(..) + | ObligationCauseCode::ItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) + | ObligationCauseCode::ExprItemObligation(..) = code + { + self.note_obligation_cause_code( + &mut diag, + &error.obligation.predicate, + error.obligation.param_env, + code, + &mut vec![], + &mut Default::default(), + ); + } + diag.emit(); } } } @@ -1500,8 +1556,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } self.probe(|_| { - let err_buf; - let mut err = &error.err; + let mut err = error.err; let mut values = None; // try to find the mismatched types to report the error with. @@ -1534,31 +1589,28 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { obligation.cause.code().peel_derives(), ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ExprItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) | ObligationCauseCode::ObjectCastObligation(..) | ObligationCauseCode::OpaqueType ); - if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( + if let Err(new_err) = self.at(&obligation.cause, obligation.param_env).eq_exp( is_normalized_ty_expected, normalized_ty, data.term, ) { - values = Some(infer::ValuePairs::Terms(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, - data.term, - ))); - err_buf = error; - err = &err_buf; + values = Some((data, is_normalized_ty_expected, normalized_ty, data.term)); + err = new_err; } } - let mut diag = struct_span_err!( - self.tcx.sess, - obligation.cause.span, - E0271, - "type mismatch resolving `{}`", - predicate - ); + let msg = values + .and_then(|(predicate, _, normalized_ty, expected_ty)| { + self.maybe_detailed_projection_msg(predicate, normalized_ty, expected_ty) + }) + .unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate)); + let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); + let secondary_span = match predicate.kind().skip_binder() { ty::PredicateKind::Projection(proj) => self .tcx @@ -1596,7 +1648,13 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { &mut diag, &obligation.cause, secondary_span, - values, + values.map(|(_, is_normalized_ty_expected, normalized_ty, term)| { + infer::ValuePairs::Terms(ExpectedFound::new( + is_normalized_ty_expected, + normalized_ty, + term, + )) + }), err, true, false, @@ -1606,6 +1664,33 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { }); } + fn maybe_detailed_projection_msg( + &self, + pred: ty::ProjectionPredicate<'tcx>, + normalized_ty: ty::Term<'tcx>, + expected_ty: ty::Term<'tcx>, + ) -> Option<String> { + let trait_def_id = pred.projection_ty.trait_def_id(self.tcx); + let self_ty = pred.projection_ty.self_ty(); + + if Some(pred.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() { + Some(format!( + "expected `{self_ty}` to be a {fn_kind} that returns `{expected_ty}`, but it returns `{normalized_ty}`", + fn_kind = self_ty.prefix_string(self.tcx) + )) + } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() { + Some(format!( + "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it resolves to `{normalized_ty}`" + )) + } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) { + Some(format!( + "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it yields `{normalized_ty}`" + )) + } else { + None + } + } + fn fuzzy_match_tys( &self, mut a: Ty<'tcx>, @@ -1731,13 +1816,21 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { return false; } if candidates.len() == 1 { + let ty_desc = match candidates[0].self_ty().kind() { + ty::FnPtr(_) => Some("fn pointer"), + _ => None, + }; + let the_desc = match ty_desc { + Some(desc) => format!(" implemented for {} `", desc), + None => " implemented for `".to_string(), + }; err.highlighted_help(vec![ ( format!("the trait `{}` ", candidates[0].print_only_trait_path()), Style::NoStyle, ), ("is".to_string(), Style::Highlight), - (" implemented for `".to_string(), Style::NoStyle), + (the_desc, Style::NoStyle), (candidates[0].self_ty().to_string(), Style::Highlight), ("`".to_string(), Style::NoStyle), ]); @@ -1802,9 +1895,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { // FIXME(compiler-errors): This could be generalized, both to // be more granular, and probably look past other `#[fundamental]` // types, too. - self.tcx - .visibility(def.did()) - .is_accessible_from(body_id.owner.to_def_id(), self.tcx) + self.tcx.visibility(def.did()).is_accessible_from(body_id.owner, self.tcx) } else { true } @@ -1940,7 +2031,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let predicate = self.resolve_vars_if_possible(obligation.predicate); let span = obligation.cause.span; - debug!(?predicate, obligation.cause.code = tracing::field::debug(&obligation.cause.code())); + debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); // Ambiguity errors are often caused as fallout from earlier errors. // We ignore them if this `infcx` is tainted in some cases below. @@ -2033,13 +2124,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } } - if let ObligationCauseCode::ItemObligation(def_id) = *obligation.cause.code() { + if let ObligationCauseCode::ItemObligation(def_id) | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); - } else if let ( - Ok(ref snippet), - &ObligationCauseCode::BindingObligation(def_id, _), - ) = - (self.tcx.sess.source_map().span_to_snippet(span), obligation.cause.code()) + } else if let Ok(snippet) = &self.tcx.sess.source_map().span_to_snippet(span) + && let ObligationCauseCode::BindingObligation(def_id, _) | ObligationCauseCode::ExprBindingObligation(def_id, ..) + = *obligation.cause.code() { let generics = self.tcx.generics_of(def_id); if generics.params.iter().any(|p| p.name != kw::SelfUpper) @@ -2119,12 +2208,12 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { && let [ .., trait_path_segment @ hir::PathSegment { - res: Some(rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id)), + res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id), .. }, hir::PathSegment { ident: assoc_item_name, - res: Some(rustc_hir::def::Res::Def(_, item_id)), + res: rustc_hir::def::Res::Def(_, item_id), .. } ] = path.segments @@ -2462,15 +2551,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>, ) { - let ( - ty::PredicateKind::Trait(pred), - &ObligationCauseCode::BindingObligation(item_def_id, span), - ) = ( - obligation.predicate.kind().skip_binder(), - obligation.cause.code().peel_derives(), - ) else { - return; - }; + let ty::PredicateKind::Trait(pred) = obligation.predicate.kind().skip_binder() else { return; }; + let (ObligationCauseCode::BindingObligation(item_def_id, span) + | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..)) + = *obligation.cause.code().peel_derives() else { return; }; debug!(?pred, ?item_def_id, ?span); let (Some(node), true) = ( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index e6907637c..e11a42201 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -143,7 +143,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } if let ObligationCauseCode::ItemObligation(item) - | ObligationCauseCode::BindingObligation(item, _) = *obligation.cause.code() + | ObligationCauseCode::BindingObligation(item, _) + | ObligationCauseCode::ExprItemObligation(item, ..) + | ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code() { // FIXME: maybe also have some way of handling methods // from other traits? That would require name resolution, @@ -254,7 +256,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } } - if let ty::Dynamic(traits, _) = self_ty.kind() { + if let ty::Dynamic(traits, _, _) = self_ty.kind() { for t in traits.iter() { if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 219413121..13d9c1600 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -20,13 +20,12 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable, - ProjectionPredicate, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitable, + ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; use rustc_session::Limit; @@ -174,7 +173,7 @@ pub trait InferCtxtExt<'tcx> { &self, err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, - proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>, + associated_item: Option<(&'static str, Ty<'tcx>)>, body_id: hir::HirId, ); @@ -467,7 +466,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, mut err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, - proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>, + associated_ty: Option<(&'static str, Ty<'tcx>)>, body_id: hir::HirId, ) { let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); @@ -604,21 +603,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_pred.print_modifiers_and_trait_path().to_string() ); - if let Some(proj_pred) = proj_pred { - let ProjectionPredicate { projection_ty, term } = proj_pred.skip_binder(); - let item = self.tcx.associated_item(projection_ty.item_def_id); - + if let Some((name, term)) = associated_ty { // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err. // That should be extracted into a helper function. if constraint.ends_with('>') { constraint = format!( - "{}, {}={}>", + "{}, {} = {}>", &constraint[..constraint.len() - 1], - item.name, + name, term ); } else { - constraint.push_str(&format!("<{}={}>", item.name, term)); + constraint.push_str(&format!("<{} = {}>", name, term)); } } @@ -648,7 +644,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .. }) if !param_ty => { // Missing generic type parameter bound. - if suggest_arbitrary_trait_bound(self.tcx, generics, &mut err, trait_pred) { + if suggest_arbitrary_trait_bound( + self.tcx, + generics, + &mut err, + trait_pred, + associated_ty, + ) { return; } } @@ -671,11 +673,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { // It only make sense when suggesting dereferences for arguments - let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else { - return false; - }; - let param_env = obligation.param_env; - let body_id = obligation.cause.body_id; + let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() + else { return false; }; + let Some(typeck_results) = self.in_progress_typeck_results + else { return false; }; + let typeck_results = typeck_results.borrow(); + let hir::Node::Expr(expr) = self.tcx.hir().get(*arg_hir_id) + else { return false; }; + let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) + else { return false; }; + let span = obligation.cause.span; let mut real_trait_pred = trait_pred; let mut code = obligation.cause.code(); @@ -685,11 +692,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { real_trait_pred = parent_trait_pred; } - // Skipping binder here, remapping below - let real_ty = real_trait_pred.self_ty().skip_binder(); + let real_ty = real_trait_pred.self_ty(); + // We `erase_late_bound_regions` here because `make_subregion` does not handle + // `ReLateBound`, and we don't particularly care about the regions. + if self + .can_eq(obligation.param_env, self.tcx.erase_late_bound_regions(real_ty), arg_ty) + .is_err() + { + continue; + } - if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { - let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span); + if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() { + let mut autoderef = Autoderef::new( + self, + obligation.param_env, + obligation.cause.body_id, + span, + base_ty, + span, + ); if let Some(steps) = autoderef.find_map(|(ty, steps)| { // Re-add the `&` let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); @@ -697,24 +718,29 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Remapping bound vars here let real_trait_pred_and_ty = real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); - let obligation = self - .mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred_and_ty); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + real_trait_pred_and_ty, + ); Some(steps).filter(|_| self.predicate_may_hold(&obligation)) }) { if steps > 0 { - if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) { - // Don't care about `&mut` because `DerefMut` is used less - // often and user will not expect autoderef happens. - if src.starts_with('&') && !src.starts_with("&mut ") { - let derefs = "*".repeat(steps); - err.span_suggestion( - span, - "consider dereferencing here", - format!("&{}{}", derefs, &src[1..]), - Applicability::MachineApplicable, - ); - return true; - } + // Don't care about `&mut` because `DerefMut` is used less + // often and user will not expect autoderef happens. + if let Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr), + .. + })) = self.tcx.hir().find(*arg_hir_id) + { + let derefs = "*".repeat(steps); + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "consider dereferencing here", + derefs, + Applicability::MachineApplicable, + ); + return true; } } } else if real_trait_pred != trait_pred { @@ -724,7 +750,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let real_trait_pred_and_base_ty = real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, base_ty)); let obligation = self.mk_trait_obligation_with_new_self_ty( - param_env, + obligation.param_env, real_trait_pred_and_base_ty, ); if self.predicate_may_hold(&obligation) { @@ -750,7 +776,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Get the local name of this closure. This can be inaccurate because // of the possibility of reassignment, but this should be good enough. match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) => { + hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => { Some(ident.name) } _ => { @@ -852,6 +878,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => return false, }; if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. }) + && obligation.cause.span.can_be_used_for_suggestions() { // When the obligation error has been ensured to have been caused by // an argument, the `obligation.cause.span` points at the expression @@ -882,6 +909,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation.cause.code() { &parent_code + } else if let ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() + { + obligation.cause.code() } else if let ExpnKind::Desugaring(DesugaringKind::ForLoop) = span.ctxt().outer_expn_data().kind { @@ -906,102 +937,121 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let param_env = obligation.param_env; // Try to apply the original trait binding obligation by borrowing. - let mut try_borrowing = - |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool { - if blacklist.contains(&old_pred.def_id()) { - return false; - } - // We map bounds to `&T` and `&mut T` - let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| { - ( - trait_pred, - self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), - ) - }); - let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| { + let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>, + blacklist: &[DefId]| + -> bool { + if blacklist.contains(&old_pred.def_id()) { + return false; + } + // We map bounds to `&T` and `&mut T` + let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| { + ( + trait_pred, + self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + ) + }); + let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| { + ( + trait_pred, + self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + ) + }); + + let mk_result = |trait_pred_and_new_ty| { + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); + self.predicate_must_hold_modulo_regions(&obligation) + }; + let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); + let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); + + let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) = + if let ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() + && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind() + { ( - trait_pred, - self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))), + matches!(mutability, hir::Mutability::Mut), ) - }); - - let mk_result = |trait_pred_and_new_ty| { - let obligation = - self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); - self.predicate_must_hold_modulo_regions(&obligation) + } else { + (false, false) }; - let imm_result = mk_result(trait_pred_and_imm_ref); - let mut_result = mk_result(trait_pred_and_mut_ref); - - if imm_result || mut_result { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - // We have a very specific type of error, where just borrowing this argument - // might solve the problem. In cases like this, the important part is the - // original type obligation, not the last one that failed, which is arbitrary. - // Because of this, we modify the error to refer to the original obligation and - // return early in the caller. - - let msg = format!("the trait bound `{}` is not satisfied", old_pred); - if has_custom_message { - err.note(&msg); - } else { - err.message = - vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; - } - if snippet.starts_with('&') { - // This is already a literal borrow and the obligation is failing - // somewhere else in the obligation chain. Do not suggest non-sense. - return false; - } - err.span_label( - span, + + if imm_ref_self_ty_satisfies_pred + || mut_ref_self_ty_satisfies_pred + || ref_inner_ty_satisfies_pred + { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + // We don't want a borrowing suggestion on the fields in structs, + // ``` + // struct Foo { + // the_foos: Vec<Foo> + // } + // ``` + if !matches!( + span.ctxt().outer_expn_data().kind, + ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) + ) { + return false; + } + if snippet.starts_with('&') { + // This is already a literal borrow and the obligation is failing + // somewhere else in the obligation chain. Do not suggest non-sense. + return false; + } + // We have a very specific type of error, where just borrowing this argument + // might solve the problem. In cases like this, the important part is the + // original type obligation, not the last one that failed, which is arbitrary. + // Because of this, we modify the error to refer to the original obligation and + // return early in the caller. + + let msg = format!("the trait bound `{}` is not satisfied", old_pred); + if has_custom_message { + err.note(&msg); + } else { + err.message = + vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; + } + err.span_label( + span, + format!( + "the trait `{}` is not implemented for `{}`", + old_pred.print_modifiers_and_trait_path(), + old_pred.self_ty().skip_binder(), + ), + ); + + if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { + err.span_suggestions( + span.shrink_to_lo(), + "consider borrowing here", + ["&".to_string(), "&mut ".to_string()].into_iter(), + Applicability::MaybeIncorrect, + ); + } else { + let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; + err.span_suggestion_verbose( + span.shrink_to_lo(), &format!( - "expected an implementor of trait `{}`", - old_pred.print_modifiers_and_trait_path(), + "consider{} borrowing here", + if is_mut { " mutably" } else { "" } ), + format!("&{}", if is_mut { "mut " } else { "" }), + Applicability::MaybeIncorrect, ); - - // This if is to prevent a special edge-case - if matches!( - span.ctxt().outer_expn_data().kind, - ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) - ) { - // We don't want a borrowing suggestion on the fields in structs, - // ``` - // struct Foo { - // the_foos: Vec<Foo> - // } - // ``` - - if imm_result && mut_result { - err.span_suggestions( - span.shrink_to_lo(), - "consider borrowing here", - ["&".to_string(), "&mut ".to_string()].into_iter(), - Applicability::MaybeIncorrect, - ); - } else { - err.span_suggestion_verbose( - span.shrink_to_lo(), - &format!( - "consider{} borrowing here", - if mut_result { " mutably" } else { "" } - ), - format!("&{}", if mut_result { "mut " } else { "" }), - Applicability::MaybeIncorrect, - ); - } - } - return true; } + return true; } - return false; - }; + } + return false; + }; if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code { try_borrowing(cause.derived.parent_trait_pred, &[]) } else if let ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ItemObligation(_) = code + | ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) = code { try_borrowing(poly_trait_pred, &never_suggest_borrow) } else { @@ -1017,7 +1067,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, object_ty: Ty<'tcx>, ) { - let ty::Dynamic(predicates, _) = object_ty.kind() else { return; }; + let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { return; }; let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty); for predicate in predicates.iter() { @@ -1110,8 +1160,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // and if not maybe suggest doing something else? If we kept the expression around we // could also check if it is an fn call (very likely) and suggest changing *that*, if // it is from the local crate. - err.span_suggestion_verbose( - expr.span.shrink_to_hi().with_hi(span.hi()), + err.span_suggestion( + span, "remove the `.await`", "", Applicability::MachineApplicable, @@ -1315,7 +1365,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let trait_pred = self.resolve_vars_if_possible(trait_pred); let ty = trait_pred.skip_binder().self_ty(); let is_object_safe = match ty.kind() { - ty::Dynamic(predicates, _) => { + ty::Dynamic(predicates, _, ty::Dyn) => { // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`. predicates .principal_def_id() @@ -1375,7 +1425,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut spans_and_needs_box = vec![]; match liberated_sig.output().kind() { - ty::Dynamic(predicates, _) => { + ty::Dynamic(predicates, _, _) => { let cause = ObligationCause::misc(ret_ty.span, fn_hir_id); let param_env = ty::ParamEnv::empty(); @@ -1541,32 +1591,38 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { expected: ty::PolyTraitRef<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { pub(crate) fn build_fn_sig_ty<'tcx>( - tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'_, 'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { let inputs = trait_ref.skip_binder().substs.type_at(1); let sig = match inputs.kind() { ty::Tuple(inputs) - if tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() => + if infcx.tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() => { - tcx.mk_fn_sig( + infcx.tcx.mk_fn_sig( inputs.iter(), - tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))), + infcx.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }), false, hir::Unsafety::Normal, abi::Abi::Rust, ) } - _ => tcx.mk_fn_sig( + _ => infcx.tcx.mk_fn_sig( std::iter::once(inputs), - tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))), + infcx.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }), false, hir::Unsafety::Normal, abi::Abi::Rust, ), }; - tcx.mk_fn_ptr(trait_ref.rebind(sig)) + infcx.tcx.mk_fn_ptr(trait_ref.rebind(sig)) } let argument_kind = match expected.skip_binder().self_ty().kind() { @@ -1586,11 +1642,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let found_span = found_span.unwrap_or(span); err.span_label(found_span, "found signature defined here"); - let expected = build_fn_sig_ty(self.tcx, expected); - let found = build_fn_sig_ty(self.tcx, found); + let expected = build_fn_sig_ty(self, expected); + let found = build_fn_sig_ty(self, found); - let (expected_str, found_str) = - self.tcx.infer_ctxt().enter(|infcx| infcx.cmp(expected, found)); + let (expected_str, found_str) = self.cmp(expected, found); let signature_kind = format!("{argument_kind} signature"); err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); @@ -2201,7 +2256,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::QuestionMark | ObligationCauseCode::CheckAssociatedTypeBounds { .. } | ObligationCauseCode::LetElse - | ObligationCauseCode::BinOp { .. } => {} + | ObligationCauseCode::BinOp { .. } + | ObligationCauseCode::AscribeUserTypeProvePredicate(..) => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } @@ -2223,11 +2279,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { region, object_ty, )); } - ObligationCauseCode::ItemObligation(_item_def_id) => { + ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) => { // We hold the `DefId` of the item introducing the obligation, but displaying it // doesn't add user usable information. It always point at an associated item. } - ObligationCauseCode::BindingObligation(item_def_id, span) => { + ObligationCauseCode::BindingObligation(item_def_id, span) + | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => { let item_name = tcx.def_path_str(item_def_id); let mut multispan = MultiSpan::from(span); if let Some(ident) = tcx.opt_item_ident(item_def_id) { @@ -2537,9 +2595,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { parent_trait_pred.remap_constness_diag(param_env); let parent_def_id = parent_trait_pred.def_id(); let msg = format!( - "required because of the requirements on the impl of `{}` for `{}`", - parent_trait_pred.print_modifiers_and_trait_path(), - parent_trait_pred.skip_binder().self_ty() + "required for `{}` to implement `{}`", + parent_trait_pred.skip_binder().self_ty(), + parent_trait_pred.print_modifiers_and_trait_path() ); let mut is_auto_trait = false; match self.tcx.hir().get_if_local(data.impl_def_id) { @@ -2608,9 +2666,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { pluralize!(count) )); err.note(&format!( - "required because of the requirements on the impl of `{}` for `{}`", - parent_trait_pred.print_modifiers_and_trait_path(), - parent_trait_pred.skip_binder().self_ty() + "required for `{}` to implement `{}`", + parent_trait_pred.skip_binder().self_ty(), + parent_trait_pred.print_modifiers_and_trait_path() )); } // #74711: avoid a stack overflow |