@@ -326,19 +326,17 @@ fn connected_to_root<'tcx>(
326
326
query : Lrc < QueryJob < ' tcx > > ,
327
327
visited : & mut FxHashSet < * const QueryJob < ' tcx > >
328
328
) -> 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
-
334
329
// We already visited this or we're deliberately ignoring it
335
330
if visited. contains ( & query. as_ptr ( ) ) {
336
331
return false ;
337
332
}
338
333
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
+ }
340
338
341
- let mut connected = false ;
339
+ visited . insert ( query . as_ptr ( ) ) ;
342
340
343
341
visit_waiters ( query, |_, successor| {
344
342
if connected_to_root ( successor, visited) {
@@ -349,6 +347,28 @@ fn connected_to_root<'tcx>(
349
347
} ) . is_some ( )
350
348
}
351
349
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
+
352
372
/// Looks for query cycles starting from the last query in `jobs`.
353
373
/// If a cycle is found, all queries in the cycle is removed from `jobs` and
354
374
/// the function return true.
@@ -388,41 +408,52 @@ fn remove_cycle<'tcx>(
388
408
389
409
// Find the queries in the cycle which are
390
410
// 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
+
397
426
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) ) )
398
434
}
399
- } ) ) ;
400
-
401
- if connected_to_root ( query. 1 . clone ( ) , & mut visited) {
402
- Some ( query. 1 . clone ( ) )
403
- } else {
404
- None
405
435
}
406
- } ) ;
436
+ } ) . collect ( ) ;
437
+
438
+ let entry_points: Vec < ( Span , Lrc < QueryJob < ' tcx > > , Option < ( Span , Lrc < QueryJob < ' tcx > > ) > ) >
439
+ = entry_points;
407
440
408
441
// 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 ( ) ) ) ;
416
443
417
444
// 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
+ } ) ;
419
448
if let Some ( pos) = entry_point_pos {
420
- stack. rotate_right ( pos) ;
449
+ stack. rotate_left ( pos) ;
421
450
}
422
451
452
+ let usage = usage. as_ref ( ) . map ( |( span, query) | ( * span, query. info . query . clone ( ) ) ) ;
453
+
423
454
// Create the cycle error
424
455
let mut error = CycleError {
425
- usage : None ,
456
+ usage,
426
457
cycle : stack. iter ( ) . map ( |& ( s, ref q) | QueryInfo {
427
458
span : s,
428
459
query : q. info . query . clone ( ) ,
0 commit comments