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            rustc_thread_pool::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 = rustc_thread_pool::Registry::current();
255        for waiter in info.waiters.drain(..) {
256            rustc_thread_pool::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        && let Some(cycle) = visit(query.span(query_map), parent)
294    {
295        return Some(cycle);
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: &rustc_thread_pool::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        rustc_thread_pool::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}