@@ -103,8 +103,11 @@ impl<'tcx> QueryJob<'tcx> {
103103 condvar : Condvar :: new ( ) ,
104104 } ) ;
105105 self . latch . await ( & waiter) ;
106-
107- match Lrc :: get_mut ( & mut waiter) . unwrap ( ) . cycle . get_mut ( ) . take ( ) {
106+ // FIXME: Get rid of this lock. We have ownership of the QueryWaiter
107+ // although another thread may still have a Lrc reference so we cannot
108+ // use Lrc::get_mut
109+ let mut cycle = waiter. cycle . lock ( ) ;
110+ match cycle. take ( ) {
108111 None => Ok ( ( ) ) ,
109112 Some ( cycle) => Err ( cycle)
110113 }
@@ -326,19 +329,17 @@ fn connected_to_root<'tcx>(
326329 query : Lrc < QueryJob < ' tcx > > ,
327330 visited : & mut FxHashSet < * const QueryJob < ' tcx > >
328331) -> 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-
334332 // We already visited this or we're deliberately ignoring it
335333 if visited. contains ( & query. as_ptr ( ) ) {
336334 return false ;
337335 }
338336
339- visited. insert ( query. as_ptr ( ) ) ;
337+ // This query is connected to the root (it has no query parent), return true
338+ if query. parent . is_none ( ) {
339+ return true ;
340+ }
340341
341- let mut connected = false ;
342+ visited . insert ( query . as_ptr ( ) ) ;
342343
343344 visit_waiters ( query, |_, successor| {
344345 if connected_to_root ( successor, visited) {
@@ -349,6 +350,28 @@ fn connected_to_root<'tcx>(
349350 } ) . is_some ( )
350351}
351352
353+ // Deterministically pick an query from a list
354+ #[ cfg( parallel_queries) ]
355+ fn pick_query < ' a , ' tcx , T , F : Fn ( & T ) -> ( Span , Lrc < QueryJob < ' tcx > > ) > (
356+ tcx : TyCtxt < ' _ , ' tcx , ' _ > ,
357+ queries : & ' a [ T ] ,
358+ f : F
359+ ) -> & ' a T {
360+ // Deterministically pick an entry point
361+ // FIXME: Sort this instead
362+ let mut hcx = tcx. create_stable_hashing_context ( ) ;
363+ queries. iter ( ) . min_by_key ( |v| {
364+ let ( span, query) = f ( v) ;
365+ let mut stable_hasher = StableHasher :: < u64 > :: new ( ) ;
366+ query. info . query . hash_stable ( & mut hcx, & mut stable_hasher) ;
367+ // Prefer entry points which have valid spans for nicer error messages
368+ // We add an integer to the tuple ensuring that entry points
369+ // with valid spans are picked first
370+ let span_cmp = if span == DUMMY_SP { 1 } else { 0 } ;
371+ ( span_cmp, stable_hasher. finish ( ) )
372+ } ) . unwrap ( )
373+ }
374+
352375/// Looks for query cycles starting from the last query in `jobs`.
353376/// If a cycle is found, all queries in the cycle is removed from `jobs` and
354377/// the function return true.
@@ -388,41 +411,52 @@ fn remove_cycle<'tcx>(
388411
389412 // Find the queries in the cycle which are
390413 // 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 {
414+ let entry_points: Vec < _ > = stack. iter ( ) . filter_map ( |( span, query) | {
415+ if query. parent . is_none ( ) {
416+ // This query is connected to the root (it has no query parent)
417+ Some ( ( * span, query. clone ( ) , None ) )
418+ } else {
419+ let mut waiters = Vec :: new ( ) ;
420+ // Find all the direct waiters who lead to the root
421+ visit_waiters ( query. clone ( ) , |span, waiter| {
422+ // Mark all the other queries in the cycle as already visited
423+ let mut visited = FxHashSet :: from_iter ( stack. iter ( ) . map ( |q| q. 1 . as_ptr ( ) ) ) ;
424+
425+ if connected_to_root ( waiter. clone ( ) , & mut visited) {
426+ waiters. push ( ( span, waiter) ) ;
427+ }
428+
429+ None
430+ } ) ;
431+ if waiters. is_empty ( ) {
397432 None
433+ } else {
434+ // Deterministically pick one of the waiters to show to the user
435+ let waiter = pick_query ( tcx, & waiters, |s| s. clone ( ) ) . clone ( ) ;
436+ Some ( ( * span, query. clone ( ) , Some ( waiter) ) )
398437 }
399- } ) ) ;
400-
401- if connected_to_root ( query. 1 . clone ( ) , & mut visited) {
402- Some ( query. 1 . clone ( ) )
403- } else {
404- None
405438 }
406- } ) ;
439+ } ) . collect ( ) ;
440+
441+ let entry_points: Vec < ( Span , Lrc < QueryJob < ' tcx > > , Option < ( Span , Lrc < QueryJob < ' tcx > > ) > ) >
442+ = entry_points;
407443
408444 // 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 ( ) ;
445+ let ( _, entry_point, usage) = pick_query ( tcx, & entry_points, |e| ( e. 0 , e. 1 . clone ( ) ) ) ;
416446
417447 // Shift the stack so that our entry point is first
418- let entry_point_pos = stack. iter ( ) . position ( |( _, query) | query. as_ptr ( ) == entry_point) ;
448+ let entry_point_pos = stack. iter ( ) . position ( |( _, query) | {
449+ query. as_ptr ( ) == entry_point. as_ptr ( )
450+ } ) ;
419451 if let Some ( pos) = entry_point_pos {
420- stack. rotate_right ( pos) ;
452+ stack. rotate_left ( pos) ;
421453 }
422454
455+ let usage = usage. as_ref ( ) . map ( |( span, query) | ( * span, query. info . query . clone ( ) ) ) ;
456+
423457 // Create the cycle error
424458 let mut error = CycleError {
425- usage : None ,
459+ usage,
426460 cycle : stack. iter ( ) . map ( |& ( s, ref q) | QueryInfo {
427461 span : s,
428462 query : q. info . query . clone ( ) ,
0 commit comments