diff options
Diffstat (limited to 'src/librustc_interface/queries.rs')
| -rw-r--r-- | src/librustc_interface/queries.rs | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs new file mode 100644 index 00000000000..57ced0464d9 --- /dev/null +++ b/src/librustc_interface/queries.rs @@ -0,0 +1,302 @@ +use interface::{Compiler, Result}; +use passes::{self, BoxedResolver, ExpansionResult, BoxedGlobalCtxt, PluginInfo}; +use rustc_incremental::DepGraphFuture; +use rustc_data_structures::sync::Lrc; +use rustc::session::config::{Input, OutputFilenames, OutputType}; +use rustc::session::Session; +use rustc::util::common::{time, ErrorReported}; +use rustc::util::profiling::ProfileCategory; +use rustc::lint; +use rustc::hir; +use rustc::hir::def_id::LOCAL_CRATE; +use rustc::ty; +use rustc::ty::steal::Steal; +use rustc::dep_graph::DepGraph; +use rustc_passes::hir_stats; +use rustc_plugin::registry::Registry; +use serialize::json; +use std::cell::{Ref, RefMut, RefCell}; +use std::ops::Deref; +use std::rc::Rc; +use std::sync::mpsc; +use std::any::Any; +use std::mem; +use syntax::parse::{self, PResult}; +use syntax::util::node_count::NodeCounter; +use syntax::{self, ast, attr, diagnostics, visit}; +use syntax_pos::hygiene; + +/// Represent the result of a query. +/// This result can be stolen with the `take` method and returned with the `give` method. +pub struct Query<T> { + result: RefCell<Option<Result<T>>>, +} + +impl<T> Query<T> { + fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<&Query<T>> { + let mut result = self.result.borrow_mut(); + if result.is_none() { + *result = Some(f()); + } + result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err) + } + + /// Takes ownership of the query result. Further attempts to take or peek the query + /// result will panic unless it is returned by calling the `give` method. + pub fn take(&self) -> T { + self.result + .borrow_mut() + .take() + .expect("missing query result") + .unwrap() + } + + /// Returns a stolen query result. Panics if there's already a result. + pub fn give(&self, value: T) { + let mut result = self.result.borrow_mut(); + assert!(result.is_none(), "a result already exists"); + *result = Some(Ok(value)); + } + + /// Borrows the query result using the RefCell. Panics if the result is stolen. + pub fn peek(&self) -> Ref<'_, T> { + Ref::map(self.result.borrow(), |r| { + r.as_ref().unwrap().as_ref().expect("missing query result") + }) + } + + /// Mutably borrows the query result using the RefCell. Panics if the result is stolen. + pub fn peek_mut(&self) -> RefMut<'_, T> { + RefMut::map(self.result.borrow_mut(), |r| { + r.as_mut().unwrap().as_mut().expect("missing query result") + }) + } +} + +impl<T> Default for Query<T> { + fn default() -> Self { + Query { + result: RefCell::new(None), + } + } +} + +#[derive(Default)] +pub(crate) struct Queries { + dep_graph_future: Query<Option<DepGraphFuture>>, + parse: Query<ast::Crate>, + crate_name: Query<String>, + register_plugins: Query<(ast::Crate, PluginInfo)>, + expansion: Query<(ast::Crate, Rc<Option<RefCell<BoxedResolver>>>)>, + dep_graph: Query<DepGraph>, + lower_to_hir: Query<(Steal<hir::map::Forest>, ExpansionResult)>, + prepare_outputs: Query<OutputFilenames>, + codegen_channel: Query<(Steal<mpsc::Sender<Box<dyn Any + Send>>>, + Steal<mpsc::Receiver<Box<dyn Any + Send>>>)>, + global_ctxt: Query<BoxedGlobalCtxt>, + ongoing_codegen: Query<Box<dyn Any>>, + link: Query<()>, +} + +impl Compiler { + pub fn dep_graph_future(&self) -> Result<&Query<Option<DepGraphFuture>>> { + self.queries.dep_graph_future.compute(|| { + Ok(if self.session().opts.build_dep_graph() { + Some(rustc_incremental::load_dep_graph(self.session())) + } else { + None + }) + }) + } + + pub fn parse(&self) -> Result<&Query<ast::Crate>> { + self.queries.parse.compute(|| { + passes::parse(self.session(), &self.input).map_err( + |mut parse_error| { + parse_error.emit(); + ErrorReported + }, + ) + }) + } + + pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, PluginInfo)>> { + self.queries.register_plugins.compute(|| { + let crate_name = self.crate_name()?.peek().clone(); + let krate = self.parse()?.take(); + + passes::register_plugins( + self, + self.session(), + self.cstore(), + krate, + &crate_name, + ) + }) + } + + pub fn crate_name(&self) -> Result<&Query<String>> { + self.queries.crate_name.compute(|| { + let parse_result = self.parse()?; + let krate = parse_result.peek(); + let result = match self.crate_name { + Some(ref crate_name) => crate_name.clone(), + None => rustc_codegen_utils::link::find_crate_name( + Some(self.session()), + &krate.attrs, + &self.input + ), + }; + Ok(result) + }) + } + + pub fn expansion( + &self + ) -> Result<&Query<(ast::Crate, Rc<Option<RefCell<BoxedResolver>>>)>> { + self.queries.expansion.compute(|| { + let crate_name = self.crate_name()?.peek().clone(); + let (krate, plugin_info) = self.register_plugins()?.take(); + passes::configure_and_expand( + self.sess.clone(), + self.cstore().clone(), + krate, + &crate_name, + plugin_info, + ).map(|(krate, resolver)| (krate, Rc::new(Some(RefCell::new(resolver))))) + }) + } + + pub fn dep_graph(&self) -> Result<&Query<DepGraph>> { + self.queries.dep_graph.compute(|| { + Ok(match self.dep_graph_future()?.take() { + None => DepGraph::new_disabled(), + Some(future) => { + let (prev_graph, prev_work_products) = + time(self.session(), "blocked while dep-graph loading finishes", || { + future.open().unwrap_or_else(|e| rustc_incremental::LoadResult::Error { + message: format!("could not decode incremental cache: {:?}", e), + }).open(self.session()) + }); + DepGraph::new(prev_graph, prev_work_products) + } + }) + }) + } + + pub fn lower_to_hir(&self) -> Result<&Query<(Steal<hir::map::Forest>, ExpansionResult)>> { + self.queries.lower_to_hir.compute(|| { + let expansion_result = self.expansion()?; + let (krate, resolver) = expansion_result.take(); + let resolver_ref = &*resolver; + let hir = Steal::new(resolver_ref.as_ref().unwrap().borrow_mut().access(|resolver| { + passes::lower_to_hir( + self.session(), + self.cstore(), + resolver, + &*self.dep_graph()?.peek(), + &krate + ) + })?); + expansion_result.give((krate, Rc::new(None))); + Ok((hir, BoxedResolver::to_expansion_result(resolver))) + }) + } + + pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> { + self.queries.prepare_outputs.compute(|| { + self.lower_to_hir()?; + let krate = self.expansion()?; + let krate = krate.peek(); + let crate_name = self.crate_name()?; + let crate_name = crate_name.peek(); + passes::prepare_outputs(self.session(), self, &krate.0, &*crate_name) + }) + } + + pub fn codegen_channel(&self) -> Result<&Query<(Steal<mpsc::Sender<Box<dyn Any + Send>>>, + Steal<mpsc::Receiver<Box<dyn Any + Send>>>)>> { + self.queries.codegen_channel.compute(|| { + let (tx, rx) = mpsc::channel(); + Ok((Steal::new(tx), Steal::new(rx))) + }) + } + + pub fn global_ctxt(&self) -> Result<&Query<BoxedGlobalCtxt>> { + self.queries.global_ctxt.compute(|| { + let crate_name = self.crate_name()?.peek().clone(); + let outputs = self.prepare_outputs()?.peek().clone(); + let hir = self.lower_to_hir()?; + let hir = hir.peek(); + let (ref hir_forest, ref expansion) = *hir; + let tx = self.codegen_channel()?.peek().0.steal(); + Ok(passes::create_global_ctxt( + self, + hir_forest.steal(), + expansion.defs.steal(), + expansion.resolutions.steal(), + outputs, + tx, + &crate_name)) + }) + } + + pub fn ongoing_codegen(&self) -> Result<&Query<Box<dyn Any>>> { + self.queries.ongoing_codegen.compute(|| { + let rx = self.codegen_channel()?.peek().1.steal(); + let outputs = self.prepare_outputs()?; + self.global_ctxt()?.peek_mut().enter(|tcx| { + tcx.analysis(LOCAL_CRATE).ok(); + + // Don't do code generation if there were any errors + self.session().compile_status()?; + + Ok(passes::start_codegen( + &***self.codegen_backend(), + tcx, + rx, + &*outputs.peek() + )) + }) + }) + } + + pub fn link(&self) -> Result<&Query<()>> { + self.queries.link.compute(|| { + let sess = self.session(); + + let ongoing_codegen = self.ongoing_codegen()?.take(); + + self.codegen_backend().join_codegen_and_link( + ongoing_codegen, + sess, + &*self.dep_graph()?.peek(), + &*self.prepare_outputs()?.peek(), + ).map_err(|_| ErrorReported)?; + + Ok(()) + }) + } + + pub fn compile(&self) -> Result<()> { + self.prepare_outputs()?; + + if self.session().opts.output_types.contains_key(&OutputType::DepInfo) + && self.session().opts.output_types.len() == 1 + { + return Ok(()) + } + + self.global_ctxt()?; + + // Drop AST after creating GlobalCtxt to free memory + mem::drop(self.expansion()?.take()); + + self.ongoing_codegen()?; + + // Drop GlobalCtxt after starting codegen to free memory + mem::drop(self.global_ctxt()?.take()); + + self.link().map(|_| ()) + } +} |
