Rollup merge of #117394 - lcnr:proof-tree-cache4, r=compiler-errors
use global cache when computing proof trees we're writing the solver while relying on the existence of the global cache to avoid exponential blowup. By disabling the global cache when building proof trees, it is easy to get hangs, e.g. when computing intercrate ambiguity causes. Removes the unstable `-Zdump_solver_proof_tree_use_cache` option, as we now always return a full proof tree. r? `@compiler-errors`
This commit is contained in:
commit
298edd6d46
11 changed files with 138 additions and 126 deletions
|
@ -69,6 +69,7 @@ macro_rules! arena_types {
|
|||
[] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>,
|
||||
[] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
|
||||
[] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
|
||||
[] canonical_goal_evaluation: rustc_middle::traits::solve::inspect::GoalEvaluationStep<'tcx>,
|
||||
[] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>,
|
||||
[] type_op_subtype:
|
||||
rustc_middle::infer::canonical::Canonical<'tcx,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::{CanonicalInput, QueryResult};
|
||||
use super::{inspect, CanonicalInput, QueryResult};
|
||||
use crate::ty::TyCtxt;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::sync::Lock;
|
||||
|
@ -14,8 +14,10 @@ pub struct EvaluationCache<'tcx> {
|
|||
map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct CacheData<'tcx> {
|
||||
pub result: QueryResult<'tcx>,
|
||||
pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
|
||||
pub reached_depth: usize,
|
||||
pub encountered_overflow: bool,
|
||||
}
|
||||
|
@ -24,22 +26,33 @@ impl<'tcx> EvaluationCache<'tcx> {
|
|||
/// Insert a final result into the global cache.
|
||||
pub fn insert(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: CanonicalInput<'tcx>,
|
||||
proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
|
||||
reached_depth: usize,
|
||||
did_overflow: bool,
|
||||
encountered_overflow: bool,
|
||||
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
|
||||
dep_node: DepNodeIndex,
|
||||
result: QueryResult<'tcx>,
|
||||
) {
|
||||
let mut map = self.map.borrow_mut();
|
||||
let entry = map.entry(key).or_default();
|
||||
let data = WithDepNode::new(dep_node, result);
|
||||
let data = WithDepNode::new(dep_node, QueryData { result, proof_tree });
|
||||
entry.cycle_participants.extend(cycle_participants);
|
||||
if did_overflow {
|
||||
if encountered_overflow {
|
||||
entry.with_overflow.insert(reached_depth, data);
|
||||
} else {
|
||||
entry.success = Some(Success { data, reached_depth });
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
drop(map);
|
||||
if Some(CacheData { result, proof_tree, reached_depth, encountered_overflow })
|
||||
!= self.get(tcx, key, |_| false, Limit(reached_depth))
|
||||
{
|
||||
bug!("unable to retrieve inserted element from cache: {key:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to fetch a cached result, checking the recursion limit
|
||||
|
@ -62,27 +75,39 @@ impl<'tcx> EvaluationCache<'tcx> {
|
|||
|
||||
if let Some(ref success) = entry.success {
|
||||
if available_depth.value_within_limit(success.reached_depth) {
|
||||
let QueryData { result, proof_tree } = success.data.get(tcx);
|
||||
return Some(CacheData {
|
||||
result: success.data.get(tcx),
|
||||
result,
|
||||
proof_tree,
|
||||
reached_depth: success.reached_depth,
|
||||
encountered_overflow: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
entry.with_overflow.get(&available_depth.0).map(|e| CacheData {
|
||||
result: e.get(tcx),
|
||||
reached_depth: available_depth.0,
|
||||
encountered_overflow: true,
|
||||
entry.with_overflow.get(&available_depth.0).map(|e| {
|
||||
let QueryData { result, proof_tree } = e.get(tcx);
|
||||
CacheData {
|
||||
result,
|
||||
proof_tree,
|
||||
reached_depth: available_depth.0,
|
||||
encountered_overflow: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Success<'tcx> {
|
||||
data: WithDepNode<QueryResult<'tcx>>,
|
||||
data: WithDepNode<QueryData<'tcx>>,
|
||||
reached_depth: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct QueryData<'tcx> {
|
||||
pub result: QueryResult<'tcx>,
|
||||
pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
|
||||
}
|
||||
|
||||
/// The cache entry for a goal `CanonicalInput`.
|
||||
///
|
||||
/// This contains results whose computation never hit the
|
||||
|
@ -96,5 +121,5 @@ struct CacheEntry<'tcx> {
|
|||
/// See the doc comment of `StackEntry::cycle_participants` for more
|
||||
/// details.
|
||||
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
|
||||
with_overflow: FxHashMap<usize, WithDepNode<QueryResult<'tcx>>>,
|
||||
with_overflow: FxHashMap<usize, WithDepNode<QueryData<'tcx>>>,
|
||||
}
|
||||
|
|
|
@ -42,12 +42,6 @@ pub struct State<'tcx, T> {
|
|||
|
||||
pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum CacheHit {
|
||||
Provisional,
|
||||
Global,
|
||||
}
|
||||
|
||||
/// When evaluating the root goals we also store the
|
||||
/// original values for the `CanonicalVarValues` of the
|
||||
/// canonicalized goal. We use this to map any [CanonicalState]
|
||||
|
@ -78,8 +72,8 @@ pub struct CanonicalGoalEvaluation<'tcx> {
|
|||
#[derive(Eq, PartialEq)]
|
||||
pub enum CanonicalGoalEvaluationKind<'tcx> {
|
||||
Overflow,
|
||||
CacheHit(CacheHit),
|
||||
Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
|
||||
CycleInStack,
|
||||
Evaluation { revisions: &'tcx [GoalEvaluationStep<'tcx>] },
|
||||
}
|
||||
impl Debug for GoalEvaluation<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
|
|
@ -74,13 +74,10 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
|||
CanonicalGoalEvaluationKind::Overflow => {
|
||||
writeln!(self.f, "OVERFLOW: {:?}", eval.result)
|
||||
}
|
||||
CanonicalGoalEvaluationKind::CacheHit(CacheHit::Global) => {
|
||||
writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result)
|
||||
CanonicalGoalEvaluationKind::CycleInStack => {
|
||||
writeln!(self.f, "CYCLE IN STACK: {:?}", eval.result)
|
||||
}
|
||||
CanonicalGoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
|
||||
writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result)
|
||||
}
|
||||
CanonicalGoalEvaluationKind::Uncached { revisions } => {
|
||||
CanonicalGoalEvaluationKind::Evaluation { revisions } => {
|
||||
for (n, step) in revisions.iter().enumerate() {
|
||||
writeln!(self.f, "REVISION {n}")?;
|
||||
self.nested(|this| this.format_evaluation_step(step))?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue