Skip to content

Commit

Permalink
Call default_span lazily when query cycles occur instead of in the ho…
Browse files Browse the repository at this point in the history
…t path for queries
  • Loading branch information
Zoxc committed Apr 16, 2018
1 parent 49317cd commit 21f6e55
Showing 1 changed file with 28 additions and 25 deletions.
53 changes: 28 additions & 25 deletions src/librustc/ty/maps/plumbing.rs
Expand Up @@ -17,6 +17,7 @@ use errors::DiagnosticBuilder;
use errors::Level;
use ty::tls;
use ty::{TyCtxt};
use ty::maps::Query;
use ty::maps::config::QueryDescription;
use ty::maps::job::{QueryResult, QueryInfo};
use ty::item_path;
Expand Down Expand Up @@ -63,6 +64,7 @@ pub(super) trait GetCacheInternal<'tcx>: QueryDescription<'tcx> + Sized {

#[derive(Clone)]
pub(super) struct CycleError<'tcx> {
/// The span of the reason the first query in `cycle` ran the last query in `cycle`
pub(super) span: Span,
pub(super) cycle: Vec<QueryInfo<'tcx>>,
}
Expand All @@ -79,27 +81,31 @@ pub(super) enum TryGetLock<'a, 'tcx: 'a, T, D: QueryDescription<'tcx> + 'a> {
}

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
pub(super) fn report_cycle(self, CycleError { span, cycle: stack }: CycleError)
pub(super) fn report_cycle(self, CycleError { span, cycle: stack }: CycleError<'gcx>)
-> DiagnosticBuilder<'a>
{
assert!(!stack.is_empty());

let fix_span = |span: Span, query: &Query<'gcx>| {
self.sess.codemap().def_span(query.default_span(self, span))
};

// Disable naming impls with types in this path, since that
// sometimes cycles itself, leading to extra cycle errors.
// (And cycle errors around impls tend to occur during the
// collect/coherence phases anyhow.)
item_path::with_forced_impl_filename_line(|| {
let span = self.sess.codemap().def_span(span);
let span = fix_span(span, &stack.first().unwrap().query);
let mut err =
struct_span_err!(self.sess, span, E0391,
"cyclic dependency detected");
err.span_label(span, "cyclic reference");

err.span_note(self.sess.codemap().def_span(stack[0].span),
err.span_note(fix_span(stack[0].span, &stack[0].query),
&format!("the cycle begins when {}...", stack[0].query.describe(self)));

for &QueryInfo { span, ref query, .. } in &stack[1..] {
err.span_note(self.sess.codemap().def_span(span),
err.span_note(fix_span(span, query),
&format!("...which then requires {}...", query.describe(self)));
}

Expand Down Expand Up @@ -266,6 +272,22 @@ macro_rules! define_maps {
r
}
}

// FIXME(eddyb) Get more valid Span's on queries.
pub fn default_span(&self, tcx: TyCtxt<'_, $tcx, '_>, span: Span) -> Span {
if span != DUMMY_SP {
return span;
}
// The def_span query is used to calculate default_span,
// so exit to avoid infinite recursion
match *self {
Query::def_span(..) => return span,
_ => ()
}
match *self {
$(Query::$name(key) => key.default_span(tcx),)*
}
}
}

pub mod queries {
Expand Down Expand Up @@ -303,7 +325,7 @@ macro_rules! define_maps {
/// 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,
span: Span,
key: &$K
) -> TryGetLock<'a, $tcx, $V, Self>
{
Expand All @@ -329,21 +351,14 @@ macro_rules! define_maps {
};
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 TryGetLock::JobCompleted(Err(cycle));
}
}
}

fn try_get_with(tcx: TyCtxt<'a, $tcx, 'lcx>,
mut span: Span,
span: Span,
key: $K)
-> Result<$V, CycleError<$tcx>>
{
Expand Down Expand Up @@ -377,18 +392,6 @@ macro_rules! define_maps {

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,
// default_span calls def_span query internally.
if span == DUMMY_SP && stringify!($name) != "def_span" {
// This might deadlock if we hold the map lock since we might be
// waiting for the def_span query and switch to some other fiber
// So we drop the lock here and reacquire it
mem::drop(lock);
span = key.default_span(tcx);
lock = get_lock_or_return!();
}

// Fast path for when incr. comp. is off. `to_dep_node` is
// expensive for some DepKinds.
if !tcx.dep_graph.is_fully_enabled() {
Expand Down

0 comments on commit 21f6e55

Please sign in to comment.