Show usages of query cycles and correctly shift queries in a cycle

This commit is contained in:
John Kåre Alsaker 2018-12-02 03:01:12 +01:00
parent 4bb5d35659
commit ecd5efa641

View file

@ -326,19 +326,17 @@ fn connected_to_root<'tcx>(
query: Lrc<QueryJob<'tcx>>,
visited: &mut FxHashSet<*const QueryJob<'tcx>>
) -> bool {
// This query is connected to the root (it has no query parent), return true
if query.parent.is_none() {
return true;
}
// We already visited this or we're deliberately ignoring it
if visited.contains(&query.as_ptr()) {
return false;
}
visited.insert(query.as_ptr());
// This query is connected to the root (it has no query parent), return true
if query.parent.is_none() {
return true;
}
let mut connected = false;
visited.insert(query.as_ptr());
visit_waiters(query, |_, successor| {
if connected_to_root(successor, visited) {
@ -349,6 +347,28 @@ fn connected_to_root<'tcx>(
}).is_some()
}
// Deterministically pick an query from a list
#[cfg(parallel_queries)]
fn pick_query<'a, 'tcx, T, F: Fn(&T) -> (Span, Lrc<QueryJob<'tcx>>)>(
tcx: TyCtxt<'_, 'tcx, '_>,
queries: &'a [T],
f: F
) -> &'a T {
// Deterministically pick an entry point
// FIXME: Sort this instead
let mut hcx = tcx.create_stable_hashing_context();
queries.iter().min_by_key(|v| {
let (span, query) = f(v);
let mut stable_hasher = StableHasher::<u64>::new();
query.info.query.hash_stable(&mut hcx, &mut stable_hasher);
// Prefer entry points which have valid spans for nicer error messages
// We add an integer to the tuple ensuring that entry points
// with valid spans are picked first
let span_cmp = if span == DUMMY_SP { 1 } else { 0 };
(span_cmp, stable_hasher.finish())
}).unwrap()
}
/// Looks for query cycles starting from the last query in `jobs`.
/// If a cycle is found, all queries in the cycle is removed from `jobs` and
/// the function return true.
@ -388,41 +408,52 @@ fn remove_cycle<'tcx>(
// Find the queries in the cycle which are
// connected to queries outside the cycle
let entry_points = stack.iter().filter_map(|query| {
// Mark all the other queries in the cycle as already visited
let mut visited = FxHashSet::from_iter(stack.iter().filter_map(|q| {
if q.1.as_ptr() != query.1.as_ptr() {
Some(q.1.as_ptr())
} else {
None
}
}));
if connected_to_root(query.1.clone(), &mut visited) {
Some(query.1.clone())
let entry_points: Vec<_> = stack.iter().filter_map(|(span, query)| {
if query.parent.is_none() {
// This query is connected to the root (it has no query parent)
Some((*span, query.clone(), None))
} else {
None
let mut waiters = Vec::new();
// Find all the direct waiters who lead to the root
visit_waiters(query.clone(), |span, waiter| {
// Mark all the other queries in the cycle as already visited
let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1.as_ptr()));
if connected_to_root(waiter.clone(), &mut visited) {
waiters.push((span, waiter));
}
None
});
if waiters.is_empty() {
None
} else {
// Deterministically pick one of the waiters to show to the user
let waiter = pick_query(tcx, &waiters, |s| s.clone()).clone();
Some((*span, query.clone(), Some(waiter)))
}
}
});
}).collect();
let entry_points: Vec<(Span, Lrc<QueryJob<'tcx>>, Option<(Span, Lrc<QueryJob<'tcx>>)>)>
= entry_points;
// Deterministically pick an entry point
// FIXME: Sort this instead
let mut hcx = tcx.create_stable_hashing_context();
let entry_point = entry_points.min_by_key(|q| {
let mut stable_hasher = StableHasher::<u64>::new();
q.info.query.hash_stable(&mut hcx, &mut stable_hasher);
stable_hasher.finish()
}).unwrap().as_ptr();
let (_, entry_point, usage) = pick_query(tcx, &entry_points, |e| (e.0, e.1.clone()));
// Shift the stack so that our entry point is first
let entry_point_pos = stack.iter().position(|(_, query)| query.as_ptr() == entry_point);
let entry_point_pos = stack.iter().position(|(_, query)| {
query.as_ptr() == entry_point.as_ptr()
});
if let Some(pos) = entry_point_pos {
stack.rotate_right(pos);
stack.rotate_left(pos);
}
let usage = usage.as_ref().map(|(span, query)| (*span, query.info.query.clone()));
// Create the cycle error
let mut error = CycleError {
usage: None,
usage,
cycle: stack.iter().map(|&(s, ref q)| QueryInfo {
span: s,
query: q.info.query.clone(),