Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 91685c0

Browse files
Make search graph generic over interner
1 parent d84b903 commit 91685c0

File tree

6 files changed

+75
-54
lines changed

6 files changed

+75
-54
lines changed

compiler/rustc_middle/src/ty/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
233233
fn parent(self, def_id: Self::DefId) -> Self::DefId {
234234
self.parent(def_id)
235235
}
236+
237+
fn recursion_limit(self) -> usize {
238+
self.recursion_limit().0
239+
}
236240
}
237241

238242
impl<'tcx> rustc_type_ir::inherent::Abi<TyCtxt<'tcx>> for abi::Abi {

compiler/rustc_middle/src/ty/predicate.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ pub struct Predicate<'tcx>(
3737
pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
3838
);
3939

40-
impl<'tcx> rustc_type_ir::inherent::Predicate<TyCtxt<'tcx>> for Predicate<'tcx> {}
40+
impl<'tcx> rustc_type_ir::inherent::Predicate<TyCtxt<'tcx>> for Predicate<'tcx> {
41+
fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool {
42+
self.is_coinductive(interner)
43+
}
44+
}
4145

4246
impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> {
4347
fn flags(&self) -> TypeFlags {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub struct EvalCtxt<'a, 'tcx> {
8585
/// new placeholders to the caller.
8686
pub(super) max_input_universe: ty::UniverseIndex,
8787

88-
pub(super) search_graph: &'a mut SearchGraph<'tcx>,
88+
pub(super) search_graph: &'a mut SearchGraph<TyCtxt<'tcx>>,
8989

9090
nested_goals: NestedGoals<TyCtxt<'tcx>>,
9191

@@ -225,7 +225,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
225225
/// and registering opaques from the canonicalized input.
226226
fn enter_canonical<R>(
227227
tcx: TyCtxt<'tcx>,
228-
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
228+
search_graph: &'a mut search_graph::SearchGraph<TyCtxt<'tcx>>,
229229
canonical_input: CanonicalInput<'tcx>,
230230
canonical_goal_evaluation: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
231231
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
@@ -287,7 +287,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
287287
#[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
288288
fn evaluate_canonical_goal(
289289
tcx: TyCtxt<'tcx>,
290-
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
290+
search_graph: &'a mut search_graph::SearchGraph<TyCtxt<'tcx>>,
291291
canonical_input: CanonicalInput<'tcx>,
292292
goal_evaluation: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
293293
) -> QueryResult<'tcx> {

compiler/rustc_trait_selection/src/solve/search_graph.rs

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
use crate::solve::FIXPOINT_STEP_LIMIT;
1+
use std::mem;
22

3-
use super::inspect;
4-
use super::inspect::ProofTreeBuilder;
5-
use super::SolverMode;
6-
use rustc_data_structures::fx::FxHashMap;
7-
use rustc_data_structures::fx::FxHashSet;
3+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
84
use rustc_index::Idx;
95
use rustc_index::IndexVec;
106
use rustc_middle::dep_graph::dep_kinds;
117
use rustc_middle::traits::solve::CacheData;
12-
use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
8+
use rustc_middle::traits::solve::EvaluationCache;
139
use rustc_middle::ty::TyCtxt;
10+
use rustc_next_trait_solver::solve::{CanonicalInput, Certainty, QueryResult};
1411
use rustc_session::Limit;
15-
use std::mem;
12+
use rustc_type_ir::inherent::*;
13+
use rustc_type_ir::Interner;
14+
15+
use super::inspect;
16+
use super::inspect::ProofTreeBuilder;
17+
use super::SolverMode;
18+
use crate::solve::FIXPOINT_STEP_LIMIT;
1619

1720
rustc_index::newtype_index! {
1821
#[orderable]
@@ -30,9 +33,10 @@ bitflags::bitflags! {
3033
}
3134
}
3235

33-
#[derive(Debug)]
34-
struct StackEntry<'tcx> {
35-
input: CanonicalInput<'tcx>,
36+
#[derive(derivative::Derivative)]
37+
#[derivative(Debug(bound = ""))]
38+
struct StackEntry<I: Interner> {
39+
input: CanonicalInput<I>,
3640

3741
available_depth: Limit,
3842

@@ -53,11 +57,11 @@ struct StackEntry<'tcx> {
5357
has_been_used: HasBeenUsed,
5458
/// Starts out as `None` and gets set when rerunning this
5559
/// goal in case we encounter a cycle.
56-
provisional_result: Option<QueryResult<'tcx>>,
60+
provisional_result: Option<QueryResult<I>>,
5761
}
5862

5963
/// The provisional result for a goal which is not on the stack.
60-
struct DetachedEntry<'tcx> {
64+
struct DetachedEntry<I: Interner> {
6165
/// The head of the smallest non-trivial cycle involving this entry.
6266
///
6367
/// Given the following rules, when proving `A` the head for
@@ -68,7 +72,7 @@ struct DetachedEntry<'tcx> {
6872
/// C :- A + B + C
6973
/// ```
7074
head: StackDepth,
71-
result: QueryResult<'tcx>,
75+
result: QueryResult<I>,
7276
}
7377

7478
/// Stores the stack depth of a currently evaluated goal *and* already
@@ -83,40 +87,41 @@ struct DetachedEntry<'tcx> {
8387
///
8488
/// The provisional cache can theoretically result in changes to the observable behavior,
8589
/// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs.
86-
#[derive(Default)]
87-
struct ProvisionalCacheEntry<'tcx> {
90+
#[derive(derivative::Derivative)]
91+
#[derivative(Default(bound = ""))]
92+
struct ProvisionalCacheEntry<I: Interner> {
8893
stack_depth: Option<StackDepth>,
89-
with_inductive_stack: Option<DetachedEntry<'tcx>>,
90-
with_coinductive_stack: Option<DetachedEntry<'tcx>>,
94+
with_inductive_stack: Option<DetachedEntry<I>>,
95+
with_coinductive_stack: Option<DetachedEntry<I>>,
9196
}
9297

93-
impl<'tcx> ProvisionalCacheEntry<'tcx> {
98+
impl<I: Interner> ProvisionalCacheEntry<I> {
9499
fn is_empty(&self) -> bool {
95100
self.stack_depth.is_none()
96101
&& self.with_inductive_stack.is_none()
97102
&& self.with_coinductive_stack.is_none()
98103
}
99104
}
100105

101-
pub(super) struct SearchGraph<'tcx> {
106+
pub(super) struct SearchGraph<I: Interner> {
102107
mode: SolverMode,
103108
/// The stack of goals currently being computed.
104109
///
105110
/// An element is *deeper* in the stack if its index is *lower*.
106-
stack: IndexVec<StackDepth, StackEntry<'tcx>>,
107-
provisional_cache: FxHashMap<CanonicalInput<'tcx>, ProvisionalCacheEntry<'tcx>>,
111+
stack: IndexVec<StackDepth, StackEntry<I>>,
112+
provisional_cache: FxHashMap<CanonicalInput<I>, ProvisionalCacheEntry<I>>,
108113
/// We put only the root goal of a coinductive cycle into the global cache.
109114
///
110115
/// If we were to use that result when later trying to prove another cycle
111116
/// participant, we can end up with unstable query results.
112117
///
113118
/// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for
114119
/// an example of where this is needed.
115-
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
120+
cycle_participants: FxHashSet<CanonicalInput<I>>,
116121
}
117122

118-
impl<'tcx> SearchGraph<'tcx> {
119-
pub(super) fn new(mode: SolverMode) -> SearchGraph<'tcx> {
123+
impl<I: Interner> SearchGraph<I> {
124+
pub(super) fn new(mode: SolverMode) -> SearchGraph<I> {
120125
Self {
121126
mode,
122127
stack: Default::default(),
@@ -144,7 +149,7 @@ impl<'tcx> SearchGraph<'tcx> {
144149
///
145150
/// Directly popping from the stack instead of using this method
146151
/// would cause us to not track overflow and recursion depth correctly.
147-
fn pop_stack(&mut self) -> StackEntry<'tcx> {
152+
fn pop_stack(&mut self) -> StackEntry<I> {
148153
let elem = self.stack.pop().unwrap();
149154
if let Some(last) = self.stack.raw.last_mut() {
150155
last.reached_depth = last.reached_depth.max(elem.reached_depth);
@@ -153,17 +158,6 @@ impl<'tcx> SearchGraph<'tcx> {
153158
elem
154159
}
155160

156-
/// The trait solver behavior is different for coherence
157-
/// so we use a separate cache. Alternatively we could use
158-
/// a single cache and share it between coherence and ordinary
159-
/// trait solving.
160-
pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> {
161-
match self.mode {
162-
SolverMode::Normal => &tcx.new_solver_evaluation_cache,
163-
SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache,
164-
}
165-
}
166-
167161
pub(super) fn is_empty(&self) -> bool {
168162
if self.stack.is_empty() {
169163
debug_assert!(self.provisional_cache.is_empty());
@@ -181,8 +175,8 @@ impl<'tcx> SearchGraph<'tcx> {
181175
/// the remaining depth of all nested goals to prevent hangs
182176
/// in case there is exponential blowup.
183177
fn allowed_depth_for_nested(
184-
tcx: TyCtxt<'tcx>,
185-
stack: &IndexVec<StackDepth, StackEntry<'tcx>>,
178+
tcx: I,
179+
stack: &IndexVec<StackDepth, StackEntry<I>>,
186180
) -> Option<Limit> {
187181
if let Some(last) = stack.raw.last() {
188182
if last.available_depth.0 == 0 {
@@ -195,13 +189,13 @@ impl<'tcx> SearchGraph<'tcx> {
195189
Limit(last.available_depth.0 - 1)
196190
})
197191
} else {
198-
Some(tcx.recursion_limit())
192+
Some(Limit(tcx.recursion_limit()))
199193
}
200194
}
201195

202196
fn stack_coinductive_from(
203-
tcx: TyCtxt<'tcx>,
204-
stack: &IndexVec<StackDepth, StackEntry<'tcx>>,
197+
tcx: I,
198+
stack: &IndexVec<StackDepth, StackEntry<I>>,
205199
head: StackDepth,
206200
) -> bool {
207201
stack.raw[head.index()..]
@@ -220,8 +214,8 @@ impl<'tcx> SearchGraph<'tcx> {
220214
// we reach a fixpoint and all other cycle participants to make sure that
221215
// their result does not get moved to the global cache.
222216
fn tag_cycle_participants(
223-
stack: &mut IndexVec<StackDepth, StackEntry<'tcx>>,
224-
cycle_participants: &mut FxHashSet<CanonicalInput<'tcx>>,
217+
stack: &mut IndexVec<StackDepth, StackEntry<I>>,
218+
cycle_participants: &mut FxHashSet<CanonicalInput<I>>,
225219
usage_kind: HasBeenUsed,
226220
head: StackDepth,
227221
) {
@@ -234,7 +228,7 @@ impl<'tcx> SearchGraph<'tcx> {
234228
}
235229

236230
fn clear_dependent_provisional_results(
237-
provisional_cache: &mut FxHashMap<CanonicalInput<'tcx>, ProvisionalCacheEntry<'tcx>>,
231+
provisional_cache: &mut FxHashMap<CanonicalInput<I>, ProvisionalCacheEntry<I>>,
238232
head: StackDepth,
239233
) {
240234
#[allow(rustc::potential_query_instability)]
@@ -244,6 +238,19 @@ impl<'tcx> SearchGraph<'tcx> {
244238
!entry.is_empty()
245239
});
246240
}
241+
}
242+
243+
impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
244+
/// The trait solver behavior is different for coherence
245+
/// so we use a separate cache. Alternatively we could use
246+
/// a single cache and share it between coherence and ordinary
247+
/// trait solving.
248+
pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> {
249+
match self.mode {
250+
SolverMode::Normal => &tcx.new_solver_evaluation_cache,
251+
SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache,
252+
}
253+
}
247254

248255
/// Probably the most involved method of the whole solver.
249256
///
@@ -252,10 +259,13 @@ impl<'tcx> SearchGraph<'tcx> {
252259
pub(super) fn with_new_goal(
253260
&mut self,
254261
tcx: TyCtxt<'tcx>,
255-
input: CanonicalInput<'tcx>,
262+
input: CanonicalInput<TyCtxt<'tcx>>,
256263
inspect: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
257-
mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<TyCtxt<'tcx>>) -> QueryResult<'tcx>,
258-
) -> QueryResult<'tcx> {
264+
mut prove_goal: impl FnMut(
265+
&mut Self,
266+
&mut ProofTreeBuilder<TyCtxt<'tcx>>,
267+
) -> QueryResult<TyCtxt<'tcx>>,
268+
) -> QueryResult<TyCtxt<'tcx>> {
259269
// Check for overflow.
260270
let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else {
261271
if let Some(last) = self.stack.raw.last_mut() {
@@ -489,9 +499,9 @@ impl<'tcx> SearchGraph<'tcx> {
489499

490500
fn response_no_constraints(
491501
tcx: TyCtxt<'tcx>,
492-
goal: CanonicalInput<'tcx>,
502+
goal: CanonicalInput<TyCtxt<'tcx>>,
493503
certainty: Certainty,
494-
) -> QueryResult<'tcx> {
504+
) -> QueryResult<TyCtxt<'tcx>> {
495505
Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty))
496506
}
497507
}

compiler/rustc_type_ir/src/inherent.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
9696
pub trait Predicate<I: Interner<Predicate = Self>>:
9797
Copy + Debug + Hash + Eq + TypeSuperVisitable<I> + TypeSuperFoldable<I> + Flags
9898
{
99+
fn is_coinductive(self, interner: I) -> bool;
99100
}
100101

101102
/// Common capabilities of placeholder kinds

compiler/rustc_type_ir/src/interner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ pub trait Interner:
124124
) -> Self::GenericArgs;
125125

126126
fn parent(self, def_id: Self::DefId) -> Self::DefId;
127+
128+
fn recursion_limit(self) -> usize;
127129
}
128130

129131
/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`

0 commit comments

Comments
 (0)