diff options
Diffstat (limited to 'compiler')
3 files changed, 80 insertions, 14 deletions
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index eee44df8645..fe7f72024d3 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -546,9 +546,17 @@ pub enum SelectionError<'tcx> { ErrorReporting, /// Multiple applicable `impl`s where found. The `DefId`s correspond to /// all the `impl`s' Items. - Ambiguous(Vec<DefId>), + Ambiguous(Vec<AmbiguousSelection>), } +#[derive(Copy, Clone, Debug)] +pub enum AmbiguousSelection { + Impl(DefId), + ParamEnv(Span), +} + +TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, } + /// When performing resolution, it is typically the case that there /// can be one of three outcomes: /// 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 8efefd476ab..aa1c9136289 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -23,7 +23,7 @@ use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; use rustc_infer::infer::error_reporting::same_type_modulo_infer; -use rustc_infer::traits::TraitEngine; +use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; use rustc_middle::thir::abstract_const::NotConstEvaluatable; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::error::ExpectedFound; @@ -1404,7 +1404,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + impls: &[AmbiguousSelection], predicate: ty::Predicate<'tcx>, ); @@ -2020,6 +2020,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ); match selcx.select_from_obligation(&obligation) { Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { + if self.is_tainted_by_errors() && 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, &impls, predicate); } _ => { @@ -2170,24 +2178,35 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + impls: &[AmbiguousSelection], 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 or_where_clause = false; + for ambig in impls { + match ambig { + AmbiguousSelection::Impl(def_id) => 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); + } } + }, + AmbiguousSelection::ParamEnv(span) => { + or_where_clause = true; + spans.push(*span); } } } - let msg = format!("multiple `impl`s satisfying `{}` found", predicate); + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if or_where_clause { " or `where` clauses" } else { "" }, + predicate + ); let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); crate_names.sort(); crate_names.dedup(); 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 0f7af41cfe3..21e14eae0ee 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -6,9 +6,11 @@ //! //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use hir::LangItem; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::traits::TraitEngine; +use rustc_infer::traits::util::elaborate_predicates_with_span; +use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -199,11 +201,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // and report ambiguity. if i > 1 { debug!("multiple matches, ambig"); + + // Collect a list of (probable) spans that point to a param-env candidate + let tcx = self.infcx.tcx; + let owner = stack.obligation.cause.body_id.owner.to_def_id(); + let predicates = tcx.predicates_of(owner).instantiate_identity(tcx); + let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span( + tcx, + std::iter::zip(predicates.predicates, predicates.spans), + ) + .filter_map(|obligation| { + let kind = obligation.predicate.kind(); + if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() { + if trait_pred.trait_ref + == ty::TraitRef::identity(tcx, trait_pred.def_id()) + .skip_binder() + { + // HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span + Some(( + kind.rebind(trait_pred), + tcx.def_span(trait_pred.def_id()), + )) + } else { + Some((kind.rebind(trait_pred), obligation.cause.span)) + } + } else { + None + } + }) + .collect(); + return Err(Ambiguous( candidates .into_iter() .filter_map(|c| match c.candidate { - SelectionCandidate::ImplCandidate(def_id) => Some(def_id), + SelectionCandidate::ImplCandidate(def_id) => { + Some(AmbiguousSelection::Impl(def_id)) + } + SelectionCandidate::ParamCandidate(predicate) => { + Some(AmbiguousSelection::ParamEnv( + *param_env_spans.get(&predicate)?, + )) + } _ => None, }) .collect(), |
