diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs')
-rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs | 469 |
1 files changed, 288 insertions, 181 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 dda7b2b2f..52971486c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1,4 +1,5 @@ mod ambiguity; +pub mod method_chain; pub mod on_unimplemented; pub mod suggestions; @@ -32,16 +33,17 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; -use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::print::{FmtPrinter, Print}; +use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, }; +use rustc_session::config::TraitSolver; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::sym; use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; use std::iter; @@ -225,7 +227,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { let arg_length = arguments.len(); let distinct = matches!(other, &[ArgKind::Tuple(..)]); match (arg_length, arguments.get(0)) { - (1, Some(&ArgKind::Tuple(_, ref fields))) => { + (1, Some(ArgKind::Tuple(_, fields))) => { format!("a single {}-tuple as argument", fields.len()) } _ => format!( @@ -372,6 +374,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }) } } + impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn report_fulfillment_errors( &self, @@ -451,9 +454,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - for (error, suppressed) in iter::zip(errors, is_suppressed) { - if !suppressed { - self.report_fulfillment_error(error, body_id); + for from_expansion in [false, true] { + for (error, suppressed) in iter::zip(errors, &is_suppressed) { + if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { + self.report_fulfillment_error(error, body_id); + } } } @@ -536,7 +541,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { |err| { self.note_obligation_cause_code( err, - &predicate, + predicate, obligation.param_env, obligation.cause.code(), &mut vec![], @@ -597,6 +602,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // can get a better error message by performing HIR-based well-formedness checking. if let ObligationCauseCode::WellFormed(Some(wf_loc)) = root_obligation.cause.code().peel_derives() + && !obligation.predicate.has_non_region_infer() { if let Some(cause) = self .tcx @@ -768,7 +774,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ), } }; - + self.check_for_binding_assigned_block_without_tail_expression( + &obligation, + &mut err, + trait_predicate, + ); if self.suggest_add_reference_to_arg( &obligation, &mut err, @@ -845,6 +855,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut suggested = self.suggest_dereferences(&obligation, &mut err, trait_predicate); suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); + let impl_candidates = self.find_similar_impl_candidates(trait_predicate); + suggested = if let &[cand] = &impl_candidates[..] { + let cand = cand.trait_ref; + if let (ty::FnPtr(_), ty::FnDef(..)) = + (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) + { + err.span_suggestion( + span.shrink_to_hi(), + format!( + "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`", + cand.print_only_trait_path(), + cand.self_ty(), + ), + format!(" as {}", cand.self_ty()), + Applicability::MaybeIncorrect, + ); + true + } else { + false + } + } else { + false + } || suggested; suggested |= self.suggest_remove_reference(&obligation, &mut err, trait_predicate); suggested |= self.suggest_semicolon_removal( @@ -866,6 +899,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } + if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) { + err.emit(); + return; + } + if self.suggest_impl_trait(&mut err, span, &obligation, trait_predicate) { err.emit(); return; @@ -978,6 +1016,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_ref, obligation.cause.body_id, &mut err, + true, ) { // This is *almost* equivalent to // `obligation.cause.code().peel_derives()`, but it gives us the @@ -1013,6 +1052,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_ref, obligation.cause.body_id, &mut err, + true, ); } } @@ -1032,7 +1072,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && self.fallback_has_occurred { let predicate = trait_predicate.map_bound(|trait_pred| { - trait_pred.with_self_type(self.tcx, self.tcx.mk_unit()) + trait_pred.with_self_ty(self.tcx, self.tcx.mk_unit()) }); let unit_obligation = obligation.with(tcx, predicate); if self.predicate_may_hold(&unit_obligation) { @@ -1088,15 +1128,19 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) - | ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => { - let predicate = self.resolve_vars_if_possible(obligation.predicate); - struct_span_err!( - self.tcx.sess, + span_bug!( span, - E0280, - "the requirement `{}` is not satisfied", - predicate + "outlives clauses should not error outside borrowck. obligation: `{:?}`", + obligation + ) + } + + ty::PredicateKind::Clause(ty::Clause::Projection(..)) => { + span_bug!( + span, + "projection clauses should be implied from elsewhere. obligation: `{:?}`", + obligation ) } @@ -1163,7 +1207,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ty::PredicateKind::WellFormed(ty) => { - if !self.tcx.sess.opts.unstable_opts.chalk { + if self.tcx.sess.opts.unstable_opts.trait_solver == TraitSolver::Classic { // WF predicates cannot themselves make // errors. They can only block due to // ambiguity; otherwise, they always @@ -1175,7 +1219,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // which bounds actually failed to hold. self.tcx.sess.struct_span_err( span, - &format!("the type `{}` is not well-formed (chalk)", ty), + &format!("the type `{}` is not well-formed", ty), ) } } @@ -1211,6 +1255,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } + OutputTypeParameterMismatch( + found_trait_ref, + expected_trait_ref, + terr @ TypeError::CyclicTy(_), + ) => { + let self_ty = found_trait_ref.self_ty().skip_binder(); + let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() { + ( + ObligationCause::dummy_with_span(tcx.def_span(def_id)), + TypeError::CyclicTy(self_ty), + ) + } else { + (obligation.cause.clone(), terr) + }; + self.report_and_explain_type_error( + TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref), + terr, + ) + } OutputTypeParameterMismatch(found_trait_ref, expected_trait_ref, _) => { let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref); let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref); @@ -1232,6 +1295,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { _ => None, }; + let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did)); let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did)); if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { @@ -1285,6 +1349,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { found_trait_ref, expected_trait_ref, obligation.cause.code(), + found_node, + obligation.param_env, ) } else { let (closure_span, closure_arg_span, found) = found_did @@ -1381,7 +1447,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.note_obligation_cause(&mut err, &obligation); self.point_at_returns_when_relevant(&mut err, &obligation); - err.emit(); } } @@ -1430,6 +1495,7 @@ trait InferCtxtPrivExt<'tcx> { trait_ref: ty::PolyTraitRef<'tcx>, body_id: hir::HirId, err: &mut Diagnostic, + other: bool, ) -> bool; /// Gets the parent trait chain start @@ -1480,7 +1546,7 @@ trait InferCtxtPrivExt<'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + impls: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ); @@ -1567,7 +1633,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { &error.obligation.cause, expected_found.expected, expected_found.found, - err.clone(), + *err, ) .emit(); } @@ -1576,7 +1642,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { &error.obligation.cause, expected_found.expected, expected_found.found, - err.clone(), + *err, ); let code = error.obligation.cause.code().peel_derives().peel_match_impls(); if let ObligationCauseCode::BindingObligation(..) @@ -1586,7 +1652,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { self.note_obligation_cause_code( &mut diag, - &error.obligation.predicate, + error.obligation.predicate, error.obligation.param_env, code, &mut vec![], @@ -1629,18 +1695,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { infer::LateBoundRegionConversionTime::HigherRankedType, bound_predicate.rebind(data), ); - let normalized_ty = ocx.normalize( - &obligation.cause, - obligation.param_env, - self.tcx - .mk_projection(data.projection_ty.item_def_id, data.projection_ty.substs), - ); + let unnormalized_term = match data.term.unpack() { + ty::TermKind::Ty(_) => self + .tcx + .mk_projection(data.projection_ty.def_id, data.projection_ty.substs) + .into(), + ty::TermKind::Const(ct) => self + .tcx + .mk_const( + ty::UnevaluatedConst { + def: ty::WithOptConstParam::unknown(data.projection_ty.def_id), + substs: data.projection_ty.substs, + }, + ct.ty(), + ) + .into(), + }; + let normalized_term = + ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); debug!(?obligation.cause, ?obligation.param_env); - debug!(?normalized_ty, data.ty = ?data.term); + debug!(?normalized_term, data.ty = ?data.term); - let is_normalized_ty_expected = !matches!( + let is_normalized_term_expected = !matches!( obligation.cause.code().peel_derives(), ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::BindingObligation(_, _) @@ -1649,7 +1727,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::ObjectCastObligation(..) | ObligationCauseCode::OpaqueType ); - let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error()); // constrain inference variables a bit more to nested obligations from normalize so // we can have more helpful errors. @@ -1658,11 +1735,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Err(new_err) = ocx.eq_exp( &obligation.cause, obligation.param_env, - is_normalized_ty_expected, - normalized_ty, - expected_ty, + is_normalized_term_expected, + normalized_term, + data.term, ) { - (Some((data, is_normalized_ty_expected, normalized_ty, expected_ty)), new_err) + (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err) } else { (None, error.err) } @@ -1671,23 +1748,31 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }; let msg = values - .and_then(|(predicate, _, normalized_ty, expected_ty)| { - self.maybe_detailed_projection_msg( - predicate, - normalized_ty.into(), - expected_ty.into(), - ) + .and_then(|(predicate, _, normalized_term, expected_term)| { + self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) }) - .unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate)); + .unwrap_or_else(|| { + with_forced_trimmed_paths!(format!( + "type mismatch resolving `{}`", + self.resolve_vars_if_possible(predicate) + .print(FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(10), + )) + .unwrap() + .into_buffer() + )) + }); let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); let secondary_span = match predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => self .tcx - .opt_associated_item(proj.projection_ty.item_def_id) + .opt_associated_item(proj.projection_ty.def_id) .and_then(|trait_assoc_item| { self.tcx - .trait_of_item(proj.projection_ty.item_def_id) + .trait_of_item(proj.projection_ty.def_id) .map(|id| (trait_assoc_item, id)) }) .and_then(|(trait_assoc_item, id)| { @@ -1709,7 +1794,20 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { kind: hir::ImplItemKind::Type(ty), .. }), - ) => Some((ty.span, format!("type mismatch resolving `{}`", predicate))), + ) => Some(( + ty.span, + with_forced_trimmed_paths!(format!( + "type mismatch resolving `{}`", + self.resolve_vars_if_possible(predicate) + .print(FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(5), + )) + .unwrap() + .into_buffer() + )), + )), _ => None, }), _ => None, @@ -1721,8 +1819,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { infer::ValuePairs::Terms(ExpectedFound::new( is_normalized_ty_expected, - normalized_ty.into(), - expected_ty.into(), + normalized_ty, + expected_ty, )) }), err, @@ -1743,21 +1841,26 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { 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 + with_forced_trimmed_paths! { + if Some(pred.projection_ty.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 + } } } @@ -1786,8 +1889,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::Closure(..) => Some(9), ty::Tuple(..) => Some(10), ty::Param(..) => Some(11), - ty::Projection(..) => Some(12), - ty::Opaque(..) => Some(13), + ty::Alias(ty::Projection, ..) => Some(12), + ty::Alias(ty::Opaque, ..) => Some(13), ty::Never => Some(14), ty::Adt(..) => Some(15), ty::Generator(..) => Some(16), @@ -1864,7 +1967,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return None; } - let imp = self.tcx.impl_trait_ref(def_id).unwrap(); + let imp = self.tcx.impl_trait_ref(def_id).unwrap().skip_binder(); self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false) .map(|similarity| ImplCandidate { trait_ref: imp, similarity }) @@ -1885,32 +1988,32 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_ref: ty::PolyTraitRef<'tcx>, body_id: hir::HirId, err: &mut Diagnostic, + other: bool, ) -> bool { + let other = if other { "other " } else { "" }; let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { candidates.sort(); candidates.dedup(); let len = candidates.len(); - if candidates.len() == 0 { + if candidates.is_empty() { 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(), - }; + if let &[cand] = &candidates[..] { + let (desc, mention_castable) = + match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) { + (ty::FnPtr(_), ty::FnDef(..)) => { + (" implemented for fn pointer `", ", cast using `as`") + } + (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""), + _ => (" implemented for `", ""), + }; err.highlighted_help(vec![ - ( - format!("the trait `{}` ", candidates[0].print_only_trait_path()), - Style::NoStyle, - ), + (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), ("is".to_string(), Style::Highlight), - (the_desc, Style::NoStyle), - (candidates[0].self_ty().to_string(), Style::Highlight), + (desc.to_string(), Style::NoStyle), + (cand.self_ty().to_string(), Style::Highlight), ("`".to_string(), Style::NoStyle), + (mention_castable.to_string(), Style::NoStyle), ]); return true; } @@ -1936,7 +2039,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { candidates.dedup(); let end = if candidates.len() <= 9 { candidates.len() } else { 8 }; err.help(&format!( - "the following other types implement trait `{}`:{}{}", + "the following {other}types implement trait `{}`:{}{}", trait_ref.print_only_trait_path(), candidates[..end].join(""), if len > 9 { format!("\nand {} others", len - 8) } else { String::new() } @@ -1962,6 +2065,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { || self.tcx.is_builtin_derive(def_id) }) .filter_map(|def_id| self.tcx.impl_trait_ref(def_id)) + .map(ty::EarlyBinder::subst_identity) .filter(|trait_ref| { let self_ty = trait_ref.self_ty(); // Avoid mentioning type parameters. @@ -2077,8 +2181,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, ) -> PredicateObligation<'tcx> { - let trait_pred = trait_ref_and_ty - .map_bound(|(tr, new_self_ty)| tr.with_self_type(self.tcx, new_self_ty)); + let trait_pred = + trait_ref_and_ty.map_bound(|(tr, new_self_ty)| tr.with_self_ty(self.tcx, new_self_ty)); Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred) } @@ -2115,7 +2219,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // This is kind of a hack: it frequently happens that some earlier // error prevents types from being fully inferred, and then we get // a bunch of uninteresting errors saying something like "<generic - // #0> doesn't implement Sized". It may even be true that we + // #0> doesn't implement Sized". It may even be true that we // could just skip over all checks where the self-ty is an // inference variable, but I was afraid that there might be an // inference variable created, registered as an obligation, and @@ -2172,19 +2276,43 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut selcx = SelectionContext::new(&self); match selcx.select_from_obligation(&obligation) { Ok(None) => { - let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation); - let has_non_region_infer = - trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer()); + let ambiguities = + ambiguity::recompute_applicable_impls(self.infcx, &obligation); + let has_non_region_infer = trait_ref + .skip_binder() + .substs + .types() + .any(|t| !t.is_ty_or_numeric_infer()); // It doesn't make sense to talk about applicable impls if there are more // than a handful of them. - if impls.len() > 1 && impls.len() < 5 && has_non_region_infer { - self.annotate_source_of_ambiguity(&mut err, &impls, predicate); + if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { + if self.tainted_by_errors().is_some() && subst.is_none() { + // If `subst.is_none()`, then this is probably two param-env + // candidates or impl candidates that are equal modulo lifetimes. + // Therefore, if we've already emitted an error, just skip this + // one, since it's not particularly actionable. + err.cancel(); + return; + } + self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); } else { if self.tainted_by_errors().is_some() { err.cancel(); return; } err.note(&format!("cannot satisfy `{}`", predicate)); + let impl_candidates = self.find_similar_impl_candidates( + predicate.to_opt_poly_trait_pred().unwrap(), + ); + if impl_candidates.len() < 10 { + self.report_similar_impl_candidates( + impl_candidates, + trait_ref, + body_id.map(|id| id.hir_id).unwrap_or(obligation.cause.body_id), + &mut err, + false, + ); + } } } _ => { @@ -2196,82 +2324,16 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - 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(snippet) = &self.tcx.sess.source_map().span_to_snippet(span) - && let ObligationCauseCode::BindingObligation(def_id, _) | ObligationCauseCode::ExprBindingObligation(def_id, ..) - = *obligation.cause.code() + if let ObligationCauseCode::ItemObligation(def_id) + | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() { - let generics = self.tcx.generics_of(def_id); - if generics.params.iter().any(|p| p.name != kw::SelfUpper) - && !snippet.ends_with('>') - && !generics.has_impl_trait() - && !self.tcx.is_fn_trait(def_id) - { - // FIXME: To avoid spurious suggestions in functions where type arguments - // where already supplied, we check the snippet to make sure it doesn't - // end with a turbofish. Ideally we would have access to a `PathSegment` - // instead. Otherwise we would produce the following output: - // - // error[E0283]: type annotations needed - // --> $DIR/issue-54954.rs:3:24 - // | - // LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); - // | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - // | | - // | cannot infer type - // | help: consider specifying the type argument - // | in the function call: - // | `Tt::const_val::<[i8; 123]>::<T>` - // ... - // LL | const fn const_val<T: Sized>() -> usize { - // | - required by this bound in `Tt::const_val` - // | - // = note: cannot satisfy `_: Tt` - - // Clear any more general suggestions in favor of our specific one - err.clear_suggestions(); - - err.span_suggestion_verbose( - span.shrink_to_hi(), - &format!( - "consider specifying the type argument{} in the function call", - pluralize!(generics.params.len()), - ), - format!( - "::<{}>", - generics - .params - .iter() - .map(|p| p.name.to_string()) - .collect::<Vec<String>>() - .join(", ") - ), - Applicability::HasPlaceholders, - ); - } + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); } if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) = (body_id, subst.map(|subst| subst.unpack())) { - struct FindExprBySpan<'hir> { - span: Span, - result: Option<&'hir hir::Expr<'hir>>, - } - - impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> { - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - if self.span == ex.span { - self.result = Some(ex); - } else { - hir::intravisit::walk_expr(self, ex); - } - } - } - - let mut expr_finder = FindExprBySpan { span, result: None }; - + let mut expr_finder = FindExprBySpan::new(span); expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); if let Some(hir::Expr { @@ -2320,18 +2382,19 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); if trait_impls.blanket_impls().is_empty() - && let Some((impl_ty, _)) = trait_impls.non_blanket_impls().iter().next() - && let Some(impl_def_id) = impl_ty.def() { - let message = if trait_impls.non_blanket_impls().len() == 1 { + && let Some(impl_def_id) = trait_impls.non_blanket_impls().values().flatten().next() + { + let non_blanket_impl_count = trait_impls.non_blanket_impls().values().flatten().count(); + let message = if non_blanket_impl_count == 1 { "use the fully-qualified path to the only available implementation".to_string() } else { format!( "use a fully-qualified path to a specific available implementation ({} found)", - trait_impls.non_blanket_impls().len() + non_blanket_impl_count ) }; let mut suggestions = vec![( - trait_path_segment.ident.span.shrink_to_lo(), + path.span.shrink_to_lo(), format!("<{} as ", self.tcx.type_of(impl_def_id)) )]; if let Some(generic_arg) = trait_path_segment.args { @@ -2339,9 +2402,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // get rid of :: between Trait and <type> // must be '::' between them, otherwise the parser won't accept the code suggestions.push((between_span, "".to_string(),)); - suggestions.push((generic_arg.span_ext.shrink_to_hi(), format!(">"))); + suggestions.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); } else { - suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), format!(">"))); + suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), ">".to_string())); } err.multipart_suggestion( message, @@ -2464,21 +2527,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + ambiguities: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ) { let mut spans = vec![]; let mut crates = vec![]; let mut post = vec![]; - for def_id in impls { - match self.tcx.span_of_impl(*def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { - post.push(header); + let mut has_param_env = false; + for ambiguity in ambiguities { + match ambiguity { + ambiguity::Ambiguity::DefId(impl_def_id) => { + match self.tcx.span_of_impl(*impl_def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { + post.push(header); + } + } } } + ambiguity::Ambiguity::ParamEnv(span) => { + has_param_env = true; + spans.push(*span); + } } } let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); @@ -2502,7 +2574,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } - let msg = format!("multiple `impl`s satisfying `{}` found", predicate); + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if has_param_env { " or `where` clauses" } else { "" }, + predicate + ); let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::<Vec<_>>().join("\n"),) } else if post.len() == 1 { @@ -2601,7 +2677,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { self.note_obligation_cause_code( err, - &obligation.predicate, + obligation.predicate, obligation.param_env, obligation.cause.code(), &mut vec![], @@ -2744,6 +2820,36 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } +/// Crude way of getting back an `Expr` from a `Span`. +pub struct FindExprBySpan<'hir> { + pub span: Span, + pub result: Option<&'hir hir::Expr<'hir>>, + pub ty_result: Option<&'hir hir::Ty<'hir>>, +} + +impl<'hir> FindExprBySpan<'hir> { + fn new(span: Span) -> Self { + Self { span, result: None, ty_result: None } + } +} + +impl<'v> Visitor<'v> for FindExprBySpan<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if self.span == ex.span { + self.result = Some(ex); + } else { + hir::intravisit::walk_expr(self, ex); + } + } + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + if self.span == ty.span { + self.ty_result = Some(ty); + } else { + hir::intravisit::walk_ty(self, ty); + } + } +} + /// Look for type `param` in an ADT being used only through a reference to confirm that suggesting /// `param: ?Sized` would be a valid constraint. struct FindTypeParam { @@ -2765,7 +2871,7 @@ impl<'v> Visitor<'v> for FindTypeParam { // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors // in that case should make what happened clear enough. match ty.kind { - hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => {} + hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {} hir::TyKind::Path(hir::QPath::Resolved(None, path)) if path.segments.len() == 1 && path.segments[0].ident.name == self.param => { @@ -2827,11 +2933,12 @@ impl<'tcx> ty::TypeVisitor<'tcx> for HasNumericInferVisitor { if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) { ControlFlow::Break(()) } else { - ControlFlow::CONTINUE + ControlFlow::Continue(()) } } } +#[derive(Copy, Clone)] pub enum DefIdOrName { DefId(DefId), Name(&'static str), |