Skip to content

Commit d213934

Browse files
authored
Rollup merge of #139564 - compiler-errors:deeply-norm, r=lcnr
Deeply normalize obligations in `BestObligation` folder Built on #139513. This establishes a somewhat rough invariant that the `Obligation`'s predicate is always deeply normalized in the folder; when we construct a new obligation we normalize it. Putting this up for discussion since it does affect some goals. r? lcnr
2 parents ea1a31b + decd7ec commit d213934

File tree

11 files changed

+194
-90
lines changed

11 files changed

+194
-90
lines changed

Diff for: compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

+35-36
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as
1313
use rustc_type_ir::solve::NoSolution;
1414
use tracing::{instrument, trace};
1515

16-
use crate::solve::Certainty;
1716
use crate::solve::delegate::SolverDelegate;
1817
use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
18+
use crate::solve::{Certainty, deeply_normalize_for_diagnostics};
1919
use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
2020

2121
pub(super) fn fulfillment_error_for_no_solution<'tcx>(
@@ -151,7 +151,7 @@ fn find_best_leaf_obligation<'tcx>(
151151
//
152152
// We should probably fix the visitor to not do so instead, as this also
153153
// means the leaf obligation may be incorrect.
154-
infcx
154+
let obligation = infcx
155155
.fudge_inference_if_ok(|| {
156156
infcx
157157
.visit_proof_tree(
@@ -161,7 +161,8 @@ fn find_best_leaf_obligation<'tcx>(
161161
.break_value()
162162
.ok_or(())
163163
})
164-
.unwrap_or(obligation)
164+
.unwrap_or(obligation);
165+
deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
165166
}
166167

167168
struct BestObligation<'tcx> {
@@ -298,7 +299,7 @@ impl<'tcx> BestObligation<'tcx> {
298299
/// `NormalizesTo` goal, so we don't fall back to the rigid projection check
299300
/// that should catch when a projection goal fails due to an unsatisfied trait
300301
/// goal.
301-
fn detect_error_in_higher_ranked_projection(
302+
fn detect_trait_error_in_higher_ranked_projection(
302303
&mut self,
303304
goal: &inspect::InspectGoal<'_, 'tcx>,
304305
) -> ControlFlow<PredicateObligation<'tcx>> {
@@ -307,7 +308,13 @@ impl<'tcx> BestObligation<'tcx> {
307308
&& !projection_clause.bound_vars().is_empty()
308309
{
309310
let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(tcx));
310-
self.with_derived_obligation(self.obligation.with(tcx, pred), |this| {
311+
let obligation = Obligation::new(
312+
tcx,
313+
self.obligation.cause.clone(),
314+
goal.goal().param_env,
315+
deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
316+
);
317+
self.with_derived_obligation(obligation, |this| {
311318
goal.infcx().visit_proof_tree_at_depth(
312319
goal.goal().with(tcx, pred),
313320
goal.depth() + 1,
@@ -388,7 +395,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
388395
(true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
389396
_ => return ControlFlow::Continue(()),
390397
}
391-
let pred_kind = goal.goal().predicate.kind();
398+
399+
let pred = goal.goal().predicate;
392400

393401
let candidates = self.non_trivial_candidates(goal);
394402
let candidate = match candidates.as_slice() {
@@ -410,20 +418,20 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
410418

411419
// FIXME: Also, what about considering >1 layer up the stack? May be necessary
412420
// for normalizes-to.
413-
let child_mode = match pred_kind.skip_binder() {
414-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
415-
ChildMode::Trait(pred_kind.rebind(pred))
421+
let child_mode = match pred.kind().skip_binder() {
422+
ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
423+
ChildMode::Trait(pred.kind().rebind(trait_pred))
416424
}
417-
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
418-
ChildMode::Host(pred_kind.rebind(pred))
425+
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(host_pred)) => {
426+
ChildMode::Host(pred.kind().rebind(host_pred))
419427
}
420428
ty::PredicateKind::NormalizesTo(normalizes_to)
421429
if matches!(
422430
normalizes_to.alias.kind(tcx),
423431
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
424432
) =>
425433
{
426-
ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
434+
ChildMode::Trait(pred.kind().rebind(ty::TraitPredicate {
427435
trait_ref: normalizes_to.alias.trait_ref(tcx),
428436
polarity: ty::PredicatePolarity::Positive,
429437
}))
@@ -457,10 +465,12 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
457465
for nested_goal in nested_goals {
458466
trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
459467

468+
let nested_pred = nested_goal.goal().predicate;
469+
460470
let make_obligation = |cause| Obligation {
461471
cause,
462472
param_env: nested_goal.goal().param_env,
463-
predicate: nested_goal.goal().predicate,
473+
predicate: nested_pred,
464474
recursion_depth: self.obligation.recursion_depth + 1,
465475
};
466476

@@ -510,31 +520,20 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
510520

511521
// alias-relate may fail because the lhs or rhs can't be normalized,
512522
// and therefore is treated as rigid.
513-
if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
514-
if let Some(obligation) = goal
515-
.infcx()
516-
.visit_proof_tree_at_depth(
517-
goal.goal().with(tcx, ty::ClauseKind::WellFormed(lhs.into())),
518-
goal.depth() + 1,
519-
self,
520-
)
521-
.break_value()
522-
{
523-
return ControlFlow::Break(obligation);
524-
} else if let Some(obligation) = goal
525-
.infcx()
526-
.visit_proof_tree_at_depth(
527-
goal.goal().with(tcx, ty::ClauseKind::WellFormed(rhs.into())),
528-
goal.depth() + 1,
529-
self,
530-
)
531-
.break_value()
532-
{
533-
return ControlFlow::Break(obligation);
534-
}
523+
if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() {
524+
goal.infcx().visit_proof_tree_at_depth(
525+
goal.goal().with(tcx, ty::ClauseKind::WellFormed(lhs.into())),
526+
goal.depth() + 1,
527+
self,
528+
)?;
529+
goal.infcx().visit_proof_tree_at_depth(
530+
goal.goal().with(tcx, ty::ClauseKind::WellFormed(rhs.into())),
531+
goal.depth() + 1,
532+
self,
533+
)?;
535534
}
536535

537-
self.detect_error_in_higher_ranked_projection(goal)?;
536+
self.detect_trait_error_in_higher_ranked_projection(goal)?;
538537

539538
ControlFlow::Break(self.obligation.clone())
540539
}

Diff for: compiler/rustc_trait_selection/src/solve/normalize.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -253,20 +253,28 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
253253
}
254254

255255
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
256-
deeply_normalize_with_skipped_universes(
257-
self.at,
258-
ty,
259-
vec![None; ty.outer_exclusive_binder().as_usize()],
260-
)
261-
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self))
256+
let infcx = self.at.infcx;
257+
infcx
258+
.commit_if_ok(|_| {
259+
deeply_normalize_with_skipped_universes(
260+
self.at,
261+
ty,
262+
vec![None; ty.outer_exclusive_binder().as_usize()],
263+
)
264+
})
265+
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self))
262266
}
263267

264268
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
265-
deeply_normalize_with_skipped_universes(
266-
self.at,
267-
ct,
268-
vec![None; ct.outer_exclusive_binder().as_usize()],
269-
)
270-
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ct.super_fold_with(self))
269+
let infcx = self.at.infcx;
270+
infcx
271+
.commit_if_ok(|_| {
272+
deeply_normalize_with_skipped_universes(
273+
self.at,
274+
ct,
275+
vec![None; ct.outer_exclusive_binder().as_usize()],
276+
)
277+
})
278+
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ct.super_fold_with(self))
271279
}
272280
}

Diff for: tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
error[E0277]: the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
1+
error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
22
--> $DIR/as_expression.rs:56:21
33
|
44
LL | SelectInt.check("bar");
5-
| ----- ^^^^^ the trait `AsExpression<<SelectInt as Expression>::SqlType>` is not implemented for `&str`
5+
| ----- ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
66
| |
77
| required by a bound introduced by this call
88
|
9-
= help: the trait `AsExpression<Text>` is implemented for `&str`
9+
= help: the trait `AsExpression<Integer>` is not implemented for `&str`
10+
but trait `AsExpression<Text>` is implemented for it
11+
= help: for that trait implementation, expected `Text`, found `Integer`
1012
note: required by a bound in `Foo::check`
1113
--> $DIR/as_expression.rs:47:12
1214
|
@@ -16,11 +18,11 @@ LL | where
1618
LL | T: AsExpression<Self::SqlType>,
1719
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
1820

19-
error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text`
21+
error[E0271]: type mismatch resolving `Integer == Text`
2022
--> $DIR/as_expression.rs:56:5
2123
|
2224
LL | SelectInt.check("bar");
23-
| ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer`
25+
| ^^^^^^^^^^^^^^^^^^^^^^ types differ
2426

2527
error: aborting due to 2 previous errors
2628

Diff for: tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ impl<T> Foo for T where T: Expression {}
5454

5555
fn main() {
5656
SelectInt.check("bar");
57-
//[current]~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
58-
//[next]~^^ ERROR the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
57+
//~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
5958
//[next]~| ERROR type mismatch
6059
}

Diff for: tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr

+121-17
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,134 @@ LL | #![feature(const_trait_impl, generic_const_exprs)]
66
|
77
= help: remove one of these features
88

9-
error[E0277]: the trait bound `T: const Trait` is not satisfied
10-
--> $DIR/unsatisfied-const-trait-bound.rs:29:37
9+
error[E0391]: cycle detected when evaluating type-level constant
10+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
1111
|
1212
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
13-
| ^
13+
| ^^^^^^^^^^^^^
14+
|
15+
note: ...which requires const-evaluating + checking `accept0::{constant#0}`...
16+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
17+
|
18+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
19+
| ^^^^^^^^^^^^^
20+
note: ...which requires caching mir of `accept0::{constant#0}` for CTFE...
21+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
22+
|
23+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
24+
| ^^^^^^^^^^^^^
25+
note: ...which requires elaborating drops for `accept0::{constant#0}`...
26+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
27+
|
28+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
29+
| ^^^^^^^^^^^^^
30+
note: ...which requires borrow-checking `accept0::{constant#0}`...
31+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
32+
|
33+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
34+
| ^^^^^^^^^^^^^
35+
note: ...which requires promoting constants in MIR for `accept0::{constant#0}`...
36+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
37+
|
38+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
39+
| ^^^^^^^^^^^^^
40+
note: ...which requires const checking `accept0::{constant#0}`...
41+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
42+
|
43+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
44+
| ^^^^^^^^^^^^^
45+
note: ...which requires building MIR for `accept0::{constant#0}`...
46+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
47+
|
48+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
49+
| ^^^^^^^^^^^^^
50+
note: ...which requires building an abstract representation for `accept0::{constant#0}`...
51+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
52+
|
53+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
54+
| ^^^^^^^^^^^^^
55+
note: ...which requires building THIR for `accept0::{constant#0}`...
56+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
57+
|
58+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
59+
| ^^^^^^^^^^^^^
60+
note: ...which requires type-checking `accept0::{constant#0}`...
61+
--> $DIR/unsatisfied-const-trait-bound.rs:29:35
62+
|
63+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
64+
| ^^^^^^^^^^^^^
65+
= note: ...which again requires evaluating type-level constant, completing the cycle
66+
note: cycle used when checking that `accept0` is well-formed
67+
--> $DIR/unsatisfied-const-trait-bound.rs:29:1
68+
|
69+
LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
70+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
71+
= 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
1472

15-
error[E0277]: the trait bound `T: const Trait` is not satisfied
16-
--> $DIR/unsatisfied-const-trait-bound.rs:33:50
73+
error[E0391]: cycle detected when caching mir of `accept1::{constant#0}` for CTFE
74+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
1775
|
1876
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
19-
| ^
20-
21-
error[E0277]: the trait bound `Ty: const Trait` is not satisfied
22-
--> $DIR/unsatisfied-const-trait-bound.rs:22:15
77+
| ^^^^^^^^^^^^^
78+
|
79+
note: ...which requires elaborating drops for `accept1::{constant#0}`...
80+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
81+
|
82+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
83+
| ^^^^^^^^^^^^^
84+
note: ...which requires borrow-checking `accept1::{constant#0}`...
85+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
86+
|
87+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
88+
| ^^^^^^^^^^^^^
89+
note: ...which requires promoting constants in MIR for `accept1::{constant#0}`...
90+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
91+
|
92+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
93+
| ^^^^^^^^^^^^^
94+
note: ...which requires const checking `accept1::{constant#0}`...
95+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
96+
|
97+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
98+
| ^^^^^^^^^^^^^
99+
note: ...which requires building MIR for `accept1::{constant#0}`...
100+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
23101
|
24-
LL | require::<Ty>();
25-
| ^^
102+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
103+
| ^^^^^^^^^^^^^
104+
note: ...which requires building an abstract representation for `accept1::{constant#0}`...
105+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
106+
|
107+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
108+
| ^^^^^^^^^^^^^
109+
note: ...which requires building THIR for `accept1::{constant#0}`...
110+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
26111
|
27-
note: required by a bound in `require`
28-
--> $DIR/unsatisfied-const-trait-bound.rs:8:15
112+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
113+
| ^^^^^^^^^^^^^
114+
note: ...which requires type-checking `accept1::{constant#0}`...
115+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
116+
|
117+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
118+
| ^^^^^^^^^^^^^
119+
note: ...which requires evaluating type-level constant...
120+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
29121
|
30-
LL | fn require<T: const Trait>() {}
31-
| ^^^^^^^^^^^ required by this bound in `require`
122+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
123+
| ^^^^^^^^^^^^^
124+
note: ...which requires const-evaluating + checking `accept1::{constant#0}`...
125+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
126+
|
127+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
128+
| ^^^^^^^^^^^^^
129+
= note: ...which again requires caching mir of `accept1::{constant#0}` for CTFE, completing the cycle
130+
note: cycle used when const-evaluating + checking `accept1::{constant#0}`
131+
--> $DIR/unsatisfied-const-trait-bound.rs:33:48
132+
|
133+
LL | const fn accept1<T: ~const Trait>(_: Container<{ T::make() }>) {}
134+
| ^^^^^^^^^^^^^
135+
= 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
32136

33-
error: aborting due to 4 previous errors
137+
error: aborting due to 3 previous errors
34138

35-
For more information about this error, try `rustc --explain E0277`.
139+
For more information about this error, try `rustc --explain E0391`.

Diff for: tests/ui/traits/next-solver/async.fail.stderr

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

0 commit comments

Comments
 (0)