about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/ty/context.rs92
-rw-r--r--src/librustc/ty/maps/job.rs69
-rw-r--r--src/librustc/ty/maps/mod.rs2
-rw-r--r--src/librustc/ty/maps/plumbing.rs80
4 files changed, 157 insertions, 86 deletions
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index adb0ecd3984..44b9d61cf02 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1489,10 +1489,13 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
 
 impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> {
     /// Call the closure with a local `TyCtxt` using the given arena.
-    pub fn enter_local<F, R>(&self,
-                             arena: &'tcx DroplessArena,
-                             f: F) -> R
-        where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
+    pub fn enter_local<F, R>(
+        &self,
+        arena: &'tcx DroplessArena,
+        f: F
+    ) -> R
+    where
+        F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
     {
         let interners = CtxtInterners::new(arena);
         let tcx = TyCtxt {
@@ -1665,12 +1668,23 @@ pub mod tls {
     use rustc_data_structures::OnDrop;
     use rustc_data_structures::sync::Lrc;
 
+    /// This is the implicit state of rustc. It contains the current
+    /// TyCtxt and query. It is updated when creating a local interner or
+    /// executing a new query. Whenever there's a TyCtxt value available
+    /// you should also have access to an ImplicitCtxt through the functions
+    /// in this module.
     #[derive(Clone)]
     pub struct ImplicitCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+        /// The current TyCtxt. Initially created by `enter_global` and updated
+        /// by `enter_local` with a new local interner
         pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
+
+        /// The current query job, if any. This is updated by start_job in
+        /// ty::maps::plumbing when executing a query
         pub query: Option<Lrc<maps::QueryJob<'gcx>>>,
     }
 
+    // A thread local value which stores a pointer to the current ImplicitCtxt
     thread_local!(static TLV: Cell<usize> = Cell::new(0));
 
     fn set_tlv<F: FnOnce() -> R, R>(value: usize, f: F) -> R {
@@ -1684,12 +1698,17 @@ pub mod tls {
         TLV.with(|tlv| tlv.get())
     }
 
+    /// This is a callback from libsyntax as it cannot access the implicit state
+    /// in librustc otherwise
     fn span_debug(span: syntax_pos::Span, f: &mut fmt::Formatter) -> fmt::Result {
         with(|tcx| {
             write!(f, "{}", tcx.sess.codemap().span_to_string(span))
         })
     }
 
+    /// This is a callback from libsyntax as it cannot access the implicit state
+    /// in librustc otherwise. It is used to when diagnostic messages are
+    /// emitted and stores them in the current query, if there is one.
     fn track_diagnostic(diagnostic: &Diagnostic) {
         with_context(|context| {
             if let Some(ref query) = context.query {
@@ -1698,6 +1717,7 @@ pub mod tls {
         })
     }
 
+    /// Sets up the callbacks from libsyntax on the current thread
     pub fn with_thread_locals<F, R>(f: F) -> R
         where F: FnOnce() -> R
     {
@@ -1722,6 +1742,20 @@ pub mod tls {
         })
     }
 
+    /// Sets `context` as the new current ImplicitCtxt for the duration of the function `f`
+    pub fn enter_context<'a, 'gcx: 'tcx, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'gcx, 'tcx>,
+                                                     f: F) -> R
+        where F: FnOnce(&ImplicitCtxt<'a, 'gcx, 'tcx>) -> R
+    {
+        set_tlv(context as *const _ as usize, || {
+            f(&context)
+        })
+    }
+
+    /// Enters GlobalCtxt by setting up libsyntax callbacks and
+    /// creating a initial TyCtxt and ImplicitCtxt.
+    /// This happens once per rustc session and TyCtxts only exists
+    /// inside the `f` function.
     pub fn enter_global<'gcx, F, R>(gcx: &GlobalCtxt<'gcx>, f: F) -> R
         where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'gcx>) -> R
     {
@@ -1740,15 +1774,7 @@ pub mod tls {
         })
     }
 
-    pub fn enter_context<'a, 'gcx: 'tcx, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'gcx, 'tcx>,
-                                                     f: F) -> R
-        where F: FnOnce(&ImplicitCtxt<'a, 'gcx, 'tcx>) -> R
-    {
-        set_tlv(context as *const _ as usize, || {
-            f(&context)
-        })
-    }
-
+    /// Allows access to the current ImplicitCtxt in a closure if one is available
     pub fn with_context_opt<F, R>(f: F) -> R
         where F: for<'a, 'gcx, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'gcx, 'tcx>>) -> R
     {
@@ -1760,46 +1786,62 @@ pub mod tls {
         }
     }
 
-    pub fn with_fully_related_context<'a, 'gcx, 'tcx, F, R>(tcx: TyCtxt<'a, 'gcx, 'tcx>, f: F) -> R
-        where F: for<'b> FnOnce(&ImplicitCtxt<'b, 'gcx, 'tcx>) -> R
+    /// Allows access to the current ImplicitCtxt.
+    /// Panics if there is no ImplicitCtxt available
+    pub fn with_context<F, R>(f: F) -> R
+        where F: for<'a, 'gcx, 'tcx> FnOnce(&ImplicitCtxt<'a, 'gcx, 'tcx>) -> R
+    {
+        with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls")))
+    }
+
+    /// Allows access to the current ImplicitCtxt whose tcx field has the same global
+    /// interner as the tcx argument passed in. This means the closure is given an ImplicitCtxt
+    /// with the same 'gcx lifetime as the TyCtxt passed in.
+    /// This will panic if you pass it a TyCtxt which has a different global interner from
+    /// the current ImplicitCtxt's tcx field.
+    pub fn with_related_context<'a, 'gcx, 'tcx1, F, R>(tcx: TyCtxt<'a, 'gcx, 'tcx1>, f: F) -> R
+        where F: for<'b, 'tcx2> FnOnce(&ImplicitCtxt<'b, 'gcx, 'tcx2>) -> R
     {
         with_context(|context| {
             unsafe {
                 let gcx = tcx.gcx as *const _ as usize;
-                let interners = tcx.interners as *const _ as usize;
                 assert!(context.tcx.gcx as *const _ as usize == gcx);
-                assert!(context.tcx.interners as *const _ as usize == interners);
                 let context: &ImplicitCtxt = mem::transmute(context);
                 f(context)
             }
         })
     }
 
-    pub fn with_related_context<'a, 'gcx, 'tcx1, F, R>(tcx: TyCtxt<'a, 'gcx, 'tcx1>, f: F) -> R
-        where F: for<'b, 'tcx2> FnOnce(&ImplicitCtxt<'b, 'gcx, 'tcx2>) -> R
+    /// Allows access to the current ImplicitCtxt whose tcx field has the same global
+    /// interner and local interner as the tcx argument passed in. This means the closure
+    /// is given an ImplicitCtxt with the same 'tcx and 'gcx lifetimes as the TyCtxt passed in.
+    /// This will panic if you pass it a TyCtxt which has a different global interner or
+    /// a different local interner from the current ImplicitCtxt's tcx field.
+    pub fn with_fully_related_context<'a, 'gcx, 'tcx, F, R>(tcx: TyCtxt<'a, 'gcx, 'tcx>, f: F) -> R
+        where F: for<'b> FnOnce(&ImplicitCtxt<'b, 'gcx, 'tcx>) -> R
     {
         with_context(|context| {
             unsafe {
                 let gcx = tcx.gcx as *const _ as usize;
+                let interners = tcx.interners as *const _ as usize;
                 assert!(context.tcx.gcx as *const _ as usize == gcx);
+                assert!(context.tcx.interners as *const _ as usize == interners);
                 let context: &ImplicitCtxt = mem::transmute(context);
                 f(context)
             }
         })
     }
 
-    pub fn with_context<F, R>(f: F) -> R
-        where F: for<'a, 'gcx, 'tcx> FnOnce(&ImplicitCtxt<'a, 'gcx, 'tcx>) -> R
-    {
-        with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls")))
-    }
-
+    /// Allows access to the TyCtxt in the current ImplicitCtxt.
+    /// Panics if there is no ImplicitCtxt available
     pub fn with<F, R>(f: F) -> R
         where F: for<'a, 'gcx, 'tcx> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
     {
         with_context(|context| f(context.tcx))
     }
 
+    /// Allows access to the TyCtxt in the current ImplicitCtxt.
+    /// The closure is passed None if there is no ImplicitCtxt available
     pub fn with_opt<F, R>(f: F) -> R
         where F: for<'a, 'gcx, 'tcx> FnOnce(Option<TyCtxt<'a, 'gcx, 'tcx>>) -> R
     {
diff --git a/src/librustc/ty/maps/job.rs b/src/librustc/ty/maps/job.rs
index 2ed993bd975..7d756fb16a4 100644
--- a/src/librustc/ty/maps/job.rs
+++ b/src/librustc/ty/maps/job.rs
@@ -8,65 +8,69 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![allow(warnings)]
-
-use std::mem;
-use std::sync::atomic::AtomicBool;
-use std::sync::atomic::Ordering;
-use rustc_data_structures::sync::{Lock, LockGuard, Lrc};
+use rustc_data_structures::sync::{Lock, Lrc};
 use syntax_pos::Span;
 use ty::tls;
 use ty::maps::Query;
 use ty::maps::plumbing::CycleError;
 use ty::context::TyCtxt;
 use errors::Diagnostic;
-use std::process;
-use std::fmt;
-use std::sync::{Arc, Mutex};
-use std::collections::HashSet;
 
-pub struct PoisonedJob;
+/// Indicates the state of a query for a given key in a query map
+pub(super) enum QueryResult<'tcx, T> {
+    /// An already executing query. The query job can be used to await for its completion
+    Started(Lrc<QueryJob<'tcx>>),
+
+    /// The query is complete and produced `T`
+    Complete(T),
 
+    /// The query panicked. Queries trying to wait on this will raise a fatal error / silently panic
+    Poisoned,
+}
+
+/// A span and a query key
 #[derive(Clone, Debug)]
-pub struct StackEntry<'tcx> {
+pub struct QueryInfo<'tcx> {
     pub span: Span,
     pub query: Query<'tcx>,
 }
 
+/// A object representing an active query job.
 pub struct QueryJob<'tcx> {
-    pub entry: StackEntry<'tcx>,
+    pub info: QueryInfo<'tcx>,
+
+    /// The parent query job which created this job and is implicitly waiting on it.
     pub parent: Option<Lrc<QueryJob<'tcx>>>,
-    pub track_diagnostics: bool,
+
+    /// Diagnostic messages which are emitted while the query executes
     pub diagnostics: Lock<Vec<Diagnostic>>,
 }
 
 impl<'tcx> QueryJob<'tcx> {
-    pub fn new(
-        entry: StackEntry<'tcx>,
-        track_diagnostics: bool,
-        parent: Option<Lrc<QueryJob<'tcx>>>,
-    ) -> Self {
+    /// Creates a new query job
+    pub fn new(info: QueryInfo<'tcx>, parent: Option<Lrc<QueryJob<'tcx>>>) -> Self {
         QueryJob {
-            track_diagnostics,
             diagnostics: Lock::new(Vec::new()),
-            entry,
+            info,
             parent,
         }
     }
 
+    /// Awaits for the query job to complete.
+    ///
+    /// For single threaded rustc there's no concurrent jobs running, so if we are waiting for any
+    /// query that means that there is a query cycle, thus this always running a cycle error.
     pub(super) fn await<'lcx>(
         &self,
         tcx: TyCtxt<'_, 'tcx, 'lcx>,
         span: Span,
     ) -> Result<(), CycleError<'tcx>> {
-        // The query is already executing, so this must be a cycle for single threaded rustc,
-        // so we find the cycle and return it
-
+        // Get the current executing query (waiter) and find the waitee amongst its parents
         let mut current_job = tls::with_related_context(tcx, |icx| icx.query.clone());
         let mut cycle = Vec::new();
 
         while let Some(job) = current_job {
-            cycle.insert(0, job.entry.clone());
+            cycle.insert(0, job.info.clone());
 
             if &*job as *const _ == self as *const _ {
                 break;
@@ -78,14 +82,9 @@ impl<'tcx> QueryJob<'tcx> {
         Err(CycleError { span, cycle })
     }
 
-    pub fn signal_complete(&self) {
-        // Signals to waiters that the query is complete.
-        // This is a no-op for single threaded rustc
-    }
-}
-
-pub(super) enum QueryResult<'tcx, T> {
-    Started(Lrc<QueryJob<'tcx>>),
-    Complete(T),
-    Poisoned,
+    /// Signals to waiters that the query is complete.
+    ///
+    /// This does nothing for single threaded rustc,
+    /// as there are no concurrent jobs which could be waiting on us
+    pub fn signal_complete(&self) {}
 }
diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs
index 6dd31baa593..f4977be7877 100644
--- a/src/librustc/ty/maps/mod.rs
+++ b/src/librustc/ty/maps/mod.rs
@@ -67,7 +67,7 @@ use self::plumbing::*;
 pub use self::plumbing::force_from_dep_node;
 
 mod job;
-pub use self::job::{QueryJob, StackEntry, PoisonedJob};
+pub use self::job::{QueryJob, QueryInfo};
 use self::job::QueryResult;
 
 mod keys;
diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs
index 895bf3d7973..c21b53cd427 100644
--- a/src/librustc/ty/maps/plumbing.rs
+++ b/src/librustc/ty/maps/plumbing.rs
@@ -16,7 +16,7 @@ use dep_graph::{DepNodeIndex, DepNode, DepKind, DepNodeColor};
 use errors::DiagnosticBuilder;
 use ty::{TyCtxt};
 use ty::maps::config::QueryDescription;
-use ty::maps::job::{QueryResult, StackEntry};
+use ty::maps::job::{QueryResult, QueryInfo};
 use ty::item_path;
 
 use rustc_data_structures::fx::{FxHashMap};
@@ -62,7 +62,18 @@ pub(super) trait GetCacheInternal<'tcx>: QueryDescription<'tcx> + Sized {
 #[derive(Clone)]
 pub(super) struct CycleError<'tcx> {
     pub(super) span: Span,
-    pub(super) cycle: Vec<StackEntry<'tcx>>,
+    pub(super) cycle: Vec<QueryInfo<'tcx>>,
+}
+
+/// The result of `try_get_lock`
+pub(super) enum TryGetLock<'a, 'tcx: 'a, T, D: QueryDescription<'tcx> + 'a> {
+    /// The query is not yet started. Contains a guard to the map eventually used to start it.
+    NotYetStarted(LockGuard<'a, QueryMap<'tcx, D>>),
+
+    /// The query was already completed.
+    /// Returns the result of the query and its dep node index
+    /// if it succeeded or a cycle error if it failed
+    JobCompleted(Result<(T, DepNodeIndex), CycleError<'tcx>>),
 }
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
@@ -85,7 +96,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             err.span_note(self.sess.codemap().def_span(stack[0].span),
                           &format!("the cycle begins when {}...", stack[0].query.describe(self)));
 
-            for &StackEntry { span, ref query, .. } in &stack[1..] {
+            for &QueryInfo { span, ref query, .. } in &stack[1..] {
                 err.span_note(self.sess.codemap().def_span(span),
                               &format!("...which then requires {}...", query.describe(self)));
             }
@@ -252,11 +263,14 @@ macro_rules! define_maps {
                 DepNode::new(tcx, $node(*key))
             }
 
-            fn try_get_lock(tcx: TyCtxt<'a, $tcx, 'lcx>,
-                            mut span: Span,
-                            key: &$K)
-                            -> Result<LockGuard<'a, QueryMap<$tcx, Self>>,
-                                      Result<($V, DepNodeIndex), CycleError<$tcx>>>
+            /// Either get the lock of the query map, allowing us to
+            /// start executing the query, or it returns with the result of the query.
+            /// If the query already executed and panicked, this will fatal error / silently panic
+            fn try_get_lock(
+                tcx: TyCtxt<'a, $tcx, 'lcx>,
+                mut span: Span,
+                key: &$K
+            ) -> TryGetLock<'a, $tcx, $V, Self>
             {
                 loop {
                     let lock = tcx.maps.$name.borrow_mut();
@@ -265,7 +279,8 @@ macro_rules! define_maps {
                             QueryResult::Started(ref job) => Some(job.clone()),
                             QueryResult::Complete(ref value) => {
                                 profq_msg!(tcx, ProfileQueriesMsg::CacheHit);
-                                return Err(Ok(((&value.value).clone(), value.index)));
+                                let result = Ok(((&value.value).clone(), value.index));
+                                return TryGetLock::JobCompleted(result);
                             },
                             QueryResult::Poisoned => FatalError.raise(),
                         }
@@ -275,16 +290,19 @@ macro_rules! define_maps {
                     let job = if let Some(job) = job {
                         job
                     } else {
-                        return Ok(lock);
+                        return TryGetLock::NotYetStarted(lock);
                     };
                     mem::drop(lock);
 
+                    // This just matches the behavior of `try_get_with` so the span when
+                    // we await matches the span we would use when executing.
+                    // See the FIXME there.
                     if span == DUMMY_SP && stringify!($name) != "def_span" {
                         span = key.default_span(tcx);
                     }
 
                     if let Err(cycle) = job.await(tcx, span) {
-                        return Err(Err(cycle));
+                        return TryGetLock::JobCompleted(Err(cycle));
                     }
                 }
             }
@@ -306,21 +324,23 @@ macro_rules! define_maps {
                     )
                 );
 
-                macro_rules! get_lock {
+                /// Get the lock used to start the query or
+                /// return the result of the completed query
+                macro_rules! get_lock_or_return {
                     () => {{
                         match Self::try_get_lock(tcx, span, &key) {
-                            Ok(lock) => lock,
-                            Err(result) => {
+                            TryGetLock::NotYetStarted(lock) => lock,
+                            TryGetLock::JobCompleted(result) => {
                                 return result.map(|(v, index)| {
                                     tcx.dep_graph.read_index(index);
                                     v
-                                });
-                            },
+                                })
+                            }
                         }
                     }}
                 }
 
-                let mut lock = get_lock!();
+                let mut lock = get_lock_or_return!();
 
                 // FIXME(eddyb) Get more valid Span's on queries.
                 // def_span guard is necessary to prevent a recursive loop,
@@ -331,7 +351,7 @@ macro_rules! define_maps {
                     // So we drop the lock here and reacquire it
                     mem::drop(lock);
                     span = key.default_span(tcx);
-                    lock = get_lock!();
+                    lock = get_lock_or_return!();
                 }
 
                 // Fast path for when incr. comp. is off. `to_dep_node` is
@@ -385,7 +405,7 @@ macro_rules! define_maps {
                                                                         dep_node_index,
                                                                         &dep_node)
                     }
-                    lock = get_lock!();
+                    lock = get_lock_or_return!();
                 }
 
                 match Self::force_with_lock(tcx, key, span, lock, dep_node) {
@@ -421,6 +441,9 @@ macro_rules! define_maps {
                 }
             }
 
+            /// Creates a job for the query and updates the query map indicating that it started.
+            /// Then it changes ImplicitCtxt to point to the new query job while it executes.
+            /// If the query panics, this updates the query map to indicate so.
             fn start_job<F, R>(tcx: TyCtxt<'_, $tcx, 'lcx>,
                                span: Span,
                                key: $K,
@@ -431,21 +454,25 @@ macro_rules! define_maps {
             {
                 let query = Query::$name(Clone::clone(&key));
 
-                let entry = StackEntry {
+                let entry = QueryInfo {
                     span,
                     query,
                 };
 
+                // The TyCtxt stored in TLS has the same global interner lifetime
+                // as `tcx`, so we use `with_related_context` to relate the 'gcx lifetimes
+                // when accessing the ImplicitCtxt
                 let (r, job) = ty::tls::with_related_context(tcx, move |icx| {
-                    let job = Lrc::new(QueryJob::new(entry, true, icx.query.clone()));
+                    let job = Lrc::new(QueryJob::new(entry, icx.query.clone()));
 
+                    // Store the job in the query map and drop the lock to allow
+                    // others to wait it
                     map.map.entry(key).or_insert(QueryResult::Started(job.clone()));
-
                     mem::drop(map);
 
                     let r = {
                         let on_drop = OnDrop(|| {
-                            // Poison the query so jobs waiting on it panics
+                            // Poison the query so jobs waiting on it panic
                             tcx.maps
                             .$name
                             .borrow_mut()
@@ -456,11 +483,13 @@ macro_rules! define_maps {
                             job.signal_complete();
                         });
 
+                        // Update the ImplicitCtxt to point to our new query job
                         let icx = ty::tls::ImplicitCtxt {
                             tcx,
                             query: Some(job.clone()),
                         };
 
+                        // Use the ImplicitCtxt while we execute the query
                         let r = ty::tls::enter_context(&icx, |icx| {
                             compute(icx.tcx)
                         });
@@ -473,6 +502,7 @@ macro_rules! define_maps {
                     (r, job)
                 });
 
+                // Extract the diagnostic from the job
                 let diagnostics: Vec<_> = mem::replace(&mut *job.diagnostics.lock(), Vec::new());
 
                 Ok(((r, diagnostics), job))
@@ -590,8 +620,8 @@ macro_rules! define_maps {
                 // We may be concurrently trying both execute and force a query
                 // Ensure that only one of them runs the query
                 let lock = match Self::try_get_lock(tcx, span, &key) {
-                    Ok(lock) => lock,
-                    Err(result) => return result,
+                    TryGetLock::NotYetStarted(lock) => lock,
+                    TryGetLock::JobCompleted(result) => return result,
                 };
                 Self::force_with_lock(tcx,
                                       key,