Skip to content

Commit

Permalink
Some cleanups and added comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoxc committed Mar 24, 2018
1 parent 29a4ec0 commit 4f7d0fd
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 86 deletions.
92 changes: 67 additions & 25 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
{
Expand All @@ -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
{
Expand All @@ -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
{
Expand All @@ -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
{
Expand Down
69 changes: 34 additions & 35 deletions src/librustc/ty/maps/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {}
}
2 changes: 1 addition & 1 deletion src/librustc/ty/maps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 4f7d0fd

Please sign in to comment.