Skip to content

Commit 2a985ea

Browse files
committed
Instead of falling back in diagnostics, the fix now addresses the actual universe mismatch during proof tree replay.
Since my last implementation was not really fixing the problem I removed the functions: try_unify_query_var_values, try_instantiate_canonical_state and InspectCandidate::try_instantiate_nested_goals. I also reverted the diagnostic callers in derive_errors.rs to use instantiate_nested_goals again. For my new implementation, in the rustc_trait_selection I changed fresh_var_for_kind_with_span to fresh_var_for_kind_in_universe which now uses next_region_var_in_universe, next_ty_var_in_universe and next_const_var_in_universe. Also in instantiate_canonical_state it is now ensured that infcx has all the universes up to prev_universe + state.max_universe, and creates any replay-only fresh vars in that explicit universe. I also split out compute_query_response_instantiation_values_in_universe so replay uses that same explicit base universe when reconstructing placeholders from the canonical state. Signed-off-by: Vicente Gusmão <vicente.gusmao@tecnico.ulisboa.pt>
1 parent 563cd20 commit 2a985ea

5 files changed

Lines changed: 161 additions & 121 deletions

File tree

compiler/rustc_next_trait_solver/src/canonical/mod.rs

Lines changed: 144 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
1111
1212
use std::iter;
13+
use std::marker::PhantomData;
1314

1415
use canonicalizer::Canonicalizer;
1516
use rustc_index::IndexVec;
16-
use rustc_type_ir::error::TypeError;
1717
use rustc_type_ir::inherent::*;
1818
use rustc_type_ir::relate::solver_relating::RelateExt;
1919
use rustc_type_ir::{
2020
self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner,
21-
TypeFoldable, TypingMode, TypingModeEqWrapper,
21+
TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode, TypingModeEqWrapper,
2222
};
2323
use tracing::instrument;
2424

@@ -145,12 +145,29 @@ where
145145
// FIXME: Longterm canonical queries should deal with all placeholders
146146
// created inside of the query directly instead of returning them to the
147147
// caller.
148-
let prev_universe = delegate.universe();
149-
let universes_created_in_query = response.max_universe.index();
150-
for _ in 0..universes_created_in_query {
151-
delegate.create_next_universe();
152-
}
148+
let prev_universe = create_universes_for_response(delegate, response);
153149

150+
compute_query_response_instantiation_values_in_universe(
151+
delegate,
152+
original_values,
153+
response,
154+
span,
155+
prev_universe,
156+
)
157+
}
158+
159+
fn compute_query_response_instantiation_values_in_universe<D, I, T>(
160+
delegate: &D,
161+
original_values: &[I::GenericArg],
162+
response: &Canonical<I, T>,
163+
span: I::Span,
164+
prev_universe: ty::UniverseIndex,
165+
) -> CanonicalVarValues<I>
166+
where
167+
D: SolverDelegate<Interner = I>,
168+
I: Interner,
169+
T: ResponseT<I>,
170+
{
154171
let var_values = response.value.var_values();
155172
assert_eq!(original_values.len(), var_values.len());
156173

@@ -234,6 +251,109 @@ where
234251
})
235252
}
236253

254+
fn create_universes_for_response<D, I, T>(
255+
delegate: &D,
256+
response: &Canonical<I, T>,
257+
) -> ty::UniverseIndex
258+
where
259+
D: SolverDelegate<Interner = I>,
260+
I: Interner,
261+
{
262+
let prev_universe = delegate.universe();
263+
let universes_created_in_query = response.max_universe.index();
264+
for _ in 0..universes_created_in_query {
265+
delegate.create_next_universe();
266+
}
267+
prev_universe
268+
}
269+
270+
fn max_input_universe_for_replay<D, I>(
271+
delegate: &D,
272+
orig_values: &[I::GenericArg],
273+
) -> ty::UniverseIndex
274+
where
275+
D: SolverDelegate<Interner = I>,
276+
I: Interner,
277+
{
278+
struct MaxUniverseVisitor<'a, D, I> {
279+
delegate: &'a D,
280+
max_universe: ty::UniverseIndex,
281+
_interner: PhantomData<I>,
282+
}
283+
284+
impl<D, I> TypeVisitor<I> for MaxUniverseVisitor<'_, D, I>
285+
where
286+
D: SolverDelegate<Interner = I>,
287+
I: Interner,
288+
{
289+
type Result = ();
290+
291+
fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
292+
match t.kind() {
293+
ty::Placeholder(placeholder) => {
294+
self.max_universe = self.max_universe.max(placeholder.universe());
295+
}
296+
ty::Infer(ty::TyVar(vid)) => {
297+
if let Some(universe) = self.delegate.universe_of_ty(vid) {
298+
self.max_universe = self.max_universe.max(universe);
299+
}
300+
}
301+
_ => {}
302+
}
303+
t.super_visit_with(self)
304+
}
305+
306+
fn visit_region(&mut self, r: I::Region) -> Self::Result {
307+
match r.kind() {
308+
ty::RePlaceholder(placeholder) => {
309+
self.max_universe = self.max_universe.max(placeholder.universe());
310+
}
311+
ty::ReVar(vid) => {
312+
if let Some(universe) = self.delegate.universe_of_lt(vid) {
313+
self.max_universe = self.max_universe.max(universe);
314+
}
315+
}
316+
_ => {}
317+
}
318+
}
319+
320+
fn visit_const(&mut self, c: I::Const) -> Self::Result {
321+
match c.kind() {
322+
ty::ConstKind::Placeholder(placeholder) => {
323+
self.max_universe = self.max_universe.max(placeholder.universe());
324+
}
325+
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
326+
if let Some(universe) = self.delegate.universe_of_ct(vid) {
327+
self.max_universe = self.max_universe.max(universe);
328+
}
329+
}
330+
_ => {}
331+
}
332+
c.super_visit_with(self)
333+
}
334+
}
335+
336+
let mut visitor = MaxUniverseVisitor {
337+
delegate,
338+
max_universe: ty::UniverseIndex::ROOT,
339+
_interner: PhantomData,
340+
};
341+
for value in orig_values {
342+
value.visit_with(&mut visitor);
343+
}
344+
visitor.max_universe
345+
}
346+
347+
fn ensure_universes_through<D, I>(delegate: &D, max_universe: ty::UniverseIndex)
348+
where
349+
D: SolverDelegate<Interner = I>,
350+
I: Interner,
351+
{
352+
while delegate.universe() < max_universe {
353+
delegate.create_next_universe();
354+
}
355+
}
356+
237357
/// Unify the `original_values` with the `var_values` returned by the canonical query..
238358
///
239359
/// This assumes that this unification will always succeed. This is the case when
@@ -247,39 +367,23 @@ where
247367
/// from the solver we assume that the solver correctly handled aliases and therefore
248368
/// always relate them structurally here.
249369
#[instrument(level = "trace", skip(delegate))]
250-
fn try_unify_query_var_values<D, I>(
370+
fn unify_query_var_values<D, I>(
251371
delegate: &D,
252372
param_env: I::ParamEnv,
253373
original_values: &[I::GenericArg],
254374
var_values: CanonicalVarValues<I>,
255375
span: I::Span,
256-
) -> Result<(), TypeError<I>>
257-
where
376+
) where
258377
D: SolverDelegate<Interner = I>,
259378
I: Interner,
260379
{
261380
assert_eq!(original_values.len(), var_values.len());
262381

263382
for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) {
264-
let goals = delegate.eq_structurally_relating_aliases(param_env, orig, response, span)?;
383+
let goals =
384+
delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap();
265385
assert!(goals.is_empty());
266386
}
267-
268-
Ok(())
269-
}
270-
271-
#[instrument(level = "trace", skip(delegate))]
272-
fn unify_query_var_values<D, I>(
273-
delegate: &D,
274-
param_env: I::ParamEnv,
275-
original_values: &[I::GenericArg],
276-
var_values: CanonicalVarValues<I>,
277-
span: I::Span,
278-
) where
279-
D: SolverDelegate<Interner = I>,
280-
I: Interner,
281-
{
282-
try_unify_query_var_values(delegate, param_env, original_values, var_values, span).unwrap();
283387
}
284388

285389
fn register_region_constraints<D, I>(
@@ -357,43 +461,34 @@ pub fn instantiate_canonical_state<D, I, T>(
357461
orig_values: &mut Vec<I::GenericArg>,
358462
state: inspect::CanonicalState<I, T>,
359463
) -> T
360-
where
361-
D: SolverDelegate<Interner = I>,
362-
I: Interner,
363-
T: TypeFoldable<I>,
364-
{
365-
try_instantiate_canonical_state(delegate, span, param_env, orig_values, state).unwrap()
366-
}
367-
368-
// FIXME: needs to be pub to be accessed by downstream
369-
// `rustc_trait_selection::solve::inspect::analyse`.
370-
pub fn try_instantiate_canonical_state<D, I, T>(
371-
delegate: &D,
372-
span: I::Span,
373-
param_env: I::ParamEnv,
374-
orig_values: &mut Vec<I::GenericArg>,
375-
state: inspect::CanonicalState<I, T>,
376-
) -> Result<T, TypeError<I>>
377464
where
378465
D: SolverDelegate<Interner = I>,
379466
I: Interner,
380467
T: TypeFoldable<I>,
381468
{
382469
// In case any fresh inference variables have been created between `state`
383470
// and the previous instantiation, extend `orig_values` for it.
471+
let prev_universe = max_input_universe_for_replay(delegate, orig_values);
472+
let max_universe = prev_universe + state.max_universe.index();
473+
ensure_universes_through(delegate, max_universe);
384474
orig_values.extend(
385475
state.value.var_values.var_values.as_slice()[orig_values.len()..]
386476
.iter()
387-
.map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)),
477+
.map(|&arg| delegate.fresh_var_for_kind_in_universe(arg, span, max_universe)),
388478
);
389479

390-
let instantiation =
391-
compute_query_response_instantiation_values(delegate, orig_values, &state, span);
480+
let instantiation = compute_query_response_instantiation_values_in_universe(
481+
delegate,
482+
orig_values,
483+
&state,
484+
span,
485+
prev_universe,
486+
);
392487

393488
let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation);
394489

395-
try_unify_query_var_values(delegate, param_env, orig_values, var_values, span)?;
396-
Ok(data)
490+
unify_query_var_values(delegate, param_env, orig_values, var_values, span);
491+
data
397492
}
398493

399494
pub fn response_no_constraints_raw<I: Interner>(

compiler/rustc_next_trait_solver/src/delegate.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
2525
span: <Self::Interner as Interner>::Span,
2626
) -> Option<Certainty>;
2727

28-
fn fresh_var_for_kind_with_span(
28+
fn fresh_var_for_kind_in_universe(
2929
&self,
3030
arg: <Self::Interner as Interner>::GenericArg,
3131
span: <Self::Interner as Interner>::Span,
32+
universe: ty::UniverseIndex,
3233
) -> <Self::Interner as Interner>::GenericArg;
3334

3435
// FIXME: Uplift the leak check into this crate.

compiler/rustc_trait_selection/src/solve/delegate.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,17 +166,18 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
166166
}
167167
}
168168

169-
fn fresh_var_for_kind_with_span(
169+
fn fresh_var_for_kind_in_universe(
170170
&self,
171171
arg: ty::GenericArg<'tcx>,
172172
span: Span,
173+
universe: ty::UniverseIndex,
173174
) -> ty::GenericArg<'tcx> {
174175
match arg.kind() {
175176
ty::GenericArgKind::Lifetime(_) => {
176-
self.next_region_var(RegionVariableOrigin::Misc(span)).into()
177+
self.next_region_var_in_universe(RegionVariableOrigin::Misc(span), universe).into()
177178
}
178-
ty::GenericArgKind::Type(_) => self.next_ty_var(span).into(),
179-
ty::GenericArgKind::Const(_) => self.next_const_var(span).into(),
179+
ty::GenericArgKind::Type(_) => self.next_ty_var_in_universe(span, universe).into(),
180+
ty::GenericArgKind::Const(_) => self.next_const_var_in_universe(span, universe).into(),
180181
}
181182
}
182183

compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -235,20 +235,14 @@ impl<'tcx> BestObligation<'tcx> {
235235
if candidates.len() > 1 {
236236
candidates.retain(|candidate| {
237237
goal.infcx().probe(|_| {
238-
// Replaying proof-tree state is only a diagnostics aid. If the
239-
// canonical state no longer reproduces cleanly in the current
240-
// inference context, keep the candidate rather than changing the
241-
// observable error into an ICE.
242-
candidate.try_instantiate_nested_goals(self.span()).is_none_or(
243-
|nested_goals| {
244-
nested_goals.iter().any(|nested_goal| {
245-
matches!(
246-
nested_goal.source(),
247-
GoalSource::ImplWhereBound
248-
| GoalSource::AliasBoundConstCondition
249-
| GoalSource::AliasWellFormed
250-
) && nested_goal.result().is_err()
251-
})
238+
candidate.instantiate_nested_goals(self.span()).iter().any(
239+
|nested_goal| {
240+
matches!(
241+
nested_goal.source(),
242+
GoalSource::ImplWhereBound
243+
| GoalSource::AliasBoundConstCondition
244+
| GoalSource::AliasWellFormed
245+
) && nested_goal.result().is_err()
252246
},
253247
)
254248
})
@@ -491,12 +485,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
491485
_ => ChildMode::PassThrough,
492486
};
493487

494-
let Some(nested_goals) = candidate.try_instantiate_nested_goals(self.span()) else {
495-
// We failed to replay this proof-tree candidate in the current inference state.
496-
// Fall back to the current obligation instead of trying to derive a more specific
497-
// nested error from diagnostics-only reconstruction.
498-
return ControlFlow::Break(self.obligation.clone());
499-
};
488+
let nested_goals = candidate.instantiate_nested_goals(self.span());
500489

501490
// If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
502491
// an actual candidate, instead we should treat them as if the impl was never considered to

0 commit comments

Comments
 (0)