1 //! Building proof trees incrementally during trait solving.
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 use rustc_middle
::traits
::query
::NoSolution
;
7 use rustc_middle
::traits
::solve
::{
8 CanonicalInput
, Certainty
, Goal
, IsNormalizesToHack
, QueryInput
, QueryResult
,
10 use rustc_middle
::ty
::{self, TyCtxt}
;
11 use rustc_session
::config
::DumpSolverProofTree
;
13 use crate::solve
::eval_ctxt
::UseGlobalCache
;
14 use crate::solve
::{self, inspect, EvalCtxt, GenerateProofTree}
;
16 /// The core data structure when building proof trees.
18 /// In case the current evaluation does not generate a proof
19 /// tree, `state` is simply `None` and we avoid any work.
21 /// The possible states of the solver are represented via
22 /// variants of [DebugSolver]. For any nested computation we call
23 /// `ProofTreeBuilder::new_nested_computation_kind` which
24 /// creates a new `ProofTreeBuilder` to temporarily replace the
25 /// current one. Once that nested computation is done,
26 /// `ProofTreeBuilder::nested_computation_kind` is called
27 /// to add the finished nested evaluation to the parent.
29 /// We provide additional information to the current state
30 /// by calling methods such as `ProofTreeBuilder::probe_kind`.
32 /// The actual structure closely mirrors the finished proof
33 /// trees. At the end of trait solving `ProofTreeBuilder::finalize`
34 /// is called to recursively convert the whole structure to a
35 /// finished proof tree.
36 pub(in crate::solve
) struct ProofTreeBuilder
<'tcx
> {
37 state
: Option
<Box
<BuilderData
<'tcx
>>>,
40 struct BuilderData
<'tcx
> {
41 tree
: DebugSolver
<'tcx
>,
42 use_global_cache
: UseGlobalCache
,
45 /// The current state of the proof tree builder, at most places
46 /// in the code, only one or two variants are actually possible.
48 /// We simply ICE in case that assumption is broken.
50 enum DebugSolver
<'tcx
> {
52 GoalEvaluation(WipGoalEvaluation
<'tcx
>),
53 CanonicalGoalEvaluation(WipCanonicalGoalEvaluation
<'tcx
>),
54 AddedGoalsEvaluation(WipAddedGoalsEvaluation
<'tcx
>),
55 GoalEvaluationStep(WipGoalEvaluationStep
<'tcx
>),
56 Probe(WipProbe
<'tcx
>),
59 impl<'tcx
> From
<WipGoalEvaluation
<'tcx
>> for DebugSolver
<'tcx
> {
60 fn from(g
: WipGoalEvaluation
<'tcx
>) -> DebugSolver
<'tcx
> {
61 DebugSolver
::GoalEvaluation(g
)
65 impl<'tcx
> From
<WipCanonicalGoalEvaluation
<'tcx
>> for DebugSolver
<'tcx
> {
66 fn from(g
: WipCanonicalGoalEvaluation
<'tcx
>) -> DebugSolver
<'tcx
> {
67 DebugSolver
::CanonicalGoalEvaluation(g
)
71 impl<'tcx
> From
<WipAddedGoalsEvaluation
<'tcx
>> for DebugSolver
<'tcx
> {
72 fn from(g
: WipAddedGoalsEvaluation
<'tcx
>) -> DebugSolver
<'tcx
> {
73 DebugSolver
::AddedGoalsEvaluation(g
)
77 impl<'tcx
> From
<WipGoalEvaluationStep
<'tcx
>> for DebugSolver
<'tcx
> {
78 fn from(g
: WipGoalEvaluationStep
<'tcx
>) -> DebugSolver
<'tcx
> {
79 DebugSolver
::GoalEvaluationStep(g
)
83 impl<'tcx
> From
<WipProbe
<'tcx
>> for DebugSolver
<'tcx
> {
84 fn from(p
: WipProbe
<'tcx
>) -> DebugSolver
<'tcx
> {
89 #[derive(Eq, PartialEq, Debug)]
90 struct WipGoalEvaluation
<'tcx
> {
91 pub uncanonicalized_goal
: Goal
<'tcx
, ty
::Predicate
<'tcx
>>,
92 pub kind
: WipGoalEvaluationKind
<'tcx
>,
93 pub evaluation
: Option
<WipCanonicalGoalEvaluation
<'tcx
>>,
94 pub returned_goals
: Vec
<Goal
<'tcx
, ty
::Predicate
<'tcx
>>>,
97 impl<'tcx
> WipGoalEvaluation
<'tcx
> {
98 fn finalize(self) -> inspect
::GoalEvaluation
<'tcx
> {
99 inspect
::GoalEvaluation
{
100 uncanonicalized_goal
: self.uncanonicalized_goal
,
101 kind
: match self.kind
{
102 WipGoalEvaluationKind
::Root { orig_values }
=> {
103 inspect
::GoalEvaluationKind
::Root { orig_values }
105 WipGoalEvaluationKind
::Nested { is_normalizes_to_hack }
=> {
106 inspect
::GoalEvaluationKind
::Nested { is_normalizes_to_hack }
109 evaluation
: self.evaluation
.unwrap().finalize(),
110 returned_goals
: self.returned_goals
,
115 #[derive(Eq, PartialEq, Debug)]
116 pub(in crate::solve
) enum WipGoalEvaluationKind
<'tcx
> {
117 Root { orig_values: Vec<ty::GenericArg<'tcx>> }
,
118 Nested { is_normalizes_to_hack: IsNormalizesToHack }
,
121 #[derive(Eq, PartialEq, Debug)]
122 pub(in crate::solve
) enum WipCanonicalGoalEvaluationKind
{
124 CacheHit(inspect
::CacheHit
),
127 #[derive(Eq, PartialEq, Debug)]
128 struct WipCanonicalGoalEvaluation
<'tcx
> {
129 goal
: CanonicalInput
<'tcx
>,
130 kind
: Option
<WipCanonicalGoalEvaluationKind
>,
131 revisions
: Vec
<WipGoalEvaluationStep
<'tcx
>>,
132 result
: Option
<QueryResult
<'tcx
>>,
135 impl<'tcx
> WipCanonicalGoalEvaluation
<'tcx
> {
136 fn finalize(self) -> inspect
::CanonicalGoalEvaluation
<'tcx
> {
137 let kind
= match self.kind
{
138 Some(WipCanonicalGoalEvaluationKind
::Overflow
) => {
139 inspect
::CanonicalGoalEvaluationKind
::Overflow
141 Some(WipCanonicalGoalEvaluationKind
::CacheHit(hit
)) => {
142 inspect
::CanonicalGoalEvaluationKind
::CacheHit(hit
)
144 None
=> inspect
::CanonicalGoalEvaluationKind
::Uncached
{
148 .map(WipGoalEvaluationStep
::finalize
)
153 inspect
::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() }
157 #[derive(Eq, PartialEq, Debug)]
158 struct WipAddedGoalsEvaluation
<'tcx
> {
159 evaluations
: Vec
<Vec
<WipGoalEvaluation
<'tcx
>>>,
160 result
: Option
<Result
<Certainty
, NoSolution
>>,
163 impl<'tcx
> WipAddedGoalsEvaluation
<'tcx
> {
164 fn finalize(self) -> inspect
::AddedGoalsEvaluation
<'tcx
> {
165 inspect
::AddedGoalsEvaluation
{
170 evaluations
.into_iter().map(WipGoalEvaluation
::finalize
).collect()
173 result
: self.result
.unwrap(),
178 #[derive(Eq, PartialEq, Debug)]
179 struct WipGoalEvaluationStep
<'tcx
> {
180 instantiated_goal
: QueryInput
<'tcx
, ty
::Predicate
<'tcx
>>,
182 evaluation
: WipProbe
<'tcx
>,
185 impl<'tcx
> WipGoalEvaluationStep
<'tcx
> {
186 fn finalize(self) -> inspect
::GoalEvaluationStep
<'tcx
> {
187 let evaluation
= self.evaluation
.finalize();
188 match evaluation
.kind
{
189 inspect
::ProbeKind
::Root { .. }
=> (),
190 _
=> unreachable
!("unexpected root evaluation: {evaluation:?}"),
192 inspect
::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation }
196 #[derive(Eq, PartialEq, Debug)]
197 struct WipProbe
<'tcx
> {
198 pub steps
: Vec
<WipProbeStep
<'tcx
>>,
199 pub kind
: Option
<inspect
::ProbeKind
<'tcx
>>,
202 impl<'tcx
> WipProbe
<'tcx
> {
203 fn finalize(self) -> inspect
::Probe
<'tcx
> {
205 steps
: self.steps
.into_iter().map(WipProbeStep
::finalize
).collect(),
206 kind
: self.kind
.unwrap(),
211 #[derive(Eq, PartialEq, Debug)]
212 enum WipProbeStep
<'tcx
> {
213 AddGoal(inspect
::CanonicalState
<'tcx
, Goal
<'tcx
, ty
::Predicate
<'tcx
>>>),
214 EvaluateGoals(WipAddedGoalsEvaluation
<'tcx
>),
215 NestedProbe(WipProbe
<'tcx
>),
218 impl<'tcx
> WipProbeStep
<'tcx
> {
219 fn finalize(self) -> inspect
::ProbeStep
<'tcx
> {
221 WipProbeStep
::AddGoal(goal
) => inspect
::ProbeStep
::AddGoal(goal
),
222 WipProbeStep
::EvaluateGoals(eval
) => inspect
::ProbeStep
::EvaluateGoals(eval
.finalize()),
223 WipProbeStep
::NestedProbe(probe
) => inspect
::ProbeStep
::NestedProbe(probe
.finalize()),
228 impl<'tcx
> ProofTreeBuilder
<'tcx
> {
230 state
: impl Into
<DebugSolver
<'tcx
>>,
231 use_global_cache
: UseGlobalCache
,
232 ) -> ProofTreeBuilder
<'tcx
> {
234 state
: Some(Box
::new(BuilderData { tree: state.into(), use_global_cache }
)),
238 fn nested
<T
: Into
<DebugSolver
<'tcx
>>>(&self, state
: impl FnOnce() -> T
) -> Self {
240 Some(prev_state
) => Self {
241 state
: Some(Box
::new(BuilderData
{
242 tree
: state().into(),
243 use_global_cache
: prev_state
.use_global_cache
,
246 None
=> Self { state: None }
,
250 fn as_mut(&mut self) -> Option
<&mut DebugSolver
<'tcx
>> {
251 self.state
.as_mut().map(|boxed
| &mut boxed
.tree
)
254 pub fn finalize(self) -> Option
<inspect
::GoalEvaluation
<'tcx
>> {
255 match self.state?
.tree
{
256 DebugSolver
::GoalEvaluation(wip_goal_evaluation
) => {
257 Some(wip_goal_evaluation
.finalize())
259 root
=> unreachable
!("unexpected proof tree builder root node: {:?}", root
),
263 pub fn use_global_cache(&self) -> bool
{
266 .map(|state
| matches
!(state
.use_global_cache
, UseGlobalCache
::Yes
))
270 pub fn new_maybe_root(
272 generate_proof_tree
: GenerateProofTree
,
273 ) -> ProofTreeBuilder
<'tcx
> {
274 match generate_proof_tree
{
275 GenerateProofTree
::Never
=> ProofTreeBuilder
::new_noop(),
276 GenerateProofTree
::IfEnabled
=> {
277 let opts
= &tcx
.sess
.opts
.unstable_opts
;
278 match opts
.dump_solver_proof_tree
{
279 DumpSolverProofTree
::Always
=> {
280 let use_cache
= opts
.dump_solver_proof_tree_use_cache
.unwrap_or(true);
281 ProofTreeBuilder
::new_root(UseGlobalCache
::from_bool(use_cache
))
283 // `OnError` is handled by reevaluating goals in error
284 // reporting with `GenerateProofTree::Yes`.
285 DumpSolverProofTree
::OnError
| DumpSolverProofTree
::Never
=> {
286 ProofTreeBuilder
::new_noop()
290 GenerateProofTree
::Yes(use_cache
) => ProofTreeBuilder
::new_root(use_cache
),
294 pub fn new_root(use_global_cache
: UseGlobalCache
) -> ProofTreeBuilder
<'tcx
> {
295 ProofTreeBuilder
::new(DebugSolver
::Root
, use_global_cache
)
298 pub fn new_noop() -> ProofTreeBuilder
<'tcx
> {
299 ProofTreeBuilder { state: None }
302 pub fn is_noop(&self) -> bool
{
306 pub(in crate::solve
) fn new_goal_evaluation(
308 goal
: Goal
<'tcx
, ty
::Predicate
<'tcx
>>,
309 orig_values
: &[ty
::GenericArg
<'tcx
>],
310 kind
: solve
::GoalEvaluationKind
,
311 ) -> ProofTreeBuilder
<'tcx
> {
312 self.nested(|| WipGoalEvaluation
{
313 uncanonicalized_goal
: goal
,
315 solve
::GoalEvaluationKind
::Root
=> {
316 WipGoalEvaluationKind
::Root { orig_values: orig_values.to_vec() }
318 solve
::GoalEvaluationKind
::Nested { is_normalizes_to_hack }
=> {
319 WipGoalEvaluationKind
::Nested { is_normalizes_to_hack }
323 returned_goals
: vec
![],
327 pub fn new_canonical_goal_evaluation(
329 goal
: CanonicalInput
<'tcx
>,
330 ) -> ProofTreeBuilder
<'tcx
> {
331 self.nested(|| WipCanonicalGoalEvaluation
{
339 pub fn canonical_goal_evaluation(&mut self, canonical_goal_evaluation
: ProofTreeBuilder
<'tcx
>) {
340 if let Some(this
) = self.as_mut() {
341 match (this
, canonical_goal_evaluation
.state
.unwrap().tree
) {
343 DebugSolver
::GoalEvaluation(goal_evaluation
),
344 DebugSolver
::CanonicalGoalEvaluation(canonical_goal_evaluation
),
345 ) => goal_evaluation
.evaluation
= Some(canonical_goal_evaluation
),
351 pub fn goal_evaluation_kind(&mut self, kind
: WipCanonicalGoalEvaluationKind
) {
352 if let Some(this
) = self.as_mut() {
354 DebugSolver
::CanonicalGoalEvaluation(canonical_goal_evaluation
) => {
355 assert_eq
!(canonical_goal_evaluation
.kind
.replace(kind
), None
);
362 pub fn returned_goals(&mut self, goals
: &[Goal
<'tcx
, ty
::Predicate
<'tcx
>>]) {
363 if let Some(this
) = self.as_mut() {
365 DebugSolver
::GoalEvaluation(evaluation
) => {
366 assert
!(evaluation
.returned_goals
.is_empty());
367 evaluation
.returned_goals
.extend(goals
);
373 pub fn goal_evaluation(&mut self, goal_evaluation
: ProofTreeBuilder
<'tcx
>) {
374 if let Some(this
) = self.as_mut() {
375 match (this
, goal_evaluation
.state
.unwrap().tree
) {
377 DebugSolver
::AddedGoalsEvaluation(WipAddedGoalsEvaluation
{
380 DebugSolver
::GoalEvaluation(goal_evaluation
),
381 ) => evaluations
.last_mut().unwrap().push(goal_evaluation
),
382 (this @ DebugSolver
::Root
, goal_evaluation
) => *this
= goal_evaluation
,
388 pub fn new_goal_evaluation_step(
390 instantiated_goal
: QueryInput
<'tcx
, ty
::Predicate
<'tcx
>>,
391 ) -> ProofTreeBuilder
<'tcx
> {
392 self.nested(|| WipGoalEvaluationStep
{
394 evaluation
: WipProbe { steps: vec![], kind: None }
,
397 pub fn goal_evaluation_step(&mut self, goal_evaluation_step
: ProofTreeBuilder
<'tcx
>) {
398 if let Some(this
) = self.as_mut() {
399 match (this
, goal_evaluation_step
.state
.unwrap().tree
) {
401 DebugSolver
::CanonicalGoalEvaluation(canonical_goal_evaluations
),
402 DebugSolver
::GoalEvaluationStep(goal_evaluation_step
),
404 canonical_goal_evaluations
.revisions
.push(goal_evaluation_step
);
411 pub fn new_probe(&mut self) -> ProofTreeBuilder
<'tcx
> {
412 self.nested(|| WipProbe { steps: vec![], kind: None }
)
415 pub fn probe_kind(&mut self, probe_kind
: inspect
::ProbeKind
<'tcx
>) {
416 if let Some(this
) = self.as_mut() {
418 DebugSolver
::Probe(this
) => {
419 assert_eq
!(this
.kind
.replace(probe_kind
), None
)
426 pub fn add_goal(ecx
: &mut EvalCtxt
<'_
, 'tcx
>, goal
: Goal
<'tcx
, ty
::Predicate
<'tcx
>>) {
427 // Can't use `if let Some(this) = ecx.inspect.as_mut()` here because
428 // we have to immutably use the `EvalCtxt` for `make_canonical_state`.
429 if ecx
.inspect
.is_noop() {
433 let goal
= Self::make_canonical_state(ecx
, goal
);
435 match ecx
.inspect
.as_mut().unwrap() {
436 DebugSolver
::GoalEvaluationStep(WipGoalEvaluationStep
{
437 evaluation
: WipProbe { steps, .. }
,
440 | DebugSolver
::Probe(WipProbe { steps, .. }
) => steps
.push(WipProbeStep
::AddGoal(goal
)),
441 s
=> unreachable
!("tried to add {goal:?} to {s:?}"),
445 pub fn finish_probe(&mut self, probe
: ProofTreeBuilder
<'tcx
>) {
446 if let Some(this
) = self.as_mut() {
447 match (this
, probe
.state
.unwrap().tree
) {
449 DebugSolver
::Probe(WipProbe { steps, .. }
)
450 | DebugSolver
::GoalEvaluationStep(WipGoalEvaluationStep
{
451 evaluation
: WipProbe { steps, .. }
,
454 DebugSolver
::Probe(probe
),
455 ) => steps
.push(WipProbeStep
::NestedProbe(probe
)),
461 pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder
<'tcx
> {
462 self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None }
)
465 pub fn evaluate_added_goals_loop_start(&mut self) {
466 if let Some(this
) = self.as_mut() {
468 DebugSolver
::AddedGoalsEvaluation(this
) => {
469 this
.evaluations
.push(vec
![]);
476 pub fn eval_added_goals_result(&mut self, result
: Result
<Certainty
, NoSolution
>) {
477 if let Some(this
) = self.as_mut() {
479 DebugSolver
::AddedGoalsEvaluation(this
) => {
480 assert_eq
!(this
.result
.replace(result
), None
);
487 pub fn added_goals_evaluation(&mut self, added_goals_evaluation
: ProofTreeBuilder
<'tcx
>) {
488 if let Some(this
) = self.as_mut() {
489 match (this
, added_goals_evaluation
.state
.unwrap().tree
) {
491 DebugSolver
::GoalEvaluationStep(WipGoalEvaluationStep
{
492 evaluation
: WipProbe { steps, .. }
,
495 | DebugSolver
::Probe(WipProbe { steps, .. }
),
496 DebugSolver
::AddedGoalsEvaluation(added_goals_evaluation
),
497 ) => steps
.push(WipProbeStep
::EvaluateGoals(added_goals_evaluation
)),
503 pub fn query_result(&mut self, result
: QueryResult
<'tcx
>) {
504 if let Some(this
) = self.as_mut() {
506 DebugSolver
::CanonicalGoalEvaluation(canonical_goal_evaluation
) => {
507 assert_eq
!(canonical_goal_evaluation
.result
.replace(result
), None
);
509 DebugSolver
::GoalEvaluationStep(evaluation_step
) => {
514 .replace(inspect
::ProbeKind
::Root { result }
),