Skip to main content

rustc_interface/
callbacks.rs

1//! Throughout the compiler tree, there are several places which want to have
2//! access to state or queries while being inside crates that are dependencies
3//! of `rustc_middle`. To facilitate this, we have the
4//! `rustc_data_structures::AtomicRef` type, which allows us to setup a global
5//! static which can then be set in this file at program startup.
6//!
7//! See `SPAN_TRACK` for an example of how to set things up.
8//!
9//! The functions in this file should fall back to the default set in their
10//! origin crate when the `TyCtxt` is not present in TLS.
11
12use std::fmt;
13use std::fmt::Arguments;
14use std::panic::Location;
15
16use rustc_errors::{DiagInner, DiagLocation, Level};
17use rustc_middle::dep_graph::{QuerySideEffect, TaskDepsRef};
18use rustc_middle::ty::tls;
19use rustc_span::{Span, Symbol};
20
21fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
22    tls::with_context_opt(|icx| {
23        if let Some(icx) = icx {
24            // `track_span_parent` gets called a lot from HIR lowering code.
25            // Skip doing anything if we aren't tracking dependencies.
26            let tracks_deps = match icx.task_deps {
27                TaskDepsRef::Allow(..) => true,
28                TaskDepsRef::EvalAlways | TaskDepsRef::Ignore | TaskDepsRef::Forbid => false,
29            };
30            if tracks_deps {
31                let _span = icx.tcx.source_span(def_id);
32                // Sanity check: relative span's parent must be an absolute span.
33                if true {
    {
        match (&_span.data_untracked().parent, &None) {
            (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!(_span.data_untracked().parent, None);
34            }
35        }
36    })
37}
38
39/// This is a callback from `rustc_errors` as it cannot access the implicit state
40/// in `rustc_middle` otherwise. It is used when diagnostic messages are
41/// emitted and stores them in the current query, if there is one.
42fn track_diagnostic<R>(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
43    tls::with_context_opt(|icx| {
44        if let Some(icx) = icx {
45            icx.tcx.dep_graph.record_diagnostic(icx.tcx, &diagnostic);
46
47            // Diagnostics are tracked, we can ignore the dependency.
48            let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..*icx };
49            tls::enter_context(&icx, move || (*f)(diagnostic))
50        } else {
51            // In any other case, invoke diagnostics anyway.
52            (*f)(diagnostic)
53        }
54    })
55}
56
57fn track_feature(feature: Symbol) {
58    tls::with_context_opt(|icx| {
59        let Some(icx) = icx else {
60            return;
61        };
62        let tcx = icx.tcx;
63
64        if let Some(dep_node_index) = tcx.query_system.used_features.lock().get(&feature).copied() {
65            tcx.dep_graph.read_index(dep_node_index);
66        } else {
67            let dep_node_index = tcx
68                .dep_graph
69                .encode_side_effect(tcx, QuerySideEffect::CheckFeature { symbol: feature });
70            tcx.query_system.used_features.lock().insert(feature, dep_node_index);
71            tcx.dep_graph.read_index(dep_node_index);
72        }
73    })
74}
75
76/// This is a callback from `rustc_hir` as it cannot access the implicit state
77/// in `rustc_middle` otherwise.
78fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79    f.write_fmt(format_args!("DefId({0}:{1}", def_id.krate, def_id.index.index()))write!(f, "DefId({}:{}", def_id.krate, def_id.index.index())?;
80    tls::with_opt(|opt_tcx| {
81        if let Some(tcx) = opt_tcx {
82            f.write_fmt(format_args!(" ~ {0}", tcx.def_path_debug_str(def_id)))write!(f, " ~ {}", tcx.def_path_debug_str(def_id))?;
83        }
84        Ok(())
85    })?;
86    f.write_fmt(format_args!(")"))write!(f, ")")
87}
88
89/// Returns true if it printed the diagnostic, which happens if a `tcx` is available.
90fn emit_bug_diagnostic(
91    span: Option<Span>,
92    args: Arguments<'_>,
93    location: &'static Location<'static>,
94) -> bool {
95    tls::with_opt(move |tcx| {
96        if let Some(tcx) = tcx {
97            let mut diag = DiagInner::new(Level::Bug, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}: {1}", location, args))
    })format!("{location}: {args}"));
98            if let Some(span) = span {
99                diag.span = span.into();
100            }
101            diag.emitted_at = DiagLocation::from_location(location);
102            // Emit the bug without aborting. We let `bug_impl` do the abort because it has
103            // `#[track_caller]` which gives a better location. (`#[track_caller]` doesn't work
104            // here because this function is called via a function pointer.)
105            tcx.dcx().emit_diagnostic(diag);
106            true
107        } else {
108            false
109        }
110    })
111}
112
113/// Sets up the callbacks in prior crates which we want to refer to the
114/// TyCtxt in.
115pub fn setup_callbacks() {
116    rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_)));
117    rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
118    rustc_errors::TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _));
119    rustc_feature::TRACK_FEATURE.swap(&(track_feature as _));
120    rustc_span::macros::EMIT_BUG_DIAGNOSTIC.swap(&(emit_bug_diagnostic as _));
121    rustc_expand_queries::setup_callbacks();
122}