@@ -13,7 +13,7 @@ mod management;
13
13
14
14
use core:: panic;
15
15
use evaluation_context:: {
16
- Arg , BlockNode , BranchControlFlow , EvalControlFlow , EvaluationContext , MutableKind , Scope ,
16
+ Arg , BlockNode , BranchControlFlow , EvalControlFlow , EvaluationContext , Scope ,
17
17
} ;
18
18
use management:: { QuantumIntrinsicsChecker , ResourceManager } ;
19
19
use miette:: Diagnostic ;
@@ -51,7 +51,7 @@ use qsc_rir::{
51
51
builder,
52
52
rir:: {
53
53
self , Callable , CallableId , CallableType , ConditionCode , Instruction , Literal , Operand ,
54
- Program ,
54
+ Program , VariableId ,
55
55
} ,
56
56
} ;
57
57
use rustc_hash:: FxHashMap ;
@@ -204,8 +204,19 @@ impl<'a> PartialEvaluator<'a> {
204
204
fn bind_value_to_ident ( & mut self , mutability : Mutability , ident : & Ident , value : Value ) {
205
205
// We do slightly different things depending on the mutability of the identifier.
206
206
match mutability {
207
- Mutability :: Immutable => self . bind_value_to_immutable_ident ( ident, value) ,
208
207
Mutability :: Mutable => self . bind_value_to_mutable_ident ( ident, value) ,
208
+ Mutability :: Immutable => {
209
+ let current_scope = self . eval_context . get_current_scope ( ) ;
210
+ if matches ! ( value, Value :: Var ( var) if current_scope. get_static_value( var. id. into( ) ) . is_none( ) )
211
+ {
212
+ // An immutable identifier is being bound to a dynamic value, so treat the identifier as mutable.
213
+ // This allows it to represent a point-in-time copy of the mutable value during evaluation.
214
+ self . bind_value_to_mutable_ident ( ident, value) ;
215
+ } else {
216
+ // The value is static, so bind it to the classical map.
217
+ self . bind_value_to_immutable_ident ( ident, value) ;
218
+ }
219
+ }
209
220
} ;
210
221
}
211
222
@@ -226,11 +237,13 @@ impl<'a> PartialEvaluator<'a> {
226
237
}
227
238
228
239
// Always bind the value to the hybrid map but do it differently depending of the value type.
229
- if let Some ( ( var_id, mutable_kind) ) = self . try_create_mutable_variable ( ident. id , & value) {
230
- // Keep track of whether the mutable variable is static or dynamic.
231
- self . eval_context
232
- . get_current_scope_mut ( )
233
- . insert_mutable_var ( var_id, mutable_kind) ;
240
+ if let Some ( ( var_id, literal) ) = self . try_create_mutable_variable ( ident. id , & value) {
241
+ // If the variable maps to a know static literal, track that mapping.
242
+ if let Some ( literal) = literal {
243
+ self . eval_context
244
+ . get_current_scope_mut ( )
245
+ . insert_static_var_mapping ( var_id, literal) ;
246
+ }
234
247
} else {
235
248
self . bind_value_in_hybrid_map ( ident, value) ;
236
249
}
@@ -1510,6 +1523,8 @@ impl<'a> PartialEvaluator<'a> {
1510
1523
} ;
1511
1524
1512
1525
// Evaluate the body expression.
1526
+ // First, we cache the current static variable mappings so that we can restore them later.
1527
+ let cached_mappings = self . clone_current_static_var_map ( ) ;
1513
1528
let if_true_branch_control_flow =
1514
1529
self . eval_expr_if_branch ( body_expr_id, continuation_block_node_id, maybe_if_expr_var) ?;
1515
1530
let if_true_block_id = match if_true_branch_control_flow {
@@ -1519,16 +1534,28 @@ impl<'a> PartialEvaluator<'a> {
1519
1534
1520
1535
// Evaluate the otherwise expression (if any), and determine the block to branch to if the condition is false.
1521
1536
let if_false_block_id = if let Some ( otherwise_expr_id) = otherwise_expr_id {
1537
+ // Cache the mappings after the true block so we can compare afterwards.
1538
+ let post_if_true_mappings = self . clone_current_static_var_map ( ) ;
1539
+ // Restore the cached mappings from before evaluating the true block.
1540
+ self . overwrite_current_static_var_map ( cached_mappings) ;
1522
1541
let if_false_branch_control_flow = self . eval_expr_if_branch (
1523
1542
otherwise_expr_id,
1524
1543
continuation_block_node_id,
1525
1544
maybe_if_expr_var,
1526
1545
) ?;
1546
+ // Only keep the static mappings that are the same in both blocks; when they are different,
1547
+ // the variable is no longer static across the if expression.
1548
+ self . keep_matching_static_var_mappings ( & post_if_true_mappings) ;
1527
1549
match if_false_branch_control_flow {
1528
1550
BranchControlFlow :: Block ( block_id) => block_id,
1529
1551
BranchControlFlow :: Return ( value) => return Ok ( EvalControlFlow :: Return ( value) ) ,
1530
1552
}
1531
1553
} else {
1554
+ // Only keep the static mappings that are the same after the true block as before; when they are different,
1555
+ // the variable is no longer static across the if expression.
1556
+ self . keep_matching_static_var_mappings ( & cached_mappings) ;
1557
+
1558
+ // Since there is no otherwise block, we branch to the continuation block.
1532
1559
continuation_block_node_id
1533
1560
} ;
1534
1561
@@ -1814,9 +1841,7 @@ impl<'a> PartialEvaluator<'a> {
1814
1841
// the variable if it is static at this moment.
1815
1842
if let Value :: Var ( var) = bound_value {
1816
1843
let current_scope = self . eval_context . get_current_scope ( ) ;
1817
- if let Some ( MutableKind :: Static ( literal) ) =
1818
- current_scope. find_mutable_kind ( var. id . into ( ) )
1819
- {
1844
+ if let Some ( literal) = current_scope. get_static_value ( var. id . into ( ) ) {
1820
1845
map_rir_literal_to_eval_value ( * literal)
1821
1846
} else {
1822
1847
bound_value. clone ( )
@@ -2229,7 +2254,7 @@ impl<'a> PartialEvaluator<'a> {
2229
2254
& mut self ,
2230
2255
local_var_id : LocalVarId ,
2231
2256
value : & Value ,
2232
- ) -> Option < ( rir:: VariableId , MutableKind ) > {
2257
+ ) -> Option < ( rir:: VariableId , Option < Literal > ) > {
2233
2258
// Check if we can create a mutable variable for this value.
2234
2259
let var_ty = try_get_eval_var_type ( value) ?;
2235
2260
@@ -2249,13 +2274,13 @@ impl<'a> PartialEvaluator<'a> {
2249
2274
let store_ins = Instruction :: Store ( value_operand, rir_var) ;
2250
2275
self . get_current_rir_block_mut ( ) . 0 . push ( store_ins) ;
2251
2276
2252
- // Create a mutable variable.
2253
- let mutable_kind = match value_operand {
2254
- Operand :: Literal ( literal) => MutableKind :: Static ( literal) ,
2255
- Operand :: Variable ( _) => MutableKind :: Dynamic ,
2277
+ // Create a mutable variable, mapping it to the static value if any .
2278
+ let static_value = match value_operand {
2279
+ Operand :: Literal ( literal) => Some ( literal) ,
2280
+ Operand :: Variable ( _) => None ,
2256
2281
} ;
2257
2282
2258
- Some ( ( var_id, mutable_kind ) )
2283
+ Some ( ( var_id, static_value ) )
2259
2284
}
2260
2285
2261
2286
fn get_or_insert_callable ( & mut self , callable : Callable ) -> CallableId {
@@ -2625,14 +2650,16 @@ impl<'a> PartialEvaluator<'a> {
2625
2650
2626
2651
// If this is a mutable variable, make sure to update whether it is static or dynamic.
2627
2652
let current_scope = self . eval_context . get_current_scope_mut ( ) ;
2628
- if matches ! ( rhs_operand, Operand :: Variable ( _) )
2629
- || current_scope. is_currently_evaluating_branch ( )
2630
- {
2631
- if let Some ( mutable_kind) = current_scope. find_mutable_var_mut ( rir_var. variable_id )
2632
- {
2633
- * mutable_kind = MutableKind :: Dynamic ;
2653
+ match rhs_operand {
2654
+ Operand :: Literal ( literal) => {
2655
+ // The variable maps to a static literal here, so track that literal value.
2656
+ current_scope. insert_static_var_mapping ( rir_var. variable_id , literal) ;
2634
2657
}
2635
- }
2658
+ Operand :: Variable ( _) => {
2659
+ // The variable is not known to be some literal value, so remove the static mapping.
2660
+ current_scope. remove_static_value ( rir_var. variable_id ) ;
2661
+ }
2662
+ } ;
2636
2663
} else {
2637
2664
// Verify that we are not updating a value that does not have a backing variable from a dynamic branch
2638
2665
// because it is unsupported.
@@ -2918,6 +2945,27 @@ impl<'a> PartialEvaluator<'a> {
2918
2945
_ => panic ! ( "{value} cannot be mapped to a RIR operand" ) ,
2919
2946
}
2920
2947
}
2948
+
2949
+ fn clone_current_static_var_map ( & self ) -> FxHashMap < VariableId , Literal > {
2950
+ self . eval_context
2951
+ . get_current_scope ( )
2952
+ . clone_static_var_mappings ( )
2953
+ }
2954
+
2955
+ fn overwrite_current_static_var_map ( & mut self , static_vars : FxHashMap < VariableId , Literal > ) {
2956
+ self . eval_context
2957
+ . get_current_scope_mut ( )
2958
+ . set_static_var_mappings ( static_vars) ;
2959
+ }
2960
+
2961
+ fn keep_matching_static_var_mappings (
2962
+ & mut self ,
2963
+ other_mappings : & FxHashMap < VariableId , Literal > ,
2964
+ ) {
2965
+ self . eval_context
2966
+ . get_current_scope_mut ( )
2967
+ . keep_matching_static_var_mappings ( other_mappings) ;
2968
+ }
2921
2969
}
2922
2970
2923
2971
fn eval_un_op_with_literals ( un_op : UnOp , value : Value ) -> Value {
0 commit comments