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