@@ -28,6 +28,7 @@ use qsc_fir::fir::{
28
28
} ;
29
29
use qsc_fir:: ty:: Ty ;
30
30
use rand:: { rngs:: StdRng , SeedableRng } ;
31
+ use std:: ops;
31
32
use std:: {
32
33
cell:: RefCell ,
33
34
fmt:: { self , Display , Formatter } ,
@@ -172,6 +173,26 @@ impl Display for Spec {
172
173
}
173
174
}
174
175
176
+ /// Utility function to identify a subset of a control flow graph corresponding to a given
177
+ /// range.
178
+ #[ must_use]
179
+ pub fn sub_cfg ( cfg : & Rc < [ CfgNode ] > , range : ops:: Range < usize > ) -> Rc < [ CfgNode ] > {
180
+ let start: u32 = range
181
+ . start
182
+ . try_into ( )
183
+ . expect ( "cfg ranges should fit into u32" ) ;
184
+ cfg[ range]
185
+ . iter ( )
186
+ . map ( |node| match node {
187
+ CfgNode :: Jump ( idx) => CfgNode :: Jump ( idx - start) ,
188
+ CfgNode :: JumpIf ( idx) => CfgNode :: JumpIf ( idx - start) ,
189
+ CfgNode :: JumpIfNot ( idx) => CfgNode :: JumpIfNot ( idx - start) ,
190
+ _ => * node,
191
+ } )
192
+ . collect :: < Vec < _ > > ( )
193
+ . into ( )
194
+ }
195
+
175
196
/// Evaluates the given code with the given context.
176
197
/// # Errors
177
198
/// Returns the first error encountered during execution.
@@ -480,24 +501,20 @@ impl State {
480
501
let cfg = self
481
502
. cfg_stack
482
503
. last ( )
483
- . expect ( "should have at least one stack frame" )
484
- . clone ( ) ;
485
- let res = match cfg
486
- . get ( self . idx as usize )
487
- . expect ( "should have next node in CFG" )
488
- {
489
- CfgNode :: Bind ( pat) => {
504
+ . expect ( "should have at least one stack frame" ) ;
505
+ let res = match cfg. get ( self . idx as usize ) {
506
+ Some ( CfgNode :: Bind ( pat) ) => {
490
507
self . idx += 1 ;
491
508
self . eval_bind ( env, globals, * pat) ;
492
509
continue ;
493
510
}
494
- CfgNode :: Expr ( expr) => {
511
+ Some ( CfgNode :: Expr ( expr) ) => {
495
512
self . idx += 1 ;
496
513
self . eval_expr ( env, sim, globals, out, * expr)
497
514
. map_err ( |e| ( e, self . get_stack_frames ( ) ) ) ?;
498
515
continue ;
499
516
}
500
- CfgNode :: Stmt ( stmt) => {
517
+ Some ( CfgNode :: Stmt ( stmt) ) => {
501
518
self . idx += 1 ;
502
519
self . current_span = globals. get_stmt ( ( self . package , * stmt) . into ( ) ) . span ;
503
520
@@ -521,11 +538,11 @@ impl State {
521
538
}
522
539
}
523
540
}
524
- CfgNode :: Jump ( idx) => {
541
+ Some ( CfgNode :: Jump ( idx) ) => {
525
542
self . idx = * idx;
526
543
continue ;
527
544
}
528
- CfgNode :: JumpIf ( idx) => {
545
+ Some ( CfgNode :: JumpIf ( idx) ) => {
529
546
let cond = self . curr_val == Some ( Value :: Bool ( true ) ) ;
530
547
if cond {
531
548
self . idx = * idx;
@@ -534,7 +551,7 @@ impl State {
534
551
}
535
552
continue ;
536
553
}
537
- CfgNode :: JumpIfNot ( idx) => {
554
+ Some ( CfgNode :: JumpIfNot ( idx) ) => {
538
555
let cond = self . curr_val == Some ( Value :: Bool ( true ) ) ;
539
556
if cond {
540
557
self . idx += 1 ;
@@ -543,21 +560,30 @@ impl State {
543
560
}
544
561
continue ;
545
562
}
546
- CfgNode :: Store => {
563
+ Some ( CfgNode :: Store ) => {
547
564
self . push_val ( ) ;
548
565
self . idx += 1 ;
549
566
continue ;
550
567
}
551
- CfgNode :: Unit => {
568
+ Some ( CfgNode :: Unit ) => {
552
569
self . idx += 1 ;
553
570
self . set_curr_val ( Value :: unit ( ) ) ;
554
571
continue ;
555
572
}
556
- CfgNode :: Ret => {
573
+ Some ( CfgNode :: Ret ) => {
557
574
self . leave_frame ( ) ;
558
575
env. leave_scope ( ) ;
559
576
continue ;
560
577
}
578
+ None => {
579
+ // We have reached the end of the current cfg without reaching an explicit return node,
580
+ // usually indicating the partial execution of a single sub-expression.
581
+ // This means we should leave the current frame but not the current environment scope,
582
+ // so bound variables are still accessible after completion.
583
+ self . leave_frame ( ) ;
584
+ assert ! ( self . cfg_stack. is_empty( ) ) ;
585
+ continue ;
586
+ }
561
587
} ;
562
588
563
589
if let StepResult :: Return ( _) = res {
0 commit comments