rustc_query_system/query/
job.rs1use std::fmt::Debug;
2use std::hash::Hash;
3use std::io::Write;
4use std::iter;
5use std::num::NonZero;
6use std::sync::Arc;
7
8use parking_lot::{Condvar, Mutex};
9use rustc_data_structures::fx::{FxHashMap, FxHashSet};
10use rustc_errors::{Diag, DiagCtxtHandle};
11use rustc_hir::def::DefKind;
12use rustc_session::Session;
13use rustc_span::{DUMMY_SP, Span};
14
15use super::QueryStackFrameExtra;
16use crate::dep_graph::DepContext;
17use crate::error::CycleStack;
18use crate::query::plumbing::CycleError;
19use crate::query::{QueryContext, QueryStackFrame};
20
21#[derive(Clone, Debug)]
23pub struct QueryInfo<I> {
24 pub span: Span,
26 pub query: QueryStackFrame<I>,
27}
28
29impl<I> QueryInfo<I> {
30 pub(crate) fn lift<Qcx: QueryContext<QueryInfo = I>>(
31 &self,
32 qcx: Qcx,
33 ) -> QueryInfo<QueryStackFrameExtra> {
34 QueryInfo { span: self.span, query: self.query.lift(qcx) }
35 }
36}
37
38pub type QueryMap<I> = FxHashMap<QueryJobId, QueryJobInfo<I>>;
39
40#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
42pub struct QueryJobId(pub NonZero<u64>);
43
44impl QueryJobId {
45 fn query<I: Clone>(self, map: &QueryMap<I>) -> QueryStackFrame<I> {
46 map.get(&self).unwrap().query.clone()
47 }
48
49 fn span<I>(self, map: &QueryMap<I>) -> Span {
50 map.get(&self).unwrap().job.span
51 }
52
53 fn parent<I>(self, map: &QueryMap<I>) -> Option<QueryJobId> {
54 map.get(&self).unwrap().job.parent
55 }
56
57 fn latch<I>(self, map: &QueryMap<I>) -> Option<&QueryLatch<I>> {
58 map.get(&self).unwrap().job.latch.as_ref()
59 }
60}
61
62#[derive(Clone, Debug)]
63pub struct QueryJobInfo<I> {
64 pub query: QueryStackFrame<I>,
65 pub job: QueryJob<I>,
66}
67
68#[derive(Debug)]
70pub struct QueryJob<I> {
71 pub id: QueryJobId,
72
73 pub span: Span,
75
76 pub parent: Option<QueryJobId>,
78
79 latch: Option<QueryLatch<I>>,
81}
82
83impl<I> Clone for QueryJob<I> {
84 fn clone(&self) -> Self {
85 Self { id: self.id, span: self.span, parent: self.parent, latch: self.latch.clone() }
86 }
87}
88
89impl<I> QueryJob<I> {
90 #[inline]
92 pub fn new(id: QueryJobId, span: Span, parent: Option<QueryJobId>) -> Self {
93 QueryJob { id, span, parent, latch: None }
94 }
95
96 pub(super) fn latch(&mut self) -> QueryLatch<I> {
97 if self.latch.is_none() {
98 self.latch = Some(QueryLatch::new());
99 }
100 self.latch.as_ref().unwrap().clone()
101 }
102
103 #[inline]
108 pub fn signal_complete(self) {
109 if let Some(latch) = self.latch {
110 latch.set();
111 }
112 }
113}
114
115impl QueryJobId {
116 pub(super) fn find_cycle_in_stack<I: Clone>(
117 &self,
118 query_map: QueryMap<I>,
119 current_job: &Option<QueryJobId>,
120 span: Span,
121 ) -> CycleError<I> {
122 let mut cycle = Vec::new();
124 let mut current_job = Option::clone(current_job);
125
126 while let Some(job) = current_job {
127 let info = query_map.get(&job).unwrap();
128 cycle.push(QueryInfo { span: info.job.span, query: info.query.clone() });
129
130 if job == *self {
131 cycle.reverse();
132
133 cycle[0].span = span;
138 let usage = info
140 .job
141 .parent
142 .as_ref()
143 .map(|parent| (info.job.span, parent.query(&query_map)));
144 return CycleError { usage, cycle };
145 }
146
147 current_job = info.job.parent;
148 }
149
150 panic!("did not find a cycle")
151 }
152
153 #[cold]
154 #[inline(never)]
155 pub fn find_dep_kind_root<I: Clone>(&self, query_map: QueryMap<I>) -> (QueryJobInfo<I>, usize) {
156 let mut depth = 1;
157 let info = query_map.get(&self).unwrap();
158 let dep_kind = info.query.dep_kind;
159 let mut current_id = info.job.parent;
160 let mut last_layout = (info.clone(), depth);
161
162 while let Some(id) = current_id {
163 let info = query_map.get(&id).unwrap();
164 if info.query.dep_kind == dep_kind {
165 depth += 1;
166 last_layout = (info.clone(), depth);
167 }
168 current_id = info.job.parent;
169 }
170 last_layout
171 }
172}
173
174#[derive(Debug)]
175struct QueryWaiter<I> {
176 query: Option<QueryJobId>,
177 condvar: Condvar,
178 span: Span,
179 cycle: Mutex<Option<CycleError<I>>>,
180}
181
182#[derive(Debug)]
183struct QueryLatchInfo<I> {
184 complete: bool,
185 waiters: Vec<Arc<QueryWaiter<I>>>,
186}
187
188#[derive(Debug)]
189pub(super) struct QueryLatch<I> {
190 info: Arc<Mutex<QueryLatchInfo<I>>>,
191}
192
193impl<I> Clone for QueryLatch<I> {
194 fn clone(&self) -> Self {
195 Self { info: Arc::clone(&self.info) }
196 }
197}
198
199impl<I> QueryLatch<I> {
200 fn new() -> Self {
201 QueryLatch {
202 info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
203 }
204 }
205
206 pub(super) fn wait_on(
208 &self,
209 qcx: impl QueryContext,
210 query: Option<QueryJobId>,
211 span: Span,
212 ) -> Result<(), CycleError<I>> {
213 let waiter =
214 Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
215 self.wait_on_inner(qcx, &waiter);
216 let mut cycle = waiter.cycle.lock();
220 match cycle.take() {
221 None => Ok(()),
222 Some(cycle) => Err(cycle),
223 }
224 }
225
226 fn wait_on_inner(&self, qcx: impl QueryContext, waiter: &Arc<QueryWaiter<I>>) {
228 let mut info = self.info.lock();
229 if !info.complete {
230 info.waiters.push(Arc::clone(waiter));
235
236 rayon_core::mark_blocked();
240 let proxy = qcx.jobserver_proxy();
241 proxy.release_thread();
242 waiter.condvar.wait(&mut info);
243 drop(info);
245 proxy.acquire_thread();
246 }
247 }
248
249 fn set(&self) {
251 let mut info = self.info.lock();
252 debug_assert!(!info.complete);
253 info.complete = true;
254 let registry = rayon_core::Registry::current();
255 for waiter in info.waiters.drain(..) {
256 rayon_core::mark_unblocked(®istry);
257 waiter.condvar.notify_one();
258 }
259 }
260
261 fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<I>> {
264 let mut info = self.info.lock();
265 debug_assert!(!info.complete);
266 info.waiters.remove(waiter)
268 }
269}
270
271type Waiter = (QueryJobId, usize);
273
274fn visit_waiters<I, F>(
284 query_map: &QueryMap<I>,
285 query: QueryJobId,
286 mut visit: F,
287) -> Option<Option<Waiter>>
288where
289 F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
290{
291 if let Some(parent) = query.parent(query_map) {
293 if let Some(cycle) = visit(query.span(query_map), parent) {
294 return Some(cycle);
295 }
296 }
297
298 if let Some(latch) = query.latch(query_map) {
300 for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
301 if let Some(waiter_query) = waiter.query {
302 if visit(waiter.span, waiter_query).is_some() {
303 return Some(Some((query, i)));
305 }
306 }
307 }
308 }
309
310 None
311}
312
313fn cycle_check<I>(
318 query_map: &QueryMap<I>,
319 query: QueryJobId,
320 span: Span,
321 stack: &mut Vec<(Span, QueryJobId)>,
322 visited: &mut FxHashSet<QueryJobId>,
323) -> Option<Option<Waiter>> {
324 if !visited.insert(query) {
325 return if let Some(p) = stack.iter().position(|q| q.1 == query) {
326 stack.drain(0..p);
330 stack[0].0 = span;
332 Some(None)
333 } else {
334 None
335 };
336 }
337
338 stack.push((span, query));
340
341 let r = visit_waiters(query_map, query, |span, successor| {
343 cycle_check(query_map, successor, span, stack, visited)
344 });
345
346 if r.is_none() {
348 stack.pop();
349 }
350
351 r
352}
353
354fn connected_to_root<I>(
358 query_map: &QueryMap<I>,
359 query: QueryJobId,
360 visited: &mut FxHashSet<QueryJobId>,
361) -> bool {
362 if !visited.insert(query) {
364 return false;
365 }
366
367 if query.parent(query_map).is_none() {
369 return true;
370 }
371
372 visit_waiters(query_map, query, |_, successor| {
373 connected_to_root(query_map, successor, visited).then_some(None)
374 })
375 .is_some()
376}
377
378fn pick_query<'a, I: Clone, T, F>(query_map: &QueryMap<I>, queries: &'a [T], f: F) -> &'a T
380where
381 F: Fn(&T) -> (Span, QueryJobId),
382{
383 queries
386 .iter()
387 .min_by_key(|v| {
388 let (span, query) = f(v);
389 let hash = query.query(query_map).hash;
390 let span_cmp = if span == DUMMY_SP { 1 } else { 0 };
394 (span_cmp, hash)
395 })
396 .unwrap()
397}
398
399fn remove_cycle<I: Clone>(
405 query_map: &QueryMap<I>,
406 jobs: &mut Vec<QueryJobId>,
407 wakelist: &mut Vec<Arc<QueryWaiter<I>>>,
408) -> bool {
409 let mut visited = FxHashSet::default();
410 let mut stack = Vec::new();
411 if let Some(waiter) =
413 cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
414 {
415 let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip();
418
419 spans.rotate_right(1);
421
422 let mut stack: Vec<_> = iter::zip(spans, queries).collect();
424
425 for r in &stack {
427 if let Some(pos) = jobs.iter().position(|j| j == &r.1) {
428 jobs.remove(pos);
429 }
430 }
431
432 let entry_points = stack
435 .iter()
436 .filter_map(|&(span, query)| {
437 if query.parent(query_map).is_none() {
438 Some((span, query, None))
440 } else {
441 let mut waiters = Vec::new();
442 visit_waiters(query_map, query, |span, waiter| {
444 let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
446
447 if connected_to_root(query_map, waiter, &mut visited) {
448 waiters.push((span, waiter));
449 }
450
451 None
452 });
453 if waiters.is_empty() {
454 None
455 } else {
456 let waiter = *pick_query(query_map, &waiters, |s| *s);
458 Some((span, query, Some(waiter)))
459 }
460 }
461 })
462 .collect::<Vec<(Span, QueryJobId, Option<(Span, QueryJobId)>)>>();
463
464 let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1));
466
467 let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point);
469 if let Some(pos) = entry_point_pos {
470 stack.rotate_left(pos);
471 }
472
473 let usage = usage.as_ref().map(|(span, query)| (*span, query.query(query_map)));
474
475 let error = CycleError {
477 usage,
478 cycle: stack
479 .iter()
480 .map(|&(s, ref q)| QueryInfo { span: s, query: q.query(query_map) })
481 .collect(),
482 };
483
484 let (waitee_query, waiter_idx) = waiter.unwrap();
487
488 let waiter = waitee_query.latch(query_map).unwrap().extract_waiter(waiter_idx);
490
491 *waiter.cycle.lock() = Some(error);
493
494 wakelist.push(waiter);
496
497 true
498 } else {
499 false
500 }
501}
502
503pub fn break_query_cycles<I: Clone + Debug>(
509 query_map: QueryMap<I>,
510 registry: &rayon_core::Registry,
511) {
512 let mut wakelist = Vec::new();
513 #[allow(rustc::potential_query_instability)]
517 let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();
518
519 let mut found_cycle = false;
520
521 while jobs.len() > 0 {
522 if remove_cycle(&query_map, &mut jobs, &mut wakelist) {
523 found_cycle = true;
524 }
525 }
526
527 if !found_cycle {
535 panic!(
536 "deadlock detected as we're unable to find a query cycle to break\n\
537 current query map:\n{:#?}",
538 query_map
539 );
540 }
541
542 for _ in 0..wakelist.len() {
546 rayon_core::mark_unblocked(registry);
547 }
548
549 for waiter in wakelist.into_iter() {
550 waiter.condvar.notify_one();
551 }
552}
553
554#[inline(never)]
555#[cold]
556pub fn report_cycle<'a>(
557 sess: &'a Session,
558 CycleError { usage, cycle: stack }: &CycleError,
559) -> Diag<'a> {
560 assert!(!stack.is_empty());
561
562 let span = stack[0].query.info.default_span(stack[1 % stack.len()].span);
563
564 let mut cycle_stack = Vec::new();
565
566 use crate::error::StackCount;
567 let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
568
569 for i in 1..stack.len() {
570 let query = &stack[i].query;
571 let span = query.info.default_span(stack[(i + 1) % stack.len()].span);
572 cycle_stack.push(CycleStack { span, desc: query.info.description.to_owned() });
573 }
574
575 let mut cycle_usage = None;
576 if let Some((span, ref query)) = *usage {
577 cycle_usage = Some(crate::error::CycleUsage {
578 span: query.info.default_span(span),
579 usage: query.info.description.to_string(),
580 });
581 }
582
583 let alias =
584 if stack.iter().all(|entry| matches!(entry.query.info.def_kind, Some(DefKind::TyAlias))) {
585 Some(crate::error::Alias::Ty)
586 } else if stack.iter().all(|entry| entry.query.info.def_kind == Some(DefKind::TraitAlias)) {
587 Some(crate::error::Alias::Trait)
588 } else {
589 None
590 };
591
592 let cycle_diag = crate::error::Cycle {
593 span,
594 cycle_stack,
595 stack_bottom: stack[0].query.info.description.to_owned(),
596 alias,
597 cycle_usage,
598 stack_count,
599 note_span: (),
600 };
601
602 sess.dcx().create_err(cycle_diag)
603}
604
605pub fn print_query_stack<Qcx: QueryContext>(
606 qcx: Qcx,
607 mut current_query: Option<QueryJobId>,
608 dcx: DiagCtxtHandle<'_>,
609 limit_frames: Option<usize>,
610 mut file: Option<std::fs::File>,
611) -> usize {
612 let mut count_printed = 0;
616 let mut count_total = 0;
617
618 let query_map = match qcx.collect_active_jobs() {
620 Ok(query_map) => query_map,
621 Err(query_map) => query_map,
622 };
623
624 if let Some(ref mut file) = file {
625 let _ = writeln!(file, "\n\nquery stack during panic:");
626 }
627 while let Some(query) = current_query {
628 let Some(query_info) = query_map.get(&query) else {
629 break;
630 };
631 let query_extra = qcx.lift_query_info(&query_info.query.info);
632 if Some(count_printed) < limit_frames || limit_frames.is_none() {
633 #[allow(rustc::diagnostic_outside_of_impl)]
636 #[allow(rustc::untranslatable_diagnostic)]
637 dcx.struct_failure_note(format!(
638 "#{} [{:?}] {}",
639 count_printed, query_info.query.dep_kind, query_extra.description
640 ))
641 .with_span(query_info.job.span)
642 .emit();
643 count_printed += 1;
644 }
645
646 if let Some(ref mut file) = file {
647 let _ = writeln!(
648 file,
649 "#{} [{}] {}",
650 count_total,
651 qcx.dep_context().dep_kind_info(query_info.query.dep_kind).name,
652 query_extra.description
653 );
654 }
655
656 current_query = query_info.job.parent;
657 count_total += 1;
658 }
659
660 if let Some(ref mut file) = file {
661 let _ = writeln!(file, "end of query stack");
662 }
663 count_total
664}