blob: 4369148baf91d1387afe48243ffc94848ba4eb7c [file] [log] [blame]
//! Building proof trees incrementally during trait solving.
//!
//! This code is *a bit* of a mess and can hopefully be
//! mostly ignored. For a general overview of how it works,
//! see the comment on [ProofTreeBuilder].
use std::marker::PhantomData;
use derive_where::derive_where;
use rustc_type_ir::inherent::*;
use rustc_type_ir::{self as ty, Interner};
use crate::canonical;
use crate::delegate::SolverDelegate;
use crate::solve::{Certainty, Goal, GoalSource, QueryResult, inspect};
/// We need to know whether to build a prove tree while evaluating. We
/// pass a `ProofTreeBuilder` with `state: Some(None)` into the search
/// graph which then causes the initial `EvalCtxt::compute_goal` to actually
/// build a proof tree which then gets written into the `state`.
///
/// Building the proof tree for a single evaluation step happens via the
/// [EvaluationStepBuilder] which is updated by the `EvalCtxt` when
/// appropriate.
pub(crate) struct ProofTreeBuilder<D, I = <D as SolverDelegate>::Interner>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
state: Option<Box<Option<inspect::Probe<I>>>>,
_infcx: PhantomData<D>,
}
impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
pub(crate) fn new() -> ProofTreeBuilder<D> {
ProofTreeBuilder { state: Some(Box::new(None)), _infcx: PhantomData }
}
pub(crate) fn new_noop() -> ProofTreeBuilder<D> {
ProofTreeBuilder { state: None, _infcx: PhantomData }
}
pub(crate) fn is_noop(&self) -> bool {
self.state.is_none()
}
pub(crate) fn new_evaluation_step(
&mut self,
var_values: ty::CanonicalVarValues<I>,
) -> EvaluationStepBuilder<D> {
if self.is_noop() {
EvaluationStepBuilder { state: None, _infcx: PhantomData }
} else {
EvaluationStepBuilder {
state: Some(Box::new(WipEvaluationStep {
var_values: var_values.var_values.to_vec(),
evaluation: WipProbe {
initial_num_var_values: var_values.len(),
steps: vec![],
kind: None,
final_state: None,
},
probe_depth: 0,
})),
_infcx: PhantomData,
}
}
}
pub(crate) fn finish_evaluation_step(
&mut self,
goal_evaluation_step: EvaluationStepBuilder<D>,
) {
if let Some(this) = self.state.as_deref_mut() {
*this = Some(goal_evaluation_step.state.unwrap().finalize());
}
}
pub(crate) fn unwrap(self) -> inspect::Probe<I> {
self.state.unwrap().unwrap()
}
}
pub(crate) struct EvaluationStepBuilder<D, I = <D as SolverDelegate>::Interner>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
state: Option<Box<WipEvaluationStep<I>>>,
_infcx: PhantomData<D>,
}
#[derive_where(PartialEq, Eq, Debug; I: Interner)]
struct WipEvaluationStep<I: Interner> {
/// Unlike `EvalCtxt::var_values`, we append a new
/// generic arg here whenever we create a new inference
/// variable.
///
/// This is necessary as we otherwise don't unify these
/// vars when instantiating multiple `CanonicalState`.
var_values: Vec<I::GenericArg>,
probe_depth: usize,
evaluation: WipProbe<I>,
}
impl<I: Interner> WipEvaluationStep<I> {
fn current_evaluation_scope(&mut self) -> &mut WipProbe<I> {
let mut current = &mut self.evaluation;
for _ in 0..self.probe_depth {
match current.steps.last_mut() {
Some(WipProbeStep::NestedProbe(p)) => current = p,
_ => panic!(),
}
}
current
}
fn finalize(self) -> inspect::Probe<I> {
let evaluation = self.evaluation.finalize();
match evaluation.kind {
inspect::ProbeKind::Root { .. } => evaluation,
_ => unreachable!("unexpected root evaluation: {evaluation:?}"),
}
}
}
#[derive_where(PartialEq, Debug; I: Interner)]
struct WipProbe<I: Interner> {
initial_num_var_values: usize,
steps: Vec<WipProbeStep<I>>,
kind: Option<inspect::ProbeKind<I>>,
final_state: Option<inspect::CanonicalState<I, ()>>,
}
impl<I: Interner> Eq for WipProbe<I> {}
impl<I: Interner> WipProbe<I> {
fn finalize(self) -> inspect::Probe<I> {
inspect::Probe {
steps: self.steps.into_iter().map(WipProbeStep::finalize).collect(),
kind: self.kind.unwrap(),
final_state: self.final_state.unwrap(),
}
}
}
#[derive_where(PartialEq, Debug; I: Interner)]
enum WipProbeStep<I: Interner> {
AddGoal(GoalSource, inspect::CanonicalState<I, Goal<I, I::Predicate>>),
NestedProbe(WipProbe<I>),
MakeCanonicalResponse { shallow_certainty: Certainty },
RecordImplArgs { impl_args: inspect::CanonicalState<I, I::GenericArgs> },
}
impl<I: Interner> Eq for WipProbeStep<I> {}
impl<I: Interner> WipProbeStep<I> {
fn finalize(self) -> inspect::ProbeStep<I> {
match self {
WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal),
WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
WipProbeStep::RecordImplArgs { impl_args } => {
inspect::ProbeStep::RecordImplArgs { impl_args }
}
WipProbeStep::MakeCanonicalResponse { shallow_certainty } => {
inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty }
}
}
}
}
impl<D: SolverDelegate<Interner = I>, I: Interner> EvaluationStepBuilder<D> {
pub(crate) fn new_noop() -> EvaluationStepBuilder<D> {
EvaluationStepBuilder { state: None, _infcx: PhantomData }
}
pub(crate) fn is_noop(&self) -> bool {
self.state.is_none()
}
fn as_mut(&mut self) -> Option<&mut WipEvaluationStep<I>> {
self.state.as_deref_mut()
}
pub(crate) fn take_and_enter_probe(&mut self) -> EvaluationStepBuilder<D> {
let mut nested = EvaluationStepBuilder { state: self.state.take(), _infcx: PhantomData };
nested.enter_probe();
nested
}
pub(crate) fn add_var_value<T: Into<I::GenericArg>>(&mut self, arg: T) {
if let Some(this) = self.as_mut() {
this.var_values.push(arg.into());
}
}
fn enter_probe(&mut self) {
if let Some(this) = self.as_mut() {
let initial_num_var_values = this.var_values.len();
this.current_evaluation_scope().steps.push(WipProbeStep::NestedProbe(WipProbe {
initial_num_var_values,
steps: vec![],
kind: None,
final_state: None,
}));
this.probe_depth += 1;
}
}
pub(crate) fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<I>) {
if let Some(this) = self.as_mut() {
let prev = this.current_evaluation_scope().kind.replace(probe_kind);
assert_eq!(prev, None);
}
}
pub(crate) fn probe_final_state(
&mut self,
delegate: &D,
max_input_universe: ty::UniverseIndex,
) {
if let Some(this) = self.as_mut() {
let final_state =
canonical::make_canonical_state(delegate, &this.var_values, max_input_universe, ());
let prev = this.current_evaluation_scope().final_state.replace(final_state);
assert_eq!(prev, None);
}
}
pub(crate) fn add_goal(
&mut self,
delegate: &D,
max_input_universe: ty::UniverseIndex,
source: GoalSource,
goal: Goal<I, I::Predicate>,
) {
if let Some(this) = self.as_mut() {
let goal = canonical::make_canonical_state(
delegate,
&this.var_values,
max_input_universe,
goal,
);
this.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal))
}
}
pub(crate) fn record_impl_args(
&mut self,
delegate: &D,
max_input_universe: ty::UniverseIndex,
impl_args: I::GenericArgs,
) {
if let Some(this) = self.as_mut() {
let impl_args = canonical::make_canonical_state(
delegate,
&this.var_values,
max_input_universe,
impl_args,
);
this.current_evaluation_scope().steps.push(WipProbeStep::RecordImplArgs { impl_args });
}
}
pub(crate) fn make_canonical_response(&mut self, shallow_certainty: Certainty) {
if let Some(this) = self.as_mut() {
this.current_evaluation_scope()
.steps
.push(WipProbeStep::MakeCanonicalResponse { shallow_certainty });
}
}
pub(crate) fn finish_probe(mut self) -> EvaluationStepBuilder<D> {
if let Some(this) = self.as_mut() {
assert_ne!(this.probe_depth, 0);
let num_var_values = this.current_evaluation_scope().initial_num_var_values;
this.var_values.truncate(num_var_values);
this.probe_depth -= 1;
}
self
}
pub(crate) fn query_result(&mut self, result: QueryResult<I>) {
if let Some(this) = self.as_mut() {
assert_eq!(this.evaluation.kind.replace(inspect::ProbeKind::Root { result }), None);
}
}
}