X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_trait_selection%2Fsrc%2Ftraits%2Ferror_reporting%2Fmod.rs;h=2dd2c568bab97be6f3a78f2cd894ba695adadeec;hb=39b2a41b39b445bf7efab02f6eade16135d7df85;hp=30ff07ee6c372b6df900d11c4f980d5adefe268f;hpb=5e38e702aac61e1c1a243e6ee09d054bfdd71598;p=rust.git 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 30ff07ee6c3..2dd2c568bab 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -35,14 +35,14 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; 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::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; @@ -980,6 +980,7 @@ fn report_selection_error( trait_ref, obligation.cause.body_id, &mut err, + true, ) { // This is *almost* equivalent to // `obligation.cause.code().peel_derives()`, but it gives us the @@ -1015,6 +1016,7 @@ fn report_selection_error( trait_ref, obligation.cause.body_id, &mut err, + true, ); } } @@ -1034,7 +1036,7 @@ fn report_selection_error( && 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) { @@ -1234,6 +1236,7 @@ fn report_selection_error( _ => 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)) { @@ -1287,6 +1290,7 @@ fn report_selection_error( found_trait_ref, expected_trait_ref, obligation.cause.code(), + found_node, ) } else { let (closure_span, closure_arg_span, found) = found_did @@ -1432,6 +1436,7 @@ fn report_similar_impl_candidates( trait_ref: ty::PolyTraitRef<'tcx>, body_id: hir::HirId, err: &mut Diagnostic, + other: bool, ) -> bool; /// Gets the parent trait chain start @@ -1482,7 +1487,7 @@ fn suggest_unsized_bound_if_applicable( fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + impls: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ); @@ -1631,18 +1636,30 @@ fn report_projection_error( 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(_, _) @@ -1651,7 +1668,6 @@ fn report_projection_error( | 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. @@ -1660,11 +1676,11 @@ fn report_projection_error( 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) } @@ -1673,12 +1689,8 @@ fn report_projection_error( }; 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)); let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); @@ -1686,10 +1698,10 @@ fn report_projection_error( 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)| { @@ -1745,21 +1757,26 @@ fn maybe_detailed_projection_msg( 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 + } } } @@ -1788,8 +1805,8 @@ fn type_category(tcx: TyCtxt<'_>, t: Ty<'_>) -> Option { 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), @@ -1887,7 +1904,9 @@ fn report_similar_impl_candidates( 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>, err: &mut Diagnostic| { candidates.sort(); candidates.dedup(); @@ -1938,7 +1957,7 @@ fn report_similar_impl_candidates( 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() } @@ -2079,8 +2098,8 @@ fn mk_trait_obligation_with_new_self_ty( 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) } @@ -2174,19 +2193,40 @@ fn maybe_report_ambiguity( let mut selcx = SelectionContext::new(&self); match selcx.select_from_obligation(&obligation) { Ok(None) => { - let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation); + 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_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, + ); + } } } _ => { @@ -2198,60 +2238,10 @@ fn maybe_report_ambiguity( } } - 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]>::` - // ... - // LL | const fn const_val() -> 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::>() - .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(_))) = @@ -2322,18 +2312,19 @@ fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { 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 { @@ -2466,21 +2457,30 @@ fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { 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(); @@ -2504,7 +2504,11 @@ fn annotate_source_of_ambiguity( 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::>().join("\n"),) } else if post.len() == 1 {