Deeply normalize obligations in BestObligation

This commit is contained in:
Michael Goulet 2025-04-09 17:25:46 +00:00
parent 62d5fb85ac
commit decd7ecd1e
11 changed files with 172 additions and 60 deletions

View file

@ -13,9 +13,9 @@ use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as
use rustc_type_ir::solve::NoSolution; use rustc_type_ir::solve::NoSolution;
use tracing::{instrument, trace}; use tracing::{instrument, trace};
use crate::solve::Certainty;
use crate::solve::delegate::SolverDelegate; use crate::solve::delegate::SolverDelegate;
use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
use crate::solve::{Certainty, deeply_normalize_for_diagnostics};
use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf}; use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
pub(super) fn fulfillment_error_for_no_solution<'tcx>( pub(super) fn fulfillment_error_for_no_solution<'tcx>(
@ -151,7 +151,7 @@ fn find_best_leaf_obligation<'tcx>(
// //
// We should probably fix the visitor to not do so instead, as this also // We should probably fix the visitor to not do so instead, as this also
// means the leaf obligation may be incorrect. // means the leaf obligation may be incorrect.
infcx let obligation = infcx
.fudge_inference_if_ok(|| { .fudge_inference_if_ok(|| {
infcx infcx
.visit_proof_tree( .visit_proof_tree(
@ -161,7 +161,8 @@ fn find_best_leaf_obligation<'tcx>(
.break_value() .break_value()
.ok_or(()) .ok_or(())
}) })
.unwrap_or(obligation) .unwrap_or(obligation);
deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
} }
struct BestObligation<'tcx> { struct BestObligation<'tcx> {
@ -298,7 +299,7 @@ impl<'tcx> BestObligation<'tcx> {
/// `NormalizesTo` goal, so we don't fall back to the rigid projection check /// `NormalizesTo` goal, so we don't fall back to the rigid projection check
/// that should catch when a projection goal fails due to an unsatisfied trait /// that should catch when a projection goal fails due to an unsatisfied trait
/// goal. /// goal.
fn detect_error_in_higher_ranked_projection( fn detect_trait_error_in_higher_ranked_projection(
&mut self, &mut self,
goal: &inspect::InspectGoal<'_, 'tcx>, goal: &inspect::InspectGoal<'_, 'tcx>,
) -> ControlFlow<PredicateObligation<'tcx>> { ) -> ControlFlow<PredicateObligation<'tcx>> {
@ -307,7 +308,13 @@ impl<'tcx> BestObligation<'tcx> {
&& !projection_clause.bound_vars().is_empty() && !projection_clause.bound_vars().is_empty()
{ {
let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(tcx)); let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(tcx));
self.with_derived_obligation(self.obligation.with(tcx, pred), |this| { let obligation = Obligation::new(
tcx,
self.obligation.cause.clone(),
goal.goal().param_env,
deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
);
self.with_derived_obligation(obligation, |this| {
goal.infcx().visit_proof_tree_at_depth( goal.infcx().visit_proof_tree_at_depth(
goal.goal().with(tcx, pred), goal.goal().with(tcx, pred),
goal.depth() + 1, goal.depth() + 1,
@ -526,7 +533,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
)?; )?;
} }
self.detect_error_in_higher_ranked_projection(goal)?; self.detect_trait_error_in_higher_ranked_projection(goal)?;
ControlFlow::Break(self.obligation.clone()) ControlFlow::Break(self.obligation.clone())
} }

View file

@ -253,20 +253,28 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
} }
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
deeply_normalize_with_skipped_universes( let infcx = self.at.infcx;
self.at, infcx
ty, .commit_if_ok(|_| {
vec![None; ty.outer_exclusive_binder().as_usize()], deeply_normalize_with_skipped_universes(
) self.at,
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self)) ty,
vec![None; ty.outer_exclusive_binder().as_usize()],
)
})
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self))
} }
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
deeply_normalize_with_skipped_universes( let infcx = self.at.infcx;
self.at, infcx
ct, .commit_if_ok(|_| {
vec![None; ct.outer_exclusive_binder().as_usize()], deeply_normalize_with_skipped_universes(
) self.at,
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ct.super_fold_with(self)) ct,
vec![None; ct.outer_exclusive_binder().as_usize()],
)
})
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ct.super_fold_with(self))
} }
} }

View file

@ -1,12 +1,14 @@
error[E0277]: the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
--> $DIR/as_expression.rs:56:21 --> $DIR/as_expression.rs:56:21
| |
LL | SelectInt.check("bar"); LL | SelectInt.check("bar");
| ----- ^^^^^ the trait `AsExpression<<SelectInt as Expression>::SqlType>` is not implemented for `&str` | ----- ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
| | | |
| required by a bound introduced by this call | required by a bound introduced by this call
| |
= help: the trait `AsExpression<Text>` is implemented for `&str` = help: the trait `AsExpression<Integer>` is not implemented for `&str`
but trait `AsExpression<Text>` is implemented for it
= help: for that trait implementation, expected `Text`, found `Integer`
note: required by a bound in `Foo::check` note: required by a bound in `Foo::check`
--> $DIR/as_expression.rs:47:12 --> $DIR/as_expression.rs:47:12
| |
@ -16,11 +18,11 @@ LL | where
LL | T: AsExpression<Self::SqlType>, LL | T: AsExpression<Self::SqlType>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text` error[E0271]: type mismatch resolving `Integer == Text`
--> $DIR/as_expression.rs:56:5 --> $DIR/as_expression.rs:56:5
| |
LL | SelectInt.check("bar"); LL | SelectInt.check("bar");
| ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer` | ^^^^^^^^^^^^^^^^^^^^^^ types differ
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -54,7 +54,6 @@ impl<T> Foo for T where T: Expression {}
fn main() { fn main() {
SelectInt.check("bar"); SelectInt.check("bar");
//[current]~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied //~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
//[next]~^^ ERROR the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
//[next]~| ERROR type mismatch //[next]~| ERROR type mismatch
} }

View file

@ -6,30 +6,134 @@ LL | #![feature(const_trait_impl, generic_const_exprs)]
| |
= help: remove one of these features = help: remove one of these features
error[E0277]: the trait bound `T: const Trait` is not satisfied error[E0391]: cycle detected when evaluating type-level constant
--> $DIR/unsatisfied-const-trait-bound.rs:29:37 --> $DIR/unsatisfied-const-trait-bound.rs:29:35
| |
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {} LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^ | ^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires caching mir of `accept0::{constant#0}` for CTFE...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires elaborating drops for `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires borrow-checking `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires const checking `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires building MIR for `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires building an abstract representation for `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires building THIR for `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires type-checking `accept0::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
= note: ...which again requires evaluating type-level constant, completing the cycle
note: cycle used when checking that `accept0` is well-formed
--> $DIR/unsatisfied-const-trait-bound.rs:29:1
|
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error[E0277]: the trait bound `T: const Trait` is not satisfied error[E0391]: cycle detected when caching mir of `accept1::{constant#0}` for CTFE
--> $DIR/unsatisfied-const-trait-bound.rs:33:50 --> $DIR/unsatisfied-const-trait-bound.rs:33:48
| |
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {} LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^ | ^^^^^^^^^^^^^
error[E0277]: the trait bound `Ty: const Trait` is not satisfied
--> $DIR/unsatisfied-const-trait-bound.rs:22:15
| |
LL | require::<Ty>(); note: ...which requires elaborating drops for `accept1::{constant#0}`...
| ^^ --> $DIR/unsatisfied-const-trait-bound.rs:33:48
| |
note: required by a bound in `require` LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
--> $DIR/unsatisfied-const-trait-bound.rs:8:15 | ^^^^^^^^^^^^^
note: ...which requires borrow-checking `accept1::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
| |
LL | fn require<T: const Trait>() {} LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^ required by this bound in `require` | ^^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `accept1::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires const checking `accept1::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires building MIR for `accept1::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires building an abstract representation for `accept1::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires building THIR for `accept1::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires type-checking `accept1::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires evaluating type-level constant...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `accept1::{constant#0}`...
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
= note: ...which again requires caching mir of `accept1::{constant#0}` for CTFE, completing the cycle
note: cycle used when const-evaluating + checking `accept1::{constant#0}`
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
|
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
| ^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 4 previous errors error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`. For more information about this error, try `rustc --explain E0391`.

View file

@ -1,8 +1,8 @@
error[E0271]: expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future that resolves to `i32`, but it resolves to `()` error[E0271]: type mismatch resolving `() == i32`
--> $DIR/async.rs:12:17 --> $DIR/async.rs:12:17
| |
LL | needs_async(async {}); LL | needs_async(async {});
| ----------- ^^^^^^^^ expected `i32`, found `()` | ----------- ^^^^^^^^ types differ
| | | |
| required by a bound introduced by this call | required by a bound introduced by this call
| |

View file

@ -10,7 +10,7 @@ fn needs_async(_: impl Future<Output = i32>) {}
#[cfg(fail)] #[cfg(fail)]
fn main() { fn main() {
needs_async(async {}); needs_async(async {});
//[fail]~^ ERROR expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future that resolves to `i32`, but it resolves to `()` //[fail]~^ ERROR type mismatch resolving `() == i32`
} }
#[cfg(pass)] #[cfg(pass)]

View file

@ -10,7 +10,7 @@ trait Trait: SuperTrait<A = <Self as SuperTrait>::B> {}
fn transmute<A, B>(x: A) -> B { fn transmute<A, B>(x: A) -> B {
foo::<A, B, dyn Trait<A = A, B = B>>(x) foo::<A, B, dyn Trait<A = A, B = B>>(x)
//~^ ERROR type mismatch resolving `<dyn Trait<A = A, B = B> as SuperTrait>::A == B` //~^ ERROR type mismatch resolving `A == B`
} }
fn foo<A, B, T: ?Sized>(x: T::A) -> B fn foo<A, B, T: ?Sized>(x: T::A) -> B

View file

@ -1,17 +1,9 @@
error[E0271]: type mismatch resolving `<dyn Trait<A = A, B = B> as SuperTrait>::A == B` error[E0271]: type mismatch resolving `A == B`
--> $DIR/more-object-bound.rs:12:5 --> $DIR/more-object-bound.rs:12:5
| |
LL | fn transmute<A, B>(x: A) -> B {
| - - expected type parameter
| |
| found type parameter
LL | foo::<A, B, dyn Trait<A = A, B = B>>(x) LL | foo::<A, B, dyn Trait<A = A, B = B>>(x)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `B`, found type parameter `A` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
| |
= note: expected type parameter `B`
found type parameter `A`
= note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
= note: required because it appears within the type `dyn Trait<A = A, B = B>` = note: required because it appears within the type `dyn Trait<A = A, B = B>`
note: required by a bound in `foo` note: required by a bound in `foo`
--> $DIR/more-object-bound.rs:18:8 --> $DIR/more-object-bound.rs:18:8

View file

@ -13,7 +13,7 @@ fn needs_bar<S: Bar>() {}
fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() { fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() {
needs_bar::<T::Assoc1>(); needs_bar::<T::Assoc1>();
//~^ ERROR the trait bound `<T as Foo1>::Assoc1: Bar` is not satisfied //~^ ERROR the trait bound `<T as Foo2>::Assoc2: Bar` is not satisfied
} }
fn main() {} fn main() {}

View file

@ -1,8 +1,8 @@
error[E0277]: the trait bound `<T as Foo1>::Assoc1: Bar` is not satisfied error[E0277]: the trait bound `<T as Foo2>::Assoc2: Bar` is not satisfied
--> $DIR/recursive-self-normalization-2.rs:15:17 --> $DIR/recursive-self-normalization-2.rs:15:17
| |
LL | needs_bar::<T::Assoc1>(); LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^ the trait `Bar` is not implemented for `<T as Foo1>::Assoc1` | ^^^^^^^^^ the trait `Bar` is not implemented for `<T as Foo2>::Assoc2`
| |
note: required by a bound in `needs_bar` note: required by a bound in `needs_bar`
--> $DIR/recursive-self-normalization-2.rs:12:17 --> $DIR/recursive-self-normalization-2.rs:12:17
@ -11,7 +11,7 @@ LL | fn needs_bar<S: Bar>() {}
| ^^^ required by this bound in `needs_bar` | ^^^ required by this bound in `needs_bar`
help: consider further restricting the associated type help: consider further restricting the associated type
| |
LL | fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() where <T as Foo1>::Assoc1: Bar { LL | fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() where <T as Foo2>::Assoc2: Bar {
| ++++++++++++++++++++++++++++++ | ++++++++++++++++++++++++++++++
error: aborting due to 1 previous error error: aborting due to 1 previous error