Skip to content

Commit e4a361a

Browse files
committed
Auto merge of #115996 - lcnr:intercrate_ambiguity_causes-uwu, r=compiler-errors
implement `intercrate_ambiguity_causes` in the new solver I added some comments but this is still somewhat of a mess. I think we should for the most part be able to treat all of this as a black box, so I can accept that this code isn't too great. I also believe that some of the weirdness here is unavoidable, as proof trees - and their visitor - hide semantically relevant information, so they cannot perfectly represent the actual solver behavior. There are some known bugs here when testing with `./x.py test tests/ui --bless -- --target-rustcflags -Ztrait-solver=next-coherence`. While I haven't diagnosed them all in detail I believe we are able to handle them all separately - `structurally_normalize` currently does not normalize opaque types, resulting in divergence between the solver internal `trait_ref_is_knowable` and the one when computing intercrate ambiguity causes. - we don't add an `intercrate_ambiguity_cause` for reserved impls - we should `deeply_normalize` the trait ref before printing it, that requires a "best effort" version of `deeply_normalize` r? `@compiler-errors`
2 parents 0a689c1 + 614760f commit e4a361a

29 files changed

+834
-181
lines changed

Diff for: compiler/rustc_middle/src/traits/solve/inspect.rs

+58-8
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,70 @@
1+
//! Data structure used to inspect trait solver behavior.
2+
//!
3+
//! During trait solving we optionally build "proof trees", the root of
4+
//! which is a [GoalEvaluation] with [GoalEvaluationKind::Root]. These
5+
//! trees are used to improve the debug experience and are also used by
6+
//! the compiler itself to provide necessary context for error messages.
7+
//!
8+
//! Because each nested goal in the solver gets [canonicalized] separately
9+
//! and we discard inference progress via "probes", we cannot mechanically
10+
//! use proof trees without somehow "lifting up" data local to the current
11+
//! `InferCtxt`. Any data used mechanically is therefore canonicalized and
12+
//! stored as [CanonicalState]. As printing canonicalized data worsens the
13+
//! debugging dumps, we do not simply canonicalize everything.
14+
//!
15+
//! This means proof trees contain inference variables and placeholders
16+
//! local to a different `InferCtxt` which must not be used with the
17+
//! current one.
18+
//!
19+
//! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
20+
121
use super::{
2-
CandidateSource, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput,
3-
QueryResult,
22+
CandidateSource, Canonical, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution,
23+
QueryInput, QueryResult,
424
};
5-
use crate::ty;
25+
use crate::{infer::canonical::CanonicalVarValues, ty};
626
use format::ProofTreeFormatter;
727
use std::fmt::{Debug, Write};
828

929
mod format;
1030

31+
/// Some `data` together with information about how they relate to the input
32+
/// of the canonical query.
33+
///
34+
/// This is only ever used as [CanonicalState]. Any type information in proof
35+
/// trees used mechanically has to be canonicalized as we otherwise leak
36+
/// inference variables from a nested `InferCtxt`.
37+
#[derive(Debug, Clone, Copy, Eq, PartialEq, TypeFoldable, TypeVisitable)]
38+
pub struct State<'tcx, T> {
39+
pub var_values: CanonicalVarValues<'tcx>,
40+
pub data: T,
41+
}
42+
43+
pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>;
44+
1145
#[derive(Debug, Eq, PartialEq)]
1246
pub enum CacheHit {
1347
Provisional,
1448
Global,
1549
}
1650

51+
/// When evaluating the root goals we also store the
52+
/// original values for the `CanonicalVarValues` of the
53+
/// canonicalized goal. We use this to map any [CanonicalState]
54+
/// from the local `InferCtxt` of the solver query to
55+
/// the `InferCtxt` of the caller.
1756
#[derive(Eq, PartialEq)]
18-
pub enum GoalEvaluationKind {
19-
Root,
57+
pub enum GoalEvaluationKind<'tcx> {
58+
Root { orig_values: Vec<ty::GenericArg<'tcx>> },
2059
Nested { is_normalizes_to_hack: IsNormalizesToHack },
2160
}
2261

2362
#[derive(Eq, PartialEq)]
2463
pub struct GoalEvaluation<'tcx> {
2564
pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
26-
pub kind: GoalEvaluationKind,
65+
pub kind: GoalEvaluationKind<'tcx>,
2766
pub evaluation: CanonicalGoalEvaluation<'tcx>,
67+
/// The nested goals from instantiating the query response.
2868
pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
2969
}
3070

@@ -66,6 +106,7 @@ pub struct GoalEvaluationStep<'tcx> {
66106
/// of a goal.
67107
#[derive(Eq, PartialEq)]
68108
pub struct Probe<'tcx> {
109+
/// What happened inside of this probe in chronological order.
69110
pub steps: Vec<ProbeStep<'tcx>>,
70111
pub kind: ProbeKind<'tcx>,
71112
}
@@ -78,12 +119,21 @@ impl Debug for Probe<'_> {
78119

79120
#[derive(Eq, PartialEq)]
80121
pub enum ProbeStep<'tcx> {
81-
AddGoal(Goal<'tcx, ty::Predicate<'tcx>>),
122+
/// We added a goal to the `EvalCtxt` which will get proven
123+
/// the next time `EvalCtxt::try_evaluate_added_goals` is called.
124+
AddGoal(CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
125+
/// The inside of a `EvalCtxt::try_evaluate_added_goals` call.
82126
EvaluateGoals(AddedGoalsEvaluation<'tcx>),
127+
/// A call to `probe` while proving the current goal. This is
128+
/// used whenever there are multiple candidates to prove the
129+
/// current goalby .
83130
NestedProbe(Probe<'tcx>),
84131
}
85132

86-
#[derive(Debug, PartialEq, Eq)]
133+
/// What kind of probe we're in. In case the probe represents a candidate, or
134+
/// the final result of the current goal - via [ProbeKind::Root] - we also
135+
/// store the [QueryResult].
136+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
87137
pub enum ProbeKind<'tcx> {
88138
/// The root inference context while proving a goal.
89139
Root { result: QueryResult<'tcx> },

Diff for: compiler/rustc_middle/src/traits/solve/inspect/format.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
4141

4242
pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result {
4343
let goal_text = match eval.kind {
44-
GoalEvaluationKind::Root => "ROOT GOAL",
44+
GoalEvaluationKind::Root { orig_values: _ } => "ROOT GOAL",
4545
GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack {
4646
IsNormalizesToHack::No => "GOAL",
4747
IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
344344
goal: Goal<'tcx, ty::Predicate<'tcx>>,
345345
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
346346
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
347-
let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, goal_evaluation_kind);
347+
let mut goal_evaluation =
348+
self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
348349
let encountered_overflow = self.search_graph.encountered_overflow();
349350
let canonical_response = EvalCtxt::evaluate_canonical_goal(
350351
self.tcx(),
@@ -568,7 +569,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
568569
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
569570
unconstrained_goal,
570571
)?;
571-
self.add_goals(instantiate_goals);
572+
self.nested_goals.goals.extend(instantiate_goals);
572573

573574
// Finally, equate the goal's RHS with the unconstrained var.
574575
// We put the nested goals from this into goals instead of
@@ -605,15 +606,15 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
605606
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
606607
goal,
607608
)?;
608-
self.add_goals(instantiate_goals);
609+
self.nested_goals.goals.extend(instantiate_goals);
609610
if has_changed {
610611
unchanged_certainty = None;
611612
}
612613

613614
match certainty {
614615
Certainty::Yes => {}
615616
Certainty::Maybe(_) => {
616-
self.add_goal(goal);
617+
self.nested_goals.goals.push(goal);
617618
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
618619
}
619620
}

Diff for: compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs

+81-16
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,21 @@
1010
//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
1111
use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
1212
use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
13-
use crate::solve::{response_no_constraints_raw, CanonicalResponse, QueryResult, Response};
13+
use crate::solve::{
14+
inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response,
15+
};
1416
use rustc_data_structures::fx::FxHashSet;
1517
use rustc_index::IndexVec;
1618
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
1719
use rustc_infer::infer::canonical::CanonicalVarValues;
1820
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
19-
use rustc_infer::infer::InferCtxt;
21+
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
22+
use rustc_middle::infer::canonical::Canonical;
2023
use rustc_middle::traits::query::NoSolution;
2124
use rustc_middle::traits::solve::{
2225
ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
2326
};
27+
use rustc_middle::traits::ObligationCause;
2428
use rustc_middle::ty::{
2529
self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
2630
TypeVisitableExt,
@@ -29,6 +33,22 @@ use rustc_span::DUMMY_SP;
2933
use std::iter;
3034
use std::ops::Deref;
3135

36+
trait ResponseT<'tcx> {
37+
fn var_values(&self) -> CanonicalVarValues<'tcx>;
38+
}
39+
40+
impl<'tcx> ResponseT<'tcx> for Response<'tcx> {
41+
fn var_values(&self) -> CanonicalVarValues<'tcx> {
42+
self.var_values
43+
}
44+
}
45+
46+
impl<'tcx, T> ResponseT<'tcx> for inspect::State<'tcx, T> {
47+
fn var_values(&self) -> CanonicalVarValues<'tcx> {
48+
self.var_values
49+
}
50+
}
51+
3252
impl<'tcx> EvalCtxt<'_, 'tcx> {
3353
/// Canonicalizes the goal remembering the original values
3454
/// for each bound variable.
@@ -188,12 +208,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
188208
original_values: Vec<ty::GenericArg<'tcx>>,
189209
response: CanonicalResponse<'tcx>,
190210
) -> Result<(Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
191-
let substitution = self.compute_query_response_substitution(&original_values, &response);
211+
let substitution =
212+
Self::compute_query_response_substitution(self.infcx, &original_values, &response);
192213

193214
let Response { var_values, external_constraints, certainty } =
194215
response.substitute(self.tcx(), &substitution);
195216

196-
let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?;
217+
let nested_goals =
218+
Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values)?;
197219

198220
let ExternalConstraintsData { region_constraints, opaque_types } =
199221
external_constraints.deref();
@@ -206,21 +228,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
206228
/// This returns the substitutions to instantiate the bound variables of
207229
/// the canonical response. This depends on the `original_values` for the
208230
/// bound variables.
209-
fn compute_query_response_substitution(
210-
&self,
231+
fn compute_query_response_substitution<T: ResponseT<'tcx>>(
232+
infcx: &InferCtxt<'tcx>,
211233
original_values: &[ty::GenericArg<'tcx>],
212-
response: &CanonicalResponse<'tcx>,
234+
response: &Canonical<'tcx, T>,
213235
) -> CanonicalVarValues<'tcx> {
214236
// FIXME: Longterm canonical queries should deal with all placeholders
215237
// created inside of the query directly instead of returning them to the
216238
// caller.
217-
let prev_universe = self.infcx.universe();
239+
let prev_universe = infcx.universe();
218240
let universes_created_in_query = response.max_universe.index();
219241
for _ in 0..universes_created_in_query {
220-
self.infcx.create_next_universe();
242+
infcx.create_next_universe();
221243
}
222244

223-
let var_values = response.value.var_values;
245+
let var_values = response.value.var_values();
224246
assert_eq!(original_values.len(), var_values.len());
225247

226248
// If the query did not make progress with constraining inference variables,
@@ -254,13 +276,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
254276
}
255277
}
256278

257-
let var_values = self.tcx().mk_args_from_iter(response.variables.iter().enumerate().map(
279+
let var_values = infcx.tcx.mk_args_from_iter(response.variables.iter().enumerate().map(
258280
|(index, info)| {
259281
if info.universe() != ty::UniverseIndex::ROOT {
260282
// A variable from inside a binder of the query. While ideally these shouldn't
261283
// exist at all (see the FIXME at the start of this method), we have to deal with
262284
// them for now.
263-
self.infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| {
285+
infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| {
264286
ty::UniverseIndex::from(prev_universe.index() + idx.index())
265287
})
266288
} else if info.is_existential() {
@@ -274,7 +296,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
274296
if let Some(v) = opt_values[BoundVar::from_usize(index)] {
275297
v
276298
} else {
277-
self.infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe)
299+
infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe)
278300
}
279301
} else {
280302
// For placeholders which were already part of the input, we simply map this
@@ -287,9 +309,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
287309
CanonicalVarValues { var_values }
288310
}
289311

290-
#[instrument(level = "debug", skip(self, param_env), ret)]
312+
#[instrument(level = "debug", skip(infcx, param_env), ret)]
291313
fn unify_query_var_values(
292-
&self,
314+
infcx: &InferCtxt<'tcx>,
293315
param_env: ty::ParamEnv<'tcx>,
294316
original_values: &[ty::GenericArg<'tcx>],
295317
var_values: CanonicalVarValues<'tcx>,
@@ -298,7 +320,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
298320

299321
let mut nested_goals = vec![];
300322
for (&orig, response) in iter::zip(original_values, var_values.var_values) {
301-
nested_goals.extend(self.eq_and_get_goals(param_env, orig, response)?);
323+
nested_goals.extend(
324+
infcx
325+
.at(&ObligationCause::dummy(), param_env)
326+
.eq(DefineOpaqueTypes::No, orig, response)
327+
.map(|InferOk { value: (), obligations }| {
328+
obligations.into_iter().map(|o| Goal::from(o))
329+
})
330+
.map_err(|e| {
331+
debug!(?e, "failed to equate");
332+
NoSolution
333+
})?,
334+
);
302335
}
303336

304337
Ok(nested_goals)
@@ -403,3 +436,35 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerResolver<'_, 'tcx> {
403436
}
404437
}
405438
}
439+
440+
impl<'tcx> inspect::ProofTreeBuilder<'tcx> {
441+
pub fn make_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>(
442+
ecx: &EvalCtxt<'_, 'tcx>,
443+
data: T,
444+
) -> inspect::CanonicalState<'tcx, T> {
445+
let state = inspect::State { var_values: ecx.var_values, data };
446+
let state = state.fold_with(&mut EagerResolver { infcx: ecx.infcx });
447+
Canonicalizer::canonicalize(
448+
ecx.infcx,
449+
CanonicalizeMode::Response { max_input_universe: ecx.max_input_universe },
450+
&mut vec![],
451+
state,
452+
)
453+
}
454+
455+
pub fn instantiate_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>(
456+
infcx: &InferCtxt<'tcx>,
457+
param_env: ty::ParamEnv<'tcx>,
458+
original_values: &[ty::GenericArg<'tcx>],
459+
state: inspect::CanonicalState<'tcx, T>,
460+
) -> Result<(Vec<Goal<'tcx, ty::Predicate<'tcx>>>, T), NoSolution> {
461+
let substitution =
462+
EvalCtxt::compute_query_response_substitution(infcx, original_values, &state);
463+
464+
let inspect::State { var_values, data } = state.substitute(infcx.tcx, &substitution);
465+
466+
let nested_goals =
467+
EvalCtxt::unify_query_var_values(infcx, param_env, original_values, var_values)?;
468+
Ok((nested_goals, data))
469+
}
470+
}

0 commit comments

Comments
 (0)