Skip to content

Commit

Permalink
rustc: Capture diagnostics from all queries
Browse files Browse the repository at this point in the history
This commit alters the `rustc::ty::maps` implementation to ensure that all
output diagnostics from the compiler are tracked for the duration of each query.
These are then intended to be replayed back the first time a cached value is
loaded, and otherwise the cache should operate the same as it does today.

Closes rust-lang#42513
  • Loading branch information
alexcrichton committed Aug 24, 2017
1 parent 2c0558f commit 8e95b3a
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 28 deletions.
82 changes: 60 additions & 22 deletions src/librustc/ty/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// except according to those terms.

use dep_graph::{DepConstructor, DepNode, DepNodeIndex};
use errors::{Diagnostic, DiagnosticBuilder};
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use hir::def::Def;
use hir;
Expand All @@ -32,7 +33,7 @@ use util::common::{profq_msg, ProfileQueriesMsg};

use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::FxHashMap;
use std::cell::{RefCell, RefMut};
use std::cell::{RefCell, RefMut, Cell};
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
Expand Down Expand Up @@ -188,7 +189,18 @@ impl<'tcx> Value<'tcx> for ty::SymbolName {

struct QueryMap<D: QueryDescription> {
phantom: PhantomData<D>,
map: FxHashMap<D::Key, (D::Value, DepNodeIndex)>,
map: FxHashMap<D::Key, QueryValue<D::Value>>,
}

struct QueryValue<T> {
value: T,
index: DepNodeIndex,
diagnostics: Option<Box<QueryDiagnostics>>,
}

struct QueryDiagnostics {
diagnostics: Vec<Diagnostic>,
emitted_diagnostics: Cell<bool>,
}

impl<M: QueryDescription> QueryMap<M> {
Expand Down Expand Up @@ -618,10 +630,20 @@ macro_rules! define_maps {
)
);

if let Some(&(ref result, dep_node_index)) = tcx.maps.$name.borrow().map.get(&key) {
tcx.dep_graph.read_index(dep_node_index);
if let Some(value) = tcx.maps.$name.borrow().map.get(&key) {
if let Some(ref d) = value.diagnostics {
if !d.emitted_diagnostics.get() {
d.emitted_diagnostics.set(true);
let handle = tcx.sess.diagnostic();
for diagnostic in d.diagnostics.iter() {
DiagnosticBuilder::new_diagnostic(handle, diagnostic.clone())
.emit();
}
}
}
profq_msg!(tcx, ProfileQueriesMsg::CacheHit);
return Ok(f(result));
tcx.dep_graph.read_index(value.index);
return Ok(f(&value.value));
}
// else, we are going to run the provider:
profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin);
Expand All @@ -633,36 +655,52 @@ macro_rules! define_maps {
span = key.default_span(tcx)
}

let (result, dep_node_index) = tcx.cycle_check(span, Query::$name(key), || {
let res = tcx.cycle_check(span, Query::$name(key), || {
let dep_node = Self::to_dep_node(tcx, &key);

if dep_node.kind.is_anon() {
tcx.dep_graph.with_anon_task(dep_node.kind, || {
let provider = tcx.maps.providers[key.map_crate()].$name;
provider(tcx.global_tcx(), key)
})
} else {
fn run_provider<'a, 'tcx, 'lcx>(tcx: TyCtxt<'a, 'tcx, 'lcx>,
key: $K)
-> $V {
let provider = tcx.maps.providers[key.map_crate()].$name;
provider(tcx.global_tcx(), key)
tcx.sess.diagnostic().track_diagnostics(|| {
if dep_node.kind.is_anon() {
tcx.dep_graph.with_anon_task(dep_node.kind, || {
let provider = tcx.maps.providers[key.map_crate()].$name;
provider(tcx.global_tcx(), key)
})
} else {
fn run_provider<'a, 'tcx, 'lcx>(tcx: TyCtxt<'a, 'tcx, 'lcx>,
key: $K)
-> $V {
let provider = tcx.maps.providers[key.map_crate()].$name;
provider(tcx.global_tcx(), key)
}

tcx.dep_graph.with_task(dep_node, tcx, key, run_provider)
}

tcx.dep_graph.with_task(dep_node, tcx, key, run_provider)
}
})
})?;
profq_msg!(tcx, ProfileQueriesMsg::ProviderEnd);
let ((result, dep_node_index), diagnostics) = res;

tcx.dep_graph.read_index(dep_node_index);

let value = QueryValue {
value: result,
index: dep_node_index,
diagnostics: if diagnostics.len() == 0 {
None
} else {
Some(Box::new(QueryDiagnostics {
diagnostics,
emitted_diagnostics: Cell::new(true),
}))
},
};

Ok(f(&tcx.maps
.$name
.borrow_mut()
.map
.entry(key)
.or_insert((result, dep_node_index))
.0))
.or_insert(value)
.value))
}

pub fn try_get(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K)
Expand Down
13 changes: 8 additions & 5 deletions src/librustc_errors/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl<'a> DiagnosticBuilder<'a> {
}
};

self.handler.emitter.borrow_mut().emit(&self);
self.handler.emit_db(&self);
self.cancel();

if is_error {
Expand Down Expand Up @@ -178,10 +178,13 @@ impl<'a> DiagnosticBuilder<'a> {
code: Option<String>,
message: &str)
-> DiagnosticBuilder<'a> {
DiagnosticBuilder {
handler,
diagnostic: Diagnostic::new_with_code(level, code, message)
}
let diagnostic = Diagnostic::new_with_code(level, code, message);
DiagnosticBuilder::new_diagnostic(handler, diagnostic)
}

/// Creates a new `DiagnosticBuilder` with an already constructed diagnostic.
pub fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
DiagnosticBuilder { handler, diagnostic }
}
}

Expand Down
23 changes: 22 additions & 1 deletion src/librustc_errors/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ use emitter::{Emitter, EmitterWriter};

use std::borrow::Cow;
use std::cell::{RefCell, Cell};
use std::{error, fmt};
use std::mem;
use std::rc::Rc;
use std::{error, fmt};

mod diagnostic;
mod diagnostic_builder;
Expand Down Expand Up @@ -275,6 +276,7 @@ pub struct Handler {
treat_err_as_bug: bool,
continue_after_error: Cell<bool>,
delayed_span_bug: RefCell<Option<(MultiSpan, String)>>,
tracked_diagnostics: RefCell<Option<Vec<Diagnostic>>>,
}

impl Handler {
Expand All @@ -298,6 +300,7 @@ impl Handler {
treat_err_as_bug,
continue_after_error: Cell::new(true),
delayed_span_bug: RefCell::new(None),
tracked_diagnostics: RefCell::new(None),
}
}

Expand Down Expand Up @@ -547,6 +550,24 @@ impl Handler {
self.abort_if_errors();
}
}

pub fn track_diagnostics<F, R>(&self, f: F) -> (R, Vec<Diagnostic>)
where F: FnOnce() -> R
{
let prev = mem::replace(&mut *self.tracked_diagnostics.borrow_mut(),
Some(Vec::new()));
let ret = f();
let diagnostics = mem::replace(&mut *self.tracked_diagnostics.borrow_mut(), prev)
.unwrap();
(ret, diagnostics)
}

fn emit_db(&self, db: &DiagnosticBuilder) {
if let Some(ref mut list) = *self.tracked_diagnostics.borrow_mut() {
list.push((**db).clone());
}
self.emitter.borrow_mut().emit(db);
}
}


Expand Down

0 comments on commit 8e95b3a

Please sign in to comment.