Skip to content

Commit fb95fda

Browse files
Rollup merge of #125343 - lcnr:eagerly-normalize-added-goals, r=compiler-errors
`-Znext-solver`: eagerly normalize when adding goals fixes #125269. I am not totally with this fix and going to keep this open until we have a more general discussion about how to handle hangs caused by lazy norm in the new solver.
2 parents 8e89f83 + 98bfd54 commit fb95fda

File tree

12 files changed

+250
-62
lines changed

12 files changed

+250
-62
lines changed

compiler/rustc_middle/src/ty/predicate.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -121,17 +121,14 @@ impl<'tcx> Predicate<'tcx> {
121121
#[inline]
122122
pub fn allow_normalization(self) -> bool {
123123
match self.kind().skip_binder() {
124-
PredicateKind::Clause(ClauseKind::WellFormed(_)) => false,
125-
// `NormalizesTo` is only used in the new solver, so this shouldn't
126-
// matter. Normalizing `term` would be 'wrong' however, as it changes whether
127-
// `normalizes-to(<T as Trait>::Assoc, <T as Trait>::Assoc)` holds.
128-
PredicateKind::NormalizesTo(..) => false,
124+
PredicateKind::Clause(ClauseKind::WellFormed(_))
125+
| PredicateKind::AliasRelate(..)
126+
| PredicateKind::NormalizesTo(..) => false,
129127
PredicateKind::Clause(ClauseKind::Trait(_))
130128
| PredicateKind::Clause(ClauseKind::RegionOutlives(_))
131129
| PredicateKind::Clause(ClauseKind::TypeOutlives(_))
132130
| PredicateKind::Clause(ClauseKind::Projection(_))
133131
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
134-
| PredicateKind::AliasRelate(..)
135132
| PredicateKind::ObjectSafe(_)
136133
| PredicateKind::Subtype(_)
137134
| PredicateKind::Coerce(_)

compiler/rustc_trait_selection/src/solve/alias_relate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
2828
) -> QueryResult<'tcx> {
2929
let tcx = self.tcx();
3030
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
31+
debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some());
3132

3233
// Structurally normalize the lhs.
3334
let lhs = if let Some(alias) = lhs.to_alias_term() {

compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs

+75-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ use rustc_middle::traits::solve::{
1313
inspect, CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaquesData, QueryResult,
1414
};
1515
use rustc_middle::traits::specialization_graph;
16+
use rustc_middle::ty::AliasRelationDirection;
17+
use rustc_middle::ty::TypeFolder;
1618
use rustc_middle::ty::{
1719
self, InferCtxtLike, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
1820
TypeVisitable, TypeVisitableExt, TypeVisitor,
1921
};
2022
use rustc_span::DUMMY_SP;
23+
use rustc_type_ir::fold::TypeSuperFoldable;
2124
use rustc_type_ir::{self as ir, CanonicalVarValues, Interner};
2225
use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
2326
use std::ops::ControlFlow;
@@ -455,13 +458,23 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
455458
}
456459

457460
#[instrument(level = "trace", skip(self))]
458-
pub(super) fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
461+
pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
462+
goal.predicate = goal
463+
.predicate
464+
.fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
459465
self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal);
460466
self.nested_goals.normalizes_to_goals.push(goal);
461467
}
462468

463469
#[instrument(level = "debug", skip(self))]
464-
pub(super) fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
470+
pub(super) fn add_goal(
471+
&mut self,
472+
source: GoalSource,
473+
mut goal: Goal<'tcx, ty::Predicate<'tcx>>,
474+
) {
475+
goal.predicate = goal
476+
.predicate
477+
.fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
465478
self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal);
466479
self.nested_goals.goals.push((source, goal));
467480
}
@@ -1084,3 +1097,63 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
10841097
});
10851098
}
10861099
}
1100+
1101+
/// Eagerly replace aliases with inference variables, emitting `AliasRelate`
1102+
/// goals, used when adding goals to the `EvalCtxt`. We compute the
1103+
/// `AliasRelate` goals before evaluating the actual goal to get all the
1104+
/// constraints we can.
1105+
///
1106+
/// This is a performance optimization to more eagerly detect cycles during trait
1107+
/// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs.
1108+
struct ReplaceAliasWithInfer<'me, 'a, 'tcx> {
1109+
ecx: &'me mut EvalCtxt<'a, InferCtxt<'tcx>>,
1110+
param_env: ty::ParamEnv<'tcx>,
1111+
}
1112+
1113+
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'_, '_, 'tcx> {
1114+
fn interner(&self) -> TyCtxt<'tcx> {
1115+
self.ecx.tcx()
1116+
}
1117+
1118+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
1119+
match *ty.kind() {
1120+
ty::Alias(..) if !ty.has_escaping_bound_vars() => {
1121+
let infer_ty = self.ecx.next_ty_infer();
1122+
let normalizes_to = ty::PredicateKind::AliasRelate(
1123+
ty.into(),
1124+
infer_ty.into(),
1125+
AliasRelationDirection::Equate,
1126+
);
1127+
self.ecx.add_goal(
1128+
GoalSource::Misc,
1129+
Goal::new(self.interner(), self.param_env, normalizes_to),
1130+
);
1131+
infer_ty
1132+
}
1133+
_ => ty.super_fold_with(self),
1134+
}
1135+
}
1136+
1137+
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
1138+
match ct.kind() {
1139+
ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
1140+
let infer_ct = self.ecx.next_const_infer(ct.ty());
1141+
let normalizes_to = ty::PredicateKind::AliasRelate(
1142+
ct.into(),
1143+
infer_ct.into(),
1144+
AliasRelationDirection::Equate,
1145+
);
1146+
self.ecx.add_goal(
1147+
GoalSource::Misc,
1148+
Goal::new(self.interner(), self.param_env, normalizes_to),
1149+
);
1150+
infer_ct
1151+
}
1152+
_ => ct.super_fold_with(self),
1153+
}
1154+
}
1155+
1156+
fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
1157+
if predicate.allow_normalization() { predicate.super_fold_with(self) } else { predicate }
1158+
}
1159+
}

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

+41-45
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,8 @@ impl<'tcx> NormalizesToTermHack<'tcx> {
8989
pub struct InspectCandidate<'a, 'tcx> {
9090
goal: &'a InspectGoal<'a, 'tcx>,
9191
kind: inspect::ProbeKind<TyCtxt<'tcx>>,
92-
nested_goals:
93-
Vec<(GoalSource, inspect::CanonicalState<TyCtxt<'tcx>, Goal<'tcx, ty::Predicate<'tcx>>>)>,
92+
steps: Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
9493
final_state: inspect::CanonicalState<TyCtxt<'tcx>, ()>,
95-
impl_args: Option<inspect::CanonicalState<TyCtxt<'tcx>, ty::GenericArgsRef<'tcx>>>,
9694
result: QueryResult<'tcx>,
9795
shallow_certainty: Certainty,
9896
}
@@ -148,7 +146,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
148146
#[instrument(
149147
level = "debug",
150148
skip_all,
151-
fields(goal = ?self.goal.goal, nested_goals = ?self.nested_goals)
149+
fields(goal = ?self.goal.goal, steps = ?self.steps)
152150
)]
153151
pub fn instantiate_nested_goals_and_opt_impl_args(
154152
&self,
@@ -157,22 +155,34 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
157155
let infcx = self.goal.infcx;
158156
let param_env = self.goal.goal.param_env;
159157
let mut orig_values = self.goal.orig_values.to_vec();
160-
let instantiated_goals: Vec<_> = self
161-
.nested_goals
162-
.iter()
163-
.map(|(source, goal)| {
164-
(
165-
*source,
158+
159+
let mut instantiated_goals = vec![];
160+
let mut opt_impl_args = None;
161+
for step in &self.steps {
162+
match **step {
163+
inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push((
164+
source,
166165
canonical::instantiate_canonical_state(
167166
infcx,
168167
span,
169168
param_env,
170169
&mut orig_values,
171-
*goal,
170+
goal,
172171
),
173-
)
174-
})
175-
.collect();
172+
)),
173+
inspect::ProbeStep::RecordImplArgs { impl_args } => {
174+
opt_impl_args = Some(canonical::instantiate_canonical_state(
175+
infcx,
176+
span,
177+
param_env,
178+
&mut orig_values,
179+
impl_args,
180+
));
181+
}
182+
inspect::ProbeStep::MakeCanonicalResponse { .. }
183+
| inspect::ProbeStep::NestedProbe(_) => unreachable!(),
184+
}
185+
}
176186

177187
let () = canonical::instantiate_canonical_state(
178188
infcx,
@@ -182,24 +192,16 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
182192
self.final_state,
183193
);
184194

185-
let impl_args = self.impl_args.map(|impl_args| {
186-
canonical::instantiate_canonical_state(
187-
infcx,
188-
span,
189-
param_env,
190-
&mut orig_values,
191-
impl_args,
192-
)
193-
.fold_with(&mut EagerResolver::new(infcx))
194-
});
195-
196195
if let Some(term_hack) = self.goal.normalizes_to_term_hack {
197196
// FIXME: We ignore the expected term of `NormalizesTo` goals
198197
// when computing the result of its candidates. This is
199198
// scuffed.
200199
let _ = term_hack.constrain(infcx, span, param_env);
201200
}
202201

202+
let opt_impl_args =
203+
opt_impl_args.map(|impl_args| impl_args.fold_with(&mut EagerResolver::new(infcx)));
204+
203205
let goals = instantiated_goals
204206
.into_iter()
205207
.map(|(source, goal)| match goal.predicate.kind().no_bound_vars() {
@@ -249,7 +251,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
249251
})
250252
.collect();
251253

252-
(goals, impl_args)
254+
(goals, opt_impl_args)
253255
}
254256

255257
/// Visit all nested goals of this candidate, rolling back
@@ -279,17 +281,18 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
279281
fn candidates_recur(
280282
&'a self,
281283
candidates: &mut Vec<InspectCandidate<'a, 'tcx>>,
282-
nested_goals: &mut Vec<(
283-
GoalSource,
284-
inspect::CanonicalState<TyCtxt<'tcx>, Goal<'tcx, ty::Predicate<'tcx>>>,
285-
)>,
286-
probe: &inspect::Probe<TyCtxt<'tcx>>,
284+
steps: &mut Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
285+
probe: &'a inspect::Probe<TyCtxt<'tcx>>,
287286
) {
288287
let mut shallow_certainty = None;
289-
let mut impl_args = None;
290288
for step in &probe.steps {
291289
match *step {
292-
inspect::ProbeStep::AddGoal(source, goal) => nested_goals.push((source, goal)),
290+
inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => {
291+
steps.push(step)
292+
}
293+
inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
294+
assert_eq!(shallow_certainty.replace(c), None);
295+
}
293296
inspect::ProbeStep::NestedProbe(ref probe) => {
294297
match probe.kind {
295298
// These never assemble candidates for the goal we're trying to solve.
@@ -305,18 +308,12 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
305308
// Nested probes have to prove goals added in their parent
306309
// but do not leak them, so we truncate the added goals
307310
// afterwards.
308-
let num_goals = nested_goals.len();
309-
self.candidates_recur(candidates, nested_goals, probe);
310-
nested_goals.truncate(num_goals);
311+
let num_steps = steps.len();
312+
self.candidates_recur(candidates, steps, probe);
313+
steps.truncate(num_steps);
311314
}
312315
}
313316
}
314-
inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
315-
assert_eq!(shallow_certainty.replace(c), None);
316-
}
317-
inspect::ProbeStep::RecordImplArgs { impl_args: i } => {
318-
assert_eq!(impl_args.replace(i), None);
319-
}
320317
}
321318
}
322319

@@ -338,11 +335,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
338335
candidates.push(InspectCandidate {
339336
goal: self,
340337
kind: probe.kind,
341-
nested_goals: nested_goals.clone(),
338+
steps: steps.clone(),
342339
final_state: probe.final_state,
343-
result,
344340
shallow_certainty,
345-
impl_args,
341+
result,
346342
});
347343
}
348344
}

tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ LL | impl<T> Trait for Box<T> {}
1212
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
1313
|
1414
= note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
15-
= note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<<std::boxed::Box<_> as WithAssoc<'a>>::Assoc>`
15+
= note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<_>`
1616

1717
error: aborting due to 1 previous error
1818

tests/ui/coherence/occurs-check/opaques.next.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ error[E0282]: type annotations needed
1111
--> $DIR/opaques.rs:13:20
1212
|
1313
LL | pub fn cast<T>(x: Container<Alias<T>, T>) -> Container<T, T> {
14-
| ^ cannot infer type for associated type `<T as Trait<T>>::Assoc`
14+
| ^ cannot infer type
1515

1616
error: aborting due to 2 previous errors
1717

tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr

+18-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@ LL | where
1616
LL | T: AsExpression<Self::SqlType>,
1717
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
1818

19-
error: aborting due to 1 previous error
19+
error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
20+
--> $DIR/as_expression.rs:57:15
21+
|
22+
LL | SelectInt.check("bar");
23+
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
24+
|
25+
= help: the trait `AsExpression<Text>` is implemented for `&str`
26+
= help: for that trait implementation, expected `Text`, found `Integer`
27+
28+
error[E0271]: type mismatch resolving `<&str as AsExpression<<SelectInt as Expression>::SqlType>>::Expression == _`
29+
--> $DIR/as_expression.rs:57:5
30+
|
31+
LL | SelectInt.check("bar");
32+
| ^^^^^^^^^^^^^^^^^^^^^^ types differ
33+
34+
error: aborting due to 3 previous errors
2035

21-
For more information about this error, try `rustc --explain E0277`.
36+
Some errors have detailed explanations: E0271, E0277.
37+
For more information about an error, try `rustc --explain E0271`.

tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ impl<T> Foo for T where T: Expression {}
5555

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

tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0284]: type annotations needed: cannot satisfy `the constant `{ || {} }` can be evaluated`
1+
error[E0284]: type annotations needed: cannot satisfy `{ || {} } == _`
22
--> $DIR/const-region-infer-to-static-in-binder.rs:4:10
33
|
44
LL | struct X<const FN: fn() = { || {} }>;
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `{ || {} }` can be evaluated`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `{ || {} } == _`
66

77
error: using function pointers as const generic parameters is forbidden
88
--> $DIR/const-region-infer-to-static-in-binder.rs:4:20

0 commit comments

Comments
 (0)