1use std::io::Write;
2use std::ops::ControlFlow;
3use std::sync::Arc;
4use std::{iter, mem};
5
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_errors::{Diag, DiagCtxtHandle};
8use rustc_hir::def::DefKind;
9use rustc_middle::queries::TaggedQueryKey;
10use rustc_middle::query::{Cycle, QueryJob, QueryJobId, QueryLatch, QueryStackFrame, QueryWaiter};
11use rustc_middle::ty::TyCtxt;
12use rustc_span::{DUMMY_SP, Span};
13
14use crate::{CollectActiveJobsKind, collect_active_query_jobs};
15
16#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for QueryJobMap<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f, "QueryJobMap",
"map", &&self.map)
}
}Debug, #[automatically_derived]
impl<'tcx> ::core::default::Default for QueryJobMap<'tcx> {
#[inline]
fn default() -> QueryJobMap<'tcx> {
QueryJobMap { map: ::core::default::Default::default() }
}
}Default)]
19pub struct QueryJobMap<'tcx> {
20 map: FxHashMap<QueryJobId, QueryJobInfo<'tcx>>,
21}
22
23impl<'tcx> QueryJobMap<'tcx> {
24 pub(crate) fn insert(&mut self, id: QueryJobId, info: QueryJobInfo<'tcx>) {
28 self.map.insert(id, info);
29 }
30
31 fn tagged_key_of(&self, id: QueryJobId) -> TaggedQueryKey<'tcx> {
32 self.map[&id].tagged_key
33 }
34
35 fn span_of(&self, id: QueryJobId) -> Span {
36 self.map[&id].job.span
37 }
38
39 fn parent_of(&self, id: QueryJobId) -> Option<QueryJobId> {
40 self.map[&id].job.parent
41 }
42
43 fn latch_of(&self, id: QueryJobId) -> Option<&QueryLatch<'tcx>> {
44 self.map[&id].job.latch.as_ref()
45 }
46}
47
48#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for QueryJobInfo<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "QueryJobInfo",
"tagged_key", &self.tagged_key, "job", &&self.job)
}
}Debug)]
49pub(crate) struct QueryJobInfo<'tcx> {
50 pub(crate) tagged_key: TaggedQueryKey<'tcx>,
51 pub(crate) job: QueryJob<'tcx>,
52}
53
54pub(crate) fn find_cycle_in_stack<'tcx>(
55 id: QueryJobId,
56 job_map: QueryJobMap<'tcx>,
57 current_job: &Option<QueryJobId>,
58 span: Span,
59) -> Cycle<'tcx> {
60 let mut frames = Vec::new();
62 let mut current_job = Option::clone(current_job);
63
64 while let Some(job) = current_job {
65 let info = &job_map.map[&job];
66 frames.push(QueryStackFrame { span: info.job.span, tagged_key: info.tagged_key });
67
68 if job == id {
69 frames.reverse();
70
71 frames[0].span = span;
75 let usage = try {
77 let parent = info.job.parent?;
78 QueryStackFrame { span: info.job.span, tagged_key: job_map.tagged_key_of(parent) }
79 };
80 return Cycle { usage, frames };
81 }
82
83 current_job = info.job.parent;
84 }
85
86 { ::core::panicking::panic_fmt(format_args!("did not find a cycle")); }panic!("did not find a cycle")
87}
88
89#[cold]
92#[inline(never)]
93pub(crate) fn find_dep_kind_root<'tcx>(
94 tcx: TyCtxt<'tcx>,
95 id: QueryJobId,
96 job_map: QueryJobMap<'tcx>,
97) -> (Span, String, usize) {
98 let mut depth = 1;
99 let mut info = &job_map.map[&id];
100 let expected_query = mem::discriminant::<TaggedQueryKey<'tcx>>(&info.tagged_key);
103 let mut last_info = info;
104
105 while let Some(id) = info.job.parent {
106 info = &job_map.map[&id];
107 if mem::discriminant(&info.tagged_key) == expected_query {
108 depth += 1;
109 last_info = info;
110 }
111 }
112 (last_info.job.span, last_info.tagged_key.description(tcx), depth)
113}
114
115type ResumableWaiterLocation = (QueryJobId, usize);
118
119struct AbstractedWaiter {
122 span: Span,
124 parent: Option<QueryJobId>,
126 resumable: Option<ResumableWaiterLocation>,
127}
128
129fn abstracted_waiters_of(job_map: &QueryJobMap<'_>, query: QueryJobId) -> Vec<AbstractedWaiter> {
132 let mut result = Vec::new();
133
134 result.push(AbstractedWaiter {
136 span: job_map.span_of(query),
137 parent: job_map.parent_of(query),
138 resumable: None,
139 });
140
141 if let Some(latch) = job_map.latch_of(query) {
143 for (i, waiter) in latch.waiters.lock().as_ref().unwrap().iter().enumerate() {
144 result.push(AbstractedWaiter {
145 span: waiter.span,
146 parent: waiter.parent,
147 resumable: Some((query, i)),
148 });
149 }
150 }
151
152 result
153}
154
155fn find_cycle<'tcx>(
160 job_map: &QueryJobMap<'tcx>,
161 query: QueryJobId,
162 span: Span,
163 stack: &mut Vec<(Span, QueryJobId)>,
164 visited: &mut FxHashSet<QueryJobId>,
165) -> ControlFlow<Option<ResumableWaiterLocation>> {
166 if !visited.insert(query) {
167 return if let Some(pos) = stack.iter().position(|q| q.1 == query) {
168 stack.drain(0..pos);
172 stack[0].0 = span;
174 ControlFlow::Break(None)
175 } else {
176 ControlFlow::Continue(())
177 };
178 }
179
180 stack.push((span, query));
182
183 for abstracted_waiter in abstracted_waiters_of(job_map, query) {
185 let Some(parent) = abstracted_waiter.parent else {
186 continue;
188 };
189 if let ControlFlow::Break(maybe_resumable) =
190 find_cycle(job_map, parent, abstracted_waiter.span, stack, visited)
191 {
192 return ControlFlow::Break(abstracted_waiter.resumable.or(maybe_resumable));
194 }
195 }
196
197 stack.pop();
199
200 ControlFlow::Continue(())
201}
202
203fn connected_to_root<'tcx>(
207 job_map: &QueryJobMap<'tcx>,
208 query: QueryJobId,
209 visited: &mut FxHashSet<QueryJobId>,
210) -> bool {
211 if !visited.insert(query) {
213 return false;
214 }
215
216 for abstracted_waiter in abstracted_waiters_of(job_map, query) {
218 match abstracted_waiter.parent {
219 None => return true,
221 Some(parent) => {
222 if connected_to_root(job_map, parent, visited) {
223 return true;
224 }
225 }
226 }
227 }
228
229 false
230}
231
232fn process_cycle<'tcx>(job_map: &QueryJobMap<'tcx>, stack: Vec<(Span, QueryJobId)>) -> Cycle<'tcx> {
234 let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip();
237
238 spans.rotate_right(1);
240
241 let mut stack: Vec<_> = iter::zip(spans, queries).collect();
243
244 struct EntryPoint {
245 query_in_cycle: QueryJobId,
246 query_waiting_on_cycle: Option<(Span, QueryJobId)>,
247 }
248
249 let entry_points = stack
252 .iter()
253 .filter_map(|&(_, query_in_cycle)| {
254 let mut entrypoint = false;
255 let mut query_waiting_on_cycle = None;
256
257 for abstracted_waiter in abstracted_waiters_of(job_map, query_in_cycle) {
259 let Some(parent) = abstracted_waiter.parent else {
260 entrypoint = true;
262 continue;
263 };
264
265 let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
268
269 if connected_to_root(job_map, parent, &mut visited) {
270 query_waiting_on_cycle = Some((abstracted_waiter.span, parent));
271 entrypoint = true;
272 break;
273 }
274 }
275
276 entrypoint.then_some(EntryPoint { query_in_cycle, query_waiting_on_cycle })
277 })
278 .collect::<Vec<EntryPoint>>();
279
280 let entry_point = entry_points
282 .iter()
283 .find(|entry_point| entry_point.query_waiting_on_cycle.is_some())
284 .unwrap_or(&entry_points[0]);
285
286 let entry_point_pos = stack.iter().position(|(_, query)| *query == entry_point.query_in_cycle);
288 if let Some(pos) = entry_point_pos {
289 stack.rotate_left(pos);
290 }
291
292 let usage = entry_point
293 .query_waiting_on_cycle
294 .map(|(span, job)| QueryStackFrame { span, tagged_key: job_map.tagged_key_of(job) });
295
296 Cycle {
298 usage,
299 frames: stack
300 .iter()
301 .map(|&(span, job)| QueryStackFrame { span, tagged_key: job_map.tagged_key_of(job) })
302 .collect(),
303 }
304}
305
306fn find_and_process_cycle<'tcx>(
309 job_map: &QueryJobMap<'tcx>,
310 query: QueryJobId,
311) -> Option<Arc<QueryWaiter<'tcx>>> {
312 let mut visited = FxHashSet::default();
313 let mut stack = Vec::new();
314 if let ControlFlow::Break(resumable) =
315 find_cycle(job_map, query, DUMMY_SP, &mut stack, &mut visited)
316 {
317 let error = process_cycle(job_map, stack);
319
320 let (waitee_query, waiter_idx) = resumable.unwrap();
323
324 let waiter = job_map.latch_of(waitee_query).unwrap().extract_waiter(waiter_idx);
326
327 *waiter.cycle.lock() = Some(error);
329
330 Some(waiter)
332 } else {
333 None
334 }
335}
336
337#[allow(rustc::potential_query_instability)]
344pub fn break_query_cycle<'tcx>(job_map: QueryJobMap<'tcx>, registry: &rustc_thread_pool::Registry) {
345 let waiter = job_map
347 .map
348 .keys()
349 .find_map(|query| find_and_process_cycle(&job_map, *query))
350 .expect("unable to find a query cycle");
351
352 rustc_thread_pool::mark_unblocked(registry);
354
355 if !waiter.condvar.notify_one() {
{
::core::panicking::panic_fmt(format_args!("unable to wake the waiter"));
}
};assert!(waiter.condvar.notify_one(), "unable to wake the waiter");
356}
357
358pub fn print_query_stack<'tcx>(
359 tcx: TyCtxt<'tcx>,
360 mut current_query: Option<QueryJobId>,
361 dcx: DiagCtxtHandle<'_>,
362 limit_frames: Option<usize>,
363 mut file: Option<std::fs::File>,
364) -> usize {
365 let mut count_printed = 0;
369 let mut count_total = 0;
370
371 let job_map = collect_active_query_jobs(tcx, CollectActiveJobsKind::PartialAllowed);
373
374 if let Some(ref mut file) = file {
375 let _ = file.write_fmt(format_args!("\n\nquery stack during panic:\n"))writeln!(file, "\n\nquery stack during panic:");
376 }
377 while let Some(query) = current_query {
378 let Some(query_info) = job_map.map.get(&query) else {
379 break;
380 };
381 let description = query_info.tagged_key.description(tcx);
382 if Some(count_printed) < limit_frames || limit_frames.is_none() {
383 dcx.struct_failure_note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#{1} [{0}] {2}",
query_info.tagged_key.query_name(), count_printed,
description))
})format!(
385 "#{count_printed} [{query_name}] {description}",
386 query_name = query_info.tagged_key.query_name(),
387 ))
388 .with_span(query_info.job.span)
389 .emit();
390 count_printed += 1;
391 }
392
393 if let Some(ref mut file) = file {
394 let _ = file.write_fmt(format_args!("#{1} [{0}] {2}\n",
query_info.tagged_key.query_name(), count_total, description))writeln!(
395 file,
396 "#{count_total} [{query_name}] {description}",
397 query_name = query_info.tagged_key.query_name(),
398 );
399 }
400
401 current_query = query_info.job.parent;
402 count_total += 1;
403 }
404
405 if let Some(ref mut file) = file {
406 let _ = file.write_fmt(format_args!("end of query stack\n"))writeln!(file, "end of query stack");
407 }
408 count_total
409}
410
411#[inline(never)]
412#[cold]
413pub(crate) fn create_cycle_error<'tcx>(
414 tcx: TyCtxt<'tcx>,
415 Cycle { usage, frames }: &Cycle<'tcx>,
416 nested: bool,
417) -> Diag<'tcx> {
418 if !!frames.is_empty() {
::core::panicking::panic("assertion failed: !frames.is_empty()")
};assert!(!frames.is_empty());
419
420 let span = frames[0].tagged_key.catch_default_span(tcx, frames[1 % frames.len()].span);
421
422 let mut cycle_stack = Vec::new();
423
424 use crate::error::StackCount;
425 let stack_bottom = frames[0].tagged_key.catch_description(tcx);
426 let stack_count = if frames.len() == 1 {
427 StackCount::Single { stack_bottom: stack_bottom.clone() }
428 } else {
429 StackCount::Multiple { stack_bottom: stack_bottom.clone() }
430 };
431
432 for i in 1..frames.len() {
433 let frame = &frames[i];
434 let span = frame.tagged_key.catch_default_span(tcx, frames[(i + 1) % frames.len()].span);
435 cycle_stack
436 .push(crate::error::CycleStack { span, desc: frame.tagged_key.catch_description(tcx) });
437 }
438
439 let cycle_usage = usage.as_ref().map(|usage| crate::error::CycleUsage {
440 span: usage.tagged_key.catch_default_span(tcx, usage.span),
441 usage: usage.tagged_key.catch_description(tcx),
442 });
443
444 let is_all_def_kind = |def_kind| {
445 frames.iter().all(|frame| match frame.tagged_key {
448 TaggedQueryKey::type_of(def_id)
449 | TaggedQueryKey::explicit_implied_predicates_of(def_id)
450 if tcx.def_kind(def_id) == def_kind =>
451 {
452 true
453 }
454 _ => false,
455 })
456 };
457
458 let alias = if !nested {
459 if is_all_def_kind(DefKind::TyAlias) {
460 Some(crate::error::Alias::Ty)
461 } else if is_all_def_kind(DefKind::TraitAlias) {
462 Some(crate::error::Alias::Trait)
463 } else {
464 None
465 }
466 } else {
467 None
468 };
469
470 if nested {
471 tcx.sess.dcx().create_err(crate::error::NestedCycle {
472 span,
473 cycle_stack,
474 stack_bottom: crate::error::NestedCycleBottom { stack_bottom },
475 cycle_usage,
476 stack_count,
477 note_span: (),
478 })
479 } else {
480 tcx.sess.dcx().create_err(crate::error::Cycle {
481 span,
482 cycle_stack,
483 stack_bottom,
484 alias,
485 cycle_usage,
486 stack_count,
487 note_span: (),
488 })
489 }
490}