@@ -6,86 +6,116 @@ use qsc_eval::{
6
6
val:: { Result , Value } ,
7
7
Env , Variable ,
8
8
} ;
9
- use qsc_fir:: fir:: { ExprId , LocalItemId , LocalVarId , PackageId } ;
9
+ use qsc_fir:: fir:: { LocalItemId , LocalVarId , PackageId } ;
10
10
use qsc_rca:: { RuntimeKind , ValueKind } ;
11
11
use qsc_rir:: rir:: BlockId ;
12
12
use rustc_hash:: FxHashMap ;
13
13
14
+ /// Struct that keeps track of the active RIR blocks (where RIR instructions are added) and the active scopes (which
15
+ /// correspond to the Q#'s program call stack).
14
16
pub struct EvaluationContext {
15
17
active_blocks : Vec < BlockNode > ,
16
18
scopes : Vec < Scope > ,
17
19
}
18
20
19
21
impl EvaluationContext {
22
+ /// Creates a new evaluation context.
20
23
pub fn new ( package_id : PackageId , initial_block : BlockId ) -> Self {
21
24
let entry_callable_scope = Scope :: new ( package_id, None , Vec :: new ( ) ) ;
22
25
Self {
23
26
active_blocks : vec ! [ BlockNode {
24
27
id: initial_block,
25
- next : None ,
28
+ successor : None ,
26
29
} ] ,
27
30
scopes : vec ! [ entry_callable_scope] ,
28
31
}
29
32
}
30
33
34
+ /// Gets the ID of the current RIR block.
31
35
pub fn get_current_block_id ( & self ) -> BlockId {
32
36
self . active_blocks . last ( ) . expect ( "no active blocks" ) . id
33
37
}
34
38
39
+ /// Gets an immutable reference to the current (call) scope.
35
40
pub fn get_current_scope ( & self ) -> & Scope {
36
41
self . scopes
37
42
. last ( )
38
43
. expect ( "the evaluation context does not have a current scope" )
39
44
}
40
45
46
+ /// Gets a mutable reference to the current (call) scope.
41
47
pub fn get_current_scope_mut ( & mut self ) -> & mut Scope {
42
48
self . scopes
43
49
. last_mut ( )
44
50
. expect ( "the evaluation context does not have a current scope" )
45
51
}
46
52
53
+ /// Pops the currently active block.
47
54
pub fn pop_block_node ( & mut self ) -> BlockNode {
55
+ self . get_current_scope_mut ( ) . active_block_count -= 1 ;
48
56
self . active_blocks
49
57
. pop ( )
50
58
. expect ( "there are no active blocks in the evaluation context" )
51
59
}
52
60
61
+ /// Pops the currently active (call) scope.
53
62
pub fn pop_scope ( & mut self ) -> Scope {
54
63
self . scopes
55
64
. pop ( )
56
65
. expect ( "there are no scopes in the evaluation context" )
57
66
}
58
67
68
+ /// Pushes a new active block.
59
69
pub fn push_block_node ( & mut self , b : BlockNode ) {
60
70
self . active_blocks . push ( b) ;
71
+ self . get_current_scope_mut ( ) . active_block_count += 1 ;
61
72
}
62
73
74
+ /// Pushes a new (call) scope.
63
75
pub fn push_scope ( & mut self , s : Scope ) {
64
76
self . scopes . push ( s) ;
65
77
}
66
78
}
67
79
80
+ /// Struct that represents a block node when we intepret an RIR program as a graph.
68
81
pub struct BlockNode {
82
+ /// The ID of the block.
69
83
pub id : BlockId ,
70
- pub next : Option < BlockId > ,
84
+ /// The block to jump to (if any) once all instructions to the block have been added.
85
+ pub successor : Option < BlockId > ,
71
86
}
72
87
88
+ /// A call scope.
73
89
pub struct Scope {
90
+ /// The package ID of the callable.
74
91
pub package_id : PackageId ,
92
+ /// The ID and functor information of the callable.
75
93
pub callable : Option < ( LocalItemId , FunctorApp ) > ,
94
+ /// The value of the arguments passed to the callable.
76
95
pub args_value_kind : Vec < ValueKind > ,
96
+ /// The classical environment of the callable, which holds values corresponding to local variables.
77
97
pub env : Env ,
78
- last_expr : Option < ExprId > ,
79
- hybrid_exprs : FxHashMap < ExprId , Value > ,
98
+ /// Number of currently active blocks (starting from where this scope was created).
99
+ active_block_count : usize ,
100
+ /// Map that holds the values of local variables.
80
101
hybrid_vars : FxHashMap < LocalVarId , Value > ,
81
102
}
82
103
83
104
impl Scope {
105
+ /// Creates a new call scope.
84
106
pub fn new (
85
107
package_id : PackageId ,
86
108
callable : Option < ( LocalItemId , FunctorApp ) > ,
87
109
args : Vec < Arg > ,
88
110
) -> Self {
111
+ // Create the environment for the classical evaluator.
112
+ // A default classical evaluator environment is created with one scope. However, we need to push an additional
113
+ // scope to the environment to be able to detect whether the classical evaluator has returned from the call
114
+ // scope.
115
+ const CLASSICAL_EVALUATOR_CALL_SCOPE_ID : usize = 1 ;
116
+ let mut env = Env :: default ( ) ;
117
+ env. push_scope ( CLASSICAL_EVALUATOR_CALL_SCOPE_ID ) ;
118
+
89
119
// Determine the runtime kind (static or dynamic) of the arguments.
90
120
let args_value_kind: Vec < ValueKind > = args
91
121
. iter ( )
@@ -100,7 +130,6 @@ impl Scope {
100
130
101
131
// Add the values to either the environment or the hybrid variables depending on whether the value is static or
102
132
// dynamic.
103
- let mut env = Env :: default ( ) ;
104
133
let mut hybrid_vars = FxHashMap :: default ( ) ;
105
134
let arg_runtime_kind_tuple = args. into_iter ( ) . zip ( args_value_kind. iter ( ) ) ;
106
135
for ( arg, value_kind) in arg_runtime_kind_tuple {
@@ -121,42 +150,80 @@ impl Scope {
121
150
callable,
122
151
args_value_kind,
123
152
env,
124
- last_expr : None ,
125
- hybrid_exprs : FxHashMap :: default ( ) ,
153
+ active_block_count : 1 ,
126
154
hybrid_vars,
127
155
}
128
156
}
129
157
130
- // Potential candidate for removal if only the last expression value is needed.
131
- pub fn _get_expr_value ( & self , expr_id : ExprId ) -> & Value {
132
- self . hybrid_exprs
133
- . get ( & expr_id)
134
- . expect ( "expression value does not exist" )
135
- }
136
-
158
+ /// Gets the value of a (hybrid) local variable.
137
159
pub fn get_local_var_value ( & self , local_var_id : LocalVarId ) -> & Value {
138
160
self . hybrid_vars
139
161
. get ( & local_var_id)
140
162
. expect ( "local variable value does not exist" )
141
163
}
142
164
143
- pub fn insert_expr_value ( & mut self , expr_id : ExprId , value : Value ) {
144
- self . last_expr = Some ( expr_id) ;
145
- self . hybrid_exprs . insert ( expr_id, value) ;
165
+ /// Determines whether we are currently evaluating a branch within the scope.
166
+ pub fn is_currently_evaluating_branch ( & self ) -> bool {
167
+ self . active_block_count > 1
168
+ }
169
+
170
+ /// Determines whether the classical evaluator has returned from the call scope.
171
+ /// This relies on the fact that the classical evaluator pops the scope when it encounters a return, so when this
172
+ /// happens the number of scopes in the environment will be exactly one.
173
+ pub fn has_classical_evaluator_returned ( & self ) -> bool {
174
+ self . env . len ( ) == 1
146
175
}
147
176
177
+ /// Inserts the value of a local variable into the hybrid variables map.
148
178
pub fn insert_local_var_value ( & mut self , local_var_id : LocalVarId , value : Value ) {
149
179
self . hybrid_vars . insert ( local_var_id, value) ;
150
180
}
181
+ }
151
182
152
- pub fn clear_last_expr ( & mut self ) {
153
- self . last_expr = None ;
183
+ /// A call argument.
184
+ pub enum Arg {
185
+ Discard ( Value ) ,
186
+ Var ( LocalVarId , Variable ) ,
187
+ }
188
+
189
+ impl Arg {
190
+ /// Converts the argument into its underlying value.
191
+ pub fn into_value ( self ) -> Value {
192
+ match self {
193
+ Self :: Discard ( value) => value,
194
+ Self :: Var ( _, var) => var. value ,
195
+ }
154
196
}
197
+ }
155
198
156
- pub fn last_expr_value ( & self ) -> Value {
157
- self . last_expr
158
- . and_then ( |expr_id| self . hybrid_exprs . get ( & expr_id) )
159
- . map_or_else ( Value :: unit, Clone :: clone)
199
+ /// Represents the possible control flow options that can result from a branch.
200
+ pub enum BranchControlFlow {
201
+ /// The block ID corresponding to a branch.
202
+ Block ( BlockId ) ,
203
+ /// The return value resulting from a branch.
204
+ Return ( Value ) ,
205
+ }
206
+
207
+ /// Represents the possible control flow options that an evaluation can have.
208
+ pub enum EvalControlFlow {
209
+ Continue ( Value ) ,
210
+ Return ( Value ) ,
211
+ }
212
+
213
+ impl EvalControlFlow {
214
+ /// Consumes the evaluation control flow and returns its value.
215
+ pub fn into_value ( self ) -> Value {
216
+ match self {
217
+ EvalControlFlow :: Continue ( value) | EvalControlFlow :: Return ( value) => value,
218
+ }
219
+ }
220
+
221
+ /// Whether this evaluation control flow is a return.
222
+ pub fn is_return ( & self ) -> bool {
223
+ match self {
224
+ Self :: Continue ( _) => false ,
225
+ Self :: Return ( _) => true ,
226
+ }
160
227
}
161
228
}
162
229
@@ -205,17 +272,3 @@ fn map_eval_value_to_value_kind(value: &Value) -> ValueKind {
205
272
| Value :: String ( _) => ValueKind :: Element ( RuntimeKind :: Static ) ,
206
273
}
207
274
}
208
-
209
- pub enum Arg {
210
- Discard ( Value ) ,
211
- Var ( LocalVarId , Variable ) ,
212
- }
213
-
214
- impl Arg {
215
- pub fn into_value ( self ) -> Value {
216
- match self {
217
- Self :: Discard ( value) => value,
218
- Self :: Var ( _, var) => var. value ,
219
- }
220
- }
221
- }
0 commit comments