Skip to content

Commit

Permalink
Rollup merge of rust-lang#105285 - compiler-errors:conflicting-param-…
Browse files Browse the repository at this point in the history
…env-2, r=estebank

Highlight conflicting param-env candidates, again

Un-reverts rust-lang#98794 (i.e. reverts rust-lang#99290).

The previous time I attempted to land this PR, it was because of an incremental issue (rust-lang#99233). The repro instructions in the issue is no longer manifest the ICE -- I think it's because this ambiguity code was refactored (I think by ``@lcnr)`` to no longer store the ambiguities in the fulfillment error, but instead recompute them on the fly.

The main motivation for trying to re-land this is that it fixes rust-lang#105131 by highlighting the root-cause of the issue, which is conflicting param-env candidates:

```
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`.
```

Fixes rust-lang#105131.
Fixes (again) rust-lang#98786
  • Loading branch information
Dylan-DPC committed Dec 13, 2022
2 parents 33b7a64 + 79aa4a1 commit 79ffacb
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 35 deletions.
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 @@ -1482,7 +1482,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 @@ -2174,13 +2174,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() < 5 && has_non_region_infer {
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
if ambiguities.len() > 1 && ambiguities.len() < 5 && 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 @@ -2466,21 +2475,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 @@ -2504,7 +2522,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,15 +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`
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`
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`.
11 changes: 10 additions & 1 deletion src/test/ui/lifetimes/issue-34979.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ error[E0283]: type annotations needed: cannot satisfy `&'a (): Foo`
LL | &'a (): Foo,
| ^^^
|
= note: cannot satisfy `&'a (): Foo`
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
9 changes: 8 additions & 1 deletion src/test/ui/traits/issue-85735.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ error[E0283]: type annotations needed: cannot satisfy `T: FnMut<(&'a (),)>`
LL | T: FnMut(&'a ()),
| ^^^^^^^^^^^^^
|
= note: cannot satisfy `T: FnMut<(&'a (),)>`
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

0 comments on commit 79ffacb

Please sign in to comment.