diff options
| author | Esteban Kuber <esteban@kuber.com.ar> | 2021-10-01 13:05:17 +0000 |
|---|---|---|
| committer | Esteban Kuber <esteban@kuber.com.ar> | 2021-10-24 18:33:04 +0000 |
| commit | ef212e7fb306626b4dc2c484aa3cf3b42a83e83a (patch) | |
| tree | bf69c7d6ae4d297c255d52b2832700e560199eb3 /compiler | |
| parent | ed08a67566d7d1d9dd2ad928ff21c23e841a4345 (diff) | |
| download | rust-ef212e7fb306626b4dc2c484aa3cf3b42a83e83a.tar.gz rust-ef212e7fb306626b4dc2c484aa3cf3b42a83e83a.zip | |
Point at overlapping impls when type annotations are needed
Diffstat (limited to 'compiler')
7 files changed, 204 insertions, 34 deletions
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index b089ae22d6d..6570d8e1567 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -440,16 +440,28 @@ pub struct DerivedObligationCause<'tcx> { #[derive(Clone, Debug, TypeFoldable, Lift)] pub enum SelectionError<'tcx> { + /// The trait is not implemented. Unimplemented, + /// After a closure impl has selected, its "outputs" were evaluated + /// (which for closures includes the "input" type params) and they + /// didn't resolve. See `confirm_poly_trait_refs` for more. OutputTypeParameterMismatch( ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>, ty::error::TypeError<'tcx>, ), + /// The trait pointed by `DefId` is not object safe. TraitNotObjectSafe(DefId), + /// A given constant couldn't be evaluated. NotConstEvaluatable(NotConstEvaluatable), + /// Exceeded the recursion depth during type projection. Overflow, + /// Signaling that an error has already been emitted, to avoid + /// multiple errors being shown. ErrorReporting, + /// Multiple applicable `impl`s where found. The `DefId`s correspond to + /// all the `impl`s' Items. + Ambiguous(Vec<DefId>), } /// When performing resolution, it is typically the case that there 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 225ff5e597e..8396e3263f9 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -34,6 +34,7 @@ use std::iter; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::AtExt as _; +use crate::traits::specialize::to_pretty_impl_header; use on_unimplemented::InferCtxtExt as _; use suggestions::InferCtxtExt as _; @@ -241,6 +242,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut span = obligation.cause.span; let mut err = match *error { + SelectionError::Ambiguous(ref impls) => { + let mut err = self.tcx.sess.struct_span_err( + obligation.cause.span, + &format!("multiple applicable `impl`s for `{}`", obligation.predicate), + ); + self.annotate_source_of_ambiguity(&mut err, impls, obligation.predicate); + err.emit(); + return; + } SelectionError::Unimplemented => { // If this obligation was generated as a result of well-formedness checking, see if we // can get a better error message by performing HIR-based well-formedness checking. @@ -1138,6 +1148,13 @@ trait InferCtxtPrivExt<'tcx> { obligation: &PredicateObligation<'tcx>, ); + fn annotate_source_of_ambiguity( + &self, + err: &mut DiagnosticBuilder<'tcx>, + impls: &[DefId], + predicate: ty::Predicate<'tcx>, + ); + fn maybe_suggest_unsized_generics( &self, err: &mut DiagnosticBuilder<'tcx>, @@ -1549,11 +1566,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ?predicate, ?obligation.cause.code, ); - // Ambiguity errors are often caused as fallout from earlier - // errors. So just ignore them if this infcx is tainted. - if self.is_tainted_by_errors() { - return; - } + // Ambiguity errors are often caused as fallout from earlier errors. + // We ignore them if this `infcx` is tainted in some cases below. let bound_predicate = predicate.kind(); let mut err = match bound_predicate.skip_binder() { @@ -1601,10 +1615,19 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // check upstream for type errors and don't add the obligations to // begin with in those cases. if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { - self.emit_inference_failure_err(body_id, span, subst, vec![], ErrorCode::E0282) + if !self.is_tainted_by_errors() { + self.emit_inference_failure_err( + body_id, + span, + subst, + vec![], + ErrorCode::E0282, + ) .emit(); + } return; } + let impl_candidates = self.find_similar_impl_candidates(trait_ref); let mut err = self.emit_inference_failure_err( body_id, @@ -1613,7 +1636,29 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { impl_candidates, ErrorCode::E0283, ); - err.note(&format!("cannot satisfy `{}`", predicate)); + + let obligation = Obligation::new( + obligation.cause.clone(), + obligation.param_env, + trait_ref.to_poly_trait_predicate(), + ); + let mut selcx = SelectionContext::with_query_mode( + &self, + crate::traits::TraitQueryMode::Standard, + ); + match selcx.select_from_obligation(&obligation) { + Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { + self.annotate_source_of_ambiguity(&mut err, &impls, predicate); + } + _ => { + if self.is_tainted_by_errors() { + err.cancel(); + return; + } + err.note(&format!("cannot satisfy `{}`", predicate)); + } + } + if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); } else if let ( @@ -1674,7 +1719,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ty::PredicateKind::WellFormed(arg) => { // Same hacky approach as above to avoid deluging user // with error messages. - if arg.references_error() || self.tcx.sess.has_errors() { + if arg.references_error() + || self.tcx.sess.has_errors() + || self.is_tainted_by_errors() + { return; } @@ -1682,7 +1730,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } ty::PredicateKind::Subtype(data) => { - if data.references_error() || self.tcx.sess.has_errors() { + if data.references_error() + || self.tcx.sess.has_errors() + || self.is_tainted_by_errors() + { // no need to overload user in such cases return; } @@ -1694,7 +1745,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ty::PredicateKind::Projection(data) => { let self_ty = data.projection_ty.self_ty(); let ty = data.ty; - if predicate.references_error() { + if predicate.references_error() || self.is_tainted_by_errors() { return; } if self_ty.needs_infer() && ty.needs_infer() { @@ -1722,7 +1773,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } _ => { - if self.tcx.sess.has_errors() { + if self.tcx.sess.has_errors() || self.is_tainted_by_errors() { return; } let mut err = struct_span_err!( @@ -1740,6 +1791,96 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { err.emit(); } + fn annotate_source_of_ambiguity( + &self, + err: &mut DiagnosticBuilder<'tcx>, + impls: &[DefId], + 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(self.tcx.sess.source_map().guess_head_span(span)), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { + post.push(header); + } + } + } + } + let msg = format!("multiple `impl`s satisfying `{}` found", predicate); + let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); + crate_names.sort(); + crate_names.dedup(); + post.sort(); + post.dedup(); + + if self.is_tainted_by_errors() + && crate_names.len() == 1 + && crate_names[0] == "`core`" + && spans.len() == 0 + { + // Avoid complaining about other inference issues for expressions like + // `42 >> 1`, where the types are still `{integer}`, but we want to + // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? + err.cancel(); + return; + } + let post = if post.len() > 4 { + format!( + ":\n{}\nand {} more", + post.iter().map(|p| format!("- {}", p)).take(4).collect::<Vec<_>>().join("\n"), + post.len() - 4, + ) + } else 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 { + format!(": `{}`", post[0]) + } else { + String::new() + }; + + match (spans.len(), crates.len(), crate_names.len()) { + (0, 0, 0) => { + err.note(&format!("cannot satisfy `{}`", predicate)); + } + (0, _, 1) => { + err.note(&format!("{} in the `{}` crate{}", msg, crates[0], post,)); + } + (0, _, _) => { + err.note(&format!( + "{} in the following crates: {}{}", + msg, + crate_names.join(", "), + post, + )); + } + (_, 0, 0) => { + let span: MultiSpan = spans.into(); + err.span_note(span, &msg); + } + (_, 1, 1) => { + let span: MultiSpan = spans.into(); + err.span_note(span, &msg); + err.note( + &format!("and another `impl` found in the `{}` crate{}", crates[0], post,), + ); + } + _ => { + let span: MultiSpan = spans.into(); + err.span_note(span, &msg); + err.note(&format!( + "and more `impl`s found in the following crates: {}{}", + crate_names.join(", "), + post, + )); + } + } + } + /// Returns `true` if the trait predicate may apply for *some* assignment /// to the type parameters. fn predicate_can_apply( 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 970fb304879..b4fd851f456 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -299,18 +299,15 @@ fn suggest_restriction( generics, trait_ref.without_const().to_predicate(tcx).to_string(), ), - (None, Some((ident, []))) => ( - ident.span.shrink_to_hi(), - format!(": {}", trait_ref.print_only_trait_path().to_string()), - ), - (_, Some((_, [.., bounds]))) => ( - bounds.span().shrink_to_hi(), - format!(" + {}", trait_ref.print_only_trait_path().to_string()), - ), - (Some(_), Some((_, []))) => ( - generics.span.shrink_to_hi(), - format!(": {}", trait_ref.print_only_trait_path().to_string()), - ), + (None, Some((ident, []))) => { + (ident.span.shrink_to_hi(), format!(": {}", trait_ref.print_only_trait_path())) + } + (_, Some((_, [.., bounds]))) => { + (bounds.span().shrink_to_hi(), format!(" + {}", trait_ref.print_only_trait_path())) + } + (Some(_), Some((_, []))) => { + (generics.span.shrink_to_hi(), format!(": {}", trait_ref.print_only_trait_path())) + } }; err.span_suggestion_verbose( diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e12b84742bf..1d0c54f86de 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -18,7 +18,7 @@ use crate::traits; use crate::traits::coherence::Conflict; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{util, SelectionResult}; -use crate::traits::{ErrorReporting, Overflow, Unimplemented}; +use crate::traits::{Ambiguous, ErrorReporting, Overflow, Unimplemented}; use super::BuiltinImplConditions; use super::IntercrateAmbiguityCause; @@ -197,7 +197,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // and report ambiguity. if i > 1 { debug!("multiple matches, ambig"); - return Ok(None); + return Err(Ambiguous( + candidates + .into_iter() + .filter_map(|c| match c.candidate { + SelectionCandidate::ImplCandidate(def_id) => Some(def_id), + _ => None, + }) + .collect(), + )); } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 43ffa285b8f..1b26e38fe0e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -357,18 +357,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { - debug_assert!(!obligation.predicate.has_escaping_bound_vars()); - - let pec = &ProvisionalEvaluationCache::default(); - let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); - - let candidate = match self.candidate_from_obligation(&stack) { + let candidate = match self.select_from_obligation(obligation) { Err(SelectionError::Overflow) => { // In standard mode, overflow must have been caught and reported // earlier. assert!(self.query_mode == TraitQueryMode::Canonical); return Err(SelectionError::Overflow); } + Err(SelectionError::Ambiguous(_)) => { + return Ok(None); + } Err(e) => { return Err(e); } @@ -391,6 +389,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + crate fn select_from_obligation( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + debug_assert!(!obligation.predicate.has_escaping_bound_vars()); + + let pec = &ProvisionalEvaluationCache::default(); + let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); + + self.candidate_from_obligation(&stack) + } + /////////////////////////////////////////////////////////////////////////// // EVALUATION // @@ -915,6 +925,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.candidate_from_obligation(stack) { Ok(Some(c)) => self.evaluate_candidate(stack, &c), + Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig), Ok(None) => Ok(EvaluatedToAmbig), Err(Overflow) => Err(OverflowError::Canonical), Err(ErrorReporting) => Err(OverflowError::ErrorReporting), diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 88aca794a6b..f9867f0671e 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -464,7 +464,7 @@ fn report_conflicting_impls( /// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a /// string. -fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { +crate fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { use std::fmt::Write; let trait_ref = tcx.impl_trait_ref(impl_def_id)?; diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 40f456de183..ad65a0ba62a 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -185,9 +185,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("coerce: unsize not object safe"); return Err(TypeError::ObjectUnsafeCoercion(did)); } - Err(_) => {} + Err(error) => { + debug!(?error, "coerce: unsize failed"); + } } - debug!("coerce: unsize failed"); // Examine the supertype and consider auto-borrowing. match *b.kind() { |
