Skip to content

Commit 298edd6

Browse files
authored
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`
2 parents 62270fb + 15ae59b commit 298edd6

File tree

11 files changed

+138
-126
lines changed

11 files changed

+138
-126
lines changed

compiler/rustc_middle/src/arena.rs

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ macro_rules! arena_types {
6969
[] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>,
7070
[] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
7171
[] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
72+
[] canonical_goal_evaluation: rustc_middle::traits::solve::inspect::GoalEvaluationStep<'tcx>,
7273
[] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>,
7374
[] type_op_subtype:
7475
rustc_middle::infer::canonical::Canonical<'tcx,

compiler/rustc_middle/src/traits/solve/cache.rs

+36-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{CanonicalInput, QueryResult};
1+
use super::{inspect, CanonicalInput, QueryResult};
22
use crate::ty::TyCtxt;
33
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
44
use rustc_data_structures::sync::Lock;
@@ -14,8 +14,10 @@ pub struct EvaluationCache<'tcx> {
1414
map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>,
1515
}
1616

17+
#[derive(PartialEq, Eq)]
1718
pub struct CacheData<'tcx> {
1819
pub result: QueryResult<'tcx>,
20+
pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
1921
pub reached_depth: usize,
2022
pub encountered_overflow: bool,
2123
}
@@ -24,22 +26,33 @@ impl<'tcx> EvaluationCache<'tcx> {
2426
/// Insert a final result into the global cache.
2527
pub fn insert(
2628
&self,
29+
tcx: TyCtxt<'tcx>,
2730
key: CanonicalInput<'tcx>,
31+
proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
2832
reached_depth: usize,
29-
did_overflow: bool,
33+
encountered_overflow: bool,
3034
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
3135
dep_node: DepNodeIndex,
3236
result: QueryResult<'tcx>,
3337
) {
3438
let mut map = self.map.borrow_mut();
3539
let entry = map.entry(key).or_default();
36-
let data = WithDepNode::new(dep_node, result);
40+
let data = WithDepNode::new(dep_node, QueryData { result, proof_tree });
3741
entry.cycle_participants.extend(cycle_participants);
38-
if did_overflow {
42+
if encountered_overflow {
3943
entry.with_overflow.insert(reached_depth, data);
4044
} else {
4145
entry.success = Some(Success { data, reached_depth });
4246
}
47+
48+
if cfg!(debug_assertions) {
49+
drop(map);
50+
if Some(CacheData { result, proof_tree, reached_depth, encountered_overflow })
51+
!= self.get(tcx, key, |_| false, Limit(reached_depth))
52+
{
53+
bug!("unable to retrieve inserted element from cache: {key:?}");
54+
}
55+
}
4356
}
4457

4558
/// Try to fetch a cached result, checking the recursion limit
@@ -62,27 +75,39 @@ impl<'tcx> EvaluationCache<'tcx> {
6275

6376
if let Some(ref success) = entry.success {
6477
if available_depth.value_within_limit(success.reached_depth) {
78+
let QueryData { result, proof_tree } = success.data.get(tcx);
6579
return Some(CacheData {
66-
result: success.data.get(tcx),
80+
result,
81+
proof_tree,
6782
reached_depth: success.reached_depth,
6883
encountered_overflow: false,
6984
});
7085
}
7186
}
7287

73-
entry.with_overflow.get(&available_depth.0).map(|e| CacheData {
74-
result: e.get(tcx),
75-
reached_depth: available_depth.0,
76-
encountered_overflow: true,
88+
entry.with_overflow.get(&available_depth.0).map(|e| {
89+
let QueryData { result, proof_tree } = e.get(tcx);
90+
CacheData {
91+
result,
92+
proof_tree,
93+
reached_depth: available_depth.0,
94+
encountered_overflow: true,
95+
}
7796
})
7897
}
7998
}
8099

81100
struct Success<'tcx> {
82-
data: WithDepNode<QueryResult<'tcx>>,
101+
data: WithDepNode<QueryData<'tcx>>,
83102
reached_depth: usize,
84103
}
85104

105+
#[derive(Clone, Copy)]
106+
pub struct QueryData<'tcx> {
107+
pub result: QueryResult<'tcx>,
108+
pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
109+
}
110+
86111
/// The cache entry for a goal `CanonicalInput`.
87112
///
88113
/// This contains results whose computation never hit the
@@ -96,5 +121,5 @@ struct CacheEntry<'tcx> {
96121
/// See the doc comment of `StackEntry::cycle_participants` for more
97122
/// details.
98123
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
99-
with_overflow: FxHashMap<usize, WithDepNode<QueryResult<'tcx>>>,
124+
with_overflow: FxHashMap<usize, WithDepNode<QueryData<'tcx>>>,
100125
}

compiler/rustc_middle/src/traits/solve/inspect.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,6 @@ pub struct State<'tcx, T> {
4242

4343
pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>;
4444

45-
#[derive(Debug, Eq, PartialEq)]
46-
pub enum CacheHit {
47-
Provisional,
48-
Global,
49-
}
50-
5145
/// When evaluating the root goals we also store the
5246
/// original values for the `CanonicalVarValues` of the
5347
/// canonicalized goal. We use this to map any [CanonicalState]
@@ -78,8 +72,8 @@ pub struct CanonicalGoalEvaluation<'tcx> {
7872
#[derive(Eq, PartialEq)]
7973
pub enum CanonicalGoalEvaluationKind<'tcx> {
8074
Overflow,
81-
CacheHit(CacheHit),
82-
Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
75+
CycleInStack,
76+
Evaluation { revisions: &'tcx [GoalEvaluationStep<'tcx>] },
8377
}
8478
impl Debug for GoalEvaluation<'_> {
8579
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

compiler/rustc_middle/src/traits/solve/inspect/format.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,10 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
7474
CanonicalGoalEvaluationKind::Overflow => {
7575
writeln!(self.f, "OVERFLOW: {:?}", eval.result)
7676
}
77-
CanonicalGoalEvaluationKind::CacheHit(CacheHit::Global) => {
78-
writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result)
77+
CanonicalGoalEvaluationKind::CycleInStack => {
78+
writeln!(self.f, "CYCLE IN STACK: {:?}", eval.result)
7979
}
80-
CanonicalGoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
81-
writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result)
82-
}
83-
CanonicalGoalEvaluationKind::Uncached { revisions } => {
80+
CanonicalGoalEvaluationKind::Evaluation { revisions } => {
8481
for (n, step) in revisions.iter().enumerate() {
8582
writeln!(self.f, "REVISION {n}")?;
8683
self.nested(|this| this.format_evaluation_step(step))?;

compiler/rustc_session/src/options.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1529,8 +1529,6 @@ options! {
15291529
dump_solver_proof_tree: DumpSolverProofTree = (DumpSolverProofTree::Never, parse_dump_solver_proof_tree, [UNTRACKED],
15301530
"dump a proof tree for every goal evaluated by the new trait solver. If the flag is specified without any options after it
15311531
then it defaults to `always`. If the flag is not specified at all it defaults to `on-request`."),
1532-
dump_solver_proof_tree_use_cache: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
1533-
"determines whether dumped proof trees use the global cache"),
15341532
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
15351533
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
15361534
dylib_lto: bool = (false, parse_bool, [UNTRACKED],

compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs

+1-15
Original file line numberDiff line numberDiff line change
@@ -119,25 +119,11 @@ impl NestedGoals<'_> {
119119

120120
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
121121
pub enum GenerateProofTree {
122-
Yes(UseGlobalCache),
122+
Yes,
123123
IfEnabled,
124124
Never,
125125
}
126126

127-
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
128-
pub enum UseGlobalCache {
129-
Yes,
130-
No,
131-
}
132-
impl UseGlobalCache {
133-
pub fn from_bool(use_cache: bool) -> Self {
134-
match use_cache {
135-
true => UseGlobalCache::Yes,
136-
false => UseGlobalCache::No,
137-
}
138-
}
139-
}
140-
141127
pub trait InferCtxtEvalExt<'tcx> {
142128
/// Evaluates a goal from **outside** of the trait solver.
143129
///

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_middle::traits::solve::{Certainty, Goal};
1717
use rustc_middle::ty;
1818

1919
use crate::solve::inspect::ProofTreeBuilder;
20-
use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
20+
use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
2121

2222
pub struct InspectGoal<'a, 'tcx> {
2323
infcx: &'a InferCtxt<'tcx>,
@@ -82,8 +82,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
8282
}
8383

8484
for &goal in &instantiated_goals {
85-
let (_, proof_tree) =
86-
infcx.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No));
85+
let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes);
8786
let proof_tree = proof_tree.unwrap();
8887
visitor.visit_goal(&InspectGoal::new(
8988
infcx,
@@ -169,11 +168,11 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
169168
let mut candidates = vec![];
170169
let last_eval_step = match self.evaluation.evaluation.kind {
171170
inspect::CanonicalGoalEvaluationKind::Overflow
172-
| inspect::CanonicalGoalEvaluationKind::CacheHit(_) => {
171+
| inspect::CanonicalGoalEvaluationKind::CycleInStack => {
173172
warn!("unexpected root evaluation: {:?}", self.evaluation);
174173
return vec![];
175174
}
176-
inspect::CanonicalGoalEvaluationKind::Uncached { ref revisions } => {
175+
inspect::CanonicalGoalEvaluationKind::Evaluation { ref revisions } => {
177176
if let Some(last) = revisions.last() {
178177
last
179178
} else {
@@ -227,8 +226,7 @@ impl<'tcx> ProofTreeInferCtxtExt<'tcx> for InferCtxt<'tcx> {
227226
goal: Goal<'tcx, ty::Predicate<'tcx>>,
228227
visitor: &mut V,
229228
) -> ControlFlow<V::BreakTy> {
230-
let (_, proof_tree) =
231-
self.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No));
229+
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
232230
let proof_tree = proof_tree.unwrap();
233231
visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
234232
}

0 commit comments

Comments
 (0)