diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index fd4b5805f02..39d2ec4417c 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::sym; use super::eval_ctxt::GenerateProofTree; -use super::inspect::{ProofTreeInferCtxtExt, ProofTreeVisitor}; +use super::inspect::{InspectCandidate, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; use super::{Certainty, InferCtxtEvalExt}; /// A trait engine using the new trait solver. @@ -304,6 +304,46 @@ impl<'tcx> BestObligation<'tcx> { self.obligation = old_obligation; res } + + /// Filter out the candidates that aren't either error or ambiguous (depending + /// on what we are looking for), and also throw out candidates that have no + /// failing WC (or higher-ranked obligations, for which there should only be + /// one candidate anyways -- but I digress). This most likely means that the + /// goal just didn't unify at all, e.g. a param candidate with an alias in it. + fn non_trivial_candidates<'a>( + &self, + goal: &'a InspectGoal<'a, 'tcx>, + ) -> Vec> { + let mut candidates = goal + .candidates() + .into_iter() + .filter(|candidate| match self.consider_ambiguities { + true => matches!(candidate.result(), Ok(Certainty::Maybe(_))), + false => matches!(candidate.result(), Err(_)), + }) + .collect::>(); + + // If we have >1 candidate, one may still be due to "boring" reasons, like + // an alias-relate that failed to hold when deeply evaluated. We really + // don't care about reasons like this. + if candidates.len() > 1 { + candidates.retain(|candidate| { + goal.infcx().probe(|_| { + candidate.instantiate_nested_goals(self.span()).iter().any(|nested_goal| { + matches!( + nested_goal.source(), + GoalSource::ImplWhereBound | GoalSource::InstantiateHigherRanked + ) && match self.consider_ambiguities { + true => matches!(nested_goal.result(), Ok(Certainty::Maybe(_))), + false => matches!(nested_goal.result(), Err(_)), + } + }) + }) + }); + } + + candidates + } } impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { @@ -314,11 +354,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { } fn visit_goal(&mut self, goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result { - // FIXME: Throw out candidates that have no failing WC and >0 failing misc goal. - // This most likely means that the goal just didn't unify at all, e.g. a param - // candidate with an alias in it. - let candidates = goal.candidates(); - + let candidates = self.non_trivial_candidates(goal); let [candidate] = candidates.as_slice() else { return ControlFlow::Break(self.obligation.clone()); }; diff --git a/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.rs b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.rs new file mode 100644 index 00000000000..4737546e404 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.rs @@ -0,0 +1,22 @@ +trait Foo {} +trait Bar {} + +impl Foo for T where T: Bar {} +fn needs_foo(_: impl Foo) {} + +trait Mirror { + type Mirror; +} +impl Mirror for T { + type Mirror = T; +} + +// Make sure the `Alias: Foo` bound doesn't "shadow" the impl, since the +// impl is really the only candidate we care about here for the purpose +// of error reporting. +fn hello() where ::Mirror: Foo { + needs_foo(()); + //~^ ERROR the trait bound `(): Foo` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr new file mode 100644 index 00000000000..77a0cc49754 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `(): Foo` is not satisfied + --> $DIR/where-clause-doesnt-apply.rs:18:15 + | +LL | needs_foo(()); + | --------- ^^ the trait `Bar` is not implemented for `()`, which is required by `(): Foo` + | | + | required by a bound introduced by this call + | +note: required for `()` to implement `Foo` + --> $DIR/where-clause-doesnt-apply.rs:4:9 + | +LL | impl Foo for T where T: Bar {} + | ^^^ ^ --- unsatisfied trait bound introduced here +note: required by a bound in `needs_foo` + --> $DIR/where-clause-doesnt-apply.rs:5:22 + | +LL | fn needs_foo(_: impl Foo) {} + | ^^^ required by this bound in `needs_foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.