summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection/src/traits/error_reporting
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/error_reporting')
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs206
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs354
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