1
+ use super :: Error ;
2
+
1
3
use rustc_data_structures:: fx:: FxHashMap ;
2
4
use rustc_data_structures:: graph:: dominators:: { self , Dominators } ;
3
5
use rustc_data_structures:: graph:: { self , GraphSuccessors , WithNumNodes , WithStartNode } ;
@@ -313,16 +315,28 @@ impl BasicCoverageBlockData {
313
315
}
314
316
315
317
#[ inline( always) ]
316
- pub fn set_counter ( & mut self , counter_kind : CoverageKind ) -> ExpressionOperandId {
318
+ pub fn set_counter (
319
+ & mut self ,
320
+ counter_kind : CoverageKind ,
321
+ ) -> Result < ExpressionOperandId , Error > {
317
322
debug_assert ! (
323
+ // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
324
+ // have an expression (to be injected into an existing `BasicBlock` represented by this
325
+ // `BasicCoverageBlock`).
318
326
self . edge_from_bcbs. is_none( ) || counter_kind. is_expression( ) ,
319
327
"attempt to add a `Counter` to a BCB target with existing incoming edge counters"
320
328
) ;
321
329
let operand = counter_kind. as_operand_id ( ) ;
322
- self . counter_kind
323
- . replace ( counter_kind)
324
- . expect_none ( "attempt to set a BasicCoverageBlock coverage counter more than once" ) ;
325
- operand
330
+ let expect_none = self . counter_kind . replace ( counter_kind) ;
331
+ if expect_none. is_some ( ) {
332
+ return Error :: from_string ( format ! (
333
+ "attempt to set a BasicCoverageBlock coverage counter more than once; \
334
+ {:?} already had counter {:?}",
335
+ self ,
336
+ expect_none. unwrap( ) ,
337
+ ) ) ;
338
+ }
339
+ Ok ( operand)
326
340
}
327
341
328
342
#[ inline( always) ]
@@ -340,19 +354,33 @@ impl BasicCoverageBlockData {
340
354
& mut self ,
341
355
from_bcb : BasicCoverageBlock ,
342
356
counter_kind : CoverageKind ,
343
- ) -> ExpressionOperandId {
344
- debug_assert ! (
345
- self . counter_kind. as_ref( ) . map_or( true , |c| c. is_expression( ) ) ,
346
- "attempt to add an incoming edge counter from {:?} when the target BCB already has a \
347
- `Counter`",
348
- from_bcb
349
- ) ;
357
+ ) -> Result < ExpressionOperandId , Error > {
358
+ if level_enabled ! ( tracing:: Level :: DEBUG ) {
359
+ // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
360
+ // have an expression (to be injected into an existing `BasicBlock` represented by this
361
+ // `BasicCoverageBlock`).
362
+ if !self . counter_kind . as_ref ( ) . map_or ( true , |c| c. is_expression ( ) ) {
363
+ return Error :: from_string ( format ! (
364
+ "attempt to add an incoming edge counter from {:?} when the target BCB already \
365
+ has a `Counter`",
366
+ from_bcb
367
+ ) ) ;
368
+ }
369
+ }
350
370
let operand = counter_kind. as_operand_id ( ) ;
351
- self . edge_from_bcbs
371
+ let expect_none = self
372
+ . edge_from_bcbs
352
373
. get_or_insert_with ( || FxHashMap :: default ( ) )
353
- . insert ( from_bcb, counter_kind)
354
- . expect_none ( "attempt to set an edge counter more than once" ) ;
355
- operand
374
+ . insert ( from_bcb, counter_kind) ;
375
+ if expect_none. is_some ( ) {
376
+ return Error :: from_string ( format ! (
377
+ "attempt to set an edge counter more than once; from_bcb: \
378
+ {:?} already had counter {:?}",
379
+ from_bcb,
380
+ expect_none. unwrap( ) ,
381
+ ) ) ;
382
+ }
383
+ Ok ( operand)
356
384
}
357
385
358
386
#[ inline( always) ]
@@ -383,6 +411,56 @@ impl BasicCoverageBlockData {
383
411
}
384
412
}
385
413
414
+ /// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`)
415
+ /// as either the successor BCB itself, if it has only one incoming edge, or the successor _plus_
416
+ /// the specific branching BCB, representing the edge between the two. The latter case
417
+ /// distinguishes this incoming edge from other incoming edges to the same `target_bcb`.
418
+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
419
+ pub ( crate ) struct BcbBranch {
420
+ pub edge_from_bcb : Option < BasicCoverageBlock > ,
421
+ pub target_bcb : BasicCoverageBlock ,
422
+ }
423
+
424
+ impl BcbBranch {
425
+ pub fn from_to (
426
+ from_bcb : BasicCoverageBlock ,
427
+ to_bcb : BasicCoverageBlock ,
428
+ basic_coverage_blocks : & CoverageGraph ,
429
+ ) -> Self {
430
+ let edge_from_bcb = if basic_coverage_blocks. predecessors [ to_bcb] . len ( ) > 1 {
431
+ Some ( from_bcb)
432
+ } else {
433
+ None
434
+ } ;
435
+ Self { edge_from_bcb, target_bcb : to_bcb }
436
+ }
437
+
438
+ pub fn counter < ' a > (
439
+ & self ,
440
+ basic_coverage_blocks : & ' a CoverageGraph ,
441
+ ) -> Option < & ' a CoverageKind > {
442
+ if let Some ( from_bcb) = self . edge_from_bcb {
443
+ basic_coverage_blocks[ self . target_bcb ] . edge_counter_from ( from_bcb)
444
+ } else {
445
+ basic_coverage_blocks[ self . target_bcb ] . counter ( )
446
+ }
447
+ }
448
+
449
+ pub fn is_only_path_to_target ( & self ) -> bool {
450
+ self . edge_from_bcb . is_none ( )
451
+ }
452
+ }
453
+
454
+ impl std:: fmt:: Debug for BcbBranch {
455
+ fn fmt ( & self , fmt : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
456
+ if let Some ( from_bcb) = self . edge_from_bcb {
457
+ write ! ( fmt, "{:?}->{:?}" , from_bcb, self . target_bcb)
458
+ } else {
459
+ write ! ( fmt, "{:?}" , self . target_bcb)
460
+ }
461
+ }
462
+ }
463
+
386
464
fn bcb_filtered_successors < ' a , ' tcx > (
387
465
body : & ' tcx & ' a mir:: Body < ' tcx > ,
388
466
term_kind : & ' tcx TerminatorKind < ' tcx > ,
@@ -437,14 +515,18 @@ impl TraverseCoverageGraphWithLoops {
437
515
}
438
516
439
517
pub fn next ( & mut self ) -> Option < BasicCoverageBlock > {
440
- // Strip contexts with empty worklists from the top of the stack
441
- while self . context_stack . last ( ) . map_or ( false , |context| context. worklist . is_empty ( ) ) {
442
- self . context_stack . pop ( ) ;
443
- }
444
- // Pop the next bcb off of the current context_stack. If none, all BCBs were visited.
445
- while let Some ( next_bcb) =
518
+ debug ! (
519
+ "TraverseCoverageGraphWithLoops::next - context_stack: {:?}" ,
520
+ self . context_stack. iter( ) . rev( ) . collect:: <Vec <_>>( )
521
+ ) ;
522
+ while let Some ( next_bcb) = {
523
+ // Strip contexts with empty worklists from the top of the stack
524
+ while self . context_stack . last ( ) . map_or ( false , |context| context. worklist . is_empty ( ) ) {
525
+ self . context_stack . pop ( ) ;
526
+ }
527
+ // Pop the next bcb off of the current context_stack. If none, all BCBs were visited.
446
528
self . context_stack . last_mut ( ) . map_or ( None , |context| context. worklist . pop ( ) )
447
- {
529
+ } {
448
530
if !self . visited . insert ( next_bcb) {
449
531
debug ! ( "Already visited: {:?}" , next_bcb) ;
450
532
continue ;
@@ -459,9 +541,19 @@ impl TraverseCoverageGraphWithLoops {
459
541
}
460
542
return Some ( next_bcb) ;
461
543
}
462
- debug_assert_eq ! ( self . visited. count( ) , self . visited. domain_size( ) ) ;
463
544
None
464
545
}
546
+
547
+ pub fn is_complete ( & self ) -> bool {
548
+ self . visited . count ( ) == self . visited . domain_size ( )
549
+ }
550
+
551
+ pub fn unvisited ( & self ) -> Vec < BasicCoverageBlock > {
552
+ let mut unvisited_set: BitSet < BasicCoverageBlock > =
553
+ BitSet :: new_filled ( self . visited . domain_size ( ) ) ;
554
+ unvisited_set. subtract ( & self . visited ) ;
555
+ unvisited_set. iter ( ) . collect :: < Vec < _ > > ( )
556
+ }
465
557
}
466
558
467
559
fn find_loop_backedges (
0 commit comments