1//! The search graph is responsible for caching and cycle detection in the trait
2//! solver. Making sure that caching doesn't result in soundness bugs or unstable
3//! query results is very challenging and makes this one of the most-involved
4//! self-contained components of the compiler.
5//!
6//! We added fuzzing support to test its correctness. The fuzzers used to verify
7//! the current implementation can be found in <https://github.com/lcnr/search_graph_fuzz>.
8//!
9//! This is just a quick overview of the general design, please check out the relevant
10//! [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for
11//! more details. Caching is split between a global cache and the per-cycle `provisional_cache`.
12//! The global cache has to be completely unobservable, while the per-cycle cache may impact
13//! behavior as long as the resulting behavior is still correct.
14use std::cmp::Ordering;
15use std::collections::hash_map::Entry;
16use std::collections::{BTreeMap, btree_map};
17use std::fmt::Debug;
18use std::hash::Hash;
19use std::iter;
20use std::marker::PhantomData;
2122use derive_where::derive_where;
23#[cfg(feature = "nightly")]
24use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash};
25use rustc_type_ir::data_structures::HashMap;
26use tracing::{debug, instrument, trace};
2728mod stack;
29use stack::{Stack, StackDepth, StackEntry};
30mod global_cache;
31use global_cache::CacheData;
32pub use global_cache::GlobalCache;
3334/// The search graph does not simply use `Interner` directly
35/// to enable its fuzzing without having to stub the rest of
36/// the interner. We don't make this a super trait of `Interner`
37/// as users of the shared type library shouldn't have to care
38/// about `Input` and `Result` as they are implementation details
39/// of the search graph.
40pub trait Cx: Copy {
41type Input: Debug + Eq + Hash + Copy;
42type Result: Debug + Eq + Hash + Copy;
43type AmbiguityKind: Debug + Eq + Hash + Copy;
4445type DepNodeIndex;
46type Tracked<T: Debug + Clone>: Debug;
47fn mk_tracked<T: Debug + Clone>(
48self,
49 data: T,
50 dep_node_index: Self::DepNodeIndex,
51 ) -> Self::Tracked<T>;
52fn get_tracked<T: Debug + Clone>(self, tracked: &Self::Tracked<T>) -> T;
53fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, Self::DepNodeIndex);
5455fn with_global_cache<R>(self, f: impl FnOnce(&mut GlobalCache<Self>) -> R) -> R;
5657fn assert_evaluation_is_concurrent(&self);
58}
5960pub trait Delegate: Sized {
61type Cx: Cx;
62/// Whether to use the provisional cache. Set to `false` by a fuzzer when
63 /// validating the search graph.
64const ENABLE_PROVISIONAL_CACHE: bool;
65type ValidationScope;
66/// Returning `Some` disables the global cache for the current goal.
67 ///
68 /// The `ValidationScope` is used when fuzzing the search graph to track
69 /// for which goals the global cache has been disabled. This is necessary
70 /// as we may otherwise ignore the global cache entry for some goal `G`
71 /// only to later use it, failing to detect a cycle goal and potentially
72 /// changing the result.
73fn enter_validation_scope(
74 cx: Self::Cx,
75 input: <Self::Cx as Cx>::Input,
76 ) -> Option<Self::ValidationScope>;
7778const FIXPOINT_STEP_LIMIT: usize;
7980type ProofTreeBuilder;
81fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool;
8283const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize;
8485fn initial_provisional_result(
86 cx: Self::Cx,
87 kind: PathKind,
88 input: <Self::Cx as Cx>::Input,
89 ) -> <Self::Cx as Cx>::Result;
90fn is_initial_provisional_result(result: <Self::Cx as Cx>::Result) -> Option<PathKind>;
91fn stack_overflow_result(
92 cx: Self::Cx,
93 input: <Self::Cx as Cx>::Input,
94 ) -> <Self::Cx as Cx>::Result;
9596const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: <Self::Cx as Cx>::AmbiguityKind;
97fn fixpoint_overflow_result(
98 cx: Self::Cx,
99 input: <Self::Cx as Cx>::Input,
100 ) -> <Self::Cx as Cx>::Result;
101102fn is_ambiguous_result(
103 result: <Self::Cx as Cx>::Result,
104 ) -> Option<<Self::Cx as Cx>::AmbiguityKind>;
105106fn compute_goal(
107 search_graph: &mut SearchGraph<Self>,
108 cx: Self::Cx,
109 input: <Self::Cx as Cx>::Input,
110 inspect: &mut Self::ProofTreeBuilder,
111 ) -> <Self::Cx as Cx>::Result;
112}
113114/// In the initial iteration of a cycle, we do not yet have a provisional
115/// result. In the case we return an initial provisional result depending
116/// on the kind of cycle.
117#[derive(#[automatically_derived]
impl ::core::fmt::Debug for PathKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
PathKind::Inductive => "Inductive",
PathKind::Unknown => "Unknown",
PathKind::Coinductive => "Coinductive",
PathKind::ForcedAmbiguity => "ForcedAmbiguity",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for PathKind {
#[inline]
fn clone(&self) -> PathKind { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for PathKind { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for PathKind {
#[inline]
fn eq(&self, other: &PathKind) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for PathKind {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for PathKind {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
let __self_discr = ::core::intrinsics::discriminant_value(self);
::core::hash::Hash::hash(&__self_discr, state)
}
}Hash)]
118#[cfg_attr(feature = "nightly", derive(const _: () =
{
impl<__D: ::rustc_serialize::Decoder>
::rustc_serialize::Decodable<__D> for PathKind {
fn decode(__decoder: &mut __D) -> Self {
match ::rustc_serialize::Decoder::read_u8(__decoder) as usize
{
0usize => { PathKind::Inductive }
1usize => { PathKind::Unknown }
2usize => { PathKind::Coinductive }
3usize => { PathKind::ForcedAmbiguity }
n => {
::core::panicking::panic_fmt(format_args!("invalid enum variant tag while decoding `PathKind`, expected 0..4, actual {0}",
n));
}
}
}
}
};Decodable_NoContext, const _: () =
{
impl<__E: ::rustc_serialize::Encoder>
::rustc_serialize::Encodable<__E> for PathKind {
fn encode(&self, __encoder: &mut __E) {
let disc =
match *self {
PathKind::Inductive => { 0usize }
PathKind::Unknown => { 1usize }
PathKind::Coinductive => { 2usize }
PathKind::ForcedAmbiguity => { 3usize }
};
::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
match *self {
PathKind::Inductive => {}
PathKind::Unknown => {}
PathKind::Coinductive => {}
PathKind::ForcedAmbiguity => {}
}
}
}
};Encodable_NoContext, const _: () =
{
impl ::rustc_data_structures::stable_hash::StableHash for PathKind {
#[inline]
fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
__hcx: &mut __Hcx,
__hasher:
&mut ::rustc_data_structures::stable_hash::StableHasher) {
::std::mem::discriminant(self).stable_hash(__hcx, __hasher);
match *self {
PathKind::Inductive => {}
PathKind::Unknown => {}
PathKind::Coinductive => {}
PathKind::ForcedAmbiguity => {}
}
}
}
};StableHash))]
119pub enum PathKind {
120/// A path consisting of only inductive/unproductive steps. Their initial
121 /// provisional result is `Err(NoSolution)`. We currently treat them as
122 /// `PathKind::Unknown` during coherence until we're fully confident in
123 /// our approach.
124Inductive,
125/// A path which is not be coinductive right now but we may want
126 /// to change of them to be so in the future. We return an ambiguous
127 /// result in this case to prevent people from relying on this.
128Unknown,
129/// A path with at least one coinductive step. Such cycles hold.
130Coinductive,
131/// A path which is treated as ambiguous. Once a path has this path kind
132 /// any other segment does not change its kind.
133 ///
134 /// This is currently only used when fuzzing to support negative reasoning.
135 /// For more details, see #143054.
136ForcedAmbiguity,
137}
138139impl PathKind {
140/// Returns the path kind when merging `self` with `rest`.
141 ///
142 /// Given an inductive path `self` and a coinductive path `rest`,
143 /// the path `self -> rest` would be coinductive.
144 ///
145 /// This operation represents an ordering and would be equivalent
146 /// to `max(self, rest)`.
147fn extend(self, rest: PathKind) -> PathKind {
148match (self, rest) {
149 (PathKind::ForcedAmbiguity, _) | (_, PathKind::ForcedAmbiguity) => {
150 PathKind::ForcedAmbiguity151 }
152 (PathKind::Coinductive, _) | (_, PathKind::Coinductive) => PathKind::Coinductive,
153 (PathKind::Unknown, _) | (_, PathKind::Unknown) => PathKind::Unknown,
154 (PathKind::Inductive, PathKind::Inductive) => PathKind::Inductive,
155 }
156 }
157}
158159/// The kinds of cycles a cycle head was involved in.
160///
161/// This is used to avoid rerunning a cycle if there's
162/// just a single usage kind and the final result matches
163/// its provisional result.
164///
165/// While it tracks the amount of usages using `u32`, we only ever
166/// care whether there are any. We only count them to be able to ignore
167/// usages from irrelevant candidates while evaluating a goal.
168///
169/// This cares about how nested goals relied on a cycle head. It does
170/// not care about how frequently the nested goal relied on it.
171#[derive(#[automatically_derived]
impl ::core::default::Default for HeadUsages {
#[inline]
fn default() -> HeadUsages {
HeadUsages {
inductive: ::core::default::Default::default(),
unknown: ::core::default::Default::default(),
coinductive: ::core::default::Default::default(),
forced_ambiguity: ::core::default::Default::default(),
}
}
}Default, #[automatically_derived]
impl ::core::fmt::Debug for HeadUsages {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field4_finish(f, "HeadUsages",
"inductive", &self.inductive, "unknown", &self.unknown,
"coinductive", &self.coinductive, "forced_ambiguity",
&&self.forced_ambiguity)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for HeadUsages {
#[inline]
fn clone(&self) -> HeadUsages {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for HeadUsages { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for HeadUsages {
#[inline]
fn eq(&self, other: &HeadUsages) -> bool {
self.inductive == other.inductive && self.unknown == other.unknown &&
self.coinductive == other.coinductive &&
self.forced_ambiguity == other.forced_ambiguity
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for HeadUsages {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}Eq)]
172struct HeadUsages {
173 inductive: u32,
174 unknown: u32,
175 coinductive: u32,
176 forced_ambiguity: u32,
177}
178179impl HeadUsages {
180fn add_usage(&mut self, path: PathKind) {
181match path {
182 PathKind::Inductive => self.inductive += 1,
183 PathKind::Unknown => self.unknown += 1,
184 PathKind::Coinductive => self.coinductive += 1,
185 PathKind::ForcedAmbiguity => self.forced_ambiguity += 1,
186 }
187 }
188189/// This adds the usages which occurred while computing a nested goal.
190 ///
191 /// We don't actually care about how frequently the nested goal relied
192 /// on its cycle heads, only whether it did.
193fn add_usages_from_nested(&mut self, usages: HeadUsages) {
194let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = usages;
195self.inductive += if inductive == 0 { 0 } else { 1 };
196self.unknown += if unknown == 0 { 0 } else { 1 };
197self.coinductive += if coinductive == 0 { 0 } else { 1 };
198self.forced_ambiguity += if forced_ambiguity == 0 { 0 } else { 1 };
199 }
200201fn ignore_usages(&mut self, usages: HeadUsages) {
202let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = usages;
203self.inductive = self.inductive.checked_sub(inductive).unwrap();
204self.unknown = self.unknown.checked_sub(unknown).unwrap();
205self.coinductive = self.coinductive.checked_sub(coinductive).unwrap();
206self.forced_ambiguity = self.forced_ambiguity.checked_sub(forced_ambiguity).unwrap();
207 }
208209fn is_empty(self) -> bool {
210let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = self;
211inductive == 0 && unknown == 0 && coinductive == 0 && forced_ambiguity == 0
212}
213214fn is_single(self, path_kind: PathKind) -> bool {
215match path_kind {
216 PathKind::Inductive => #[allow(non_exhaustive_omitted_patterns)] match self {
HeadUsages { inductive: _, unknown: 0, coinductive: 0, forced_ambiguity: 0
} => true,
_ => false,
}matches!(
217self,
218 HeadUsages { inductive: _, unknown: 0, coinductive: 0, forced_ambiguity: 0 },
219 ),
220 PathKind::Unknown => #[allow(non_exhaustive_omitted_patterns)] match self {
HeadUsages { inductive: 0, unknown: _, coinductive: 0, forced_ambiguity: 0
} => true,
_ => false,
}matches!(
221self,
222 HeadUsages { inductive: 0, unknown: _, coinductive: 0, forced_ambiguity: 0 },
223 ),
224 PathKind::Coinductive => #[allow(non_exhaustive_omitted_patterns)] match self {
HeadUsages { inductive: 0, unknown: 0, coinductive: _, forced_ambiguity: 0
} => true,
_ => false,
}matches!(
225self,
226 HeadUsages { inductive: 0, unknown: 0, coinductive: _, forced_ambiguity: 0 },
227 ),
228 PathKind::ForcedAmbiguity => #[allow(non_exhaustive_omitted_patterns)] match self {
HeadUsages { inductive: 0, unknown: 0, coinductive: 0, forced_ambiguity: _
} => true,
_ => false,
}matches!(
229self,
230 HeadUsages { inductive: 0, unknown: 0, coinductive: 0, forced_ambiguity: _ },
231 ),
232 }
233 }
234}
235236#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CandidateHeadUsages {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f,
"CandidateHeadUsages", "usages", &&self.usages)
}
}Debug, #[automatically_derived]
impl ::core::default::Default for CandidateHeadUsages {
#[inline]
fn default() -> CandidateHeadUsages {
CandidateHeadUsages { usages: ::core::default::Default::default() }
}
}Default)]
237pub struct CandidateHeadUsages {
238 usages: Option<Box<HashMap<StackDepth, HeadUsages>>>,
239}
240impl CandidateHeadUsages {
241pub fn merge_usages(&mut self, other: CandidateHeadUsages) {
242if let Some(other_usages) = other.usages {
243if let Some(ref mut self_usages) = self.usages {
244// Each head is merged independently, so the final usage counts are the same
245 // regardless of hash iteration order.
246#[allow(rustc::potential_query_instability)]
247for (head_index, head) in other_usages.into_iter() {
248let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = head;
249let self_usages = self_usages.entry(head_index).or_default();
250 self_usages.inductive += inductive;
251 self_usages.unknown += unknown;
252 self_usages.coinductive += coinductive;
253 self_usages.forced_ambiguity += forced_ambiguity;
254 }
255 } else {
256self.usages = Some(other_usages);
257 }
258 }
259 }
260}
261262/// Whether evaluating a given goal should be done with a lower available depth from
263/// its parent goal.
264///
265/// Normally, it should be `Yes`, but among rustc's predicate goals, `normalizes-to`
266/// goals are exceptions. They act like functions that used for normalizing associated
267/// terms while evaluating projection goals with fully unconstrained expected term.
268/// We don't want to lower the available depths for those function-like goals, otherwise
269/// we will encounter recursion limit overflows more often.
270#[derive(#[automatically_derived]
impl ::core::fmt::Debug for LowerAvailableDepth {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
LowerAvailableDepth::Yes => "Yes",
LowerAvailableDepth::No => "No",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for LowerAvailableDepth {
#[inline]
fn clone(&self) -> LowerAvailableDepth { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LowerAvailableDepth { }Copy)]
271pub enum LowerAvailableDepth {
272 Yes,
273 No,
274}
275276#[derive(#[automatically_derived]
impl ::core::fmt::Debug for AvailableDepth {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "AvailableDepth",
&&self.0)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for AvailableDepth {
#[inline]
fn clone(&self) -> AvailableDepth {
let _: ::core::clone::AssertParamIsClone<usize>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for AvailableDepth { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for AvailableDepth {
#[inline]
fn eq(&self, other: &AvailableDepth) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for AvailableDepth {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<usize>;
}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for AvailableDepth {
#[inline]
fn partial_cmp(&self, other: &AvailableDepth)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
}
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for AvailableDepth {
#[inline]
fn cmp(&self, other: &AvailableDepth) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}Ord)]
277struct AvailableDepth(usize);
278impl AvailableDepth {
279/// Returns the remaining depth allowed for nested goals.
280 ///
281 /// This is generally simply one less than the current depth.
282 /// However, if we encountered overflow, we significantly reduce
283 /// the remaining depth of all nested goals to prevent hangs
284 /// in case there is exponential blowup.
285fn allowed_depth_for_nested<D: Delegate>(
286 root_depth: AvailableDepth,
287 stack: &Stack<D::Cx>,
288 lower_available_depth: LowerAvailableDepth,
289 ) -> Option<AvailableDepth> {
290if let Some(last) = stack.last() {
291match lower_available_depth {
292 LowerAvailableDepth::Yes => {}
293 LowerAvailableDepth::No => {
294return Some(last.available_depth);
295 }
296 }
297298if last.available_depth.0 == 0 {
299return None;
300 }
301302Some(if last.encountered_overflow {
303AvailableDepth(last.available_depth.0 / D::DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW)
304 } else {
305AvailableDepth(last.available_depth.0 - 1)
306 })
307 } else {
308Some(root_depth)
309 }
310 }
311312/// Whether we're allowed to use a global cache entry which required
313 /// the given depth.
314fn cache_entry_is_applicable(self, additional_depth: usize) -> bool {
315self.0 >= additional_depth316 }
317}
318319#[derive(#[automatically_derived]
impl ::core::clone::Clone for CycleHead {
#[inline]
fn clone(&self) -> CycleHead {
let _: ::core::clone::AssertParamIsClone<PathsToNested>;
let _: ::core::clone::AssertParamIsClone<HeadUsages>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CycleHead { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for CycleHead {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "CycleHead",
"paths_to_head", &self.paths_to_head, "usages", &&self.usages)
}
}Debug)]
320struct CycleHead {
321 paths_to_head: PathsToNested,
322/// If the `usages` are empty, the result of that head does not matter
323 /// for the current goal. However, we still don't completely drop this
324 /// cycle head as whether or not it exists impacts which queries we
325 /// access, so ignoring it would cause incremental compilation verification
326 /// failures or hide query cycles.
327usages: HeadUsages,
328}
329330/// All cycle heads a given goal depends on, ordered by their stack depth.
331///
332/// We also track all paths from this goal to that head. This is necessary
333/// when rebasing provisional cache results.
334#[derive(#[automatically_derived]
impl ::core::clone::Clone for CycleHeads {
#[inline]
fn clone(&self) -> CycleHeads {
CycleHeads { heads: ::core::clone::Clone::clone(&self.heads) }
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for CycleHeads {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f, "CycleHeads",
"heads", &&self.heads)
}
}Debug, #[automatically_derived]
impl ::core::default::Default for CycleHeads {
#[inline]
fn default() -> CycleHeads {
CycleHeads { heads: ::core::default::Default::default() }
}
}Default)]
335struct CycleHeads {
336 heads: BTreeMap<StackDepth, CycleHead>,
337}
338339impl CycleHeads {
340fn is_empty(&self) -> bool {
341self.heads.is_empty()
342 }
343344fn highest_cycle_head(&self) -> (StackDepth, CycleHead) {
345self.heads.last_key_value().map(|(k, v)| (*k, *v)).unwrap()
346 }
347348fn highest_cycle_head_index(&self) -> StackDepth {
349self.opt_highest_cycle_head_index().unwrap()
350 }
351352fn opt_highest_cycle_head_index(&self) -> Option<StackDepth> {
353self.heads.last_key_value().map(|(k, _)| *k)
354 }
355356fn opt_lowest_cycle_head_index(&self) -> Option<StackDepth> {
357self.heads.first_key_value().map(|(k, _)| *k)
358 }
359360fn remove_highest_cycle_head(&mut self) -> CycleHead {
361let last = self.heads.pop_last();
362last.unwrap().1
363}
364365fn insert(
366&mut self,
367 head_index: StackDepth,
368 path_from_entry: impl Into<PathsToNested> + Copy,
369 usages: HeadUsages,
370 ) {
371match self.heads.entry(head_index) {
372 btree_map::Entry::Vacant(entry) => {
373entry.insert(CycleHead { paths_to_head: path_from_entry.into(), usages });
374 }
375 btree_map::Entry::Occupied(entry) => {
376let head = entry.into_mut();
377head.paths_to_head |= path_from_entry.into();
378head.usages.add_usages_from_nested(usages);
379 }
380 }
381 }
382383fn ignore_usages(&mut self, head_index: StackDepth, usages: HeadUsages) {
384self.heads.get_mut(&head_index).unwrap().usages.ignore_usages(usages)
385 }
386387fn iter(&self) -> impl Iterator<Item = (StackDepth, CycleHead)> + '_ {
388self.heads.iter().map(|(k, v)| (*k, *v))
389 }
390}
391392#[doc =
r" Tracks how nested goals have been accessed. This is necessary to disable"]
#[doc =
r" global cache entries if computing them would otherwise result in a cycle or"]
#[doc = r" access a provisional cache entry."]
pub struct PathsToNested(<PathsToNested as
::bitflags::__private::PublicFlags>::Internal);
#[automatically_derived]
impl ::core::fmt::Debug for PathsToNested {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "PathsToNested",
&&self.0)
}
}
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for PathsToNested { }
#[automatically_derived]
impl ::core::clone::Clone for PathsToNested {
#[inline]
fn clone(&self) -> PathsToNested {
let _:
::core::clone::AssertParamIsClone<<PathsToNested as
::bitflags::__private::PublicFlags>::Internal>;
*self
}
}
#[automatically_derived]
impl ::core::marker::Copy for PathsToNested { }
#[automatically_derived]
impl ::core::marker::StructuralPartialEq for PathsToNested { }
#[automatically_derived]
impl ::core::cmp::PartialEq for PathsToNested {
#[inline]
fn eq(&self, other: &PathsToNested) -> bool { self.0 == other.0 }
}
#[automatically_derived]
impl ::core::cmp::Eq for PathsToNested {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _:
::core::cmp::AssertParamIsEq<<PathsToNested as
::bitflags::__private::PublicFlags>::Internal>;
}
}
impl PathsToNested {
#[doc = r" The initial value when adding a goal to its own nested goals."]
#[allow(deprecated, non_upper_case_globals,)]
pub const EMPTY: Self = Self::from_bits_retain(1 << 0);
#[allow(deprecated, non_upper_case_globals,)]
pub const INDUCTIVE: Self = Self::from_bits_retain(1 << 1);
#[allow(deprecated, non_upper_case_globals,)]
pub const UNKNOWN: Self = Self::from_bits_retain(1 << 2);
#[allow(deprecated, non_upper_case_globals,)]
pub const COINDUCTIVE: Self = Self::from_bits_retain(1 << 3);
#[allow(deprecated, non_upper_case_globals,)]
pub const FORCED_AMBIGUITY: Self = Self::from_bits_retain(1 << 4);
}
impl ::bitflags::Flags for PathsToNested {
const FLAGS: &'static [::bitflags::Flag<PathsToNested>] =
&[{
#[allow(deprecated, non_upper_case_globals,)]
::bitflags::Flag::new("EMPTY", PathsToNested::EMPTY)
},
{
#[allow(deprecated, non_upper_case_globals,)]
::bitflags::Flag::new("INDUCTIVE", PathsToNested::INDUCTIVE)
},
{
#[allow(deprecated, non_upper_case_globals,)]
::bitflags::Flag::new("UNKNOWN", PathsToNested::UNKNOWN)
},
{
#[allow(deprecated, non_upper_case_globals,)]
::bitflags::Flag::new("COINDUCTIVE",
PathsToNested::COINDUCTIVE)
},
{
#[allow(deprecated, non_upper_case_globals,)]
::bitflags::Flag::new("FORCED_AMBIGUITY",
PathsToNested::FORCED_AMBIGUITY)
}];
type Bits = u8;
fn bits(&self) -> u8 { PathsToNested::bits(self) }
fn from_bits_retain(bits: u8) -> PathsToNested {
PathsToNested::from_bits_retain(bits)
}
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy ::
assign_op_pattern, clippy :: indexing_slicing, clippy :: same_name_method,
clippy :: iter_without_into_iter,)]
const _: () =
{
#[repr(transparent)]
pub struct InternalBitFlags(u8);
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for InternalBitFlags { }
#[automatically_derived]
impl ::core::clone::Clone for InternalBitFlags {
#[inline]
fn clone(&self) -> InternalBitFlags {
let _: ::core::clone::AssertParamIsClone<u8>;
*self
}
}
#[automatically_derived]
impl ::core::marker::Copy for InternalBitFlags { }
#[automatically_derived]
impl ::core::marker::StructuralPartialEq for InternalBitFlags { }
#[automatically_derived]
impl ::core::cmp::PartialEq for InternalBitFlags {
#[inline]
fn eq(&self, other: &InternalBitFlags) -> bool {
self.0 == other.0
}
}
#[automatically_derived]
impl ::core::cmp::Eq for InternalBitFlags {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u8>;
}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for InternalBitFlags {
#[inline]
fn partial_cmp(&self, other: &InternalBitFlags)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self,
other))
}
}
#[automatically_derived]
impl ::core::cmp::Ord for InternalBitFlags {
#[inline]
fn cmp(&self, other: &InternalBitFlags) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}
#[automatically_derived]
impl ::core::hash::Hash for InternalBitFlags {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}
impl ::bitflags::__private::PublicFlags for PathsToNested {
type Primitive = u8;
type Internal = InternalBitFlags;
}
impl ::bitflags::__private::core::default::Default for
InternalBitFlags {
#[inline]
fn default() -> Self { InternalBitFlags::empty() }
}
impl ::bitflags::__private::core::fmt::Debug for InternalBitFlags {
fn fmt(&self,
f: &mut ::bitflags::__private::core::fmt::Formatter<'_>)
-> ::bitflags::__private::core::fmt::Result {
if self.is_empty() {
f.write_fmt(format_args!("{0:#x}",
<u8 as ::bitflags::Bits>::EMPTY))
} else {
::bitflags::__private::core::fmt::Display::fmt(self, f)
}
}
}
impl ::bitflags::__private::core::fmt::Display for InternalBitFlags {
fn fmt(&self,
f: &mut ::bitflags::__private::core::fmt::Formatter<'_>)
-> ::bitflags::__private::core::fmt::Result {
::bitflags::parser::to_writer(&PathsToNested(*self), f)
}
}
impl ::bitflags::__private::core::str::FromStr for InternalBitFlags {
type Err = ::bitflags::parser::ParseError;
fn from_str(s: &str)
->
::bitflags::__private::core::result::Result<Self,
Self::Err> {
::bitflags::parser::from_str::<PathsToNested>(s).map(|flags|
flags.0)
}
}
impl ::bitflags::__private::core::convert::AsRef<u8> for
InternalBitFlags {
fn as_ref(&self) -> &u8 { &self.0 }
}
impl ::bitflags::__private::core::convert::From<u8> for
InternalBitFlags {
fn from(bits: u8) -> Self { Self::from_bits_retain(bits) }
}
#[allow(dead_code, deprecated, unused_attributes)]
impl InternalBitFlags {
/// Get a flags value with all bits unset.
#[inline]
pub const fn empty() -> Self {
Self(<u8 as ::bitflags::Bits>::EMPTY)
}
/// Get a flags value with all known bits set.
#[inline]
pub const fn all() -> Self {
let mut truncated = <u8 as ::bitflags::Bits>::EMPTY;
let mut i = 0;
{
{
let flag =
<PathsToNested as
::bitflags::Flags>::FLAGS[i].value().bits();
truncated = truncated | flag;
i += 1;
}
};
{
{
let flag =
<PathsToNested as
::bitflags::Flags>::FLAGS[i].value().bits();
truncated = truncated | flag;
i += 1;
}
};
{
{
let flag =
<PathsToNested as
::bitflags::Flags>::FLAGS[i].value().bits();
truncated = truncated | flag;
i += 1;
}
};
{
{
let flag =
<PathsToNested as
::bitflags::Flags>::FLAGS[i].value().bits();
truncated = truncated | flag;
i += 1;
}
};
{
{
let flag =
<PathsToNested as
::bitflags::Flags>::FLAGS[i].value().bits();
truncated = truncated | flag;
i += 1;
}
};
let _ = i;
Self(truncated)
}
/// Get the underlying bits value.
///
/// The returned value is exactly the bits set in this flags value.
#[inline]
pub const fn bits(&self) -> u8 { self.0 }
/// Convert from a bits value.
///
/// This method will return `None` if any unknown bits are set.
#[inline]
pub const fn from_bits(bits: u8)
-> ::bitflags::__private::core::option::Option<Self> {
let truncated = Self::from_bits_truncate(bits).0;
if truncated == bits {
::bitflags::__private::core::option::Option::Some(Self(bits))
} else { ::bitflags::__private::core::option::Option::None }
}
/// Convert from a bits value, unsetting any unknown bits.
#[inline]
pub const fn from_bits_truncate(bits: u8) -> Self {
Self(bits & Self::all().0)
}
/// Convert from a bits value exactly.
#[inline]
pub const fn from_bits_retain(bits: u8) -> Self { Self(bits) }
/// Get a flags value with the bits of a flag with the given name set.
///
/// This method will return `None` if `name` is empty or doesn't
/// correspond to any named flag.
#[inline]
pub fn from_name(name: &str)
-> ::bitflags::__private::core::option::Option<Self> {
{
if name == "EMPTY" {
return ::bitflags::__private::core::option::Option::Some(Self(PathsToNested::EMPTY.bits()));
}
};
;
{
if name == "INDUCTIVE" {
return ::bitflags::__private::core::option::Option::Some(Self(PathsToNested::INDUCTIVE.bits()));
}
};
;
{
if name == "UNKNOWN" {
return ::bitflags::__private::core::option::Option::Some(Self(PathsToNested::UNKNOWN.bits()));
}
};
;
{
if name == "COINDUCTIVE" {
return ::bitflags::__private::core::option::Option::Some(Self(PathsToNested::COINDUCTIVE.bits()));
}
};
;
{
if name == "FORCED_AMBIGUITY" {
return ::bitflags::__private::core::option::Option::Some(Self(PathsToNested::FORCED_AMBIGUITY.bits()));
}
};
;
let _ = name;
::bitflags::__private::core::option::Option::None
}
/// Whether all bits in this flags value are unset.
#[inline]
pub const fn is_empty(&self) -> bool {
self.0 == <u8 as ::bitflags::Bits>::EMPTY
}
/// Whether all known bits in this flags value are set.
#[inline]
pub const fn is_all(&self) -> bool {
Self::all().0 | self.0 == self.0
}
/// Whether any set bits in a source flags value are also set in a target flags value.
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
self.0 & other.0 != <u8 as ::bitflags::Bits>::EMPTY
}
/// Whether all set bits in a source flags value are also set in a target flags value.
#[inline]
pub const fn contains(&self, other: Self) -> bool {
self.0 & other.0 == other.0
}
/// The bitwise or (`|`) of the bits in two flags values.
#[inline]
pub fn insert(&mut self, other: Self) {
*self = Self(self.0).union(other);
}
/// The intersection of a source flags value with the complement of a target flags
/// value (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `remove` won't truncate `other`, but the `!` operator will.
#[inline]
pub fn remove(&mut self, other: Self) {
*self = Self(self.0).difference(other);
}
/// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
pub fn toggle(&mut self, other: Self) {
*self = Self(self.0).symmetric_difference(other);
}
/// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
if value { self.insert(other); } else { self.remove(other); }
}
/// The bitwise and (`&`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0 & other.0)
}
/// The bitwise or (`|`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
/// The intersection of a source flags value with the complement of a target flags
/// value (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
/// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0 ^ other.0)
}
/// The bitwise negation (`!`) of the bits in a flags value, truncating the result.
#[inline]
#[must_use]
pub const fn complement(self) -> Self {
Self::from_bits_truncate(!self.0)
}
}
impl ::bitflags::__private::core::fmt::Binary for InternalBitFlags {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::Binary::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::Octal for InternalBitFlags {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::Octal::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::LowerHex for InternalBitFlags {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::LowerHex::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::UpperHex for InternalBitFlags {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::UpperHex::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::ops::BitOr for InternalBitFlags {
type Output = Self;
/// The bitwise or (`|`) of the bits in two flags values.
#[inline]
fn bitor(self, other: InternalBitFlags) -> Self {
self.union(other)
}
}
impl ::bitflags::__private::core::ops::BitOrAssign for
InternalBitFlags {
/// The bitwise or (`|`) of the bits in two flags values.
#[inline]
fn bitor_assign(&mut self, other: Self) { self.insert(other); }
}
impl ::bitflags::__private::core::ops::BitXor for InternalBitFlags {
type Output = Self;
/// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
}
}
impl ::bitflags::__private::core::ops::BitXorAssign for
InternalBitFlags {
/// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
}
impl ::bitflags::__private::core::ops::BitAnd for InternalBitFlags {
type Output = Self;
/// The bitwise and (`&`) of the bits in two flags values.
#[inline]
fn bitand(self, other: Self) -> Self { self.intersection(other) }
}
impl ::bitflags::__private::core::ops::BitAndAssign for
InternalBitFlags {
/// The bitwise and (`&`) of the bits in two flags values.
#[inline]
fn bitand_assign(&mut self, other: Self) {
*self =
Self::from_bits_retain(self.bits()).intersection(other);
}
}
impl ::bitflags::__private::core::ops::Sub for InternalBitFlags {
type Output = Self;
/// The intersection of a source flags value with the complement of a target flags value (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub(self, other: Self) -> Self { self.difference(other) }
}
impl ::bitflags::__private::core::ops::SubAssign for InternalBitFlags
{
/// The intersection of a source flags value with the complement of a target flags value (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub_assign(&mut self, other: Self) { self.remove(other); }
}
impl ::bitflags::__private::core::ops::Not for InternalBitFlags {
type Output = Self;
/// The bitwise negation (`!`) of the bits in a flags value, truncating the result.
#[inline]
fn not(self) -> Self { self.complement() }
}
impl ::bitflags::__private::core::iter::Extend<InternalBitFlags> for
InternalBitFlags {
/// The bitwise or (`|`) of the bits in each flags value.
fn extend<T: ::bitflags::__private::core::iter::IntoIterator<Item
= Self>>(&mut self, iterator: T) {
for item in iterator { self.insert(item) }
}
}
impl ::bitflags::__private::core::iter::FromIterator<InternalBitFlags>
for InternalBitFlags {
/// The bitwise or (`|`) of the bits in each flags value.
fn from_iter<T: ::bitflags::__private::core::iter::IntoIterator<Item
= Self>>(iterator: T) -> Self {
use ::bitflags::__private::core::iter::Extend;
let mut result = Self::empty();
result.extend(iterator);
result
}
}
impl InternalBitFlags {
/// Yield a set of contained flags values.
///
/// Each yielded flags value will correspond to a defined named flag. Any unknown bits
/// will be yielded together as a final flags value.
#[inline]
pub const fn iter(&self)
-> ::bitflags::iter::Iter<PathsToNested> {
::bitflags::iter::Iter::__private_const_new(<PathsToNested as
::bitflags::Flags>::FLAGS,
PathsToNested::from_bits_retain(self.bits()),
PathsToNested::from_bits_retain(self.bits()))
}
/// Yield a set of contained named flags values.
///
/// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
/// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
#[inline]
pub const fn iter_names(&self)
-> ::bitflags::iter::IterNames<PathsToNested> {
::bitflags::iter::IterNames::__private_const_new(<PathsToNested
as ::bitflags::Flags>::FLAGS,
PathsToNested::from_bits_retain(self.bits()),
PathsToNested::from_bits_retain(self.bits()))
}
}
impl ::bitflags::__private::core::iter::IntoIterator for
InternalBitFlags {
type Item = PathsToNested;
type IntoIter = ::bitflags::iter::Iter<PathsToNested>;
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
impl InternalBitFlags {
/// Returns a mutable reference to the raw value of the flags currently stored.
#[inline]
pub fn bits_mut(&mut self) -> &mut u8 { &mut self.0 }
}
#[allow(dead_code, deprecated, unused_attributes)]
impl PathsToNested {
/// Get a flags value with all bits unset.
#[inline]
pub const fn empty() -> Self { Self(InternalBitFlags::empty()) }
/// Get a flags value with all known bits set.
#[inline]
pub const fn all() -> Self { Self(InternalBitFlags::all()) }
/// Get the underlying bits value.
///
/// The returned value is exactly the bits set in this flags value.
#[inline]
pub const fn bits(&self) -> u8 { self.0.bits() }
/// Convert from a bits value.
///
/// This method will return `None` if any unknown bits are set.
#[inline]
pub const fn from_bits(bits: u8)
-> ::bitflags::__private::core::option::Option<Self> {
match InternalBitFlags::from_bits(bits) {
::bitflags::__private::core::option::Option::Some(bits) =>
::bitflags::__private::core::option::Option::Some(Self(bits)),
::bitflags::__private::core::option::Option::None =>
::bitflags::__private::core::option::Option::None,
}
}
/// Convert from a bits value, unsetting any unknown bits.
#[inline]
pub const fn from_bits_truncate(bits: u8) -> Self {
Self(InternalBitFlags::from_bits_truncate(bits))
}
/// Convert from a bits value exactly.
#[inline]
pub const fn from_bits_retain(bits: u8) -> Self {
Self(InternalBitFlags::from_bits_retain(bits))
}
/// Get a flags value with the bits of a flag with the given name set.
///
/// This method will return `None` if `name` is empty or doesn't
/// correspond to any named flag.
#[inline]
pub fn from_name(name: &str)
-> ::bitflags::__private::core::option::Option<Self> {
match InternalBitFlags::from_name(name) {
::bitflags::__private::core::option::Option::Some(bits) =>
::bitflags::__private::core::option::Option::Some(Self(bits)),
::bitflags::__private::core::option::Option::None =>
::bitflags::__private::core::option::Option::None,
}
}
/// Whether all bits in this flags value are unset.
#[inline]
pub const fn is_empty(&self) -> bool { self.0.is_empty() }
/// Whether all known bits in this flags value are set.
#[inline]
pub const fn is_all(&self) -> bool { self.0.is_all() }
/// Whether any set bits in a source flags value are also set in a target flags value.
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
self.0.intersects(other.0)
}
/// Whether all set bits in a source flags value are also set in a target flags value.
#[inline]
pub const fn contains(&self, other: Self) -> bool {
self.0.contains(other.0)
}
/// The bitwise or (`|`) of the bits in two flags values.
#[inline]
pub fn insert(&mut self, other: Self) { self.0.insert(other.0) }
/// The intersection of a source flags value with the complement of a target flags
/// value (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `remove` won't truncate `other`, but the `!` operator will.
#[inline]
pub fn remove(&mut self, other: Self) { self.0.remove(other.0) }
/// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
pub fn toggle(&mut self, other: Self) { self.0.toggle(other.0) }
/// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
self.0.set(other.0, value)
}
/// The bitwise and (`&`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0.intersection(other.0))
}
/// The bitwise or (`|`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0.union(other.0))
}
/// The intersection of a source flags value with the complement of a target flags
/// value (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0.difference(other.0))
}
/// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0.symmetric_difference(other.0))
}
/// The bitwise negation (`!`) of the bits in a flags value, truncating the result.
#[inline]
#[must_use]
pub const fn complement(self) -> Self {
Self(self.0.complement())
}
}
impl ::bitflags::__private::core::fmt::Binary for PathsToNested {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::Binary::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::Octal for PathsToNested {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::Octal::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::LowerHex for PathsToNested {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::LowerHex::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::UpperHex for PathsToNested {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::UpperHex::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::ops::BitOr for PathsToNested {
type Output = Self;
/// The bitwise or (`|`) of the bits in two flags values.
#[inline]
fn bitor(self, other: PathsToNested) -> Self { self.union(other) }
}
impl ::bitflags::__private::core::ops::BitOrAssign for PathsToNested {
/// The bitwise or (`|`) of the bits in two flags values.
#[inline]
fn bitor_assign(&mut self, other: Self) { self.insert(other); }
}
impl ::bitflags::__private::core::ops::BitXor for PathsToNested {
type Output = Self;
/// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
}
}
impl ::bitflags::__private::core::ops::BitXorAssign for PathsToNested
{
/// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
}
impl ::bitflags::__private::core::ops::BitAnd for PathsToNested {
type Output = Self;
/// The bitwise and (`&`) of the bits in two flags values.
#[inline]
fn bitand(self, other: Self) -> Self { self.intersection(other) }
}
impl ::bitflags::__private::core::ops::BitAndAssign for PathsToNested
{
/// The bitwise and (`&`) of the bits in two flags values.
#[inline]
fn bitand_assign(&mut self, other: Self) {
*self =
Self::from_bits_retain(self.bits()).intersection(other);
}
}
impl ::bitflags::__private::core::ops::Sub for PathsToNested {
type Output = Self;
/// The intersection of a source flags value with the complement of a target flags value (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub(self, other: Self) -> Self { self.difference(other) }
}
impl ::bitflags::__private::core::ops::SubAssign for PathsToNested {
/// The intersection of a source flags value with the complement of a target flags value (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub_assign(&mut self, other: Self) { self.remove(other); }
}
impl ::bitflags::__private::core::ops::Not for PathsToNested {
type Output = Self;
/// The bitwise negation (`!`) of the bits in a flags value, truncating the result.
#[inline]
fn not(self) -> Self { self.complement() }
}
impl ::bitflags::__private::core::iter::Extend<PathsToNested> for
PathsToNested {
/// The bitwise or (`|`) of the bits in each flags value.
fn extend<T: ::bitflags::__private::core::iter::IntoIterator<Item
= Self>>(&mut self, iterator: T) {
for item in iterator { self.insert(item) }
}
}
impl ::bitflags::__private::core::iter::FromIterator<PathsToNested>
for PathsToNested {
/// The bitwise or (`|`) of the bits in each flags value.
fn from_iter<T: ::bitflags::__private::core::iter::IntoIterator<Item
= Self>>(iterator: T) -> Self {
use ::bitflags::__private::core::iter::Extend;
let mut result = Self::empty();
result.extend(iterator);
result
}
}
impl PathsToNested {
/// Yield a set of contained flags values.
///
/// Each yielded flags value will correspond to a defined named flag. Any unknown bits
/// will be yielded together as a final flags value.
#[inline]
pub const fn iter(&self)
-> ::bitflags::iter::Iter<PathsToNested> {
::bitflags::iter::Iter::__private_const_new(<PathsToNested as
::bitflags::Flags>::FLAGS,
PathsToNested::from_bits_retain(self.bits()),
PathsToNested::from_bits_retain(self.bits()))
}
/// Yield a set of contained named flags values.
///
/// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
/// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
#[inline]
pub const fn iter_names(&self)
-> ::bitflags::iter::IterNames<PathsToNested> {
::bitflags::iter::IterNames::__private_const_new(<PathsToNested
as ::bitflags::Flags>::FLAGS,
PathsToNested::from_bits_retain(self.bits()),
PathsToNested::from_bits_retain(self.bits()))
}
}
impl ::bitflags::__private::core::iter::IntoIterator for PathsToNested
{
type Item = PathsToNested;
type IntoIter = ::bitflags::iter::Iter<PathsToNested>;
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
};bitflags::bitflags! {
393/// Tracks how nested goals have been accessed. This is necessary to disable
394 /// global cache entries if computing them would otherwise result in a cycle or
395 /// access a provisional cache entry.
396#[derive(Debug, Clone, Copy, PartialEq, Eq)]
397pub struct PathsToNested: u8 {
398/// The initial value when adding a goal to its own nested goals.
399const EMPTY = 1 << 0;
400const INDUCTIVE = 1 << 1;
401const UNKNOWN = 1 << 2;
402const COINDUCTIVE = 1 << 3;
403const FORCED_AMBIGUITY = 1 << 4;
404 }
405}406impl From<PathKind> for PathsToNested {
407fn from(path: PathKind) -> PathsToNested {
408match path {
409 PathKind::Inductive => PathsToNested::INDUCTIVE,
410 PathKind::Unknown => PathsToNested::UNKNOWN,
411 PathKind::Coinductive => PathsToNested::COINDUCTIVE,
412 PathKind::ForcedAmbiguity => PathsToNested::FORCED_AMBIGUITY,
413 }
414 }
415}
416impl PathsToNested {
417/// The implementation of this function is kind of ugly. We check whether
418 /// there currently exist 'weaker' paths in the set, if so we upgrade these
419 /// paths to at least `path`.
420#[must_use]
421fn extend_with(mut self, path: PathKind) -> Self {
422match path {
423 PathKind::Inductive => {
424if self.intersects(PathsToNested::EMPTY) {
425self.remove(PathsToNested::EMPTY);
426self.insert(PathsToNested::INDUCTIVE);
427 }
428 }
429 PathKind::Unknown => {
430if self.intersects(PathsToNested::EMPTY | PathsToNested::INDUCTIVE) {
431self.remove(PathsToNested::EMPTY | PathsToNested::INDUCTIVE);
432self.insert(PathsToNested::UNKNOWN);
433 }
434 }
435 PathKind::Coinductive => {
436if self.intersects(
437PathsToNested::EMPTY | PathsToNested::INDUCTIVE | PathsToNested::UNKNOWN,
438 ) {
439self.remove(
440PathsToNested::EMPTY | PathsToNested::INDUCTIVE | PathsToNested::UNKNOWN,
441 );
442self.insert(PathsToNested::COINDUCTIVE);
443 }
444 }
445 PathKind::ForcedAmbiguity => {
446if self.intersects(
447PathsToNested::EMPTY448 | PathsToNested::INDUCTIVE449 | PathsToNested::UNKNOWN450 | PathsToNested::COINDUCTIVE,
451 ) {
452self.remove(
453PathsToNested::EMPTY454 | PathsToNested::INDUCTIVE455 | PathsToNested::UNKNOWN456 | PathsToNested::COINDUCTIVE,
457 );
458self.insert(PathsToNested::FORCED_AMBIGUITY);
459 }
460 }
461 }
462463self464 }
465466#[must_use]
467fn extend_with_paths(self, path: PathsToNested) -> Self {
468let mut new = PathsToNested::empty();
469for p in path.iter_paths() {
470 new |= self.extend_with(p);
471 }
472new473 }
474475fn iter_paths(self) -> impl Iterator<Item = PathKind> {
476let (PathKind::Inductive477 | PathKind::Unknown478 | PathKind::Coinductive479 | PathKind::ForcedAmbiguity);
480 [PathKind::Inductive, PathKind::Unknown, PathKind::Coinductive, PathKind::ForcedAmbiguity]
481 .into_iter()
482 .filter(move |&p| self.contains(p.into()))
483 }
484}
485486/// The nested goals of each stack entry and the path from the
487/// stack entry to that nested goal.
488///
489/// They are used when checking whether reevaluating a global cache
490/// would encounter a cycle or use a provisional cache entry given the
491/// current search graph state. We need to disable the global cache
492/// in this case as it could otherwise result in behavioral differences.
493/// Cycles can impact behavior. The cycle ABA may have different final
494/// results from a the cycle BAB depending on the cycle root.
495///
496/// We only start tracking nested goals once we've either encountered
497/// overflow or a solver cycle. This is a performance optimization to
498/// avoid tracking nested goals on the happy path.
499#[automatically_derived]
impl<X: Cx> ::core::clone::Clone for NestedGoals<X> where X: Cx {
#[inline]
fn clone(&self) -> Self {
match self {
NestedGoals { nested_goals: ref __field_nested_goals } =>
NestedGoals {
nested_goals: ::core::clone::Clone::clone(__field_nested_goals),
},
}
}
}#[derive_where(Debug, Default, Clone; X: Cx)]500struct NestedGoals<X: Cx> {
501 nested_goals: HashMap<X::Input, PathsToNested>,
502}
503impl<X: Cx> NestedGoals<X> {
504fn is_empty(&self) -> bool {
505self.nested_goals.is_empty()
506 }
507508fn insert(&mut self, input: X::Input, paths_to_nested: PathsToNested) {
509match self.nested_goals.entry(input) {
510 Entry::Occupied(mut entry) => *entry.get_mut() |= paths_to_nested,
511 Entry::Vacant(entry) => drop(entry.insert(paths_to_nested)),
512 }
513 }
514515/// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal
516 /// to the parent goal.
517 ///
518 /// If the path from this goal to the nested goal is inductive, the paths from this goal
519 /// to all nested goals of that nested goal are also inductive. Otherwise the paths are
520 /// the same as for the child.
521fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals<X>) {
522// Each nested goal is updated independently, and `insert` only unions paths for that
523 // goal, so traversal order cannot affect the result.
524#[allow(rustc::potential_query_instability)]
525for (input, paths_to_nested) in nested_goals.iter() {
526let paths_to_nested = paths_to_nested.extend_with(step_kind);
527self.insert(input, paths_to_nested);
528 }
529 }
530531// This helper intentionally exposes unstable hash iteration so each caller must opt in
532 // locally and justify why its traversal is order-insensitive.
533#[cfg_attr(feature = "nightly", rustc_lint_query_instability)]
534 #[allow(rustc::potential_query_instability)]
535fn iter(&self) -> impl Iterator<Item = (X::Input, PathsToNested)> + '_ {
536self.nested_goals.iter().map(|(i, p)| (*i, *p))
537 }
538539fn contains(&self, input: X::Input) -> bool {
540self.nested_goals.contains_key(&input)
541 }
542}
543544/// A provisional result of an already computed goals which depends on other
545/// goals still on the stack.
546#[automatically_derived]
impl<X: Cx> ::core::fmt::Debug for ProvisionalCacheEntry<X> where X: Cx {
fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
match self {
ProvisionalCacheEntry {
encountered_overflow: ref __field_encountered_overflow,
heads: ref __field_heads,
path_from_head: ref __field_path_from_head,
result: ref __field_result } => {
let mut __builder =
::core::fmt::Formatter::debug_struct(__f,
"ProvisionalCacheEntry");
::core::fmt::DebugStruct::field(&mut __builder,
"encountered_overflow", __field_encountered_overflow);
::core::fmt::DebugStruct::field(&mut __builder, "heads",
__field_heads);
::core::fmt::DebugStruct::field(&mut __builder,
"path_from_head", __field_path_from_head);
::core::fmt::DebugStruct::field(&mut __builder, "result",
__field_result);
::core::fmt::DebugStruct::finish(&mut __builder)
}
}
}
}#[derive_where(Debug; X: Cx)]547struct ProvisionalCacheEntry<X: Cx> {
548/// Whether evaluating the goal encountered overflow. This is used to
549 /// disable the cache entry except if the last goal on the stack is
550 /// already involved in this cycle.
551encountered_overflow: bool,
552/// All cycle heads this cache entry depends on.
553heads: CycleHeads,
554/// The path from the highest cycle head to this goal. This differs from
555 /// `heads` which tracks the path to the cycle head *from* this goal.
556path_from_head: PathKind,
557 result: X::Result,
558}
559560/// The final result of evaluating a goal.
561///
562/// We reset `encountered_overflow` when reevaluating a goal,
563/// but need to track whether we've hit the recursion limit at
564/// all for correctness.
565///
566/// We've previously simply returned the final `StackEntry` but this
567/// made it easy to accidentally drop information from the previous
568/// evaluation.
569#[automatically_derived]
impl<X: Cx> ::core::fmt::Debug for EvaluationResult<X> where X: Cx {
fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
match self {
EvaluationResult {
encountered_overflow: ref __field_encountered_overflow,
required_depth: ref __field_required_depth,
heads: ref __field_heads,
nested_goals: ref __field_nested_goals,
result: ref __field_result } => {
let mut __builder =
::core::fmt::Formatter::debug_struct(__f,
"EvaluationResult");
::core::fmt::DebugStruct::field(&mut __builder,
"encountered_overflow", __field_encountered_overflow);
::core::fmt::DebugStruct::field(&mut __builder,
"required_depth", __field_required_depth);
::core::fmt::DebugStruct::field(&mut __builder, "heads",
__field_heads);
::core::fmt::DebugStruct::field(&mut __builder,
"nested_goals", __field_nested_goals);
::core::fmt::DebugStruct::field(&mut __builder, "result",
__field_result);
::core::fmt::DebugStruct::finish(&mut __builder)
}
}
}
}#[derive_where(Debug; X: Cx)]570struct EvaluationResult<X: Cx> {
571 encountered_overflow: bool,
572 required_depth: usize,
573 heads: CycleHeads,
574 nested_goals: NestedGoals<X>,
575 result: X::Result,
576}
577578impl<X: Cx> EvaluationResult<X> {
579fn finalize(
580 final_entry: StackEntry<X>,
581 encountered_overflow: bool,
582 result: X::Result,
583 ) -> EvaluationResult<X> {
584EvaluationResult {
585encountered_overflow,
586// Unlike `encountered_overflow`, we share `heads`, `required_depth`,
587 // and `nested_goals` between evaluations.
588required_depth: final_entry.required_depth(),
589 heads: final_entry.heads,
590 nested_goals: final_entry.nested_goals,
591// We only care about the final result.
592result,
593 }
594 }
595}
596597pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
598 root_depth: AvailableDepth,
599 stack: Stack<X>,
600/// The provisional cache contains entries for already computed goals which
601 /// still depend on goals higher-up in the stack. We don't move them to the
602 /// global cache and track them locally instead. A provisional cache entry
603 /// is only valid until the result of one of its cycle heads changes.
604provisional_cache: HashMap<X::Input, Vec<ProvisionalCacheEntry<X>>>,
605606 _marker: PhantomData<D>,
607}
608609/// While [`SearchGraph::update_parent_goal`] can be mostly shared between
610/// ordinary nested goals/global cache hits and provisional cache hits,
611/// using the provisional cache should not add any nested goals.
612///
613/// `nested_goals` are only used when checking whether global cache entries
614/// are applicable. This only cares about whether a goal is actually accessed.
615/// Given that the usage of the provisional cache is fully deterministic, we
616/// don't need to track the nested goals used while computing a provisional
617/// cache entry.
618enum UpdateParentGoalCtxt<'a, X: Cx> {
619 Ordinary { nested_goals: &'a NestedGoals<X>, min_reachable_available_depth: AvailableDepth },
620 CycleOnStack(X::Input),
621 ProvisionalCacheHit,
622}
623624impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
625pub fn new(root_depth: usize) -> SearchGraph<D> {
626Self {
627 root_depth: AvailableDepth(root_depth),
628 stack: Default::default(),
629 provisional_cache: Default::default(),
630 _marker: PhantomData,
631 }
632 }
633634/// Lazily update the stack entry for the parent goal.
635 /// This behavior is shared between actually evaluating goals
636 /// and using existing global cache entries to make sure they
637 /// have the same impact on the remaining evaluation.
638fn update_parent_goal(
639 stack: &mut Stack<X>,
640 step_kind_from_parent: PathKind,
641 heads: impl Iterator<Item = (StackDepth, CycleHead)>,
642 encountered_overflow: bool,
643 context: UpdateParentGoalCtxt<'_, X>,
644 ) {
645if let Some((parent_index, parent)) = stack.last_mut_with_index() {
646parent.encountered_overflow |= encountered_overflow;
647648for (head_index, head) in heads {
649if let Some(candidate_usages) = &mut parent.candidate_usages {
650 candidate_usages
651 .usages
652 .get_or_insert_default()
653 .entry(head_index)
654 .or_default()
655 .add_usages_from_nested(head.usages);
656 }
657match head_index.cmp(&parent_index) {
658 Ordering::Less => parent.heads.insert(
659 head_index,
660 head.paths_to_head.extend_with(step_kind_from_parent),
661 head.usages,
662 ),
663 Ordering::Equal => {
664 parent.usages.get_or_insert_default().add_usages_from_nested(head.usages);
665 }
666 Ordering::Greater => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
667 }
668 }
669let parent_depends_on_cycle = match context {
670 UpdateParentGoalCtxt::Ordinary { nested_goals, min_reachable_available_depth } => {
671parent.min_reached_available_depth =
672parent.min_reached_available_depth.min(min_reachable_available_depth);
673parent.nested_goals.extend_from_child(step_kind_from_parent, nested_goals);
674 !nested_goals.is_empty()
675 }
676 UpdateParentGoalCtxt::CycleOnStack(head) => {
677// We lookup provisional cache entries before detecting cycles.
678 // We therefore can't use a global cache entry if it contains a cycle
679 // whose head is in the provisional cache.
680parent.nested_goals.insert(head, step_kind_from_parent.into());
681true
682}
683 UpdateParentGoalCtxt::ProvisionalCacheHit => true,
684 };
685// Once we've got goals which encountered overflow or a cycle,
686 // we track all goals whose behavior may depend depend on these
687 // goals as this change may cause them to now depend on additional
688 // goals, resulting in new cycles. See the dev-guide for examples.
689if parent_depends_on_cycle {
690parent.nested_goals.insert(parent.input, PathsToNested::EMPTY);
691 }
692 }
693 }
694695pub fn is_empty(&self) -> bool {
696if self.stack.is_empty() {
697if true {
if !self.provisional_cache.is_empty() {
::core::panicking::panic("assertion failed: self.provisional_cache.is_empty()")
};
};debug_assert!(self.provisional_cache.is_empty());
698true
699} else {
700false
701}
702 }
703704/// The number of goals currently in the search graph. This should only be
705 /// used for debugging purposes.
706pub fn debug_current_depth(&self) -> usize {
707self.stack.len()
708 }
709710/// Whether the path from `head` to the current stack entry is inductive or coinductive.
711 ///
712 /// The `step_kind_to_head` is used to add a single additional path segment to the path on
713 /// the stack which completes the cycle. This given an inductive step AB which then cycles
714 /// coinductively with A, we need to treat this cycle as coinductive.
715fn cycle_path_kind(
716 stack: &Stack<X>,
717 step_kind_to_head: PathKind,
718 head: StackDepth,
719 ) -> PathKind {
720stack.cycle_step_kinds(head).fold(step_kind_to_head, |curr, step| curr.extend(step))
721 }
722723pub fn enter_single_candidate(&mut self) {
724let prev = self.stack.last_mut().unwrap().candidate_usages.replace(Default::default());
725if true {
if !prev.is_none() {
{
::core::panicking::panic_fmt(format_args!("existing candidate_usages: {0:?}",
prev));
}
};
};debug_assert!(prev.is_none(), "existing candidate_usages: {prev:?}");
726 }
727728pub fn finish_single_candidate(&mut self) -> CandidateHeadUsages {
729self.stack.last_mut().unwrap().candidate_usages.take().unwrap()
730 }
731732pub fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) {
733if let Some(usages) = usages.usages {
734let (entry_index, entry) = self.stack.last_mut_with_index().unwrap();
735// Ignoring usages only mutates the state for the current `head_index`, so the
736 // resulting per-head state is unchanged by iteration order.
737#[allow(rustc::potential_query_instability)]
738for (head_index, usages) in usages.into_iter() {
739if head_index == entry_index {
740 entry.usages.unwrap().ignore_usages(usages);
741 } else {
742 entry.heads.ignore_usages(head_index, usages);
743 }
744 }
745 }
746 }
747748pub fn evaluate_root_goal_for_proof_tree(
749 cx: X,
750 root_depth: usize,
751 input: X::Input,
752 inspect: &mut D::ProofTreeBuilder,
753 ) -> X::Result {
754let mut this = SearchGraph::<D>::new(root_depth);
755let available_depth = AvailableDepth(root_depth);
756let step_kind_from_parent = PathKind::Inductive; // is never used
757this.stack.push(StackEntry {
758input,
759step_kind_from_parent,
760available_depth,
761 min_reached_available_depth: available_depth,
762 provisional_result: None,
763 heads: Default::default(),
764 encountered_overflow: false,
765 usages: None,
766 candidate_usages: None,
767 nested_goals: Default::default(),
768 });
769let evaluation_result = this.evaluate_goal_in_task(cx, input, inspect);
770evaluation_result.result
771 }
772773/// Probably the most involved method of the whole solver.
774 ///
775 /// While goals get computed via `D::compute_goal`, this function handles
776 /// caching, overflow, and cycles.
777x;#[instrument(level = "debug", skip(self, cx, inspect), ret)]778pub fn evaluate_goal(
779&mut self,
780 cx: X,
781 input: X::Input,
782 step_kind_from_parent: PathKind,
783 lower_available_depth: LowerAvailableDepth,
784 inspect: &mut D::ProofTreeBuilder,
785 ) -> X::Result {
786let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::<D>(
787self.root_depth,
788&self.stack,
789 lower_available_depth,
790 ) else {
791return self.handle_overflow(cx, input);
792 };
793794// We check the provisional cache before checking the global cache. This simplifies
795 // the implementation as we can avoid worrying about cases where both the global and
796 // provisional cache may apply, e.g. consider the following example
797 //
798 // - xxBA overflow
799 // - A
800 // - BA cycle
801 // - CB :x:
802if let Some(result) = self.lookup_provisional_cache(input, step_kind_from_parent) {
803return result;
804 }
805806// Lookup the global cache unless we're building proof trees or are currently
807 // fuzzing.
808let validate_cache = if !D::inspect_is_noop(inspect) {
809None
810} else if let Some(scope) = D::enter_validation_scope(cx, input) {
811// When validating the global cache we need to track the goals for which the
812 // global cache has been disabled as it may otherwise change the result for
813 // cyclic goals. We don't care about goals which are not on the current stack
814 // so it's fine to drop their scope eagerly.
815self.lookup_global_cache_untracked(cx, input, step_kind_from_parent, available_depth)
816 .inspect(|expected| debug!(?expected, "validate cache entry"))
817 .map(|r| (scope, r))
818 } else if let Some(result) =
819self.lookup_global_cache(cx, input, step_kind_from_parent, available_depth)
820 {
821return result;
822 } else {
823None
824};
825826// Detect cycles on the stack. We do this after the global cache lookup to
827 // avoid iterating over the stack in case a goal has already been computed.
828 // This may not have an actual performance impact and we could reorder them
829 // as it may reduce the number of `nested_goals` we need to track.
830if let Some(result) = self.check_cycle_on_stack(cx, input, step_kind_from_parent) {
831debug_assert!(validate_cache.is_none(), "global cache and cycle on stack: {input:?}");
832return result;
833 }
834835// Unfortunate, it looks like we actually have to compute this goal.
836self.stack.push(StackEntry {
837 input,
838 step_kind_from_parent,
839 available_depth,
840 provisional_result: None,
841 min_reached_available_depth: available_depth,
842 heads: Default::default(),
843 encountered_overflow: false,
844 usages: None,
845 candidate_usages: None,
846 nested_goals: Default::default(),
847 });
848849// This is for global caching, so we properly track query dependencies.
850 // Everything that affects the `result` should be performed within this
851 // `with_cached_task` closure. If computing this goal depends on something
852 // not tracked by the cache key and from outside of this anon task, it
853 // must not be added to the global cache. Notably, this is the case for
854 // trait solver cycles participants.
855let (evaluation_result, dep_node) =
856 cx.with_cached_task(|| self.evaluate_goal_in_task(cx, input, inspect));
857858// We've finished computing the goal and have popped it from the stack,
859 // lazily update its parent goal.
860Self::update_parent_goal(
861&mut self.stack,
862 step_kind_from_parent,
863 evaluation_result.heads.iter(),
864 evaluation_result.encountered_overflow,
865 UpdateParentGoalCtxt::Ordinary {
866 nested_goals: &evaluation_result.nested_goals,
867 min_reachable_available_depth: AvailableDepth(
868 available_depth.0 - evaluation_result.required_depth,
869 ),
870 },
871 );
872let result = evaluation_result.result;
873874// We're now done with this goal. We only add the root of cycles to the global cache.
875 // In case this goal is involved in a larger cycle add it to the provisional cache.
876if evaluation_result.heads.is_empty() {
877if let Some((_scope, expected)) = validate_cache {
878// Do not try to move a goal into the cache again if we're testing
879 // the global cache.
880assert_eq!(expected, result, "input={input:?}");
881 } else if D::inspect_is_noop(inspect) {
882self.insert_global_cache(cx, input, evaluation_result, dep_node)
883 }
884 } else if D::ENABLE_PROVISIONAL_CACHE {
885debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}");
886let entry = self.provisional_cache.entry(input).or_default();
887let EvaluationResult {
888 encountered_overflow,
889 required_depth: _,
890 heads,
891 nested_goals: _,
892 result,
893 } = evaluation_result;
894let path_from_head = Self::cycle_path_kind(
895&self.stack,
896 step_kind_from_parent,
897 heads.highest_cycle_head_index(),
898 );
899let provisional_cache_entry =
900 ProvisionalCacheEntry { encountered_overflow, heads, path_from_head, result };
901debug!(?provisional_cache_entry);
902 entry.push(provisional_cache_entry);
903 } else {
904debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}");
905 }
906907 result
908 }
909910fn handle_overflow(&mut self, cx: X, input: X::Input) -> X::Result {
911if let Some(last) = self.stack.last_mut() {
912last.encountered_overflow = true;
913// If computing a goal `B` depends on another goal `A` and
914 // `A` has a nested goal which overflows, then computing `B`
915 // at the same depth, but with `A` already on the stack,
916 // would encounter a solver cycle instead, potentially
917 // changing the result.
918 //
919 // We must therefore not use the global cache entry for `B` in that case.
920 // See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs
921last.nested_goals.insert(last.input, PathsToNested::EMPTY);
922 }
923924{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:924",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(924u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("encountered stack overflow")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("encountered stack overflow");
925 D::stack_overflow_result(cx, input)
926 }
927928/// When reevaluating a goal with a changed provisional result, all provisional cache entry
929 /// which depend on this goal get invalidated.
930 ///
931 /// Note that we keep provisional cache entries which accessed this goal as a cycle head, but
932 /// don't depend on its value.
933fn clear_dependent_provisional_results_for_rerun(&mut self) {
934let rerun_index = self.stack.next_index();
935// Each cached entry is filtered independently based on whether it depends on
936 // `rerun_index`, so bucket traversal order does not matter.
937#[allow(rustc::potential_query_instability)]
938self.provisional_cache.retain(|_, entries| {
939entries.retain(|entry| {
940let (head_index, head) = entry.heads.highest_cycle_head();
941head_index != rerun_index || head.usages.is_empty()
942 });
943 !entries.is_empty()
944 });
945 }
946}
947948/// We need to rebase provisional cache entries when popping one of their cycle
949/// heads from the stack. This may not necessarily mean that we've actually
950/// reached a fixpoint for that cycle head, which impacts the way we rebase
951/// provisional cache entries.
952#[automatically_derived]
impl<X: Cx> ::core::fmt::Debug for RebaseReason<X> where X: Cx {
fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
match self {
RebaseReason::NoCycleUsages =>
::core::fmt::Formatter::write_str(__f, "NoCycleUsages"),
RebaseReason::Ambiguity(ref __field_0) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f, "Ambiguity");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RebaseReason::ReachedFixpoint(ref __field_0) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f, "ReachedFixpoint");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::finish(&mut __builder)
}
}
}
}#[derive_where(Debug; X: Cx)]953enum RebaseReason<X: Cx> {
954 NoCycleUsages,
955 Ambiguity(X::AmbiguityKind),
956/// We've actually reached a fixpoint.
957 ///
958 /// This either happens in the first evaluation step for the cycle head.
959 /// In this case the used provisional result depends on the cycle `PathKind`.
960 /// We store this path kind to check whether the provisional cache entry
961 /// we're rebasing relied on the same cycles.
962 ///
963 /// In later iterations cycles always return `stack_entry.provisional_result`
964 /// so we no longer depend on the `PathKind`. We store `None` in that case.
965ReachedFixpoint(Option<PathKind>),
966}
967968impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
969/// A necessary optimization to handle complex solver cycles. A provisional cache entry
970 /// relies on a set of cycle heads and the path towards these heads. When popping a cycle
971 /// head from the stack after we've finished computing it, we can't be sure that the
972 /// provisional cache entry is still applicable. We need to keep the cache entries to
973 /// prevent hangs.
974 ///
975 /// This can be thought of as pretending to reevaluate the popped head as nested goals
976 /// of this provisional result. For this to be correct, all cycles encountered while
977 /// we'd reevaluate the cycle head as a nested goal must keep the same cycle kind.
978 /// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html).
979 ///
980 /// In case the popped cycle head failed to reach a fixpoint anything which depends on
981 /// its provisional result is invalid. Actually discarding provisional cache entries in
982 /// this case would cause hangs, so we instead change the result of dependant provisional
983 /// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested
984 /// goals whose result doesn't actually depend on this cycle head, but that's acceptable
985 /// to me.
986#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("rebase_provisional_cache_entries",
"rustc_type_ir::search_graph", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(986u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("stack_entry")
}> =
::tracing::__macro_support::FieldName::new("stack_entry");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("rebase_reason")
}> =
::tracing::__macro_support::FieldName::new("rebase_reason");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&stack_entry)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&rebase_reason)
as &dyn ::tracing::field::Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
let popped_head_index = self.stack.next_index();
#[allow(rustc::potential_query_instability)]
self.provisional_cache.retain(|&input, entries|
{
entries.retain_mut(|entry|
{
let ProvisionalCacheEntry {
encountered_overflow: _, heads, path_from_head, result } =
entry;
let popped_head =
if heads.highest_cycle_head_index() == popped_head_index {
heads.remove_highest_cycle_head()
} else {
if true {
if !(heads.highest_cycle_head_index() < popped_head_index) {
::core::panicking::panic("assertion failed: heads.highest_cycle_head_index() < popped_head_index")
};
};
return true;
};
if popped_head.usages.is_empty() {
for (head_index, _) in stack_entry.heads.iter() {
heads.insert(head_index, PathsToNested::EMPTY,
HeadUsages::default());
}
} else {
let ep = popped_head.paths_to_head;
for (head_index, head) in stack_entry.heads.iter() {
let ph = head.paths_to_head;
let hp =
Self::cycle_path_kind(&self.stack,
stack_entry.step_kind_from_parent, head_index);
let he = hp.extend(*path_from_head);
for ph in ph.iter_paths() {
let hph = hp.extend(ph);
for ep in ep.iter_paths() {
let hep = ep.extend(he);
let heph = hep.extend(ph);
if hph != heph { return false; }
}
}
let eph = ep.extend_with_paths(ph);
heads.insert(head_index, eph, head.usages);
}
match rebase_reason {
RebaseReason::NoCycleUsages => return false,
RebaseReason::Ambiguity(kind) => {
if !D::is_ambiguous_result(*result).is_some_and(|k|
k == kind) {
return false;
}
}
RebaseReason::ReachedFixpoint(None) => {}
RebaseReason::ReachedFixpoint(Some(path_kind)) => {
if !popped_head.usages.is_single(path_kind) {
return false;
}
}
};
}
let Some(new_highest_head_index) =
heads.opt_highest_cycle_head_index() else { return false; };
*path_from_head =
path_from_head.extend(Self::cycle_path_kind(&self.stack,
stack_entry.step_kind_from_parent, new_highest_head_index));
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1100",
"rustc_type_ir::search_graph", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1100u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("input")
}> =
::tracing::__macro_support::FieldName::new("input");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("entry")
}> =
::tracing::__macro_support::FieldName::new("entry");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("rebased provisional cache entry")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&input)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&entry)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};
true
});
!entries.is_empty()
});
}
}
}#[instrument(level = "trace", skip(self))]987fn rebase_provisional_cache_entries(
988&mut self,
989 stack_entry: &StackEntry<X>,
990 rebase_reason: RebaseReason<X>,
991 ) {
992let popped_head_index = self.stack.next_index();
993// Rebasing decisions depend only on each provisional entry and the current stack state,
994 // so traversing the cache in hash order cannot change the final cache contents.
995#[allow(rustc::potential_query_instability)]
996self.provisional_cache.retain(|&input, entries| {
997 entries.retain_mut(|entry| {
998let ProvisionalCacheEntry {
999 encountered_overflow: _,
1000 heads,
1001 path_from_head,
1002 result,
1003 } = entry;
1004let popped_head = if heads.highest_cycle_head_index() == popped_head_index {
1005 heads.remove_highest_cycle_head()
1006 } else {
1007debug_assert!(heads.highest_cycle_head_index() < popped_head_index);
1008return true;
1009 };
10101011// We're rebasing an entry `e` over a head `p`. This head
1012 // has a number of own heads `h` it depends on.
1013 //
1014 // This causes our provisional result to depend on the heads
1015 // of `p` to avoid moving any goal which uses this cache entry to
1016 // the global cache.
1017if popped_head.usages.is_empty() {
1018// The result of `e` does not depend on the value of `p`. This we can
1019 // keep using the result of this provisional cache entry even if evaluating
1020 // `p` as a nested goal of `e` would have a different result.
1021for (head_index, _) in stack_entry.heads.iter() {
1022 heads.insert(head_index, PathsToNested::EMPTY, HeadUsages::default());
1023 }
1024 } else {
1025// The entry `e` actually depends on the value of `p`. We need
1026 // to make sure that the value of `p` wouldn't change even if we
1027 // were to reevaluate it as a nested goal of `e` instead. For this
1028 // we check that the path kind of all paths `hph` remain the
1029 // same after rebasing.
1030 //
1031 // After rebasing the cycles `hph` will go through `e`. We need to make
1032 // sure that forall possible paths `hep`, `heph` is equal to `hph.`
1033let ep = popped_head.paths_to_head;
1034for (head_index, head) in stack_entry.heads.iter() {
1035let ph = head.paths_to_head;
1036let hp = Self::cycle_path_kind(
1037&self.stack,
1038 stack_entry.step_kind_from_parent,
1039 head_index,
1040 );
1041// We first validate that all cycles while computing `p` would stay
1042 // the same if we were to recompute it as a nested goal of `e`.
1043let he = hp.extend(*path_from_head);
1044for ph in ph.iter_paths() {
1045let hph = hp.extend(ph);
1046for ep in ep.iter_paths() {
1047let hep = ep.extend(he);
1048let heph = hep.extend(ph);
1049if hph != heph {
1050return false;
1051 }
1052 }
1053 }
10541055// If so, all paths reached while computing `p` have to get added
1056 // the heads of `e` to make sure that rebasing `e` again also considers
1057 // them.
1058let eph = ep.extend_with_paths(ph);
1059 heads.insert(head_index, eph, head.usages);
1060 }
10611062// The provisional cache entry does depend on the provisional result
1063 // of the popped cycle head. In case we didn't actually reach a fixpoint,
1064 // we must not keep potentially incorrect provisional cache entries around.
1065match rebase_reason {
1066// If the cycle head does not actually depend on itself, then
1067 // the provisional result used by the provisional cache entry
1068 // is not actually equal to the final provisional result. We
1069 // need to discard the provisional cache entry in this case.
1070RebaseReason::NoCycleUsages => return false,
1071// If we avoid rerunning a goal due to ambiguity, we only keep provisional
1072 // results which depend on that cycle head if these are already ambiguous
1073 // themselves.
1074RebaseReason::Ambiguity(kind) => {
1075if !D::is_ambiguous_result(*result).is_some_and(|k| k == kind) {
1076return false;
1077 }
1078 }
1079 RebaseReason::ReachedFixpoint(None) => {}
1080 RebaseReason::ReachedFixpoint(Some(path_kind)) => {
1081if !popped_head.usages.is_single(path_kind) {
1082return false;
1083 }
1084 }
1085 };
1086 }
10871088let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else {
1089return false;
1090 };
10911092// We now care about the path from the next highest cycle head to the
1093 // provisional cache entry.
1094*path_from_head = path_from_head.extend(Self::cycle_path_kind(
1095&self.stack,
1096 stack_entry.step_kind_from_parent,
1097 new_highest_head_index,
1098 ));
10991100trace!(?input, ?entry, "rebased provisional cache entry");
11011102true
1103});
1104 !entries.is_empty()
1105 });
1106 }
11071108fn lookup_provisional_cache(
1109&mut self,
1110 input: X::Input,
1111 step_kind_from_parent: PathKind,
1112 ) -> Option<X::Result> {
1113if !D::ENABLE_PROVISIONAL_CACHE {
1114return None;
1115 }
11161117let entries = self.provisional_cache.get(&input)?;
1118for &ProvisionalCacheEntry { encountered_overflow, ref heads, path_from_head, result } in
1119entries
1120 {
1121let head_index = heads.highest_cycle_head_index();
1122if encountered_overflow {
1123// This check is overly strict and very subtle. We need to make sure that if
1124 // a global cache entry depends on some goal without adding it to its
1125 // `nested_goals`, that goal must never have an applicable provisional
1126 // cache entry to avoid incorrectly applying the cache entry.
1127 //
1128 // As we'd have to otherwise track literally all nested goals, we only
1129 // apply provisional cache entries which encountered overflow once the
1130 // current goal is already part of the same cycle. This check could be
1131 // improved but seems to be good enough for now.
1132let last = self.stack.last().unwrap();
1133if last.heads.opt_lowest_cycle_head_index().is_none_or(|lowest| lowest > head_index)
1134 {
1135continue;
1136 }
1137 }
11381139// A provisional cache entry is only valid if the current path from its
1140 // highest cycle head to the goal is the same.
1141if path_from_head
1142 == Self::cycle_path_kind(&self.stack, step_kind_from_parent, head_index)
1143 {
1144Self::update_parent_goal(
1145&mut self.stack,
1146 step_kind_from_parent,
1147 heads.iter(),
1148 encountered_overflow,
1149 UpdateParentGoalCtxt::ProvisionalCacheHit,
1150 );
1151{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1151",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1151u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("head_index")
}> =
::tracing::__macro_support::FieldName::new("head_index");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("path_from_head")
}> =
::tracing::__macro_support::FieldName::new("path_from_head");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("provisional cache hit")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&head_index)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&path_from_head)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?head_index, ?path_from_head, "provisional cache hit");
1152return Some(result);
1153 }
1154 }
11551156None1157 }
11581159/// Even if there is a global cache entry for a given goal, we need to make sure
1160 /// evaluating this entry would not have ended up depending on either a goal
1161 /// already on the stack or a provisional cache entry.
1162fn candidate_is_applicable(
1163&self,
1164 step_kind_from_parent: PathKind,
1165 nested_goals: &NestedGoals<X>,
1166 ) -> bool {
1167// If the global cache entry didn't depend on any nested goals, it always
1168 // applies.
1169if nested_goals.is_empty() {
1170return true;
1171 }
11721173// If a nested goal of the global cache entry is on the stack, we would
1174 // definitely encounter a cycle.
1175if self.stack.iter().any(|e| nested_goals.contains(e.input)) {
1176{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1176",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1176u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("cache entry not applicable due to stack")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("cache entry not applicable due to stack");
1177return false;
1178 }
11791180// The global cache entry is also invalid if there's a provisional cache entry
1181 // would apply for any of its nested goals.
1182 // Any matching provisional entry rejects the candidate,
1183 // so iteration order only affects when we return `false`, not the final answer.
1184#[allow(rustc::potential_query_instability)]
1185for (input, path_from_global_entry) in nested_goals.iter() {
1186let Some(entries) = self.provisional_cache.get(&input) else {
1187continue;
1188 };
11891190{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1190",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1190u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("input")
}> =
::tracing::__macro_support::FieldName::new("input");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("path_from_global_entry")
}> =
::tracing::__macro_support::FieldName::new("path_from_global_entry");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("entries")
}> =
::tracing::__macro_support::FieldName::new("entries");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("candidate_is_applicable")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&input)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&path_from_global_entry)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&entries)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?input, ?path_from_global_entry, ?entries, "candidate_is_applicable");
1191// A provisional cache entry is applicable if the path to
1192 // its highest cycle head is equal to the expected path.
1193for &ProvisionalCacheEntry {
1194 encountered_overflow,
1195ref heads,
1196 path_from_head: head_to_provisional,
1197 result: _,
1198 } in entries.iter()
1199 {
1200// We don't have to worry about provisional cache entries which encountered
1201 // overflow, see the relevant comment in `lookup_provisional_cache`.
1202if encountered_overflow {
1203continue;
1204 }
12051206// A provisional cache entry only applies if the path from its highest head
1207 // matches the path when encountering the goal.
1208 //
1209 // We check if any of the paths taken while computing the global goal
1210 // would end up with an applicable provisional cache entry.
1211let head_index = heads.highest_cycle_head_index();
1212let head_to_curr =
1213Self::cycle_path_kind(&self.stack, step_kind_from_parent, head_index);
1214let full_paths = path_from_global_entry.extend_with(head_to_curr);
1215if full_paths.contains(head_to_provisional.into()) {
1216{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1216",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1216u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("full_paths")
}> =
::tracing::__macro_support::FieldName::new("full_paths");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("head_to_provisional")
}> =
::tracing::__macro_support::FieldName::new("head_to_provisional");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("cache entry not applicable due to matching paths")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&full_paths)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&head_to_provisional)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(
1217?full_paths,
1218?head_to_provisional,
1219"cache entry not applicable due to matching paths"
1220);
1221return false;
1222 }
1223 }
1224 }
12251226true
1227}
12281229/// Used when fuzzing the global cache. Accesses the global cache without
1230 /// updating the state of the search graph.
1231fn lookup_global_cache_untracked(
1232&self,
1233 cx: X,
1234 input: X::Input,
1235 step_kind_from_parent: PathKind,
1236 available_depth: AvailableDepth,
1237 ) -> Option<X::Result> {
1238cx.with_global_cache(|cache| {
1239cache1240 .get(cx, input, available_depth, |nested_goals| {
1241self.candidate_is_applicable(step_kind_from_parent, nested_goals)
1242 })
1243 .map(|c| c.result)
1244 })
1245 }
12461247/// Try to fetch a previously computed result from the global cache,
1248 /// making sure to only do so if it would match the result of reevaluating
1249 /// this goal.
1250fn lookup_global_cache(
1251&mut self,
1252 cx: X,
1253 input: X::Input,
1254 step_kind_from_parent: PathKind,
1255 available_depth: AvailableDepth,
1256 ) -> Option<X::Result> {
1257cx.with_global_cache(|cache| {
1258let CacheData { result, required_depth, encountered_overflow, nested_goals } = cache
1259 .get(cx, input, available_depth, |nested_goals| {
1260self.candidate_is_applicable(step_kind_from_parent, nested_goals)
1261 })?;
12621263// We don't move cycle participants to the global cache, so the
1264 // cycle heads are always empty.
1265let heads = iter::empty();
1266Self::update_parent_goal(
1267&mut self.stack,
1268step_kind_from_parent,
1269heads,
1270encountered_overflow,
1271 UpdateParentGoalCtxt::Ordinary {
1272nested_goals,
1273 min_reachable_available_depth: AvailableDepth(
1274available_depth.0 - required_depth,
1275 ),
1276 },
1277 );
12781279{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1279",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1279u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("required_depth")
}> =
::tracing::__macro_support::FieldName::new("required_depth");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("global cache hit")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&required_depth)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?required_depth, "global cache hit");
1280Some(result)
1281 })
1282 }
12831284fn check_cycle_on_stack(
1285&mut self,
1286 cx: X,
1287 input: X::Input,
1288 step_kind_from_parent: PathKind,
1289 ) -> Option<X::Result> {
1290let head_index = self.stack.find(input)?;
1291// We have a nested goal which directly relies on a goal deeper in the stack.
1292 //
1293 // We start by tagging all cycle participants, as that's necessary for caching.
1294 //
1295 // Finally we can return either the provisional response or the initial response
1296 // in case we're in the first fixpoint iteration for this goal.
1297let path_kind = Self::cycle_path_kind(&self.stack, step_kind_from_parent, head_index);
1298{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1298",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1298u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("path_kind")
}> =
::tracing::__macro_support::FieldName::new("path_kind");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("encountered cycle with depth {0:?}",
head_index) as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&path_kind)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?path_kind, "encountered cycle with depth {head_index:?}");
1299let mut usages = HeadUsages::default();
1300usages.add_usage(path_kind);
1301let head = CycleHead { paths_to_head: step_kind_from_parent.into(), usages };
1302Self::update_parent_goal(
1303&mut self.stack,
1304step_kind_from_parent,
1305 iter::once((head_index, head)),
1306false,
1307 UpdateParentGoalCtxt::CycleOnStack(input),
1308 );
13091310// Return the provisional result or, if we're in the first iteration,
1311 // start with no constraints.
1312if let Some(result) = self.stack[head_index].provisional_result {
1313Some(result)
1314 } else {
1315Some(D::initial_provisional_result(cx, path_kind, input))
1316 }
1317 }
13181319/// Whether we've reached a fixpoint when evaluating a cycle head.
1320x;#[instrument(level = "trace", skip(self, stack_entry), ret)]1321fn reached_fixpoint(
1322&mut self,
1323 stack_entry: &StackEntry<X>,
1324 usages: HeadUsages,
1325 result: X::Result,
1326 ) -> Result<Option<PathKind>, ()> {
1327let provisional_result = stack_entry.provisional_result;
1328if let Some(provisional_result) = provisional_result {
1329if provisional_result == result { Ok(None) } else { Err(()) }
1330 } else if let Some(path_kind) = D::is_initial_provisional_result(result)
1331 .filter(|&path_kind| usages.is_single(path_kind))
1332 {
1333Ok(Some(path_kind))
1334 } else {
1335Err(())
1336 }
1337 }
13381339/// When we encounter a coinductive cycle, we have to fetch the
1340 /// result of that cycle while we are still computing it. Because
1341 /// of this we continuously recompute the cycle until the result
1342 /// of the previous iteration is equal to the final result, at which
1343 /// point we are done.
1344fn evaluate_goal_in_task(
1345&mut self,
1346 cx: X,
1347 input: X::Input,
1348 inspect: &mut D::ProofTreeBuilder,
1349 ) -> EvaluationResult<X> {
1350// We reset `encountered_overflow` each time we rerun this goal
1351 // but need to make sure we currently propagate it to the global
1352 // cache even if only some of the evaluations actually reach the
1353 // recursion limit.
1354let mut encountered_overflow = false;
1355let mut i = 0;
1356loop {
1357let result = D::compute_goal(self, cx, input, inspect);
1358let stack_entry = self.stack.pop();
1359encountered_overflow |= stack_entry.encountered_overflow;
1360if true {
{
match (&stack_entry.input, &input) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(stack_entry.input, input);
13611362// If the current goal is not a cycle head, we are done.
1363 //
1364 // There are no provisional cache entries which depend on this goal.
1365let Some(usages) = stack_entry.usages else {
1366return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
1367 };
13681369// If it is a cycle head, we have to keep trying to prove it until
1370 // we reach a fixpoint. We need to do so for all cycle heads,
1371 // not only for the root.
1372 //
1373 // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
1374 // for an example.
1375 //
1376 // Check whether we reached a fixpoint, either because the final result
1377 // is equal to the provisional result of the previous iteration, or because
1378 // this was only the head of either coinductive or inductive cycles, and the
1379 // final result is equal to the initial response for that case.
1380if let Ok(fixpoint) = self.reached_fixpoint(&stack_entry, usages, result) {
1381self.rebase_provisional_cache_entries(
1382&stack_entry,
1383 RebaseReason::ReachedFixpoint(fixpoint),
1384 );
1385return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
1386 } else if usages.is_empty() {
1387self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::NoCycleUsages);
1388return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
1389 }
13901391// If computing this goal results in ambiguity with no constraints,
1392 // we do not rerun it. It's incredibly difficult to get a different
1393 // response in the next iteration in this case. These changes would
1394 // likely either be caused by incompleteness or can change the maybe
1395 // cause from ambiguity to overflow. Returning ambiguity always
1396 // preserves soundness and completeness even if the goal could
1397 // otherwise succeed or fail.
1398 //
1399 // This prevents exponential blowup affecting multiple major crates.
1400 // As we only get to this branch if we haven't yet reached a fixpoint,
1401 // we also taint all provisional cache entries which depend on the
1402 // current goal.
1403if let Some(kind) = D::is_ambiguous_result(result) {
1404self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::Ambiguity(kind));
1405return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
1406 };
14071408// If we've reached the fixpoint step limit, we bail with overflow and taint all
1409 // provisional cache entries which depend on the current goal.
1410i += 1;
1411if i >= D::FIXPOINT_STEP_LIMIT {
1412{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1412",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1412u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("canonical cycle overflow")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("canonical cycle overflow");
1413let result = D::fixpoint_overflow_result(cx, input);
1414self.rebase_provisional_cache_entries(
1415&stack_entry,
1416 RebaseReason::Ambiguity(D::FIXPOINT_OVERFLOW_AMBIGUITY_KIND),
1417 );
1418return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
1419 }
14201421// Clear all provisional cache entries which depend on a previous provisional
1422 // result of this goal and rerun. This does not remove goals which accessed this
1423 // goal without depending on its result.
1424self.clear_dependent_provisional_results_for_rerun();
14251426{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1426",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1426u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("result")
}> =
::tracing::__macro_support::FieldName::new("result");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("fixpoint changed provisional results")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&result)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?result, "fixpoint changed provisional results");
1427self.stack.push(StackEntry {
1428input,
1429 step_kind_from_parent: stack_entry.step_kind_from_parent,
1430 available_depth: stack_entry.available_depth,
1431 provisional_result: Some(result),
1432// We can keep these goals from previous iterations as they are only
1433 // ever read after finalizing this evaluation.
1434min_reached_available_depth: stack_entry.min_reached_available_depth,
1435 heads: stack_entry.heads,
1436 nested_goals: stack_entry.nested_goals,
1437// We reset these two fields when rerunning this goal. We could
1438 // keep `encountered_overflow` as it's only used as a performance
1439 // optimization. However, given that the proof tree will likely look
1440 // similar to the previous iterations when reevaluating, it's better
1441 // for caching if the reevaluation also starts out with `false`.
1442encountered_overflow: false,
1443// We keep provisional cache entries around if they used this goal
1444 // without depending on its result.
1445 //
1446 // We still need to drop or rebase these cache entries once we've
1447 // finished evaluating this goal.
1448usages: Some(HeadUsages::default()),
1449 candidate_usages: None,
1450 });
1451 }
1452 }
14531454/// When encountering a cycle, both inductive and coinductive, we only
1455 /// move the root into the global cache. We also store all other cycle
1456 /// participants involved.
1457 ///
1458 /// We must not use the global cache entry of a root goal if a cycle
1459 /// participant is on the stack. This is necessary to prevent unstable
1460 /// results. See the comment of `StackEntry::nested_goals` for
1461 /// more details.
1462fn insert_global_cache(
1463&mut self,
1464 cx: X,
1465 input: X::Input,
1466 evaluation_result: EvaluationResult<X>,
1467 dep_node: X::DepNodeIndex,
1468 ) {
1469{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/search_graph/mod.rs:1469",
"rustc_type_ir::search_graph", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/search_graph/mod.rs"),
::tracing_core::__macro_support::Option::Some(1469u32),
::tracing_core::__macro_support::Option::Some("rustc_type_ir::search_graph"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("evaluation_result")
}> =
::tracing::__macro_support::FieldName::new("evaluation_result");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("insert global cache")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&evaluation_result)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?evaluation_result, "insert global cache");
1470cx.with_global_cache(|cache| cache.insert(cx, input, evaluation_result, dep_node))
1471 }
1472}