Skip to content

Commit 0813525

Browse files
Highlight conflicting param-env candidates
1 parent 9b21131 commit 0813525

File tree

8 files changed

+114
-19
lines changed

8 files changed

+114
-19
lines changed

Diff for: compiler/rustc_middle/src/traits/mod.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,17 @@ pub enum SelectionError<'tcx> {
546546
ErrorReporting,
547547
/// Multiple applicable `impl`s where found. The `DefId`s correspond to
548548
/// all the `impl`s' Items.
549-
Ambiguous(Vec<DefId>),
549+
Ambiguous(Vec<AmbiguousSelection>),
550550
}
551551

552+
#[derive(Copy, Clone, Debug)]
553+
pub enum AmbiguousSelection {
554+
Impl(DefId),
555+
ParamEnv(Span),
556+
}
557+
558+
TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, }
559+
552560
/// When performing resolution, it is typically the case that there
553561
/// can be one of three outcomes:
554562
///

Diff for: compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+30-11
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_hir::GenericParam;
2323
use rustc_hir::Item;
2424
use rustc_hir::Node;
2525
use rustc_infer::infer::error_reporting::same_type_modulo_infer;
26-
use rustc_infer::traits::TraitEngine;
26+
use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
2727
use rustc_middle::thir::abstract_const::NotConstEvaluatable;
2828
use rustc_middle::traits::select::OverflowError;
2929
use rustc_middle::ty::error::ExpectedFound;
@@ -1404,7 +1404,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
14041404
fn annotate_source_of_ambiguity(
14051405
&self,
14061406
err: &mut Diagnostic,
1407-
impls: &[DefId],
1407+
impls: &[AmbiguousSelection],
14081408
predicate: ty::Predicate<'tcx>,
14091409
);
14101410

@@ -2020,6 +2020,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
20202020
);
20212021
match selcx.select_from_obligation(&obligation) {
20222022
Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => {
2023+
if self.is_tainted_by_errors() && subst.is_none() {
2024+
// If `subst.is_none()`, then this is probably two param-env
2025+
// candidates or impl candidates that are equal modulo lifetimes.
2026+
// Therefore, if we've already emitted an error, just skip this
2027+
// one, since it's not particularly actionable.
2028+
err.cancel();
2029+
return;
2030+
}
20232031
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
20242032
}
20252033
_ => {
@@ -2170,24 +2178,35 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
21702178
fn annotate_source_of_ambiguity(
21712179
&self,
21722180
err: &mut Diagnostic,
2173-
impls: &[DefId],
2181+
impls: &[AmbiguousSelection],
21742182
predicate: ty::Predicate<'tcx>,
21752183
) {
21762184
let mut spans = vec![];
21772185
let mut crates = vec![];
21782186
let mut post = vec![];
2179-
for def_id in impls {
2180-
match self.tcx.span_of_impl(*def_id) {
2181-
Ok(span) => spans.push(span),
2182-
Err(name) => {
2183-
crates.push(name);
2184-
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
2185-
post.push(header);
2187+
let mut or_where_clause = false;
2188+
for ambig in impls {
2189+
match ambig {
2190+
AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) {
2191+
Ok(span) => spans.push(span),
2192+
Err(name) => {
2193+
crates.push(name);
2194+
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
2195+
post.push(header);
2196+
}
21862197
}
2198+
},
2199+
AmbiguousSelection::ParamEnv(span) => {
2200+
or_where_clause = true;
2201+
spans.push(*span);
21872202
}
21882203
}
21892204
}
2190-
let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
2205+
let msg = format!(
2206+
"multiple `impl`s{} satisfying `{}` found",
2207+
if or_where_clause { " or `where` clauses" } else { "" },
2208+
predicate
2209+
);
21912210
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
21922211
crate_names.sort();
21932212
crate_names.dedup();

Diff for: compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+41-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
//!
77
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
88
use hir::LangItem;
9+
use rustc_data_structures::fx::FxHashMap;
910
use rustc_hir as hir;
1011
use rustc_hir::def_id::DefId;
11-
use rustc_infer::traits::TraitEngine;
12+
use rustc_infer::traits::util::elaborate_predicates_with_span;
13+
use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
1214
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
1315
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
1416
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -199,11 +201,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
199201
// and report ambiguity.
200202
if i > 1 {
201203
debug!("multiple matches, ambig");
204+
205+
// Collect a list of (probable) spans that point to a param-env candidate
206+
let tcx = self.infcx.tcx;
207+
let owner = stack.obligation.cause.body_id.owner.to_def_id();
208+
let predicates = tcx.predicates_of(owner).instantiate_identity(tcx);
209+
let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span(
210+
tcx,
211+
std::iter::zip(predicates.predicates, predicates.spans),
212+
)
213+
.filter_map(|obligation| {
214+
let kind = obligation.predicate.kind();
215+
if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() {
216+
if trait_pred.trait_ref
217+
== ty::TraitRef::identity(tcx, trait_pred.def_id())
218+
.skip_binder()
219+
{
220+
// HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span
221+
Some((
222+
kind.rebind(trait_pred),
223+
tcx.def_span(trait_pred.def_id()),
224+
))
225+
} else {
226+
Some((kind.rebind(trait_pred), obligation.cause.span))
227+
}
228+
} else {
229+
None
230+
}
231+
})
232+
.collect();
233+
202234
return Err(Ambiguous(
203235
candidates
204236
.into_iter()
205237
.filter_map(|c| match c.candidate {
206-
SelectionCandidate::ImplCandidate(def_id) => Some(def_id),
238+
SelectionCandidate::ImplCandidate(def_id) => {
239+
Some(AmbiguousSelection::Impl(def_id))
240+
}
241+
SelectionCandidate::ParamCandidate(predicate) => {
242+
Some(AmbiguousSelection::ParamEnv(
243+
*param_env_spans.get(&predicate)?,
244+
))
245+
}
207246
_ => None,
208247
})
209248
.collect(),

Diff for: src/test/ui/issues/issue-21974.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo`
44
LL | where &'a T : Foo,
55
| ^^^
66
|
7-
= note: cannot satisfy `&'a T: Foo`
7+
note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found
8+
--> $DIR/issue-21974.rs:11:19
9+
|
10+
LL | where &'a T : Foo,
11+
| ^^^
12+
LL | &'b T : Foo
13+
| ^^^
814

915
error: aborting due to previous error
1016

Diff for: src/test/ui/issues/issue-24424.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ error[E0283]: type annotations needed: cannot satisfy `T0: Trait0<'l0>`
44
LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {}
55
| ^^^^^^^^^^^
66
|
7-
= note: cannot satisfy `T0: Trait0<'l0>`
7+
note: multiple `impl`s or `where` clauses satisfying `T0: Trait0<'l0>` found
8+
--> $DIR/issue-24424.rs:4:57
9+
|
10+
LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {}
11+
| ^^^^^^^^^^^ ^^^^^^^^^^^
812

913
error: aborting due to previous error
1014

Diff for: src/test/ui/lifetimes/issue-34979.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a (): Foo`
44
LL | &'a (): Foo,
55
| ^^^
66
|
7-
= note: cannot satisfy `&'a (): Foo`
7+
note: multiple `impl`s or `where` clauses satisfying `&'a (): Foo` found
8+
--> $DIR/issue-34979.rs:6:13
9+
|
10+
LL | &'a (): Foo,
11+
| ^^^
12+
LL | &'static (): Foo;
13+
| ^^^
814

915
error: aborting due to previous error
1016

Diff for: src/test/ui/traits/issue-85735.stderr

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ error[E0283]: type annotations needed: cannot satisfy `T: FnMut<(&'a (),)>`
44
LL | T: FnMut(&'a ()),
55
| ^^^^^^^^^^^^^
66
|
7-
= note: cannot satisfy `T: FnMut<(&'a (),)>`
7+
note: multiple `impl`s or `where` clauses satisfying `T: FnMut<(&'a (),)>` found
8+
--> $DIR/issue-85735.rs:7:8
9+
|
10+
LL | T: FnMut(&'a ()),
11+
| ^^^^^^^^^^^^^
12+
LL |
13+
LL | T: FnMut(&'b ()),
14+
| ^^^^^^^^^^^^^
815

916
error: aborting due to previous error
1017

Diff for: src/test/ui/type/type-check/issue-40294.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo`
44
LL | where &'a T : Foo,
55
| ^^^
66
|
7-
= note: cannot satisfy `&'a T: Foo`
7+
note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found
8+
--> $DIR/issue-40294.rs:6:19
9+
|
10+
LL | where &'a T : Foo,
11+
| ^^^
12+
LL | &'b T : Foo
13+
| ^^^
814

915
error: aborting due to previous error
1016

0 commit comments

Comments
 (0)