Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Point at overlapping impls when type annotations are needed #89427

Merged
merged 2 commits into from
Oct 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,10 +465,14 @@ impl Diagnostic {
suggestions: impl Iterator<Item = String>,
applicability: Applicability,
) -> &mut Self {
let mut suggestions: Vec<_> = suggestions.collect();
suggestions.sort();
let substitutions = suggestions
.into_iter()
.map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
.collect();
self.suggestions.push(CodeSuggestion {
substitutions: suggestions
.map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
.collect(),
substitutions,
msg: msg.to_owned(),
style: SuggestionStyle::ShowCode,
applicability,
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>),
estebank marked this conversation as resolved.
Show resolved Hide resolved
}

/// When performing resolution, it is typically the case that there
Expand Down
163 changes: 152 additions & 11 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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,
Expand All @@ -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 (
Expand Down Expand Up @@ -1674,15 +1719,21 @@ 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;
}

self.emit_inference_failure_err(body_id, span, arg, vec![], ErrorCode::E0282)
}

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;
}
Expand All @@ -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() {
Expand Down Expand Up @@ -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!(
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
estebank marked this conversation as resolved.
Show resolved Hide resolved
candidates
.into_iter()
.filter_map(|c| match c.candidate {
SelectionCandidate::ImplCandidate(def_id) => Some(def_id),
_ => None,
})
.collect(),
));
}
}
}
Expand Down
23 changes: 17 additions & 6 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
//
Expand Down Expand Up @@ -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),
Expand Down
Loading