1use std::io::Write;
2use std::iter;
3use std::ops::ControlFlow;
4use std::sync::Arc;
5
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_errors::{Diag, DiagCtxtHandle};
8use rustc_hir::def::DefKind;
9use rustc_middle::query::{
10 CycleError, QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryStackDeferred, QueryStackFrame,
11 QueryWaiter,
12};
13use rustc_middle::ty::TyCtxt;
14use rustc_session::Session;
15use rustc_span::{DUMMY_SP, Span};
16
17use crate::plumbing::collect_active_jobs_from_all_queries;
18
19#[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)]
22pub struct QueryJobMap<'tcx> {
23 map: FxHashMap<QueryJobId, QueryJobInfo<'tcx>>,
24}
25
26impl<'tcx> QueryJobMap<'tcx> {
27 pub(crate) fn insert(&mut self, id: QueryJobId, info: QueryJobInfo<'tcx>) {
31 self.map.insert(id, info);
32 }
33
34 fn frame_of(&self, id: QueryJobId) -> &QueryStackFrame<QueryStackDeferred<'tcx>> {
35 &self.map[&id].frame
36 }
37
38 fn span_of(&self, id: QueryJobId) -> Span {
39 self.map[&id].job.span
40 }
41
42 fn parent_of(&self, id: QueryJobId) -> Option<QueryJobId> {
43 self.map[&id].job.parent
44 }
45
46 fn latch_of(&self, id: QueryJobId) -> Option<&QueryLatch<'tcx>> {
47 self.map[&id].job.latch.as_ref()
48 }
49}
50
51#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for QueryJobInfo<'tcx> {
#[inline]
fn clone(&self) -> QueryJobInfo<'tcx> {
QueryJobInfo {
frame: ::core::clone::Clone::clone(&self.frame),
job: ::core::clone::Clone::clone(&self.job),
}
}
}Clone, #[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",
"frame", &self.frame, "job", &&self.job)
}
}Debug)]
52pub(crate) struct QueryJobInfo<'tcx> {
53 pub(crate) frame: QueryStackFrame<QueryStackDeferred<'tcx>>,
54 pub(crate) job: QueryJob<'tcx>,
55}
56
57pub(crate) fn find_cycle_in_stack<'tcx>(
58 id: QueryJobId,
59 job_map: QueryJobMap<'tcx>,
60 current_job: &Option<QueryJobId>,
61 span: Span,
62) -> CycleError<QueryStackDeferred<'tcx>> {
63 let mut cycle = Vec::new();
65 let mut current_job = Option::clone(current_job);
66
67 while let Some(job) = current_job {
68 let info = &job_map.map[&job];
69 cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() });
70
71 if job == id {
72 cycle.reverse();
73
74 cycle[0].span = span;
79 let usage = try {
81 let parent = info.job.parent?;
82 (info.job.span, job_map.frame_of(parent).clone())
83 };
84 return CycleError { usage, cycle };
85 }
86
87 current_job = info.job.parent;
88 }
89
90 { ::core::panicking::panic_fmt(format_args!("did not find a cycle")); }panic!("did not find a cycle")
91}
92
93#[cold]
94#[inline(never)]
95pub(crate) fn find_dep_kind_root<'tcx>(
96 id: QueryJobId,
97 job_map: QueryJobMap<'tcx>,
98) -> (QueryJobInfo<'tcx>, usize) {
99 let mut depth = 1;
100 let info = &job_map.map[&id];
101 let dep_kind = info.frame.dep_kind;
102 let mut current_id = info.job.parent;
103 let mut last_layout = (info.clone(), depth);
104
105 while let Some(id) = current_id {
106 let info = &job_map.map[&id];
107 if info.frame.dep_kind == dep_kind {
108 depth += 1;
109 last_layout = (info.clone(), depth);
110 }
111 current_id = info.job.parent;
112 }
113 last_layout
114}
115
116type Waiter = (QueryJobId, usize);
118
119fn visit_waiters<'tcx>(
128 job_map: &QueryJobMap<'tcx>,
129 query: QueryJobId,
130 mut visit: impl FnMut(Span, QueryJobId) -> ControlFlow<Option<Waiter>>,
131) -> ControlFlow<Option<Waiter>> {
132 if let Some(parent) = job_map.parent_of(query) {
134 visit(job_map.span_of(query), parent)?;
135 }
136
137 if let Some(latch) = job_map.latch_of(query) {
139 for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
140 if let Some(waiter_query) = waiter.query {
141 visit(waiter.span, waiter_query).map_break(|_| Some((query, i)))?;
143 }
144 }
145 }
146
147 ControlFlow::Continue(())
148}
149
150fn cycle_check<'tcx>(
155 job_map: &QueryJobMap<'tcx>,
156 query: QueryJobId,
157 span: Span,
158 stack: &mut Vec<(Span, QueryJobId)>,
159 visited: &mut FxHashSet<QueryJobId>,
160) -> ControlFlow<Option<Waiter>> {
161 if !visited.insert(query) {
162 return if let Some(p) = stack.iter().position(|q| q.1 == query) {
163 stack.drain(0..p);
167 stack[0].0 = span;
169 ControlFlow::Break(None)
170 } else {
171 ControlFlow::Continue(())
172 };
173 }
174
175 stack.push((span, query));
177
178 let r = visit_waiters(job_map, query, |span, successor| {
180 cycle_check(job_map, successor, span, stack, visited)
181 });
182
183 if r.is_continue() {
185 stack.pop();
186 }
187
188 r
189}
190
191fn connected_to_root<'tcx>(
195 job_map: &QueryJobMap<'tcx>,
196 query: QueryJobId,
197 visited: &mut FxHashSet<QueryJobId>,
198) -> ControlFlow<Option<Waiter>> {
199 if !visited.insert(query) {
201 return ControlFlow::Continue(());
202 }
203
204 if job_map.parent_of(query).is_none() {
206 return ControlFlow::Break(None);
207 }
208
209 visit_waiters(job_map, query, |_, successor| connected_to_root(job_map, successor, visited))
210}
211
212fn remove_cycle<'tcx>(
218 job_map: &QueryJobMap<'tcx>,
219 jobs: &mut Vec<QueryJobId>,
220 wakelist: &mut Vec<Arc<QueryWaiter<'tcx>>>,
221) -> bool {
222 let mut visited = FxHashSet::default();
223 let mut stack = Vec::new();
224 if let ControlFlow::Break(waiter) =
226 cycle_check(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
227 {
228 let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip();
231
232 spans.rotate_right(1);
234
235 let mut stack: Vec<_> = iter::zip(spans, queries).collect();
237
238 for r in &stack {
240 if let Some(pos) = jobs.iter().position(|j| j == &r.1) {
241 jobs.remove(pos);
242 }
243 }
244
245 struct EntryPoint {
246 query_in_cycle: QueryJobId,
247 waiter: Option<(Span, QueryJobId)>,
248 }
249
250 let entry_points = stack
253 .iter()
254 .filter_map(|&(_, query_in_cycle)| {
255 if job_map.parent_of(query_in_cycle).is_none() {
256 Some(EntryPoint { query_in_cycle, waiter: None })
258 } else {
259 let mut waiter_on_cycle = None;
260 let _ = visit_waiters(job_map, query_in_cycle, |span, waiter| {
262 let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
264
265 if connected_to_root(job_map, waiter, &mut visited).is_break() {
266 waiter_on_cycle = Some((span, waiter));
267 ControlFlow::Break(None)
268 } else {
269 ControlFlow::Continue(())
270 }
271 });
272
273 waiter_on_cycle.map(|waiter_on_cycle| EntryPoint {
274 query_in_cycle,
275 waiter: Some(waiter_on_cycle),
276 })
277 }
278 })
279 .collect::<Vec<EntryPoint>>();
280
281 let entry_point = entry_points
283 .iter()
284 .find(|entry_point| entry_point.waiter.is_some())
285 .unwrap_or(&entry_points[0]);
286
287 let entry_point_pos =
289 stack.iter().position(|(_, query)| *query == entry_point.query_in_cycle);
290 if let Some(pos) = entry_point_pos {
291 stack.rotate_left(pos);
292 }
293
294 let usage = entry_point.waiter.map(|(span, job)| (span, job_map.frame_of(job).clone()));
295
296 let error = CycleError {
298 usage,
299 cycle: stack
300 .iter()
301 .map(|&(span, job)| QueryInfo { span, frame: job_map.frame_of(job).clone() })
302 .collect(),
303 };
304
305 let (waitee_query, waiter_idx) = waiter.unwrap();
308
309 let waiter = job_map.latch_of(waitee_query).unwrap().extract_waiter(waiter_idx);
311
312 *waiter.cycle.lock() = Some(error);
314
315 wakelist.push(waiter);
317
318 true
319 } else {
320 false
321 }
322}
323
324pub fn break_query_cycles<'tcx>(
330 job_map: QueryJobMap<'tcx>,
331 registry: &rustc_thread_pool::Registry,
332) {
333 let mut wakelist = Vec::new();
334 #[allow(rustc::potential_query_instability)]
338 let mut jobs: Vec<QueryJobId> = job_map.map.keys().copied().collect();
339
340 let mut found_cycle = false;
341
342 while jobs.len() > 0 {
343 if remove_cycle(&job_map, &mut jobs, &mut wakelist) {
344 found_cycle = true;
345 }
346 }
347
348 if !found_cycle {
356 {
::core::panicking::panic_fmt(format_args!("deadlock detected as we\'re unable to find a query cycle to break\ncurrent query map:\n{0:#?}",
job_map));
};panic!(
357 "deadlock detected as we're unable to find a query cycle to break\n\
358 current query map:\n{job_map:#?}",
359 );
360 }
361
362 for _ in 0..wakelist.len() {
366 rustc_thread_pool::mark_unblocked(registry);
367 }
368
369 for waiter in wakelist.into_iter() {
370 waiter.condvar.notify_one();
371 }
372}
373
374pub fn print_query_stack<'tcx>(
375 tcx: TyCtxt<'tcx>,
376 mut current_query: Option<QueryJobId>,
377 dcx: DiagCtxtHandle<'_>,
378 limit_frames: Option<usize>,
379 mut file: Option<std::fs::File>,
380) -> usize {
381 let mut count_printed = 0;
385 let mut count_total = 0;
386
387 let job_map: QueryJobMap<'_> = collect_active_jobs_from_all_queries(tcx, false)
389 .unwrap_or_else(|partial_job_map| partial_job_map);
390
391 if let Some(ref mut file) = file {
392 let _ = file.write_fmt(format_args!("\n\nquery stack during panic:\n"))writeln!(file, "\n\nquery stack during panic:");
393 }
394 while let Some(query) = current_query {
395 let Some(query_info) = job_map.map.get(&query) else {
396 break;
397 };
398 let query_extra = query_info.frame.info.extract();
399 if Some(count_printed) < limit_frames || limit_frames.is_none() {
400 dcx.struct_failure_note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#{0} [{1:?}] {2}", count_printed,
query_info.frame.dep_kind, query_extra.description))
})format!(
402 "#{} [{:?}] {}",
403 count_printed, query_info.frame.dep_kind, query_extra.description
404 ))
405 .with_span(query_info.job.span)
406 .emit();
407 count_printed += 1;
408 }
409
410 if let Some(ref mut file) = file {
411 let _ = file.write_fmt(format_args!("#{0} [{1:?}] {2}\n", count_total,
query_info.frame.dep_kind, query_extra.description))writeln!(
412 file,
413 "#{} [{:?}] {}",
414 count_total, query_info.frame.dep_kind, query_extra.description
415 );
416 }
417
418 current_query = query_info.job.parent;
419 count_total += 1;
420 }
421
422 if let Some(ref mut file) = file {
423 let _ = file.write_fmt(format_args!("end of query stack\n"))writeln!(file, "end of query stack");
424 }
425 count_total
426}
427
428#[inline(never)]
429#[cold]
430pub(crate) fn report_cycle<'a>(
431 sess: &'a Session,
432 CycleError { usage, cycle: stack }: &CycleError,
433) -> Diag<'a> {
434 if !!stack.is_empty() {
::core::panicking::panic("assertion failed: !stack.is_empty()")
};assert!(!stack.is_empty());
435
436 let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span);
437
438 let mut cycle_stack = Vec::new();
439
440 use crate::error::StackCount;
441 let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
442
443 for i in 1..stack.len() {
444 let frame = &stack[i].frame;
445 let span = frame.info.default_span(stack[(i + 1) % stack.len()].span);
446 cycle_stack
447 .push(crate::error::CycleStack { span, desc: frame.info.description.to_owned() });
448 }
449
450 let mut cycle_usage = None;
451 if let Some((span, ref query)) = *usage {
452 cycle_usage = Some(crate::error::CycleUsage {
453 span: query.info.default_span(span),
454 usage: query.info.description.to_string(),
455 });
456 }
457
458 let alias =
459 if stack.iter().all(|entry| #[allow(non_exhaustive_omitted_patterns)] match entry.frame.info.def_kind {
Some(DefKind::TyAlias) => true,
_ => false,
}matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) {
460 Some(crate::error::Alias::Ty)
461 } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) {
462 Some(crate::error::Alias::Trait)
463 } else {
464 None
465 };
466
467 let cycle_diag = crate::error::Cycle {
468 span,
469 cycle_stack,
470 stack_bottom: stack[0].frame.info.description.to_owned(),
471 alias,
472 cycle_usage,
473 stack_count,
474 note_span: (),
475 };
476
477 sess.dcx().create_err(cycle_diag)
478}