about summary refs log tree commit diff
path: root/src/librustc_interface/queries.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_interface/queries.rs')
-rw-r--r--src/librustc_interface/queries.rs302
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(|_| ())
+    }
+}