rustc_next_trait_solver/solve/inspect/
build.rs

1//! Building proof trees incrementally during trait solving.
2//!
3//! This code is *a bit* of a mess and can hopefully be
4//! mostly ignored. For a general overview of how it works,
5//! see the comment on [ProofTreeBuilder].
6
7use std::marker::PhantomData;
8
9use derive_where::derive_where;
10use rustc_type_ir::inherent::*;
11use rustc_type_ir::{self as ty, Interner};
12
13use crate::delegate::SolverDelegate;
14use crate::solve::eval_ctxt::canonical;
15use crate::solve::{
16    CanonicalInput, Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource,
17    QueryResult, inspect,
18};
19
20/// The core data structure when building proof trees.
21///
22/// In case the current evaluation does not generate a proof
23/// tree, `state` is simply `None` and we avoid any work.
24///
25/// The possible states of the solver are represented via
26/// variants of [DebugSolver]. For any nested computation we call
27/// `ProofTreeBuilder::new_nested_computation_kind` which
28/// creates a new `ProofTreeBuilder` to temporarily replace the
29/// current one. Once that nested computation is done,
30/// `ProofTreeBuilder::nested_computation_kind` is called
31/// to add the finished nested evaluation to the parent.
32///
33/// We provide additional information to the current state
34/// by calling methods such as `ProofTreeBuilder::probe_kind`.
35///
36/// The actual structure closely mirrors the finished proof
37/// trees. At the end of trait solving `ProofTreeBuilder::finalize`
38/// is called to recursively convert the whole structure to a
39/// finished proof tree.
40pub(crate) struct ProofTreeBuilder<D, I = <D as SolverDelegate>::Interner>
41where
42    D: SolverDelegate<Interner = I>,
43    I: Interner,
44{
45    _infcx: PhantomData<D>,
46    state: Option<Box<DebugSolver<I>>>,
47}
48
49/// The current state of the proof tree builder, at most places
50/// in the code, only one or two variants are actually possible.
51///
52/// We simply ICE in case that assumption is broken.
53#[derive_where(Debug; I: Interner)]
54enum DebugSolver<I: Interner> {
55    Root,
56    GoalEvaluation(WipGoalEvaluation<I>),
57    CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<I>),
58    CanonicalGoalEvaluationStep(WipCanonicalGoalEvaluationStep<I>),
59}
60
61impl<I: Interner> From<WipGoalEvaluation<I>> for DebugSolver<I> {
62    fn from(g: WipGoalEvaluation<I>) -> DebugSolver<I> {
63        DebugSolver::GoalEvaluation(g)
64    }
65}
66
67impl<I: Interner> From<WipCanonicalGoalEvaluation<I>> for DebugSolver<I> {
68    fn from(g: WipCanonicalGoalEvaluation<I>) -> DebugSolver<I> {
69        DebugSolver::CanonicalGoalEvaluation(g)
70    }
71}
72
73impl<I: Interner> From<WipCanonicalGoalEvaluationStep<I>> for DebugSolver<I> {
74    fn from(g: WipCanonicalGoalEvaluationStep<I>) -> DebugSolver<I> {
75        DebugSolver::CanonicalGoalEvaluationStep(g)
76    }
77}
78
79#[derive_where(PartialEq, Eq, Debug; I: Interner)]
80struct WipGoalEvaluation<I: Interner> {
81    pub uncanonicalized_goal: Goal<I, I::Predicate>,
82    pub orig_values: Vec<I::GenericArg>,
83    pub evaluation: Option<WipCanonicalGoalEvaluation<I>>,
84}
85
86impl<I: Interner> WipGoalEvaluation<I> {
87    fn finalize(self) -> inspect::GoalEvaluation<I> {
88        inspect::GoalEvaluation {
89            uncanonicalized_goal: self.uncanonicalized_goal,
90            orig_values: self.orig_values,
91            evaluation: self.evaluation.unwrap().finalize(),
92        }
93    }
94}
95
96#[derive_where(PartialEq, Eq, Debug; I: Interner)]
97struct WipCanonicalGoalEvaluation<I: Interner> {
98    goal: CanonicalInput<I>,
99    encountered_overflow: bool,
100    /// Only used for uncached goals. After we finished evaluating
101    /// the goal, this is interned and moved into `kind`.
102    final_revision: Option<WipCanonicalGoalEvaluationStep<I>>,
103    result: Option<QueryResult<I>>,
104}
105
106impl<I: Interner> WipCanonicalGoalEvaluation<I> {
107    fn finalize(self) -> inspect::CanonicalGoalEvaluation<I> {
108        inspect::CanonicalGoalEvaluation {
109            goal: self.goal,
110            kind: if self.encountered_overflow {
111                assert!(self.final_revision.is_none());
112                inspect::CanonicalGoalEvaluationKind::Overflow
113            } else {
114                let final_revision = self.final_revision.unwrap().finalize();
115                inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision }
116            },
117            result: self.result.unwrap(),
118        }
119    }
120}
121
122/// This only exists during proof tree building and does not have
123/// a corresponding struct in `inspect`. We need this to track a
124/// bunch of metadata about the current evaluation.
125#[derive_where(PartialEq, Eq, Debug; I: Interner)]
126struct WipCanonicalGoalEvaluationStep<I: Interner> {
127    /// Unlike `EvalCtxt::var_values`, we append a new
128    /// generic arg here whenever we create a new inference
129    /// variable.
130    ///
131    /// This is necessary as we otherwise don't unify these
132    /// vars when instantiating multiple `CanonicalState`.
133    var_values: Vec<I::GenericArg>,
134    probe_depth: usize,
135    evaluation: WipProbe<I>,
136}
137
138impl<I: Interner> WipCanonicalGoalEvaluationStep<I> {
139    fn current_evaluation_scope(&mut self) -> &mut WipProbe<I> {
140        let mut current = &mut self.evaluation;
141        for _ in 0..self.probe_depth {
142            match current.steps.last_mut() {
143                Some(WipProbeStep::NestedProbe(p)) => current = p,
144                _ => panic!(),
145            }
146        }
147        current
148    }
149
150    fn finalize(self) -> inspect::Probe<I> {
151        let evaluation = self.evaluation.finalize();
152        match evaluation.kind {
153            inspect::ProbeKind::Root { .. } => evaluation,
154            _ => unreachable!("unexpected root evaluation: {evaluation:?}"),
155        }
156    }
157}
158
159#[derive_where(PartialEq, Eq, Debug; I: Interner)]
160struct WipProbe<I: Interner> {
161    initial_num_var_values: usize,
162    steps: Vec<WipProbeStep<I>>,
163    kind: Option<inspect::ProbeKind<I>>,
164    final_state: Option<inspect::CanonicalState<I, ()>>,
165}
166
167impl<I: Interner> WipProbe<I> {
168    fn finalize(self) -> inspect::Probe<I> {
169        inspect::Probe {
170            steps: self.steps.into_iter().map(WipProbeStep::finalize).collect(),
171            kind: self.kind.unwrap(),
172            final_state: self.final_state.unwrap(),
173        }
174    }
175}
176
177#[derive_where(PartialEq, Eq, Debug; I: Interner)]
178enum WipProbeStep<I: Interner> {
179    AddGoal(GoalSource, inspect::CanonicalState<I, Goal<I, I::Predicate>>),
180    NestedProbe(WipProbe<I>),
181    MakeCanonicalResponse { shallow_certainty: Certainty },
182    RecordImplArgs { impl_args: inspect::CanonicalState<I, I::GenericArgs> },
183}
184
185impl<I: Interner> WipProbeStep<I> {
186    fn finalize(self) -> inspect::ProbeStep<I> {
187        match self {
188            WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal),
189            WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
190            WipProbeStep::RecordImplArgs { impl_args } => {
191                inspect::ProbeStep::RecordImplArgs { impl_args }
192            }
193            WipProbeStep::MakeCanonicalResponse { shallow_certainty } => {
194                inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty }
195            }
196        }
197    }
198}
199
200impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
201    fn new(state: impl Into<DebugSolver<I>>) -> ProofTreeBuilder<D> {
202        ProofTreeBuilder { state: Some(Box::new(state.into())), _infcx: PhantomData }
203    }
204
205    fn opt_nested<T: Into<DebugSolver<I>>>(&self, state: impl FnOnce() -> Option<T>) -> Self {
206        ProofTreeBuilder {
207            state: self.state.as_ref().and_then(|_| Some(state()?.into())).map(Box::new),
208            _infcx: PhantomData,
209        }
210    }
211
212    fn nested<T: Into<DebugSolver<I>>>(&self, state: impl FnOnce() -> T) -> Self {
213        ProofTreeBuilder {
214            state: self.state.as_ref().map(|_| Box::new(state().into())),
215            _infcx: PhantomData,
216        }
217    }
218
219    fn as_mut(&mut self) -> Option<&mut DebugSolver<I>> {
220        self.state.as_deref_mut()
221    }
222
223    pub(crate) fn take_and_enter_probe(&mut self) -> ProofTreeBuilder<D> {
224        let mut nested = ProofTreeBuilder { state: self.state.take(), _infcx: PhantomData };
225        nested.enter_probe();
226        nested
227    }
228
229    pub(crate) fn finalize(self) -> Option<inspect::GoalEvaluation<I>> {
230        match *self.state? {
231            DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
232                Some(wip_goal_evaluation.finalize())
233            }
234            root => unreachable!("unexpected proof tree builder root node: {:?}", root),
235        }
236    }
237
238    pub(crate) fn new_maybe_root(generate_proof_tree: GenerateProofTree) -> ProofTreeBuilder<D> {
239        match generate_proof_tree {
240            GenerateProofTree::No => ProofTreeBuilder::new_noop(),
241            GenerateProofTree::Yes => ProofTreeBuilder::new_root(),
242        }
243    }
244
245    fn new_root() -> ProofTreeBuilder<D> {
246        ProofTreeBuilder::new(DebugSolver::Root)
247    }
248
249    fn new_noop() -> ProofTreeBuilder<D> {
250        ProofTreeBuilder { state: None, _infcx: PhantomData }
251    }
252
253    pub(crate) fn is_noop(&self) -> bool {
254        self.state.is_none()
255    }
256
257    pub(in crate::solve) fn new_goal_evaluation(
258        &mut self,
259        goal: Goal<I, I::Predicate>,
260        orig_values: &[I::GenericArg],
261        kind: GoalEvaluationKind,
262    ) -> ProofTreeBuilder<D> {
263        self.opt_nested(|| match kind {
264            GoalEvaluationKind::Root => Some(WipGoalEvaluation {
265                uncanonicalized_goal: goal,
266                orig_values: orig_values.to_vec(),
267                evaluation: None,
268            }),
269            GoalEvaluationKind::Nested => None,
270        })
271    }
272
273    pub(crate) fn new_canonical_goal_evaluation(
274        &mut self,
275        goal: CanonicalInput<I>,
276    ) -> ProofTreeBuilder<D> {
277        self.nested(|| WipCanonicalGoalEvaluation {
278            goal,
279            encountered_overflow: false,
280            final_revision: None,
281            result: None,
282        })
283    }
284
285    pub(crate) fn canonical_goal_evaluation(
286        &mut self,
287        canonical_goal_evaluation: ProofTreeBuilder<D>,
288    ) {
289        if let Some(this) = self.as_mut() {
290            match (this, *canonical_goal_evaluation.state.unwrap()) {
291                (
292                    DebugSolver::GoalEvaluation(goal_evaluation),
293                    DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation),
294                ) => {
295                    let prev = goal_evaluation.evaluation.replace(canonical_goal_evaluation);
296                    assert_eq!(prev, None);
297                }
298                _ => unreachable!(),
299            }
300        }
301    }
302
303    pub(crate) fn canonical_goal_evaluation_overflow(&mut self) {
304        if let Some(this) = self.as_mut() {
305            match this {
306                DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
307                    canonical_goal_evaluation.encountered_overflow = true;
308                }
309                _ => unreachable!(),
310            };
311        }
312    }
313
314    pub(crate) fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<D>) {
315        if let Some(this) = self.as_mut() {
316            match this {
317                DebugSolver::Root => *this = *goal_evaluation.state.unwrap(),
318                DebugSolver::CanonicalGoalEvaluationStep(_) => {
319                    assert!(goal_evaluation.state.is_none())
320                }
321                _ => unreachable!(),
322            }
323        }
324    }
325
326    pub(crate) fn new_goal_evaluation_step(
327        &mut self,
328        var_values: ty::CanonicalVarValues<I>,
329    ) -> ProofTreeBuilder<D> {
330        self.nested(|| WipCanonicalGoalEvaluationStep {
331            var_values: var_values.var_values.to_vec(),
332            evaluation: WipProbe {
333                initial_num_var_values: var_values.len(),
334                steps: vec![],
335                kind: None,
336                final_state: None,
337            },
338            probe_depth: 0,
339        })
340    }
341
342    pub(crate) fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<D>) {
343        if let Some(this) = self.as_mut() {
344            match (this, *goal_evaluation_step.state.unwrap()) {
345                (
346                    DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations),
347                    DebugSolver::CanonicalGoalEvaluationStep(goal_evaluation_step),
348                ) => {
349                    canonical_goal_evaluations.final_revision = Some(goal_evaluation_step);
350                }
351                _ => unreachable!(),
352            }
353        }
354    }
355
356    pub(crate) fn add_var_value<T: Into<I::GenericArg>>(&mut self, arg: T) {
357        match self.as_mut() {
358            None => {}
359            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
360                state.var_values.push(arg.into());
361            }
362            Some(s) => panic!("tried to add var values to {s:?}"),
363        }
364    }
365
366    fn enter_probe(&mut self) {
367        match self.as_mut() {
368            None => {}
369            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
370                let initial_num_var_values = state.var_values.len();
371                state.current_evaluation_scope().steps.push(WipProbeStep::NestedProbe(WipProbe {
372                    initial_num_var_values,
373                    steps: vec![],
374                    kind: None,
375                    final_state: None,
376                }));
377                state.probe_depth += 1;
378            }
379            Some(s) => panic!("tried to start probe to {s:?}"),
380        }
381    }
382
383    pub(crate) fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<I>) {
384        match self.as_mut() {
385            None => {}
386            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
387                let prev = state.current_evaluation_scope().kind.replace(probe_kind);
388                assert_eq!(prev, None);
389            }
390            _ => panic!(),
391        }
392    }
393
394    pub(crate) fn probe_final_state(
395        &mut self,
396        delegate: &D,
397        max_input_universe: ty::UniverseIndex,
398    ) {
399        match self.as_mut() {
400            None => {}
401            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
402                let final_state = canonical::make_canonical_state(
403                    delegate,
404                    &state.var_values,
405                    max_input_universe,
406                    (),
407                );
408                let prev = state.current_evaluation_scope().final_state.replace(final_state);
409                assert_eq!(prev, None);
410            }
411            _ => panic!(),
412        }
413    }
414
415    pub(crate) fn add_normalizes_to_goal(
416        &mut self,
417        delegate: &D,
418        max_input_universe: ty::UniverseIndex,
419        goal: Goal<I, ty::NormalizesTo<I>>,
420    ) {
421        self.add_goal(
422            delegate,
423            max_input_universe,
424            GoalSource::Misc,
425            goal.with(delegate.cx(), goal.predicate),
426        );
427    }
428
429    pub(crate) fn add_goal(
430        &mut self,
431        delegate: &D,
432        max_input_universe: ty::UniverseIndex,
433        source: GoalSource,
434        goal: Goal<I, I::Predicate>,
435    ) {
436        match self.as_mut() {
437            None => {}
438            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
439                let goal = canonical::make_canonical_state(
440                    delegate,
441                    &state.var_values,
442                    max_input_universe,
443                    goal,
444                );
445                state.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal))
446            }
447            _ => panic!(),
448        }
449    }
450
451    pub(crate) fn record_impl_args(
452        &mut self,
453        delegate: &D,
454        max_input_universe: ty::UniverseIndex,
455        impl_args: I::GenericArgs,
456    ) {
457        match self.as_mut() {
458            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
459                let impl_args = canonical::make_canonical_state(
460                    delegate,
461                    &state.var_values,
462                    max_input_universe,
463                    impl_args,
464                );
465                state
466                    .current_evaluation_scope()
467                    .steps
468                    .push(WipProbeStep::RecordImplArgs { impl_args });
469            }
470            None => {}
471            _ => panic!(),
472        }
473    }
474
475    pub(crate) fn make_canonical_response(&mut self, shallow_certainty: Certainty) {
476        match self.as_mut() {
477            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
478                state
479                    .current_evaluation_scope()
480                    .steps
481                    .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty });
482            }
483            None => {}
484            _ => panic!(),
485        }
486    }
487
488    pub(crate) fn finish_probe(mut self) -> ProofTreeBuilder<D> {
489        match self.as_mut() {
490            None => {}
491            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
492                assert_ne!(state.probe_depth, 0);
493                let num_var_values = state.current_evaluation_scope().initial_num_var_values;
494                state.var_values.truncate(num_var_values);
495                state.probe_depth -= 1;
496            }
497            _ => panic!(),
498        }
499
500        self
501    }
502
503    pub(crate) fn query_result(&mut self, result: QueryResult<I>) {
504        if let Some(this) = self.as_mut() {
505            match this {
506                DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
507                    assert_eq!(canonical_goal_evaluation.result.replace(result), None);
508                }
509                DebugSolver::CanonicalGoalEvaluationStep(evaluation_step) => {
510                    assert_eq!(
511                        evaluation_step
512                            .evaluation
513                            .kind
514                            .replace(inspect::ProbeKind::Root { result }),
515                        None
516                    );
517                }
518                _ => unreachable!(),
519            }
520        }
521    }
522}