Skip to content

Commit e40dd9e

Browse files
Highlight conflicting param-env candidates, again
1 parent d144956 commit e40dd9e

File tree

8 files changed

+158
-35
lines changed

8 files changed

+158
-35
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,101 @@
11
use rustc_hir::def_id::DefId;
2-
use rustc_infer::infer::InferCtxt;
2+
use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime};
3+
use rustc_infer::traits::util::elaborate_predicates_with_span;
34
use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
4-
use rustc_span::DUMMY_SP;
5+
use rustc_middle::ty;
6+
use rustc_span::{Span, DUMMY_SP};
57

68
use crate::traits::ObligationCtxt;
79

10+
pub enum Ambiguity {
11+
DefId(DefId),
12+
ParamEnv(Span),
13+
}
14+
815
pub fn recompute_applicable_impls<'tcx>(
916
infcx: &InferCtxt<'tcx>,
1017
obligation: &TraitObligation<'tcx>,
11-
) -> Vec<DefId> {
18+
) -> Vec<Ambiguity> {
1219
let tcx = infcx.tcx;
1320
let param_env = obligation.param_env;
14-
let dummy_cause = ObligationCause::dummy();
21+
1522
let impl_may_apply = |impl_def_id| {
1623
let ocx = ObligationCtxt::new_in_snapshot(infcx);
1724
let placeholder_obligation =
1825
infcx.replace_bound_vars_with_placeholders(obligation.predicate);
1926
let obligation_trait_ref =
20-
ocx.normalize(&dummy_cause, param_env, placeholder_obligation.trait_ref);
27+
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
2128

2229
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
2330
let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
2431
let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
2532

26-
if let Err(_) = ocx.eq(&dummy_cause, param_env, obligation_trait_ref, impl_trait_ref) {
33+
if let Err(_) =
34+
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
35+
{
2736
return false;
2837
}
2938

3039
let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs);
31-
ocx.register_obligations(
32-
impl_predicates
33-
.predicates
34-
.iter()
35-
.map(|&predicate| Obligation::new(tcx, dummy_cause.clone(), param_env, predicate)),
40+
ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
41+
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
42+
}));
43+
44+
ocx.select_where_possible().is_empty()
45+
};
46+
47+
let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
48+
let ocx = ObligationCtxt::new_in_snapshot(infcx);
49+
let placeholder_obligation =
50+
infcx.replace_bound_vars_with_placeholders(obligation.predicate);
51+
let obligation_trait_ref =
52+
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
53+
54+
let param_env_predicate = infcx.replace_bound_vars_with_fresh_vars(
55+
DUMMY_SP,
56+
LateBoundRegionConversionTime::HigherRankedType,
57+
poly_trait_predicate,
3658
);
59+
let param_env_trait_ref =
60+
ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
61+
62+
if let Err(_) =
63+
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref)
64+
{
65+
return false;
66+
}
3767

3868
ocx.select_where_possible().is_empty()
3969
};
4070

41-
let mut impls = Vec::new();
71+
let mut ambiguities = Vec::new();
72+
4273
tcx.for_each_relevant_impl(
4374
obligation.predicate.def_id(),
4475
obligation.predicate.skip_binder().trait_ref.self_ty(),
4576
|impl_def_id| {
46-
if infcx.probe(move |_snapshot| impl_may_apply(impl_def_id)) {
47-
impls.push(impl_def_id)
77+
if infcx.probe(|_| impl_may_apply(impl_def_id)) {
78+
ambiguities.push(Ambiguity::DefId(impl_def_id))
4879
}
4980
},
5081
);
51-
impls
82+
83+
let predicates =
84+
tcx.predicates_of(obligation.cause.body_id.owner.to_def_id()).instantiate_identity(tcx);
85+
for obligation in
86+
elaborate_predicates_with_span(tcx, std::iter::zip(predicates.predicates, predicates.spans))
87+
{
88+
let kind = obligation.predicate.kind();
89+
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
90+
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
91+
{
92+
if kind.rebind(trait_pred.trait_ref) == ty::TraitRef::identity(tcx, trait_pred.def_id()) {
93+
ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id())))
94+
} else {
95+
ambiguities.push(Ambiguity::ParamEnv(obligation.cause.span))
96+
}
97+
}
98+
}
99+
100+
ambiguities
52101
}

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+35-13
Original file line numberDiff line numberDiff line change
@@ -1438,7 +1438,7 @@ trait InferCtxtPrivExt<'tcx> {
14381438
fn annotate_source_of_ambiguity(
14391439
&self,
14401440
err: &mut Diagnostic,
1441-
impls: &[DefId],
1441+
impls: &[ambiguity::Ambiguity],
14421442
predicate: ty::Predicate<'tcx>,
14431443
);
14441444

@@ -2126,13 +2126,22 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
21262126
let mut selcx = SelectionContext::new(&self);
21272127
match selcx.select_from_obligation(&obligation) {
21282128
Ok(None) => {
2129-
let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation);
2129+
let ambiguities =
2130+
ambiguity::recompute_applicable_impls(self.infcx, &obligation);
21302131
let has_non_region_infer =
21312132
trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer());
21322133
// It doesn't make sense to talk about applicable impls if there are more
21332134
// than a handful of them.
2134-
if impls.len() > 1 && impls.len() < 5 && has_non_region_infer {
2135-
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
2135+
if ambiguities.len() > 1 && ambiguities.len() < 5 && has_non_region_infer {
2136+
if self.tainted_by_errors().is_some() && subst.is_none() {
2137+
// If `subst.is_none()`, then this is probably two param-env
2138+
// candidates or impl candidates that are equal modulo lifetimes.
2139+
// Therefore, if we've already emitted an error, just skip this
2140+
// one, since it's not particularly actionable.
2141+
err.cancel();
2142+
return;
2143+
}
2144+
self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
21362145
} else {
21372146
if self.tainted_by_errors().is_some() {
21382147
err.cancel();
@@ -2418,21 +2427,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
24182427
fn annotate_source_of_ambiguity(
24192428
&self,
24202429
err: &mut Diagnostic,
2421-
impls: &[DefId],
2430+
ambiguities: &[ambiguity::Ambiguity],
24222431
predicate: ty::Predicate<'tcx>,
24232432
) {
24242433
let mut spans = vec![];
24252434
let mut crates = vec![];
24262435
let mut post = vec![];
2427-
for def_id in impls {
2428-
match self.tcx.span_of_impl(*def_id) {
2429-
Ok(span) => spans.push(span),
2430-
Err(name) => {
2431-
crates.push(name);
2432-
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
2433-
post.push(header);
2436+
let mut has_param_env = false;
2437+
for ambiguity in ambiguities {
2438+
match ambiguity {
2439+
ambiguity::Ambiguity::DefId(impl_def_id) => {
2440+
match self.tcx.span_of_impl(*impl_def_id) {
2441+
Ok(span) => spans.push(span),
2442+
Err(name) => {
2443+
crates.push(name);
2444+
if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
2445+
post.push(header);
2446+
}
2447+
}
24342448
}
24352449
}
2450+
ambiguity::Ambiguity::ParamEnv(span) => {
2451+
has_param_env = true;
2452+
spans.push(*span);
2453+
}
24362454
}
24372455
}
24382456
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
@@ -2456,7 +2474,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
24562474
return;
24572475
}
24582476

2459-
let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
2477+
let msg = format!(
2478+
"multiple `impl`s{} satisfying `{}` found",
2479+
if has_param_env { " or `where` clauses" } else { "" },
2480+
predicate
2481+
);
24602482
let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
24612483
format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::<Vec<_>>().join("\n"),)
24622484
} else if post.len() == 1 {

src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr

+22-2
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,35 @@ error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual<I, 8>: True
4040
LL | IsLessOrEqual<I, 8>: True,
4141
| ^^^^
4242
|
43-
= note: cannot satisfy `IsLessOrEqual<I, 8>: True`
43+
note: multiple `impl`s or `where` clauses satisfying `IsLessOrEqual<I, 8>: True` found
44+
--> $DIR/issue-72787.rs:10:1
45+
|
46+
LL | impl<const LHS: u32, const RHS: u32> True for IsLessOrEqual<LHS, RHS> where
47+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
48+
...
49+
LL | IsLessOrEqual<I, 8>: True,
50+
| ^^^^
51+
...
52+
LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
53+
| ^^^^
4454

4555
error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual<I, 8>: True`
4656
--> $DIR/issue-72787.rs:21:26
4757
|
4858
LL | IsLessOrEqual<I, 8>: True,
4959
| ^^^^
5060
|
51-
= note: cannot satisfy `IsLessOrEqual<I, 8>: True`
61+
note: multiple `impl`s or `where` clauses satisfying `IsLessOrEqual<I, 8>: True` found
62+
--> $DIR/issue-72787.rs:10:1
63+
|
64+
LL | impl<const LHS: u32, const RHS: u32> True for IsLessOrEqual<LHS, RHS> where
65+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66+
...
67+
LL | IsLessOrEqual<I, 8>: True,
68+
| ^^^^
69+
...
70+
LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
71+
| ^^^^
5272

5373
error: aborting due to 6 previous errors
5474

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

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

src/test/ui/lifetimes/issue-34979.stderr

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@ 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:2:1
9+
|
10+
LL | impl<'a, T> Foo for &'a T {}
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
12+
...
13+
LL | &'a (): Foo,
14+
| ^^^
15+
LL | &'static (): Foo;
16+
| ^^^
817

918
error: aborting due to previous error
1019

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

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)