Skip to content

Commit 51338ca

Browse files
committed
expand fuzzing support
this allows us to only sometimes disable the global cache.
1 parent 7b86c98 commit 51338ca

File tree

2 files changed

+56
-9
lines changed

2 files changed

+56
-9
lines changed

compiler/rustc_next_trait_solver/src/solve/search_graph.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::convert::Infallible;
12
use std::marker::PhantomData;
23

34
use rustc_type_ir::inherent::*;
@@ -22,6 +23,14 @@ where
2223
{
2324
type Cx = D::Interner;
2425

26+
type ValidationScope = Infallible;
27+
fn enter_validation_scope(
28+
_cx: Self::Cx,
29+
_input: <Self::Cx as search_graph::Cx>::Input,
30+
) -> Option<Self::ValidationScope> {
31+
None
32+
}
33+
2534
const FIXPOINT_STEP_LIMIT: usize = FIXPOINT_STEP_LIMIT;
2635

2736
type ProofTreeBuilder = ProofTreeBuilder<D>;

compiler/rustc_type_ir/src/search_graph/mod.rs

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ pub trait Cx: Copy {
4444

4545
pub trait Delegate {
4646
type Cx: Cx;
47+
type ValidationScope;
48+
/// Returning `Some` disables the global cache for the current goal.
49+
///
50+
/// The `ValidationScope` is used when fuzzing the search graph to track
51+
/// for which goals the global cache has been disabled. This is necessary
52+
/// as we may otherwise ignore the global cache entry for some goal `G`
53+
/// only to later use it, failing to detect a cycle goal and potentially
54+
/// changing the result.
55+
fn enter_validation_scope(
56+
cx: Self::Cx,
57+
input: <Self::Cx as Cx>::Input,
58+
) -> Option<Self::ValidationScope>;
59+
4760
const FIXPOINT_STEP_LIMIT: usize;
4861

4962
type ProofTreeBuilder;
@@ -356,11 +369,21 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
356369
return D::on_stack_overflow(cx, inspect, input);
357370
};
358371

359-
if D::inspect_is_noop(inspect) {
360-
if let Some(result) = self.lookup_global_cache(cx, input, available_depth) {
361-
return result;
362-
}
363-
}
372+
let validate_cache = if !D::inspect_is_noop(inspect) {
373+
None
374+
} else if let Some(scope) = D::enter_validation_scope(cx, input) {
375+
// When validating the global cache we need to track the goals for which the
376+
// global cache has been disabled as it may otherwise change the result for
377+
// cyclic goals. We don't care about goals which are not on the current stack
378+
// so it's fine to drop their scope eagerly.
379+
self.lookup_global_cache_untracked(cx, input, available_depth)
380+
.inspect(|expected| debug!(?expected, "validate cache entry"))
381+
.map(|r| (scope, r))
382+
} else if let Some(result) = self.lookup_global_cache(cx, input, available_depth) {
383+
return result;
384+
} else {
385+
None
386+
};
364387

365388
// Check whether the goal is in the provisional cache.
366389
// The provisional result may rely on the path to its cycle roots,
@@ -452,6 +475,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
452475
// do not remove it from the provisional cache and update its provisional result.
453476
// We only add the root of cycles to the global cache.
454477
if let Some(head) = final_entry.non_root_cycle_participant {
478+
debug_assert!(validate_cache.is_none());
455479
let coinductive_stack = Self::stack_coinductive_from(cx, &self.stack, head);
456480

457481
let entry = self.provisional_cache.get_mut(&input).unwrap();
@@ -463,16 +487,29 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
463487
}
464488
} else {
465489
self.provisional_cache.remove(&input);
466-
if D::inspect_is_noop(inspect) {
490+
if let Some((_scope, expected)) = validate_cache {
491+
// Do not try to move a goal into the cache again if we're testing
492+
// the global cache.
493+
assert_eq!(result, expected, "input={input:?}");
494+
} else if D::inspect_is_noop(inspect) {
467495
self.insert_global_cache(cx, input, final_entry, result, dep_node)
468496
}
469497
}
470498

471-
self.check_invariants();
472-
473499
result
474500
}
475501

502+
fn lookup_global_cache_untracked(
503+
&self,
504+
cx: X,
505+
input: X::Input,
506+
available_depth: AvailableDepth,
507+
) -> Option<X::Result> {
508+
cx.with_global_cache(self.mode, |cache| {
509+
cache.get(cx, input, &self.stack, available_depth).map(|c| c.result)
510+
})
511+
}
512+
476513
/// Try to fetch a previously computed result from the global cache,
477514
/// making sure to only do so if it would match the result of reevaluating
478515
/// this goal.
@@ -496,7 +533,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
496533
let reached_depth = self.stack.next_index().plus(additional_depth);
497534
self.update_parent_goal(reached_depth, encountered_overflow);
498535

499-
debug!("global cache hit");
536+
debug!(?additional_depth, "global cache hit");
500537
Some(result)
501538
})
502539
}
@@ -518,6 +555,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
518555
dep_node: X::DepNodeIndex,
519556
) {
520557
let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len();
558+
debug!(?final_entry, ?result, "insert global cache");
521559
cx.with_global_cache(self.mode, |cache| {
522560
cache.insert(
523561
cx,

0 commit comments

Comments
 (0)