]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_interface/src/queries.rs
New upstream version 1.54.0+dfsg1
[rustc.git] / compiler / rustc_interface / src / queries.rs
1 use crate::interface::{Compiler, Result};
2 use crate::passes::{self, BoxedResolver, QueryContext};
3
4 use rustc_ast as ast;
5 use rustc_codegen_ssa::traits::CodegenBackend;
6 use rustc_data_structures::steal::Steal;
7 use rustc_data_structures::svh::Svh;
8 use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
9 use rustc_errors::ErrorReported;
10 use rustc_hir::def_id::LOCAL_CRATE;
11 use rustc_hir::Crate;
12 use rustc_incremental::DepGraphFuture;
13 use rustc_lint::LintStore;
14 use rustc_middle::arena::Arena;
15 use rustc_middle::dep_graph::DepGraph;
16 use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt};
17 use rustc_query_impl::Queries as TcxQueries;
18 use rustc_serialize::json;
19 use rustc_session::config::{self, OutputFilenames, OutputType};
20 use rustc_session::{output::find_crate_name, Session};
21 use rustc_span::symbol::sym;
22 use std::any::Any;
23 use std::cell::{Ref, RefCell, RefMut};
24 use std::rc::Rc;
25
26 /// Represent the result of a query.
27 ///
28 /// This result can be stolen with the [`take`] method and generated with the [`compute`] method.
29 ///
30 /// [`take`]: Self::take
31 /// [`compute`]: Self::compute
32 pub struct Query<T> {
33 result: RefCell<Option<Result<T>>>,
34 }
35
36 impl<T> Query<T> {
37 fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<&Query<T>> {
38 let mut result = self.result.borrow_mut();
39 if result.is_none() {
40 *result = Some(f());
41 }
42 result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err)
43 }
44
45 /// Takes ownership of the query result. Further attempts to take or peek the query
46 /// result will panic unless it is generated by calling the `compute` method.
47 pub fn take(&self) -> T {
48 self.result.borrow_mut().take().expect("missing query result").unwrap()
49 }
50
51 /// Borrows the query result using the RefCell. Panics if the result is stolen.
52 pub fn peek(&self) -> Ref<'_, T> {
53 Ref::map(self.result.borrow(), |r| {
54 r.as_ref().unwrap().as_ref().expect("missing query result")
55 })
56 }
57
58 /// Mutably borrows the query result using the RefCell. Panics if the result is stolen.
59 pub fn peek_mut(&self) -> RefMut<'_, T> {
60 RefMut::map(self.result.borrow_mut(), |r| {
61 r.as_mut().unwrap().as_mut().expect("missing query result")
62 })
63 }
64 }
65
66 impl<T> Default for Query<T> {
67 fn default() -> Self {
68 Query { result: RefCell::new(None) }
69 }
70 }
71
72 pub struct Queries<'tcx> {
73 compiler: &'tcx Compiler,
74 gcx: OnceCell<GlobalCtxt<'tcx>>,
75 queries: OnceCell<TcxQueries<'tcx>>,
76
77 arena: WorkerLocal<Arena<'tcx>>,
78 hir_arena: WorkerLocal<rustc_ast_lowering::Arena<'tcx>>,
79
80 dep_graph_future: Query<Option<DepGraphFuture>>,
81 parse: Query<ast::Crate>,
82 crate_name: Query<String>,
83 register_plugins: Query<(ast::Crate, Lrc<LintStore>)>,
84 expansion: Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>, Lrc<LintStore>)>,
85 dep_graph: Query<DepGraph>,
86 lower_to_hir: Query<(&'tcx Crate<'tcx>, Steal<ResolverOutputs>)>,
87 prepare_outputs: Query<OutputFilenames>,
88 global_ctxt: Query<QueryContext<'tcx>>,
89 ongoing_codegen: Query<Box<dyn Any>>,
90 }
91
92 impl<'tcx> Queries<'tcx> {
93 pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> {
94 Queries {
95 compiler,
96 gcx: OnceCell::new(),
97 queries: OnceCell::new(),
98 arena: WorkerLocal::new(|_| Arena::default()),
99 hir_arena: WorkerLocal::new(|_| rustc_ast_lowering::Arena::default()),
100 dep_graph_future: Default::default(),
101 parse: Default::default(),
102 crate_name: Default::default(),
103 register_plugins: Default::default(),
104 expansion: Default::default(),
105 dep_graph: Default::default(),
106 lower_to_hir: Default::default(),
107 prepare_outputs: Default::default(),
108 global_ctxt: Default::default(),
109 ongoing_codegen: Default::default(),
110 }
111 }
112
113 fn session(&self) -> &Lrc<Session> {
114 &self.compiler.sess
115 }
116 fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> {
117 &self.compiler.codegen_backend()
118 }
119
120 pub fn dep_graph_future(&self) -> Result<&Query<Option<DepGraphFuture>>> {
121 self.dep_graph_future.compute(|| {
122 Ok(self
123 .session()
124 .opts
125 .build_dep_graph()
126 .then(|| rustc_incremental::load_dep_graph(self.session())))
127 })
128 }
129
130 pub fn parse(&self) -> Result<&Query<ast::Crate>> {
131 self.parse.compute(|| {
132 passes::parse(self.session(), &self.compiler.input).map_err(|mut parse_error| {
133 parse_error.emit();
134 ErrorReported
135 })
136 })
137 }
138
139 pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc<LintStore>)>> {
140 self.register_plugins.compute(|| {
141 let crate_name = self.crate_name()?.peek().clone();
142 let krate = self.parse()?.take();
143
144 let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
145 let result = passes::register_plugins(
146 self.session(),
147 &*self.codegen_backend().metadata_loader(),
148 self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
149 krate,
150 &crate_name,
151 )?;
152
153 // Compute the dependency graph (in the background). We want to do
154 // this as early as possible, to give the DepGraph maximum time to
155 // load before dep_graph() is called, but it also can't happen
156 // until after rustc_incremental::prepare_session_directory() is
157 // called, which happens within passes::register_plugins().
158 self.dep_graph_future().ok();
159
160 Ok(result)
161 })
162 }
163
164 pub fn crate_name(&self) -> Result<&Query<String>> {
165 self.crate_name.compute(|| {
166 Ok({
167 let parse_result = self.parse()?;
168 let krate = parse_result.peek();
169 // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
170 find_crate_name(self.session(), &krate.attrs, &self.compiler.input)
171 })
172 })
173 }
174
175 pub fn expansion(
176 &self,
177 ) -> Result<&Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>, Lrc<LintStore>)>> {
178 tracing::trace!("expansion");
179 self.expansion.compute(|| {
180 let crate_name = self.crate_name()?.peek().clone();
181 let (krate, lint_store) = self.register_plugins()?.take();
182 let _timer = self.session().timer("configure_and_expand");
183 passes::configure_and_expand(
184 self.session().clone(),
185 lint_store.clone(),
186 self.codegen_backend().metadata_loader(),
187 krate,
188 &crate_name,
189 )
190 .map(|(krate, resolver)| {
191 (krate, Steal::new(Rc::new(RefCell::new(resolver))), lint_store)
192 })
193 })
194 }
195
196 pub fn dep_graph(&self) -> Result<&Query<DepGraph>> {
197 self.dep_graph.compute(|| {
198 Ok(match self.dep_graph_future()?.take() {
199 None => DepGraph::new_disabled(),
200 Some(future) => {
201 let (prev_graph, prev_work_products) =
202 self.session().time("blocked_on_dep_graph_loading", || {
203 future
204 .open()
205 .unwrap_or_else(|e| rustc_incremental::LoadResult::Error {
206 message: format!("could not decode incremental cache: {:?}", e),
207 })
208 .open(self.session())
209 });
210
211 rustc_incremental::build_dep_graph(
212 self.session(),
213 prev_graph,
214 prev_work_products,
215 )
216 .unwrap_or_else(DepGraph::new_disabled)
217 }
218 })
219 })
220 }
221
222 pub fn lower_to_hir(&'tcx self) -> Result<&Query<(&'tcx Crate<'tcx>, Steal<ResolverOutputs>)>> {
223 self.lower_to_hir.compute(|| {
224 let expansion_result = self.expansion()?;
225 let peeked = expansion_result.peek();
226 let krate = &peeked.0;
227 let resolver = peeked.1.steal();
228 let lint_store = &peeked.2;
229 let hir = resolver.borrow_mut().access(|resolver| {
230 Ok(passes::lower_to_hir(
231 self.session(),
232 lint_store,
233 resolver,
234 &*self.dep_graph()?.peek(),
235 &krate,
236 &self.hir_arena,
237 ))
238 })?;
239 let hir = self.hir_arena.alloc(hir);
240 Ok((hir, Steal::new(BoxedResolver::to_resolver_outputs(resolver))))
241 })
242 }
243
244 pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> {
245 self.prepare_outputs.compute(|| {
246 let expansion_result = self.expansion()?;
247 let (krate, boxed_resolver, _) = &*expansion_result.peek();
248 let crate_name = self.crate_name()?.peek();
249 passes::prepare_outputs(
250 self.session(),
251 self.compiler,
252 &krate,
253 &boxed_resolver,
254 &crate_name,
255 )
256 })
257 }
258
259 pub fn global_ctxt(&'tcx self) -> Result<&Query<QueryContext<'tcx>>> {
260 self.global_ctxt.compute(|| {
261 let crate_name = self.crate_name()?.peek().clone();
262 let outputs = self.prepare_outputs()?.peek().clone();
263 let lint_store = self.expansion()?.peek().2.clone();
264 let hir = self.lower_to_hir()?.peek();
265 let dep_graph = self.dep_graph()?.peek().clone();
266 let (ref krate, ref resolver_outputs) = &*hir;
267 let _timer = self.session().timer("create_global_ctxt");
268 Ok(passes::create_global_ctxt(
269 self.compiler,
270 lint_store,
271 krate,
272 dep_graph,
273 resolver_outputs.steal(),
274 outputs,
275 &crate_name,
276 &self.queries,
277 &self.gcx,
278 &self.arena,
279 ))
280 })
281 }
282
283 pub fn ongoing_codegen(&'tcx self) -> Result<&Query<Box<dyn Any>>> {
284 self.ongoing_codegen.compute(|| {
285 let outputs = self.prepare_outputs()?;
286 self.global_ctxt()?.peek_mut().enter(|tcx| {
287 tcx.analysis(()).ok();
288
289 // Don't do code generation if there were any errors
290 self.session().compile_status()?;
291
292 // Hook for UI tests.
293 Self::check_for_rustc_errors_attr(tcx);
294
295 Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek()))
296 })
297 })
298 }
299
300 /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used
301 /// to write UI tests that actually test that compilation succeeds without reporting
302 /// an error.
303 fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) {
304 let def_id = match tcx.entry_fn(()) {
305 Some((def_id, _)) => def_id,
306 _ => return,
307 };
308
309 let attrs = &*tcx.get_attrs(def_id);
310 let attrs = attrs.iter().filter(|attr| tcx.sess.check_name(attr, sym::rustc_error));
311 for attr in attrs {
312 match attr.meta_item_list() {
313 // Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`.
314 Some(list)
315 if list.iter().any(|list_item| {
316 matches!(
317 list_item.ident().map(|i| i.name),
318 Some(sym::delay_span_bug_from_inside_query)
319 )
320 }) =>
321 {
322 tcx.ensure().trigger_delay_span_bug(def_id);
323 }
324
325 // Bare `#[rustc_error]`.
326 None => {
327 tcx.sess.span_fatal(
328 tcx.def_span(def_id),
329 "fatal error triggered by #[rustc_error]",
330 );
331 }
332
333 // Some other attribute.
334 Some(_) => {
335 tcx.sess.span_warn(
336 tcx.def_span(def_id),
337 "unexpected annotation used with `#[rustc_error(...)]!",
338 );
339 }
340 }
341 }
342 }
343
344 pub fn linker(&'tcx self) -> Result<Linker> {
345 let sess = self.session().clone();
346 let codegen_backend = self.codegen_backend().clone();
347
348 let dep_graph = self.dep_graph()?.peek().clone();
349 let prepare_outputs = self.prepare_outputs()?.take();
350 let crate_hash = self.global_ctxt()?.peek_mut().enter(|tcx| tcx.crate_hash(LOCAL_CRATE));
351 let ongoing_codegen = self.ongoing_codegen()?.take();
352
353 Ok(Linker {
354 sess,
355 codegen_backend,
356
357 dep_graph,
358 prepare_outputs,
359 crate_hash,
360 ongoing_codegen,
361 })
362 }
363 }
364
365 pub struct Linker {
366 // compilation inputs
367 sess: Lrc<Session>,
368 codegen_backend: Lrc<Box<dyn CodegenBackend>>,
369
370 // compilation outputs
371 dep_graph: DepGraph,
372 prepare_outputs: OutputFilenames,
373 crate_hash: Svh,
374 ongoing_codegen: Box<dyn Any>,
375 }
376
377 impl Linker {
378 pub fn link(self) -> Result<()> {
379 let (codegen_results, work_products) =
380 self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess)?;
381
382 self.sess.compile_status()?;
383
384 let sess = &self.sess;
385 let dep_graph = self.dep_graph;
386 sess.time("serialize_work_products", || {
387 rustc_incremental::save_work_product_index(&sess, &dep_graph, work_products)
388 });
389
390 let prof = self.sess.prof.clone();
391 prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph));
392
393 // Now that we won't touch anything in the incremental compilation directory
394 // any more, we can finalize it (which involves renaming it)
395 rustc_incremental::finalize_session_directory(&self.sess, self.crate_hash);
396
397 if !self
398 .sess
399 .opts
400 .output_types
401 .keys()
402 .any(|&i| i == OutputType::Exe || i == OutputType::Metadata)
403 {
404 return Ok(());
405 }
406
407 if sess.opts.debugging_opts.no_link {
408 // FIXME: use a binary format to encode the `.rlink` file
409 let rlink_data = json::encode(&codegen_results).map_err(|err| {
410 sess.fatal(&format!("failed to encode rlink: {}", err));
411 })?;
412 let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT);
413 std::fs::write(&rlink_file, rlink_data).map_err(|err| {
414 sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err));
415 })?;
416 return Ok(());
417 }
418
419 let _timer = sess.prof.verbose_generic_activity("link_crate");
420 self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs)
421 }
422 }
423
424 impl Compiler {
425 pub fn enter<F, T>(&self, f: F) -> T
426 where
427 F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T,
428 {
429 let mut _timer = None;
430 let queries = Queries::new(&self);
431 let ret = f(&queries);
432
433 // NOTE: intentionally does not compute the global context if it hasn't been built yet,
434 // since that likely means there was a parse error.
435 if let Some(Ok(gcx)) = &mut *queries.global_ctxt.result.borrow_mut() {
436 // We assume that no queries are run past here. If there are new queries
437 // after this point, they'll show up as "<unknown>" in self-profiling data.
438 {
439 let _prof_timer =
440 queries.session().prof.generic_activity("self_profile_alloc_query_strings");
441 gcx.enter(rustc_query_impl::alloc_self_profile_query_strings);
442 }
443
444 if self.session().opts.debugging_opts.query_stats {
445 gcx.enter(rustc_query_impl::print_stats);
446 }
447
448 self.session()
449 .time("serialize_dep_graph", || gcx.enter(rustc_incremental::save_dep_graph));
450 }
451
452 _timer = Some(self.session().timer("free_global_ctxt"));
453
454 ret
455 }
456 }