1
Fork 0

Some cleanups and added comments

This commit is contained in:
John Kåre Alsaker 2018-03-24 06:19:20 +01:00
parent 29a4ec0d43
commit 4f7d0fde1c
4 changed files with 164 additions and 93 deletions

View file

@ -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,6 +1786,37 @@ pub mod tls {
}
}
/// 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;
assert!(context.tcx.gcx as *const _ as usize == gcx);
let context: &ImplicitCtxt = mem::transmute(context);
f(context)
}
})
}
/// 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
{
@ -1775,31 +1832,16 @@ pub mod tls {
})
}
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;
assert!(context.tcx.gcx as *const _ as usize == gcx);
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
{

View file

@ -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) {}
}

View file

@ -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;

View file

@ -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,