]> git.lizzy.rs Git - rust.git/blob - src/librustc_interface/queries.rs
Rollup merge of #63055 - Mark-Simulacrum:save-analysis-clean-2, r=Xanewok
[rust.git] / src / librustc_interface / queries.rs
1 use crate::interface::{Compiler, Result};
2 use crate::passes::{self, BoxedResolver, ExpansionResult, BoxedGlobalCtxt, PluginInfo};
3
4 use rustc_incremental::DepGraphFuture;
5 use rustc::session::config::{OutputFilenames, OutputType};
6 use rustc::util::common::{time, ErrorReported};
7 use rustc::hir;
8 use rustc::hir::def_id::LOCAL_CRATE;
9 use rustc::ty::steal::Steal;
10 use rustc::dep_graph::DepGraph;
11 use std::cell::{Ref, RefMut, RefCell};
12 use std::rc::Rc;
13 use std::sync::mpsc;
14 use std::any::Any;
15 use std::mem;
16 use syntax::{self, ast};
17
18 /// Represent the result of a query.
19 /// This result can be stolen with the `take` method and returned with the `give` method.
20 pub struct Query<T> {
21     result: RefCell<Option<Result<T>>>,
22 }
23
24 impl<T> Query<T> {
25     fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<&Query<T>> {
26         let mut result = self.result.borrow_mut();
27         if result.is_none() {
28             *result = Some(f());
29         }
30         result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err)
31     }
32
33     /// Takes ownership of the query result. Further attempts to take or peek the query
34     /// result will panic unless it is returned by calling the `give` method.
35     pub fn take(&self) -> T {
36         self.result
37             .borrow_mut()
38             .take()
39             .expect("missing query result")
40             .unwrap()
41     }
42
43     /// Returns a stolen query result. Panics if there's already a result.
44     pub fn give(&self, value: T) {
45         let mut result = self.result.borrow_mut();
46         assert!(result.is_none(), "a result already exists");
47         *result = Some(Ok(value));
48     }
49
50     /// Borrows the query result using the RefCell. Panics if the result is stolen.
51     pub fn peek(&self) -> Ref<'_, T> {
52         Ref::map(self.result.borrow(), |r| {
53             r.as_ref().unwrap().as_ref().expect("missing query result")
54         })
55     }
56
57     /// Mutably borrows the query result using the RefCell. Panics if the result is stolen.
58     pub fn peek_mut(&self) -> RefMut<'_, T> {
59         RefMut::map(self.result.borrow_mut(), |r| {
60             r.as_mut().unwrap().as_mut().expect("missing query result")
61         })
62     }
63 }
64
65 impl<T> Default for Query<T> {
66     fn default() -> Self {
67         Query {
68             result: RefCell::new(None),
69         }
70     }
71 }
72
73 #[derive(Default)]
74 pub(crate) struct Queries {
75     dep_graph_future: Query<Option<DepGraphFuture>>,
76     parse: Query<ast::Crate>,
77     crate_name: Query<String>,
78     register_plugins: Query<(ast::Crate, PluginInfo)>,
79     expansion: Query<(ast::Crate, Rc<Option<RefCell<BoxedResolver>>>)>,
80     dep_graph: Query<DepGraph>,
81     lower_to_hir: Query<(Steal<hir::map::Forest>, ExpansionResult)>,
82     prepare_outputs: Query<OutputFilenames>,
83     codegen_channel: Query<(Steal<mpsc::Sender<Box<dyn Any + Send>>>,
84                             Steal<mpsc::Receiver<Box<dyn Any + Send>>>)>,
85     global_ctxt: Query<BoxedGlobalCtxt>,
86     ongoing_codegen: Query<Box<dyn Any>>,
87     link: Query<()>,
88 }
89
90 impl Compiler {
91     pub fn dep_graph_future(&self) -> Result<&Query<Option<DepGraphFuture>>> {
92         self.queries.dep_graph_future.compute(|| {
93             Ok(if self.session().opts.build_dep_graph() {
94                 Some(rustc_incremental::load_dep_graph(self.session()))
95             } else {
96                 None
97             })
98         })
99     }
100
101     pub fn parse(&self) -> Result<&Query<ast::Crate>> {
102         self.queries.parse.compute(|| {
103             passes::parse(self.session(), &self.input).map_err(
104                 |mut parse_error| {
105                     parse_error.emit();
106                     ErrorReported
107                 },
108             )
109         })
110     }
111
112     pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, PluginInfo)>> {
113         self.queries.register_plugins.compute(|| {
114             let crate_name = self.crate_name()?.peek().clone();
115             let krate = self.parse()?.take();
116
117             passes::register_plugins(
118                 self,
119                 self.session(),
120                 self.cstore(),
121                 krate,
122                 &crate_name,
123             )
124         })
125     }
126
127     pub fn crate_name(&self) -> Result<&Query<String>> {
128         self.queries.crate_name.compute(|| {
129             let parse_result = self.parse()?;
130             let krate = parse_result.peek();
131             let result = match self.crate_name {
132                 Some(ref crate_name) => crate_name.clone(),
133                 None => rustc_codegen_utils::link::find_crate_name(
134                     Some(self.session()),
135                     &krate.attrs,
136                     &self.input
137                 ),
138             };
139             Ok(result)
140         })
141     }
142
143     pub fn expansion(
144         &self
145     ) -> Result<&Query<(ast::Crate, Rc<Option<RefCell<BoxedResolver>>>)>> {
146         self.queries.expansion.compute(|| {
147             let crate_name = self.crate_name()?.peek().clone();
148             let (krate, plugin_info) = self.register_plugins()?.take();
149             passes::configure_and_expand(
150                 self.sess.clone(),
151                 self.cstore().clone(),
152                 krate,
153                 &crate_name,
154                 plugin_info,
155             ).map(|(krate, resolver)| (krate, Rc::new(Some(RefCell::new(resolver)))))
156         })
157     }
158
159     pub fn dep_graph(&self) -> Result<&Query<DepGraph>> {
160         self.queries.dep_graph.compute(|| {
161             Ok(match self.dep_graph_future()?.take() {
162                 None => DepGraph::new_disabled(),
163                 Some(future) => {
164                     let (prev_graph, prev_work_products) =
165                         time(self.session(), "blocked while dep-graph loading finishes", || {
166                             future.open().unwrap_or_else(|e| rustc_incremental::LoadResult::Error {
167                                 message: format!("could not decode incremental cache: {:?}", e),
168                             }).open(self.session())
169                         });
170                     DepGraph::new(prev_graph, prev_work_products)
171                 }
172             })
173         })
174     }
175
176     pub fn lower_to_hir(&self) -> Result<&Query<(Steal<hir::map::Forest>, ExpansionResult)>> {
177         self.queries.lower_to_hir.compute(|| {
178             let expansion_result = self.expansion()?;
179             let (krate, resolver) = expansion_result.take();
180             let resolver_ref = &*resolver;
181             let hir = Steal::new(resolver_ref.as_ref().unwrap().borrow_mut().access(|resolver| {
182                 passes::lower_to_hir(
183                     self.session(),
184                     self.cstore(),
185                     resolver,
186                     &*self.dep_graph()?.peek(),
187                     &krate
188                 )
189             })?);
190             expansion_result.give((krate, Rc::new(None)));
191             Ok((hir, BoxedResolver::to_expansion_result(resolver)))
192         })
193     }
194
195     pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> {
196         self.queries.prepare_outputs.compute(|| {
197             self.lower_to_hir()?;
198             let krate = self.expansion()?;
199             let krate = krate.peek();
200             let crate_name = self.crate_name()?;
201             let crate_name = crate_name.peek();
202             passes::prepare_outputs(self.session(), self, &krate.0, &*crate_name)
203         })
204     }
205
206     pub fn codegen_channel(&self) -> Result<&Query<(Steal<mpsc::Sender<Box<dyn Any + Send>>>,
207                                                     Steal<mpsc::Receiver<Box<dyn Any + Send>>>)>> {
208         self.queries.codegen_channel.compute(|| {
209             let (tx, rx) = mpsc::channel();
210             Ok((Steal::new(tx), Steal::new(rx)))
211         })
212     }
213
214     pub fn global_ctxt(&self) -> Result<&Query<BoxedGlobalCtxt>> {
215         self.queries.global_ctxt.compute(|| {
216             let crate_name = self.crate_name()?.peek().clone();
217             let outputs = self.prepare_outputs()?.peek().clone();
218             let hir = self.lower_to_hir()?;
219             let hir = hir.peek();
220             let (ref hir_forest, ref expansion) = *hir;
221             let tx = self.codegen_channel()?.peek().0.steal();
222             Ok(passes::create_global_ctxt(
223                 self,
224                 hir_forest.steal(),
225                 expansion.defs.steal(),
226                 expansion.resolutions.steal(),
227                 outputs,
228                 tx,
229                 &crate_name))
230         })
231     }
232
233     pub fn ongoing_codegen(&self) -> Result<&Query<Box<dyn Any>>> {
234         self.queries.ongoing_codegen.compute(|| {
235             let rx = self.codegen_channel()?.peek().1.steal();
236             let outputs = self.prepare_outputs()?;
237             self.global_ctxt()?.peek_mut().enter(|tcx| {
238                 tcx.analysis(LOCAL_CRATE).ok();
239
240                 // Don't do code generation if there were any errors
241                 self.session().compile_status()?;
242
243                 Ok(passes::start_codegen(
244                     &***self.codegen_backend(),
245                     tcx,
246                     rx,
247                     &*outputs.peek()
248                 ))
249             })
250         })
251     }
252
253     pub fn link(&self) -> Result<&Query<()>> {
254         self.queries.link.compute(|| {
255             let sess = self.session();
256
257             let ongoing_codegen = self.ongoing_codegen()?.take();
258
259             self.codegen_backend().join_codegen_and_link(
260                 ongoing_codegen,
261                 sess,
262                 &*self.dep_graph()?.peek(),
263                 &*self.prepare_outputs()?.peek(),
264             ).map_err(|_| ErrorReported)?;
265
266             Ok(())
267         })
268     }
269
270     pub fn compile(&self) -> Result<()> {
271         self.prepare_outputs()?;
272
273         if self.session().opts.output_types.contains_key(&OutputType::DepInfo)
274             && self.session().opts.output_types.len() == 1
275         {
276             return Ok(())
277         }
278
279         self.global_ctxt()?;
280
281         // Drop AST after creating GlobalCtxt to free memory
282         mem::drop(self.expansion()?.take());
283
284         self.ongoing_codegen()?;
285
286         // Drop GlobalCtxt after starting codegen to free memory
287         mem::drop(self.global_ctxt()?.take());
288
289         self.link().map(|_| ())
290     }
291 }