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

Highlight conflicting param-env candidates, again #105285

Merged
merged 2 commits into from
Dec 15, 2022
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
Original file line number Diff line number Diff line change
@@ -1,52 +1,101 @@
use rustc_hir::def_id::DefId;
use rustc_infer::infer::InferCtxt;
use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime};
use rustc_infer::traits::util::elaborate_predicates_with_span;
use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
use rustc_span::DUMMY_SP;
use rustc_middle::ty;
use rustc_span::{Span, DUMMY_SP};

use crate::traits::ObligationCtxt;

pub enum Ambiguity {
DefId(DefId),
ParamEnv(Span),
}

pub fn recompute_applicable_impls<'tcx>(
infcx: &InferCtxt<'tcx>,
obligation: &TraitObligation<'tcx>,
) -> Vec<DefId> {
) -> Vec<Ambiguity> {
let tcx = infcx.tcx;
let param_env = obligation.param_env;
let dummy_cause = ObligationCause::dummy();

let impl_may_apply = |impl_def_id| {
let ocx = ObligationCtxt::new_in_snapshot(infcx);
let placeholder_obligation =
infcx.replace_bound_vars_with_placeholders(obligation.predicate);
let obligation_trait_ref =
ocx.normalize(&dummy_cause, param_env, placeholder_obligation.trait_ref);
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);

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

if let Err(_) = ocx.eq(&dummy_cause, param_env, obligation_trait_ref, impl_trait_ref) {
if let Err(_) =
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
{
return false;
}

let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs);
ocx.register_obligations(
impl_predicates
.predicates
.iter()
.map(|&predicate| Obligation::new(tcx, dummy_cause.clone(), param_env, predicate)),
ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
}));

ocx.select_where_possible().is_empty()
};

let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
let ocx = ObligationCtxt::new_in_snapshot(infcx);
let placeholder_obligation =
infcx.replace_bound_vars_with_placeholders(obligation.predicate);
let obligation_trait_ref =
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);

let param_env_predicate = infcx.replace_bound_vars_with_fresh_vars(
DUMMY_SP,
LateBoundRegionConversionTime::HigherRankedType,
poly_trait_predicate,
);
let param_env_trait_ref =
ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);

if let Err(_) =
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref)
{
return false;
}

ocx.select_where_possible().is_empty()
};

let mut impls = Vec::new();
let mut ambiguities = Vec::new();

tcx.for_each_relevant_impl(
obligation.predicate.def_id(),
obligation.predicate.skip_binder().trait_ref.self_ty(),
|impl_def_id| {
if infcx.probe(move |_snapshot| impl_may_apply(impl_def_id)) {
impls.push(impl_def_id)
if infcx.probe(|_| impl_may_apply(impl_def_id)) {
ambiguities.push(Ambiguity::DefId(impl_def_id))
}
},
);
impls

let predicates =
tcx.predicates_of(obligation.cause.body_id.owner.to_def_id()).instantiate_identity(tcx);
for obligation in
elaborate_predicates_with_span(tcx, std::iter::zip(predicates.predicates, predicates.spans))
{
let kind = obligation.predicate.kind();
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
{
if kind.rebind(trait_pred.trait_ref) == ty::TraitRef::identity(tcx, trait_pred.def_id()) {
ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id())))
} else {
ambiguities.push(Ambiguity::ParamEnv(obligation.cause.span))
}
}
}

ambiguities
}
48 changes: 35 additions & 13 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,7 @@ trait InferCtxtPrivExt<'tcx> {
fn annotate_source_of_ambiguity(
&self,
err: &mut Diagnostic,
impls: &[DefId],
impls: &[ambiguity::Ambiguity],
predicate: ty::Predicate<'tcx>,
);

Expand Down Expand Up @@ -2180,13 +2180,22 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut selcx = SelectionContext::new(&self);
match selcx.select_from_obligation(&obligation) {
Ok(None) => {
let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation);
let ambiguities =
ambiguity::recompute_applicable_impls(self.infcx, &obligation);
let has_non_region_infer =
trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer());
// It doesn't make sense to talk about applicable impls if there are more
// than a handful of them.
if impls.len() > 1 && impls.len() < 10 && has_non_region_infer {
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
if self.tainted_by_errors().is_some() && 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, &ambiguities, predicate);
} else {
if self.tainted_by_errors().is_some() {
err.cancel();
Expand Down Expand Up @@ -2434,21 +2443,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn annotate_source_of_ambiguity(
&self,
err: &mut Diagnostic,
impls: &[DefId],
ambiguities: &[ambiguity::Ambiguity],
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 has_param_env = false;
for ambiguity in ambiguities {
match ambiguity {
ambiguity::Ambiguity::DefId(impl_def_id) => {
match self.tcx.span_of_impl(*impl_def_id) {
Ok(span) => spans.push(span),
Err(name) => {
crates.push(name);
if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
post.push(header);
}
}
}
}
ambiguity::Ambiguity::ParamEnv(span) => {
has_param_env = true;
spans.push(*span);
}
}
}
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
Expand All @@ -2472,7 +2490,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return;
}

let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
let msg = format!(
"multiple `impl`s{} satisfying `{}` found",
if has_param_env { " or `where` clauses" } else { "" },
predicate
);
let post = 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,35 @@ error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual<I, 8>: True
LL | IsLessOrEqual<I, 8>: True,
| ^^^^
|
= note: cannot satisfy `IsLessOrEqual<I, 8>: True`
= help: the trait `True` is implemented for `IsLessOrEqual<LHS, RHS>`
note: multiple `impl`s or `where` clauses satisfying `IsLessOrEqual<I, 8>: True` found
--> $DIR/issue-72787.rs:10:1
|
LL | impl<const LHS: u32, const RHS: u32> True for IsLessOrEqual<LHS, RHS> where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | IsLessOrEqual<I, 8>: True,
| ^^^^
...
LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
| ^^^^

error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual<I, 8>: True`
--> $DIR/issue-72787.rs:21:26
|
LL | IsLessOrEqual<I, 8>: True,
| ^^^^
|
= note: cannot satisfy `IsLessOrEqual<I, 8>: True`
= help: the trait `True` is implemented for `IsLessOrEqual<LHS, RHS>`
note: multiple `impl`s or `where` clauses satisfying `IsLessOrEqual<I, 8>: True` found
--> $DIR/issue-72787.rs:10:1
|
LL | impl<const LHS: u32, const RHS: u32> True for IsLessOrEqual<LHS, RHS> where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | IsLessOrEqual<I, 8>: True,
| ^^^^
...
LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
| ^^^^

error: aborting due to 6 previous errors

Expand Down
8 changes: 7 additions & 1 deletion src/test/ui/issues/issue-21974.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo`
LL | where &'a T : Foo,
| ^^^
|
= note: cannot satisfy `&'a T: Foo`
note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found
--> $DIR/issue-21974.rs:11:19
|
LL | where &'a T : Foo,
| ^^^
LL | &'b T : Foo
| ^^^

error: aborting due to previous error

Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/issues/issue-24424.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ error[E0283]: type annotations needed: cannot satisfy `T0: Trait0<'l0>`
LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {}
| ^^^^^^^^^^^
|
= note: cannot satisfy `T0: Trait0<'l0>`
note: multiple `impl`s or `where` clauses satisfying `T0: Trait0<'l0>` found
--> $DIR/issue-24424.rs:4:57
|
LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {}
| ^^^^^^^^^^^ ^^^^^^^^^^^

error: aborting due to previous error

Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/lifetimes/conflicting-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//~ type annotations needed: cannot satisfy `Self: Gen<'source>`

pub trait Gen<'source> {
type Output;

fn gen<T>(&self) -> T
where
Self: for<'s> Gen<'s, Output = T>;
}

fn main() {}
14 changes: 14 additions & 0 deletions src/test/ui/lifetimes/conflicting-bounds.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0283]: type annotations needed: cannot satisfy `Self: Gen<'source>`
|
note: multiple `impl`s or `where` clauses satisfying `Self: Gen<'source>` found
--> $DIR/conflicting-bounds.rs:3:1
|
LL | pub trait Gen<'source> {
| ^^^^^^^^^^^^^^^^^^^^^^
...
LL | Self: for<'s> Gen<'s, Output = T>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0283`.
12 changes: 10 additions & 2 deletions src/test/ui/lifetimes/issue-34979.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@ error[E0283]: type annotations needed: cannot satisfy `&'a (): Foo`
LL | &'a (): Foo,
| ^^^
|
= note: cannot satisfy `&'a (): Foo`
= help: the trait `Foo` is implemented for `&'a T`
note: multiple `impl`s or `where` clauses satisfying `&'a (): Foo` found
--> $DIR/issue-34979.rs:2:1
|
LL | impl<'a, T> Foo for &'a T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | &'a (): Foo,
| ^^^
LL | &'static (): Foo;
| ^^^

error: aborting due to previous error

Expand Down
12 changes: 8 additions & 4 deletions src/test/ui/traits/issue-85735.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ error[E0283]: type annotations needed: cannot satisfy `T: FnMut<(&'a (),)>`
LL | T: FnMut(&'a ()),
| ^^^^^^^^^^^^^
|
= note: cannot satisfy `T: FnMut<(&'a (),)>`
= help: the following types implement trait `FnMut<Args>`:
&F
&mut F
note: multiple `impl`s or `where` clauses satisfying `T: FnMut<(&'a (),)>` found
--> $DIR/issue-85735.rs:7:8
|
LL | T: FnMut(&'a ()),
| ^^^^^^^^^^^^^
LL |
LL | T: FnMut(&'b ()),
| ^^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
8 changes: 7 additions & 1 deletion src/test/ui/type/type-check/issue-40294.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo`
LL | where &'a T : Foo,
| ^^^
|
= note: cannot satisfy `&'a T: Foo`
note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found
--> $DIR/issue-40294.rs:6:19
|
LL | where &'a T : Foo,
| ^^^
LL | &'b T : Foo
| ^^^

error: aborting due to previous error

Expand Down