add flag for disabling global cache and printing proof trees on error
This commit is contained in:
@@ -19,7 +19,9 @@ use rustc_middle::ty::{
|
||||
self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
|
||||
TypeVisitableExt, TypeVisitor,
|
||||
};
|
||||
use rustc_session::config::SolverProofTreeCondition;
|
||||
use rustc_span::DUMMY_SP;
|
||||
use std::io::Write;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::traits::specialization_graph;
|
||||
@@ -113,9 +115,23 @@ impl NestedGoals<'_> {
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||
pub enum GenerateProofTree {
|
||||
Yes(DisableGlobalCache),
|
||||
No,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||
pub enum DisableGlobalCache {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
impl DisableGlobalCache {
|
||||
pub fn from_bool(disable_cache: bool) -> Self {
|
||||
match disable_cache {
|
||||
true => DisableGlobalCache::Yes,
|
||||
false => DisableGlobalCache::No,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InferCtxtEvalExt<'tcx> {
|
||||
/// Evaluates a goal from **outside** of the trait solver.
|
||||
@@ -164,6 +180,36 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
|
||||
let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
|
||||
|
||||
let inspect = {
|
||||
let generate_proof_tree = match (
|
||||
infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree,
|
||||
infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree_uses_cache,
|
||||
generate_proof_tree,
|
||||
) {
|
||||
(_, Some(use_cache), GenerateProofTree::Yes(_)) => {
|
||||
GenerateProofTree::Yes(DisableGlobalCache::from_bool(!use_cache))
|
||||
}
|
||||
|
||||
(SolverProofTreeCondition::Always, use_cache, GenerateProofTree::No) => {
|
||||
let use_cache = use_cache.unwrap_or(true);
|
||||
GenerateProofTree::Yes(DisableGlobalCache::from_bool(!use_cache))
|
||||
}
|
||||
|
||||
(_, None, GenerateProofTree::Yes(_)) => generate_proof_tree,
|
||||
// `Never` is kind of weird- it doesn't actually force us to not generate proof trees
|
||||
// its just the default setting for rustflags forced proof tree generation.
|
||||
(SolverProofTreeCondition::Never, _, _) => generate_proof_tree,
|
||||
(SolverProofTreeCondition::OnError, _, _) => generate_proof_tree,
|
||||
};
|
||||
|
||||
match generate_proof_tree {
|
||||
GenerateProofTree::No => ProofTreeBuilder::new_noop(),
|
||||
GenerateProofTree::Yes(global_cache_disabled) => {
|
||||
ProofTreeBuilder::new_root(global_cache_disabled)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut ecx = EvalCtxt {
|
||||
search_graph: &mut search_graph,
|
||||
infcx: infcx,
|
||||
@@ -177,17 +223,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
var_values: CanonicalVarValues::dummy(),
|
||||
nested_goals: NestedGoals::new(),
|
||||
tainted: Ok(()),
|
||||
inspect: (infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
|
||||
|| matches!(generate_proof_tree, GenerateProofTree::Yes))
|
||||
.then(ProofTreeBuilder::new_root)
|
||||
.unwrap_or_else(ProofTreeBuilder::new_noop),
|
||||
inspect,
|
||||
};
|
||||
let result = f(&mut ecx);
|
||||
|
||||
let tree = ecx.inspect.finalize();
|
||||
if let Some(tree) = &tree {
|
||||
// module to allow more granular RUSTC_LOG filtering to just proof tree output
|
||||
super::inspect::dump::print_tree(tree);
|
||||
if let (Some(tree), SolverProofTreeCondition::Always) =
|
||||
(&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree)
|
||||
{
|
||||
let mut lock = std::io::stdout().lock();
|
||||
let _ = lock.write_fmt(format_args!("{tree:?}"));
|
||||
let _ = lock.flush();
|
||||
}
|
||||
|
||||
assert!(
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustc_middle::traits::solve::{
|
||||
};
|
||||
use rustc_middle::ty;
|
||||
|
||||
pub mod dump;
|
||||
use super::eval_ctxt::DisableGlobalCache;
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
|
||||
pub struct WipGoalEvaluation<'tcx> {
|
||||
@@ -145,11 +145,15 @@ impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> {
|
||||
|
||||
pub struct ProofTreeBuilder<'tcx> {
|
||||
state: Option<Box<DebugSolver<'tcx>>>,
|
||||
disable_global_cache: DisableGlobalCache,
|
||||
}
|
||||
|
||||
impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
fn new(state: impl Into<DebugSolver<'tcx>>) -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder { state: Some(Box::new(state.into())) }
|
||||
fn new(
|
||||
state: impl Into<DebugSolver<'tcx>>,
|
||||
disable_global_cache: DisableGlobalCache,
|
||||
) -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder { state: Some(Box::new(state.into())), disable_global_cache }
|
||||
}
|
||||
|
||||
fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> {
|
||||
@@ -165,12 +169,16 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_root() -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder::new(DebugSolver::Root)
|
||||
pub fn disable_global_cache(&self) -> DisableGlobalCache {
|
||||
self.disable_global_cache
|
||||
}
|
||||
|
||||
pub fn new_root(disable_global_cache: DisableGlobalCache) -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder::new(DebugSolver::Root, disable_global_cache)
|
||||
}
|
||||
|
||||
pub fn new_noop() -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder { state: None }
|
||||
ProofTreeBuilder { state: None, disable_global_cache: DisableGlobalCache::No }
|
||||
}
|
||||
|
||||
pub fn is_noop(&self) -> bool {
|
||||
@@ -183,18 +191,24 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
is_normalizes_to_hack: IsNormalizesToHack,
|
||||
) -> ProofTreeBuilder<'tcx> {
|
||||
if self.state.is_none() {
|
||||
return ProofTreeBuilder { state: None };
|
||||
return ProofTreeBuilder {
|
||||
state: None,
|
||||
disable_global_cache: self.disable_global_cache,
|
||||
};
|
||||
}
|
||||
|
||||
ProofTreeBuilder::new(WipGoalEvaluation {
|
||||
uncanonicalized_goal: goal,
|
||||
canonicalized_goal: None,
|
||||
evaluation_steps: vec![],
|
||||
is_normalizes_to_hack,
|
||||
cache_hit: None,
|
||||
returned_goals: vec![],
|
||||
result: None,
|
||||
})
|
||||
ProofTreeBuilder::new(
|
||||
WipGoalEvaluation {
|
||||
uncanonicalized_goal: goal,
|
||||
canonicalized_goal: None,
|
||||
evaluation_steps: vec![],
|
||||
is_normalizes_to_hack,
|
||||
cache_hit: None,
|
||||
returned_goals: vec![],
|
||||
result: None,
|
||||
},
|
||||
self.disable_global_cache,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) {
|
||||
@@ -250,15 +264,21 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
|
||||
) -> ProofTreeBuilder<'tcx> {
|
||||
if self.state.is_none() {
|
||||
return ProofTreeBuilder { state: None };
|
||||
return ProofTreeBuilder {
|
||||
state: None,
|
||||
disable_global_cache: self.disable_global_cache,
|
||||
};
|
||||
}
|
||||
|
||||
ProofTreeBuilder::new(WipGoalEvaluationStep {
|
||||
instantiated_goal,
|
||||
nested_goal_evaluations: vec![],
|
||||
candidates: vec![],
|
||||
result: None,
|
||||
})
|
||||
ProofTreeBuilder::new(
|
||||
WipGoalEvaluationStep {
|
||||
instantiated_goal,
|
||||
nested_goal_evaluations: vec![],
|
||||
candidates: vec![],
|
||||
result: None,
|
||||
},
|
||||
self.disable_global_cache,
|
||||
)
|
||||
}
|
||||
pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
|
||||
if let Some(this) = self.as_mut() {
|
||||
@@ -273,14 +293,17 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
|
||||
pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> {
|
||||
if self.state.is_none() {
|
||||
return ProofTreeBuilder { state: None };
|
||||
return ProofTreeBuilder {
|
||||
state: None,
|
||||
|
||||
disable_global_cache: self.disable_global_cache,
|
||||
};
|
||||
}
|
||||
|
||||
ProofTreeBuilder::new(WipGoalCandidate {
|
||||
nested_goal_evaluations: vec![],
|
||||
candidates: vec![],
|
||||
kind: None,
|
||||
})
|
||||
ProofTreeBuilder::new(
|
||||
WipGoalCandidate { nested_goal_evaluations: vec![], candidates: vec![], kind: None },
|
||||
self.disable_global_cache,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn candidate_kind(&mut self, candidate_kind: CandidateKind<'tcx>) {
|
||||
@@ -309,10 +332,17 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
|
||||
pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
|
||||
if self.state.is_none() {
|
||||
return ProofTreeBuilder { state: None };
|
||||
return ProofTreeBuilder {
|
||||
state: None,
|
||||
|
||||
disable_global_cache: self.disable_global_cache,
|
||||
};
|
||||
}
|
||||
|
||||
ProofTreeBuilder::new(WipAddedGoalsEvaluation { evaluations: vec![], result: None })
|
||||
ProofTreeBuilder::new(
|
||||
WipAddedGoalsEvaluation { evaluations: vec![], result: None },
|
||||
self.disable_global_cache,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn evaluate_added_goals_loop_start(&mut self) {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
use rustc_middle::traits::solve::inspect::GoalEvaluation;
|
||||
|
||||
pub fn print_tree(tree: &GoalEvaluation<'_>) {
|
||||
debug!(?tree);
|
||||
}
|
||||
@@ -33,7 +33,9 @@ mod search_graph;
|
||||
mod trait_goals;
|
||||
mod weak_types;
|
||||
|
||||
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt, InferCtxtSelectExt};
|
||||
pub use eval_ctxt::{
|
||||
DisableGlobalCache, EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt,
|
||||
};
|
||||
pub use fulfill::FulfillmentCtxt;
|
||||
pub(crate) use normalize::deeply_normalize;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryRe
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use std::{collections::hash_map::Entry, mem};
|
||||
|
||||
use super::eval_ctxt::DisableGlobalCache;
|
||||
use super::inspect::ProofTreeBuilder;
|
||||
use super::SolverMode;
|
||||
|
||||
@@ -213,7 +214,9 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
inspect: &mut ProofTreeBuilder<'tcx>,
|
||||
mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if self.should_use_global_cache() {
|
||||
if self.should_use_global_cache()
|
||||
&& inspect.disable_global_cache() == DisableGlobalCache::No
|
||||
{
|
||||
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
|
||||
debug!(?canonical_input, ?result, "cache hit");
|
||||
inspect.cache_hit(CacheHit::Global);
|
||||
|
||||
Reference in New Issue
Block a user