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