@@ -232,6 +232,83 @@ fn connected_to_root<'tcx>(
232232 false
233233}
234234
235+ /// Processes a found query cycle into a `CycleError`
236+ fn process_cycle < ' tcx > (
237+ job_map : & QueryJobMap < ' tcx > ,
238+ stack : Vec < ( Span , QueryJobId ) > ,
239+ ) -> CycleError < ' tcx > {
240+ // The stack is a vector of pairs of spans and queries; reverse it so that
241+ // the earlier entries require later entries
242+ let ( mut spans, queries) : ( Vec < _ > , Vec < _ > ) = stack. into_iter ( ) . rev ( ) . unzip ( ) ;
243+
244+ // Shift the spans so that queries are matched with the span for their waitee
245+ spans. rotate_right ( 1 ) ;
246+
247+ // Zip them back together
248+ let mut stack: Vec < _ > = iter:: zip ( spans, queries) . collect ( ) ;
249+
250+ struct EntryPoint {
251+ query_in_cycle : QueryJobId ,
252+ query_waiting_on_cycle : Option < ( Span , QueryJobId ) > ,
253+ }
254+
255+ // Find the queries in the cycle which are
256+ // connected to queries outside the cycle
257+ let entry_points = stack
258+ . iter ( )
259+ . filter_map ( |& ( _, query_in_cycle) | {
260+ let mut entrypoint = false ;
261+ let mut query_waiting_on_cycle = None ;
262+
263+ // Find a direct waiter who leads to the root
264+ for abstracted_waiter in abstracted_waiters_of ( job_map, query_in_cycle) {
265+ let Some ( parent) = abstracted_waiter. parent else {
266+ // The query in the cycle is directly connected to root.
267+ entrypoint = true ;
268+ continue ;
269+ } ;
270+
271+ // Mark all the other queries in the cycle as already visited,
272+ // so paths to the root through the cycle itself won't count.
273+ let mut visited = FxHashSet :: from_iter ( stack. iter ( ) . map ( |q| q. 1 ) ) ;
274+
275+ if connected_to_root ( job_map, parent, & mut visited) {
276+ query_waiting_on_cycle = Some ( ( abstracted_waiter. span , parent) ) ;
277+ entrypoint = true ;
278+ break ;
279+ }
280+ }
281+
282+ entrypoint. then_some ( EntryPoint { query_in_cycle, query_waiting_on_cycle } )
283+ } )
284+ . collect :: < Vec < EntryPoint > > ( ) ;
285+
286+ // Pick an entry point, preferring ones with waiters
287+ let entry_point = entry_points
288+ . iter ( )
289+ . find ( |entry_point| entry_point. query_waiting_on_cycle . is_some ( ) )
290+ . unwrap_or ( & entry_points[ 0 ] ) ;
291+
292+ // Shift the stack so that our entry point is first
293+ let entry_point_pos = stack. iter ( ) . position ( |( _, query) | * query == entry_point. query_in_cycle ) ;
294+ if let Some ( pos) = entry_point_pos {
295+ stack. rotate_left ( pos) ;
296+ }
297+
298+ let usage = entry_point
299+ . query_waiting_on_cycle
300+ . map ( |( span, job) | QueryStackFrame { span, tagged_key : job_map. tagged_key_of ( job) } ) ;
301+
302+ // Create the cycle error
303+ CycleError {
304+ usage,
305+ cycle : stack
306+ . iter ( )
307+ . map ( |& ( span, job) | QueryStackFrame { span, tagged_key : job_map. tagged_key_of ( job) } )
308+ . collect ( ) ,
309+ }
310+ }
311+
235312/// Looks for a query cycle using the last query in `jobs`.
236313/// If a cycle is found, all queries in the cycle is removed from `jobs` and
237314/// the function return true.
@@ -248,87 +325,15 @@ fn remove_cycle<'tcx>(
248325 if let ControlFlow :: Break ( resumable) =
249326 find_cycle ( job_map, jobs. pop ( ) . unwrap ( ) , DUMMY_SP , & mut stack, & mut visited)
250327 {
251- // The stack is a vector of pairs of spans and queries; reverse it so that
252- // the earlier entries require later entries
253- let ( mut spans, queries) : ( Vec < _ > , Vec < _ > ) = stack. into_iter ( ) . rev ( ) . unzip ( ) ;
254-
255- // Shift the spans so that queries are matched with the span for their waitee
256- spans. rotate_right ( 1 ) ;
257-
258- // Zip them back together
259- let mut stack: Vec < _ > = iter:: zip ( spans, queries) . collect ( ) ;
260-
261328 // Remove the queries in our cycle from the list of jobs to look at
262329 for r in & stack {
263330 if let Some ( pos) = jobs. iter ( ) . position ( |j| j == & r. 1 ) {
264331 jobs. remove ( pos) ;
265332 }
266333 }
267334
268- struct EntryPoint {
269- query_in_cycle : QueryJobId ,
270- query_waiting_on_cycle : Option < ( Span , QueryJobId ) > ,
271- }
272-
273- // Find the queries in the cycle which are
274- // connected to queries outside the cycle
275- let entry_points = stack
276- . iter ( )
277- . filter_map ( |& ( _, query_in_cycle) | {
278- let mut entrypoint = false ;
279- let mut query_waiting_on_cycle = None ;
280-
281- // Find a direct waiter who leads to the root
282- for abstracted_waiter in abstracted_waiters_of ( job_map, query_in_cycle) {
283- let Some ( parent) = abstracted_waiter. parent else {
284- // The query in the cycle is directly connected to root.
285- entrypoint = true ;
286- continue ;
287- } ;
288-
289- // Mark all the other queries in the cycle as already visited,
290- // so paths to the root through the cycle itself won't count.
291- let mut visited = FxHashSet :: from_iter ( stack. iter ( ) . map ( |q| q. 1 ) ) ;
292-
293- if connected_to_root ( job_map, parent, & mut visited) {
294- query_waiting_on_cycle = Some ( ( abstracted_waiter. span , parent) ) ;
295- entrypoint = true ;
296- break ;
297- }
298- }
299-
300- entrypoint. then_some ( EntryPoint { query_in_cycle, query_waiting_on_cycle } )
301- } )
302- . collect :: < Vec < EntryPoint > > ( ) ;
303-
304- // Pick an entry point, preferring ones with waiters
305- let entry_point = entry_points
306- . iter ( )
307- . find ( |entry_point| entry_point. query_waiting_on_cycle . is_some ( ) )
308- . unwrap_or ( & entry_points[ 0 ] ) ;
309-
310- // Shift the stack so that our entry point is first
311- let entry_point_pos =
312- stack. iter ( ) . position ( |( _, query) | * query == entry_point. query_in_cycle ) ;
313- if let Some ( pos) = entry_point_pos {
314- stack. rotate_left ( pos) ;
315- }
316-
317- let usage = entry_point
318- . query_waiting_on_cycle
319- . map ( |( span, job) | QueryStackFrame { span, tagged_key : job_map. tagged_key_of ( job) } ) ;
320-
321- // Create the cycle error
322- let error = CycleError {
323- usage,
324- cycle : stack
325- . iter ( )
326- . map ( |& ( span, job) | QueryStackFrame {
327- span,
328- tagged_key : job_map. tagged_key_of ( job) ,
329- } )
330- . collect ( ) ,
331- } ;
335+ // Create a `CycleError` for this cycle
336+ let error = process_cycle ( job_map, stack) ;
332337
333338 // We unwrap `resumable` here since there must always be one
334339 // edge which is resumable / waited using a query latch
0 commit comments