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