Some cleanups and added comments
This commit is contained in:
parent
29a4ec0d43
commit
4f7d0fde1c
4 changed files with 164 additions and 93 deletions
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue