@@ -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.
@@ -279,8 +237,9 @@ impl MCDCState {
279237 // - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
280238 fn record_conditions ( & mut self , op : LogicalOp , span : Span ) {
281239 let decision_depth = self . decision_depth ( ) ;
282- let decision_ctx =
283- self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
240+ let Some ( decision_ctx) = self . decision_ctx_stack . last_mut ( ) else {
241+ bug ! ( "Unexpected empty decision_ctx_stack" )
242+ } ;
284243 let decision = match decision_ctx. processing_decision . as_mut ( ) {
285244 Some ( decision) => {
286245 decision. span = decision. span . to ( span) ;
@@ -343,8 +302,9 @@ impl MCDCState {
343302 true_marker : BlockMarkerId ,
344303 false_marker : BlockMarkerId ,
345304 ) -> ( Option < ConditionInfo > , Option < MCDCDecisionSpan > ) {
346- let decision_ctx =
347- self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
305+ let Some ( decision_ctx) = self . decision_ctx_stack . last_mut ( ) else {
306+ bug ! ( "Unexpected empty decision_ctx_stack" )
307+ } ;
348308 let Some ( condition_info) = decision_ctx. decision_stack . pop_back ( ) else {
349309 return ( None , None ) ;
350310 } ;
@@ -366,6 +326,74 @@ impl MCDCState {
366326 }
367327}
368328
329+ struct MCDCInfoBuilder {
330+ branch_spans : Vec < MCDCBranchSpan > ,
331+ decision_spans : Vec < MCDCDecisionSpan > ,
332+ state : MCDCState ,
333+ }
334+
335+ impl MCDCInfoBuilder {
336+ fn new ( ) -> Self {
337+ Self { branch_spans : vec ! [ ] , decision_spans : vec ! [ ] , state : MCDCState :: new ( ) }
338+ }
339+
340+ fn visit_evaluated_condition (
341+ & mut self ,
342+ tcx : TyCtxt < ' _ > ,
343+ source_info : SourceInfo ,
344+ true_block : BasicBlock ,
345+ false_block : BasicBlock ,
346+ mut inject_block_marker : impl FnMut ( SourceInfo , BasicBlock ) -> BlockMarkerId ,
347+ ) {
348+ let true_marker = inject_block_marker ( source_info, true_block) ;
349+ let false_marker = inject_block_marker ( source_info, false_block) ;
350+
351+ let decision_depth = self . state . decision_depth ( ) ;
352+ let ( mut condition_info, decision_result) =
353+ self . state . take_condition ( true_marker, false_marker) ;
354+ // take_condition() returns Some for decision_result when the decision stack
355+ // is empty, i.e. when all the conditions of the decision were instrumented,
356+ // and the decision is "complete".
357+ if let Some ( decision) = decision_result {
358+ match decision. conditions_num {
359+ 0 => {
360+ unreachable ! ( "Decision with no condition is not expected" ) ;
361+ }
362+ 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
363+ self . decision_spans . push ( decision) ;
364+ }
365+ _ => {
366+ // Do not generate mcdc mappings and statements for decisions with too many conditions.
367+ let rebase_idx = self . branch_spans . len ( ) - decision. conditions_num + 1 ;
368+ for branch in & mut self . branch_spans [ rebase_idx..] {
369+ branch. condition_info = None ;
370+ }
371+
372+ // ConditionInfo of this branch shall also be reset.
373+ condition_info = None ;
374+
375+ tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
376+ span : decision. span ,
377+ conditions_num : decision. conditions_num ,
378+ max_conditions_num : MAX_CONDITIONS_NUM_IN_DECISION ,
379+ } ) ;
380+ }
381+ }
382+ }
383+ self . branch_spans . push ( MCDCBranchSpan {
384+ span : source_info. span ,
385+ condition_info,
386+ true_marker,
387+ false_marker,
388+ decision_depth,
389+ } ) ;
390+ }
391+
392+ fn into_done ( self ) -> ( Vec < MCDCDecisionSpan > , Vec < MCDCBranchSpan > ) {
393+ ( self . decision_spans , self . branch_spans )
394+ }
395+ }
396+
369397impl Builder < ' _ , ' _ > {
370398 /// If branch coverage is enabled, inject marker statements into `then_block`
371399 /// and `else_block`, and record their IDs in the table of branch spans.
@@ -390,23 +418,17 @@ impl Builder<'_, '_> {
390418 let source_info = SourceInfo { span : self . thir [ expr_id] . span , scope : self . source_scope } ;
391419
392420 // 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- } ) ;
421+ if let Some ( mcdc_info) = branch_info. mcdc_info . as_mut ( ) {
422+ let inject_block_marker = |source_info, block| {
423+ branch_info. markers . inject_block_marker ( & mut self . cfg , source_info, block)
424+ } ;
425+ mcdc_info. visit_evaluated_condition (
426+ self . tcx ,
427+ source_info,
428+ then_block,
429+ else_block,
430+ inject_block_marker,
431+ ) ;
410432 return ;
411433 }
412434
@@ -415,25 +437,27 @@ impl Builder<'_, '_> {
415437
416438 pub ( crate ) fn visit_coverage_branch_operation ( & mut self , logical_op : LogicalOp , span : Span ) {
417439 if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
418- && let Some ( mcdc_state ) = branch_info. mcdc_state . as_mut ( )
440+ && let Some ( mcdc_info ) = branch_info. mcdc_info . as_mut ( )
419441 {
420- mcdc_state . record_conditions ( logical_op, span) ;
442+ mcdc_info . state . record_conditions ( logical_op, span) ;
421443 }
422444 }
423445
424446 pub ( crate ) fn mcdc_increment_depth_if_enabled ( & mut self ) {
425447 if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
426- && let Some ( mcdc_state ) = branch_info. mcdc_state . as_mut ( )
448+ && let Some ( mcdc_info ) = branch_info. mcdc_info . as_mut ( )
427449 {
428- mcdc_state . decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
450+ mcdc_info . state . decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
429451 } ;
430452 }
431453
432454 pub ( crate ) fn mcdc_decrement_depth_if_enabled ( & mut self ) {
433455 if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
434- && let Some ( mcdc_state ) = branch_info. mcdc_state . as_mut ( )
456+ && let Some ( mcdc_info ) = branch_info. mcdc_info . as_mut ( )
435457 {
436- mcdc_state. decision_ctx_stack . pop ( ) . expect ( "Unexpected empty decision stack" ) ;
458+ if mcdc_info. state . decision_ctx_stack . pop ( ) . is_none ( ) {
459+ bug ! ( "Unexpected empty decision stack" ) ;
460+ }
437461 } ;
438462 }
439463}
0 commit comments