@@ -23,9 +23,7 @@ pub(crate) struct BranchInfoBuilder {
2323 markers : BlockMarkerGen ,
2424 branch_spans : Vec < BranchSpan > ,
2525
26- mcdc_branch_spans : Vec < MCDCBranchSpan > ,
27- mcdc_decision_spans : Vec < MCDCDecisionSpan > ,
28- mcdc_state : Option < MCDCState > ,
26+ mcdc_info : Option < MCDCInfoBuilder > ,
2927}
3028
3129#[ derive( Clone , Copy ) ]
@@ -76,9 +74,7 @@ impl BranchInfoBuilder {
7674 nots : FxHashMap :: default ( ) ,
7775 markers : BlockMarkerGen :: default ( ) ,
7876 branch_spans : vec ! [ ] ,
79- mcdc_branch_spans : vec ! [ ] ,
80- mcdc_decision_spans : vec ! [ ] ,
81- mcdc_state : MCDCState :: new_if_enabled ( tcx) ,
77+ mcdc_info : tcx. sess . instrument_coverage_mcdc ( ) . then ( MCDCInfoBuilder :: new) ,
8278 } )
8379 } else {
8480 None
@@ -125,48 +121,6 @@ impl BranchInfoBuilder {
125121 }
126122 }
127123
128- fn fetch_mcdc_condition_info (
129- & mut self ,
130- tcx : TyCtxt < ' _ > ,
131- true_marker : BlockMarkerId ,
132- false_marker : BlockMarkerId ,
133- ) -> Option < ( u16 , ConditionInfo ) > {
134- let mcdc_state = self . mcdc_state . as_mut ( ) ?;
135- let decision_depth = mcdc_state. decision_depth ( ) ;
136- let ( mut condition_info, decision_result) =
137- mcdc_state. take_condition ( true_marker, false_marker) ;
138- // take_condition() returns Some for decision_result when the decision stack
139- // is empty, i.e. when all the conditions of the decision were instrumented,
140- // and the decision is "complete".
141- if let Some ( decision) = decision_result {
142- match decision. conditions_num {
143- 0 => {
144- unreachable ! ( "Decision with no condition is not expected" ) ;
145- }
146- 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
147- self . mcdc_decision_spans . push ( decision) ;
148- }
149- _ => {
150- // Do not generate mcdc mappings and statements for decisions with too many conditions.
151- let rebase_idx = self . mcdc_branch_spans . len ( ) - decision. conditions_num + 1 ;
152- for branch in & mut self . mcdc_branch_spans [ rebase_idx..] {
153- branch. condition_info = None ;
154- }
155-
156- // ConditionInfo of this branch shall also be reset.
157- condition_info = None ;
158-
159- tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
160- span : decision. span ,
161- conditions_num : decision. conditions_num ,
162- max_conditions_num : MAX_CONDITIONS_NUM_IN_DECISION ,
163- } ) ;
164- }
165- }
166- }
167- condition_info. map ( |cond_info| ( decision_depth, cond_info) )
168- }
169-
170124 fn add_two_way_branch < ' tcx > (
171125 & mut self ,
172126 cfg : & mut CFG < ' tcx > ,
@@ -185,16 +139,17 @@ impl BranchInfoBuilder {
185139 nots : _,
186140 markers : BlockMarkerGen { num_block_markers } ,
187141 branch_spans,
188- mcdc_branch_spans,
189- mcdc_decision_spans,
190- mcdc_state : _,
142+ mcdc_info,
191143 } = self ;
192144
193145 if num_block_markers == 0 {
194146 assert ! ( branch_spans. is_empty( ) ) ;
195147 return None ;
196148 }
197149
150+ let ( mcdc_decision_spans, mcdc_branch_spans) =
151+ mcdc_info. map ( MCDCInfoBuilder :: into_done) . unwrap_or_default ( ) ;
152+
198153 Some ( Box :: new ( mir:: coverage:: BranchInfo {
199154 num_block_markers,
200155 branch_spans,
@@ -221,20 +176,23 @@ struct MCDCState {
221176}
222177
223178impl MCDCState {
224- fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
225- tcx. sess
226- . instrument_coverage_mcdc ( )
227- . then ( || Self { decision_ctx_stack : vec ! [ MCDCDecisionCtx :: default ( ) ] } )
179+ fn new ( ) -> Self {
180+ Self { decision_ctx_stack : vec ! [ MCDCDecisionCtx :: default ( ) ] }
228181 }
229182
230183 /// Decision depth is given as a u16 to reduce the size of the `CoverageKind`,
231184 /// as it is very unlikely that the depth ever reaches 2^16.
232185 #[ inline]
233186 fn decision_depth ( & self ) -> u16 {
234- u16:: try_from (
235- self . decision_ctx_stack . len ( ) . checked_sub ( 1 ) . expect ( "Unexpected empty decision stack" ) ,
236- )
237- . expect ( "decision depth did not fit in u16, this is likely to be an instrumentation error" )
187+ match u16:: try_from ( self . decision_ctx_stack . len ( ) )
188+ . expect (
189+ "decision depth did not fit in u16, this is likely to be an instrumentation error" ,
190+ )
191+ . checked_sub ( 1 )
192+ {
193+ Some ( d) => d,
194+ None => bug ! ( "Unexpected empty decision stack" ) ,
195+ }
238196 }
239197
240198 // At first we assign ConditionIds for each sub expression.
@@ -343,8 +301,9 @@ impl MCDCState {
343301 true_marker : BlockMarkerId ,
344302 false_marker : BlockMarkerId ,
345303 ) -> ( Option < ConditionInfo > , Option < MCDCDecisionSpan > ) {
346- let decision_ctx =
347- self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
304+ let Some ( decision_ctx) = self . decision_ctx_stack . last_mut ( ) else {
305+ bug ! ( "Unexpected empty decision_ctx_stack" )
306+ } ;
348307 let Some ( condition_info) = decision_ctx. decision_stack . pop_back ( ) else {
349308 return ( None , None ) ;
350309 } ;
@@ -366,6 +325,74 @@ impl MCDCState {
366325 }
367326}
368327
328+ struct MCDCInfoBuilder {
329+ branch_spans : Vec < MCDCBranchSpan > ,
330+ decision_spans : Vec < MCDCDecisionSpan > ,
331+ state : MCDCState ,
332+ }
333+
334+ impl MCDCInfoBuilder {
335+ fn new ( ) -> Self {
336+ Self { branch_spans : vec ! [ ] , decision_spans : vec ! [ ] , state : MCDCState :: new ( ) }
337+ }
338+
339+ fn visit_evaluated_condition (
340+ & mut self ,
341+ tcx : TyCtxt < ' _ > ,
342+ source_info : SourceInfo ,
343+ true_block : BasicBlock ,
344+ false_block : BasicBlock ,
345+ mut inject_block_marker : impl FnMut ( SourceInfo , BasicBlock ) -> BlockMarkerId ,
346+ ) {
347+ let true_marker = inject_block_marker ( source_info, true_block) ;
348+ let false_marker = inject_block_marker ( source_info, false_block) ;
349+
350+ let decision_depth = self . state . decision_depth ( ) ;
351+ let ( mut condition_info, decision_result) =
352+ self . state . take_condition ( true_marker, false_marker) ;
353+ // take_condition() returns Some for decision_result when the decision stack
354+ // is empty, i.e. when all the conditions of the decision were instrumented,
355+ // and the decision is "complete".
356+ if let Some ( decision) = decision_result {
357+ match decision. conditions_num {
358+ 0 => {
359+ unreachable ! ( "Decision with no condition is not expected" ) ;
360+ }
361+ 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
362+ self . decision_spans . push ( decision) ;
363+ }
364+ _ => {
365+ // Do not generate mcdc mappings and statements for decisions with too many conditions.
366+ let rebase_idx = self . branch_spans . len ( ) - decision. conditions_num + 1 ;
367+ for branch in & mut self . branch_spans [ rebase_idx..] {
368+ branch. condition_info = None ;
369+ }
370+
371+ // ConditionInfo of this branch shall also be reset.
372+ condition_info = None ;
373+
374+ tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
375+ span : decision. span ,
376+ conditions_num : decision. conditions_num ,
377+ max_conditions_num : MAX_CONDITIONS_NUM_IN_DECISION ,
378+ } ) ;
379+ }
380+ }
381+ }
382+ self . branch_spans . push ( MCDCBranchSpan {
383+ span : source_info. span ,
384+ condition_info,
385+ true_marker,
386+ false_marker,
387+ decision_depth,
388+ } ) ;
389+ }
390+
391+ fn into_done ( self ) -> ( Vec < MCDCDecisionSpan > , Vec < MCDCBranchSpan > ) {
392+ ( self . decision_spans , self . branch_spans )
393+ }
394+ }
395+
369396impl Builder < ' _ , ' _ > {
370397 /// If branch coverage is enabled, inject marker statements into `then_block`
371398 /// and `else_block`, and record their IDs in the table of branch spans.
@@ -390,50 +417,52 @@ impl Builder<'_, '_> {
390417 let source_info = SourceInfo { span : self . thir [ expr_id] . span , scope : self . source_scope } ;
391418
392419 // Separate path for handling branches when MC/DC is enabled.
393- if branch_info. mcdc_state . is_some ( ) {
394- let mut inject_block_marker =
395- |block| branch_info. markers . inject_block_marker ( & mut self . cfg , source_info, block) ;
396- let true_marker = inject_block_marker ( then_block) ;
397- let false_marker = inject_block_marker ( else_block) ;
398- let ( decision_depth, condition_info) = branch_info
399- . fetch_mcdc_condition_info ( self . tcx , true_marker, false_marker)
400- . map_or ( ( 0 , None ) , |( decision_depth, condition_info) | {
401- ( decision_depth, Some ( condition_info) )
402- } ) ;
403- branch_info. mcdc_branch_spans . push ( MCDCBranchSpan {
404- span : source_info. span ,
405- condition_info,
406- true_marker,
407- false_marker,
408- decision_depth,
409- } ) ;
420+ if let Some ( mcdc_info) = branch_info. mcdc_info . as_mut ( ) {
421+ let inject_block_marker = |source_info, block| {
422+ branch_info. markers . inject_block_marker ( & mut self . cfg , source_info, block)
423+ } ;
424+ mcdc_info. visit_evaluated_condition (
425+ self . tcx ,
426+ source_info,
427+ then_block,
428+ else_block,
429+ inject_block_marker,
430+ ) ;
410431 return ;
411432 }
412433
413434 branch_info. add_two_way_branch ( & mut self . cfg , source_info, then_block, else_block) ;
414435 }
415436
416437 pub ( crate ) fn visit_coverage_branch_operation ( & mut self , logical_op : LogicalOp , span : Span ) {
417- if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
418- && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
438+ if let Some ( mcdc_info) = self
439+ . coverage_branch_info
440+ . as_mut ( )
441+ . and_then ( |branch_info| branch_info. mcdc_info . as_mut ( ) )
419442 {
420- mcdc_state . record_conditions ( logical_op, span) ;
443+ mcdc_info . state . record_conditions ( logical_op, span) ;
421444 }
422445 }
423446
424447 pub ( crate ) fn mcdc_increment_depth_if_enabled ( & mut self ) {
425- if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
426- && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
448+ if let Some ( mcdc_info) = self
449+ . coverage_branch_info
450+ . as_mut ( )
451+ . and_then ( |branch_info| branch_info. mcdc_info . as_mut ( ) )
427452 {
428- mcdc_state . decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
453+ mcdc_info . state . decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
429454 } ;
430455 }
431456
432457 pub ( crate ) fn mcdc_decrement_depth_if_enabled ( & mut self ) {
433- if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
434- && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
458+ if let Some ( mcdc_info) = self
459+ . coverage_branch_info
460+ . as_mut ( )
461+ . and_then ( |branch_info| branch_info. mcdc_info . as_mut ( ) )
435462 {
436- mcdc_state. decision_ctx_stack . pop ( ) . expect ( "Unexpected empty decision stack" ) ;
463+ if mcdc_info. state . decision_ctx_stack . pop ( ) . is_none ( ) {
464+ bug ! ( "Unexpected empty decision stack" ) ;
465+ }
437466 } ;
438467 }
439468}
0 commit comments