Skip to content

Commit 8aca388

Browse files
committed
rewrite stack dependent overflow handling
1 parent a745cbb commit 8aca388

File tree

5 files changed

+282
-175
lines changed

5 files changed

+282
-175
lines changed

compiler/rustc_middle/src/traits/solve.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::ops::ControlFlow;
22

33
use rustc_data_structures::intern::Interned;
4-
use rustc_query_system::cache::Cache;
54

65
use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints};
76
use crate::traits::query::NoSolution;
@@ -11,9 +10,10 @@ use crate::ty::{
1110
TypeVisitor,
1211
};
1312

13+
mod cache;
1414
pub mod inspect;
1515

16-
pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
16+
pub use cache::{CacheData, EvaluationCache};
1717

1818
/// A goal is a statement, i.e. `predicate`, we want to prove
1919
/// given some assumptions, i.e. `param_env`.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use super::{CanonicalInput, QueryResult};
2+
use crate::ty::TyCtxt;
3+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
4+
use rustc_data_structures::sync::Lock;
5+
use rustc_query_system::cache::WithDepNode;
6+
use rustc_query_system::dep_graph::DepNodeIndex;
7+
use rustc_session::Limit;
8+
/// The trait solver cache used by `-Ztrait-solver=next`.
9+
///
10+
/// FIXME(@lcnr): link to some official documentation of how
11+
/// this works.
12+
#[derive(Default)]
13+
pub struct EvaluationCache<'tcx> {
14+
map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>,
15+
}
16+
17+
pub struct CacheData<'tcx> {
18+
pub result: QueryResult<'tcx>,
19+
pub reached_depth: usize,
20+
pub encountered_overflow: bool,
21+
}
22+
23+
impl<'tcx> EvaluationCache<'tcx> {
24+
/// Insert a final result into the global cache.
25+
pub fn insert(
26+
&self,
27+
key: CanonicalInput<'tcx>,
28+
reached_depth: usize,
29+
did_overflow: bool,
30+
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
31+
dep_node: DepNodeIndex,
32+
result: QueryResult<'tcx>,
33+
) {
34+
let mut map = self.map.borrow_mut();
35+
let entry = map.entry(key).or_default();
36+
let data = WithDepNode::new(dep_node, result);
37+
entry.cycle_participants.extend(cycle_participants);
38+
if did_overflow {
39+
entry.with_overflow.insert(reached_depth, data);
40+
} else {
41+
entry.success = Some(Success { data, reached_depth });
42+
}
43+
}
44+
45+
/// Try to fetch a cached result, checking the recursion limit
46+
/// and handling root goals of coinductive cycles.
47+
///
48+
/// If this returns `Some` the cache result can be used.
49+
pub fn get(
50+
&self,
51+
tcx: TyCtxt<'tcx>,
52+
key: CanonicalInput<'tcx>,
53+
cycle_participant_in_stack: impl FnOnce(&FxHashSet<CanonicalInput<'tcx>>) -> bool,
54+
available_depth: Limit,
55+
) -> Option<CacheData<'tcx>> {
56+
let map = self.map.borrow();
57+
let entry = map.get(&key)?;
58+
59+
if cycle_participant_in_stack(&entry.cycle_participants) {
60+
return None;
61+
}
62+
63+
if let Some(ref success) = entry.success {
64+
if available_depth.value_within_limit(success.reached_depth) {
65+
return Some(CacheData {
66+
result: success.data.get(tcx),
67+
reached_depth: success.reached_depth,
68+
encountered_overflow: false,
69+
});
70+
}
71+
}
72+
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,
77+
})
78+
}
79+
}
80+
81+
struct Success<'tcx> {
82+
data: WithDepNode<QueryResult<'tcx>>,
83+
reached_depth: usize,
84+
}
85+
86+
/// The cache entry for a goal `CanonicalInput`.
87+
///
88+
/// This contains results whose computation never hit the
89+
/// recursion limit in `success`, and all results which hit
90+
/// the recursion limit in `with_overflow`.
91+
#[derive(Default)]
92+
struct CacheEntry<'tcx> {
93+
success: Option<Success<'tcx>>,
94+
/// We have to be careful when caching roots of cycles.
95+
///
96+
/// See the doc comment of `StackEntry::cycle_participants` for more
97+
/// details.
98+
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
99+
with_overflow: FxHashMap<usize, WithDepNode<QueryResult<'tcx>>>,
100+
}

compiler/rustc_trait_selection/src/solve/eval_ctxt.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
340340
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
341341
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
342342
let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack);
343+
let encountered_overflow = self.search_graph.encountered_overflow();
343344
let canonical_response = EvalCtxt::evaluate_canonical_goal(
344345
self.tcx(),
345346
self.search_graph,
@@ -388,6 +389,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
388389
&& !self.search_graph.in_cycle()
389390
{
390391
debug!("rerunning goal to check result is stable");
392+
self.search_graph.reset_encountered_overflow(encountered_overflow);
391393
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
392394
let new_canonical_response = EvalCtxt::evaluate_canonical_goal(
393395
self.tcx(),

0 commit comments

Comments
 (0)