Skip to content

Commit ecd5efa

Browse files
committed
Show usages of query cycles and correctly shift queries in a cycle
1 parent 4bb5d35 commit ecd5efa

File tree

1 file changed

+61
-30
lines changed

1 file changed

+61
-30
lines changed

src/librustc/ty/query/job.rs

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -326,19 +326,17 @@ fn connected_to_root<'tcx>(
326326
query: Lrc<QueryJob<'tcx>>,
327327
visited: &mut FxHashSet<*const QueryJob<'tcx>>
328328
) -> bool {
329-
// This query is connected to the root (it has no query parent), return true
330-
if query.parent.is_none() {
331-
return true;
332-
}
333-
334329
// We already visited this or we're deliberately ignoring it
335330
if visited.contains(&query.as_ptr()) {
336331
return false;
337332
}
338333

339-
visited.insert(query.as_ptr());
334+
// This query is connected to the root (it has no query parent), return true
335+
if query.parent.is_none() {
336+
return true;
337+
}
340338

341-
let mut connected = false;
339+
visited.insert(query.as_ptr());
342340

343341
visit_waiters(query, |_, successor| {
344342
if connected_to_root(successor, visited) {
@@ -349,6 +347,28 @@ fn connected_to_root<'tcx>(
349347
}).is_some()
350348
}
351349

350+
// Deterministically pick an query from a list
351+
#[cfg(parallel_queries)]
352+
fn pick_query<'a, 'tcx, T, F: Fn(&T) -> (Span, Lrc<QueryJob<'tcx>>)>(
353+
tcx: TyCtxt<'_, 'tcx, '_>,
354+
queries: &'a [T],
355+
f: F
356+
) -> &'a T {
357+
// Deterministically pick an entry point
358+
// FIXME: Sort this instead
359+
let mut hcx = tcx.create_stable_hashing_context();
360+
queries.iter().min_by_key(|v| {
361+
let (span, query) = f(v);
362+
let mut stable_hasher = StableHasher::<u64>::new();
363+
query.info.query.hash_stable(&mut hcx, &mut stable_hasher);
364+
// Prefer entry points which have valid spans for nicer error messages
365+
// We add an integer to the tuple ensuring that entry points
366+
// with valid spans are picked first
367+
let span_cmp = if span == DUMMY_SP { 1 } else { 0 };
368+
(span_cmp, stable_hasher.finish())
369+
}).unwrap()
370+
}
371+
352372
/// Looks for query cycles starting from the last query in `jobs`.
353373
/// If a cycle is found, all queries in the cycle is removed from `jobs` and
354374
/// the function return true.
@@ -388,41 +408,52 @@ fn remove_cycle<'tcx>(
388408

389409
// Find the queries in the cycle which are
390410
// connected to queries outside the cycle
391-
let entry_points = stack.iter().filter_map(|query| {
392-
// Mark all the other queries in the cycle as already visited
393-
let mut visited = FxHashSet::from_iter(stack.iter().filter_map(|q| {
394-
if q.1.as_ptr() != query.1.as_ptr() {
395-
Some(q.1.as_ptr())
396-
} else {
411+
let entry_points: Vec<_> = stack.iter().filter_map(|(span, query)| {
412+
if query.parent.is_none() {
413+
// This query is connected to the root (it has no query parent)
414+
Some((*span, query.clone(), None))
415+
} else {
416+
let mut waiters = Vec::new();
417+
// Find all the direct waiters who lead to the root
418+
visit_waiters(query.clone(), |span, waiter| {
419+
// Mark all the other queries in the cycle as already visited
420+
let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1.as_ptr()));
421+
422+
if connected_to_root(waiter.clone(), &mut visited) {
423+
waiters.push((span, waiter));
424+
}
425+
397426
None
427+
});
428+
if waiters.is_empty() {
429+
None
430+
} else {
431+
// Deterministically pick one of the waiters to show to the user
432+
let waiter = pick_query(tcx, &waiters, |s| s.clone()).clone();
433+
Some((*span, query.clone(), Some(waiter)))
398434
}
399-
}));
400-
401-
if connected_to_root(query.1.clone(), &mut visited) {
402-
Some(query.1.clone())
403-
} else {
404-
None
405435
}
406-
});
436+
}).collect();
437+
438+
let entry_points: Vec<(Span, Lrc<QueryJob<'tcx>>, Option<(Span, Lrc<QueryJob<'tcx>>)>)>
439+
= entry_points;
407440

408441
// Deterministically pick an entry point
409-
// FIXME: Sort this instead
410-
let mut hcx = tcx.create_stable_hashing_context();
411-
let entry_point = entry_points.min_by_key(|q| {
412-
let mut stable_hasher = StableHasher::<u64>::new();
413-
q.info.query.hash_stable(&mut hcx, &mut stable_hasher);
414-
stable_hasher.finish()
415-
}).unwrap().as_ptr();
442+
let (_, entry_point, usage) = pick_query(tcx, &entry_points, |e| (e.0, e.1.clone()));
416443

417444
// Shift the stack so that our entry point is first
418-
let entry_point_pos = stack.iter().position(|(_, query)| query.as_ptr() == entry_point);
445+
let entry_point_pos = stack.iter().position(|(_, query)| {
446+
query.as_ptr() == entry_point.as_ptr()
447+
});
419448
if let Some(pos) = entry_point_pos {
420-
stack.rotate_right(pos);
449+
stack.rotate_left(pos);
421450
}
422451

452+
let usage = usage.as_ref().map(|(span, query)| (*span, query.info.query.clone()));
453+
423454
// Create the cycle error
424455
let mut error = CycleError {
425-
usage: None,
456+
usage,
426457
cycle: stack.iter().map(|&(s, ref q)| QueryInfo {
427458
span: s,
428459
query: q.info.query.clone(),

0 commit comments

Comments
 (0)