Skip to content

Commit 1f12f1c

Browse files
committed
try_normalize_ty end with rigid alias on failure
1 parent adda05f commit 1f12f1c

File tree

9 files changed

+146
-30
lines changed

9 files changed

+146
-30
lines changed

compiler/rustc_middle/src/traits/solve/inspect.rs

+5
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ pub enum ProbeStep<'tcx> {
122122
/// used whenever there are multiple candidates to prove the
123123
/// current goalby .
124124
NestedProbe(Probe<'tcx>),
125+
CommitIfOkStart,
126+
CommitIfOkSuccess,
125127
}
126128

127129
/// What kind of probe we're in. In case the probe represents a candidate, or
@@ -142,6 +144,9 @@ pub enum ProbeKind<'tcx> {
142144
/// Used in the probe that wraps normalizing the non-self type for the unsize
143145
/// trait, which is also structurally matched on.
144146
UnsizeAssembly,
147+
/// A call to `EvalCtxt::commit_if_ok` which failed, causing the work
148+
/// to be discarded.
149+
CommitIfOk,
145150
/// During upcasting from some source object to target object type, used to
146151
/// do a probe to find out what projection type(s) may be used to prove that
147152
/// the source type upholds all of the target type's object bounds.

compiler/rustc_middle/src/traits/solve/inspect/format.rs

+5
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
109109
ProbeKind::UpcastProjectionCompatibility => {
110110
writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
111111
}
112+
ProbeKind::CommitIfOk => {
113+
writeln!(self.f, "COMMIT_IF_OK:")
114+
}
112115
ProbeKind::MiscCandidate { name, result } => {
113116
writeln!(self.f, "CANDIDATE {name}: {result:?}")
114117
}
@@ -123,6 +126,8 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
123126
ProbeStep::AddGoal(goal) => writeln!(this.f, "ADDED GOAL: {goal:?}")?,
124127
ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
125128
ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
129+
ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?,
130+
ProbeStep::CommitIfOkSuccess => writeln!(this.f, "COMMIT_IF_OK SUCCESS")?,
126131
}
127132
}
128133
Ok(())

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

+5-10
Original file line numberDiff line numberDiff line change
@@ -864,23 +864,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
864864

865865
let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
866866
let trait_ref = goal.predicate.trait_ref(tcx);
867-
868867
#[derive(Debug)]
869-
enum FailureKind {
870-
Overflow,
871-
NoSolution(NoSolution),
872-
}
868+
struct Overflow;
873869
let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) {
874-
Ok(Some(ty)) => Ok(ty),
875-
Ok(None) => Err(FailureKind::Overflow),
876-
Err(e) => Err(FailureKind::NoSolution(e)),
870+
Some(ty) => Ok(ty),
871+
None => Err(Overflow),
877872
};
878873

879874
match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) {
880-
Err(FailureKind::Overflow) => {
875+
Err(Overflow) => {
881876
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
882877
}
883-
Err(FailureKind::NoSolution(NoSolution)) | Ok(Ok(())) => Err(NoSolution),
878+
Ok(Ok(())) => Err(NoSolution),
884879
Ok(Err(_)) => {
885880
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
886881
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use super::EvalCtxt;
2+
use crate::solve::inspect;
3+
use rustc_middle::traits::query::NoSolution;
4+
5+
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
6+
pub(in crate::solve) fn commit_if_ok<T>(
7+
&mut self,
8+
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result<T, NoSolution>,
9+
) -> Result<T, NoSolution> {
10+
let mut nested_ecx = EvalCtxt {
11+
infcx: self.infcx,
12+
variables: self.variables,
13+
var_values: self.var_values,
14+
predefined_opaques_in_body: self.predefined_opaques_in_body,
15+
max_input_universe: self.max_input_universe,
16+
search_graph: self.search_graph,
17+
nested_goals: self.nested_goals.clone(),
18+
tainted: self.tainted,
19+
inspect: self.inspect.new_probe(),
20+
};
21+
22+
let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx));
23+
if result.is_ok() {
24+
let EvalCtxt {
25+
infcx: _,
26+
variables: _,
27+
var_values: _,
28+
predefined_opaques_in_body: _,
29+
max_input_universe: _,
30+
search_graph: _,
31+
nested_goals,
32+
tainted,
33+
inspect,
34+
} = nested_ecx;
35+
self.nested_goals = nested_goals;
36+
self.tainted = tainted;
37+
self.inspect.integrate_snapshot(inspect);
38+
} else {
39+
nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk);
40+
self.inspect.finish_probe(nested_ecx.inspect);
41+
}
42+
43+
result
44+
}
45+
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use super::{search_graph::SearchGraph, Goal};
3434
pub use select::InferCtxtSelectExt;
3535

3636
mod canonical;
37+
mod commit_if_ok;
3738
mod probe;
3839
mod select;
3940

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
120120
for step in &probe.steps {
121121
match step {
122122
&inspect::ProbeStep::AddGoal(goal) => nested_goals.push(goal),
123-
inspect::ProbeStep::EvaluateGoals(_) => (),
124123
inspect::ProbeStep::NestedProbe(ref probe) => {
125124
// Nested probes have to prove goals added in their parent
126125
// but do not leak them, so we truncate the added goals
@@ -129,13 +128,17 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
129128
self.candidates_recur(candidates, nested_goals, probe);
130129
nested_goals.truncate(num_goals);
131130
}
131+
inspect::ProbeStep::EvaluateGoals(_)
132+
| inspect::ProbeStep::CommitIfOkStart
133+
| inspect::ProbeStep::CommitIfOkSuccess => (),
132134
}
133135
}
134136

135137
match probe.kind {
136138
inspect::ProbeKind::NormalizedSelfTyAssembly
137139
| inspect::ProbeKind::UnsizeAssembly
138-
| inspect::ProbeKind::UpcastProjectionCompatibility => (),
140+
| inspect::ProbeKind::UpcastProjectionCompatibility
141+
| inspect::ProbeKind::CommitIfOk => (),
139142
// We add a candidate for the root evaluation if there
140143
// is only one way to prove a given goal, e.g. for `WellFormed`.
141144
//

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

+27
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ enum WipProbeStep<'tcx> {
219219
AddGoal(inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
220220
EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
221221
NestedProbe(WipProbe<'tcx>),
222+
CommitIfOkStart,
223+
CommitIfOkSuccess,
222224
}
223225

224226
impl<'tcx> WipProbeStep<'tcx> {
@@ -227,6 +229,8 @@ impl<'tcx> WipProbeStep<'tcx> {
227229
WipProbeStep::AddGoal(goal) => inspect::ProbeStep::AddGoal(goal),
228230
WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
229231
WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
232+
WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart,
233+
WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess,
230234
}
231235
}
232236
}
@@ -459,6 +463,29 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
459463
}
460464
}
461465

466+
/// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside
467+
/// of the probe into the parent.
468+
pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) {
469+
if let Some(this) = self.as_mut() {
470+
match (this, *probe.state.unwrap()) {
471+
(
472+
DebugSolver::Probe(WipProbe { steps, .. })
473+
| DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
474+
evaluation: WipProbe { steps, .. },
475+
..
476+
}),
477+
DebugSolver::Probe(probe),
478+
) => {
479+
steps.push(WipProbeStep::CommitIfOkStart);
480+
assert_eq!(probe.kind, None);
481+
steps.extend(probe.steps);
482+
steps.push(WipProbeStep::CommitIfOkSuccess);
483+
}
484+
_ => unreachable!(),
485+
}
486+
}
487+
}
488+
462489
pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
463490
self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None })
464491
}

compiler/rustc_trait_selection/src/solve/mod.rs

+50-14
Original file line numberDiff line numberDiff line change
@@ -297,25 +297,61 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
297297
fn try_normalize_ty(
298298
&mut self,
299299
param_env: ty::ParamEnv<'tcx>,
300-
mut ty: Ty<'tcx>,
301-
) -> Result<Option<Ty<'tcx>>, NoSolution> {
302-
for _ in 0..self.local_overflow_limit() {
303-
let ty::Alias(_, projection_ty) = *ty.kind() else {
304-
return Ok(Some(ty));
305-
};
306-
307-
let normalized_ty = self.next_ty_infer();
300+
ty: Ty<'tcx>,
301+
) -> Option<Ty<'tcx>> {
302+
self.try_normalize_ty_recur(param_env, 0, ty)
303+
}
304+
305+
fn try_normalize_ty_recur(
306+
&mut self,
307+
param_env: ty::ParamEnv<'tcx>,
308+
depth: usize,
309+
ty: Ty<'tcx>,
310+
) -> Option<Ty<'tcx>> {
311+
if depth >= self.local_overflow_limit() {
312+
return None;
313+
}
314+
315+
let ty::Alias(kind, projection_ty) = *ty.kind() else {
316+
return Some(ty);
317+
};
318+
319+
// We do no always define opaque types eagerly to allow non-defining uses in the defining scope.
320+
if let (DefineOpaqueTypes::No, ty::AliasKind::Opaque) = (define_opaque_types, kind) {
321+
if let Some(def_id) = projection_ty.def_id.as_local() {
322+
if self
323+
.unify_existing_opaque_tys(
324+
param_env,
325+
OpaqueTypeKey { def_id, args: projection_ty.args },
326+
self.next_ty_infer(),
327+
)
328+
.is_empty()
329+
{
330+
return Some(ty);
331+
}
332+
}
333+
}
334+
335+
// FIXME(@lcnr): If the normalization of the alias adds an inference constraint which
336+
// causes a previously added goal to fail, then we treat the alias as rigid.
337+
//
338+
// These feels like a potential issue, I should look into writing some tests here
339+
// and then probably changing `commit_if_ok` to not inherit the parent goals.
340+
match self.commit_if_ok(|this| {
341+
let normalized_ty = this.next_ty_infer();
308342
let normalizes_to_goal = Goal::new(
309-
self.tcx(),
343+
this.tcx(),
310344
param_env,
311345
ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
312346
);
313-
self.add_goal(normalizes_to_goal);
314-
self.try_evaluate_added_goals()?;
315-
ty = self.resolve_vars_if_possible(normalized_ty);
347+
this.add_goal(normalizes_to_goal);
348+
this.try_evaluate_added_goals()?;
349+
let ty = this.resolve_vars_if_possible(normalized_ty);
350+
Ok(this.try_normalize_ty_recur(param_env, depth + 1, ty))
351+
}) {
352+
Ok(ty) => ty,
353+
Err(NoSolution) => Some(ty),
316354
}
317-
318-
Ok(None)
319355
}
320356
}
321357

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
471471
let a_ty = goal.predicate.self_ty();
472472
// We need to normalize the b_ty since it's destructured as a `dyn Trait`.
473473
let Some(b_ty) =
474-
ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))?
474+
ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
475475
else {
476476
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
477477
};
@@ -538,9 +538,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
538538
let b_ty = match ecx
539539
.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
540540
{
541-
Ok(Some(b_ty)) => b_ty,
542-
Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
543-
Err(_) => return vec![],
541+
Some(b_ty) => b_ty,
542+
None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
544543
};
545544

546545
let goal = goal.with(ecx.tcx(), (a_ty, b_ty));

0 commit comments

Comments
 (0)