microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
5dbe5d195e0f59f2c7ff8d562e4516703b3f072e

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/compiler/qsc_partial_eval/src/lib.rs

4217lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! The Q# partial evaluator residualizes a Q# program, producing RIR from FIR.
5//! It does this by evaluating all purely classical expressions and generating RIR instructions for expressions that are
6//! not purely classical.
7
8#[cfg(test)]
9mod tests;
10
11mod evaluation_context;
12mod management;
13
14use core::panic;
15use evaluation_context::{Arg, BlockNode, EvalControlFlow, EvaluationContext, Scope};
16use management::{QuantumIntrinsicsChecker, ResourceManager};
17use miette::Diagnostic;
18use qsc_data_structures::{functors::FunctorApp, span::Span, target::TargetCapabilityFlags};
19use qsc_eval::{
20 self, Error as EvalError, ErrorBehavior, PackageSpan, State, StepAction, StepResult, Variable,
21 are_ctls_unique,
22 backend::TracingBackend,
23 intrinsic::qubit_relabel,
24 output::GenericReceiver,
25 resolve_closure,
26 val::{
27 self, Value, Var, VarTy, index_array, slice_array, update_functor_app, update_index_range,
28 update_index_single,
29 },
30};
31use qsc_fir::{
32 fir::{
33 self, BinOp, Block, BlockId, CallableDecl, CallableImpl, ExecGraph, ExecGraphConfig, Expr,
34 ExprId, ExprKind, Field, Global, Ident, LocalVarId, Mutability, PackageId, PackageStore,
35 PackageStoreLookup, Pat, PatId, PatKind, PrimField, Res, SpecDecl, SpecImpl, Stmt, StmtId,
36 StmtKind, StoreBlockId, StoreExprId, StoreItemId, StorePatId, StoreStmtId, StringComponent,
37 UnOp,
38 },
39 ty::{Prim, Ty},
40};
41use qsc_lowerer::map_fir_package_to_hir;
42use qsc_rca::{
43 ComputeKind, ComputePropertiesLookup, ItemComputeProperties, PackageStoreComputeProperties,
44 RuntimeFeatureFlags, ValueKind,
45 errors::{
46 Error as CapabilityError, generate_errors_from_runtime_features,
47 get_missing_runtime_features,
48 },
49};
50pub use qsc_rir::{
51 builder::{self, initialize_decl},
52 debug::{
53 DbgLocation, DbgLocationId, DbgPackageOffset, DbgScope, DbgScopeId, InstructionDbgMetadata,
54 },
55 rir::{
56 self, Callable, CallableId, CallableType, ConditionCode, FcmpConditionCode, Instruction,
57 Literal, Operand, Program, VariableId,
58 },
59};
60use rustc_hash::FxHashMap;
61use std::{collections::hash_map::Entry, rc::Rc, result::Result};
62use thiserror::Error;
63
64/// Partially evaluates a program with the specified entry expression.
65pub fn partially_evaluate(
66 package_store: &PackageStore,
67 compute_properties: &PackageStoreComputeProperties,
68 entry: &ProgramEntry,
69 capabilities: TargetCapabilityFlags,
70 config: PartialEvalConfig,
71) -> Result<Program, Error> {
72 let partial_evaluator = PartialEvaluator::new(
73 package_store,
74 compute_properties,
75 entry,
76 capabilities,
77 config,
78 );
79 partial_evaluator.eval()
80}
81
82/// Partially evaluates a callable with the specified arguments.
83pub fn partially_evaluate_call(
84 package_store: &PackageStore,
85 compute_properties: &PackageStoreComputeProperties,
86 callable: StoreItemId,
87 args: Value,
88 capabilities: TargetCapabilityFlags,
89 config: PartialEvalConfig,
90) -> Result<Program, Error> {
91 let partial_evaluator = PartialEvaluator::new_from_package_id(
92 package_store,
93 compute_properties,
94 callable.package,
95 capabilities,
96 config,
97 );
98 partial_evaluator.invoke(callable, args)
99}
100
101/// A partial evaluation error.
102#[derive(Clone, Debug, Diagnostic, Error)]
103pub enum Error {
104 #[error(transparent)]
105 #[diagnostic(transparent)]
106 CapabilityError(CapabilityError),
107
108 #[error("cannot use a dynamic value returned from a runtime-resolved callable")]
109 #[diagnostic(code("Qsc.PartialEval.UnexpectedDynamicValue"))]
110 #[diagnostic(help("try invoking the desired callable directly"))]
111 UnexpectedDynamicValue(#[label] PackageSpan),
112
113 #[error("unsupported type `{0}` in custom intrinsic callable")]
114 #[diagnostic(help(
115 "variables of type `{0}` cannot be emitted into QIR and should not appear in custom intrinsic callable signatures"
116 ))]
117 #[diagnostic(code("Qsc.PartialEval.UnsupportedType"))]
118 UnsupportedCustomIntrinsicType(String, #[label] PackageSpan),
119
120 #[error("partial evaluation failed with error: {0}")]
121 #[diagnostic(code("Qsc.PartialEval.EvaluationFailed"))]
122 EvaluationFailed(String, #[label] PackageSpan),
123
124 #[error("unsupported Result literal in output")]
125 #[diagnostic(help(
126 "Result literals `One` and `Zero` cannot be included in generated QIR output recording."
127 ))]
128 #[diagnostic(code("Qsc.PartialEval.OutputResultLiteral"))]
129 OutputResultLiteral(#[label] PackageSpan),
130
131 #[error("an unexpected error occurred related to: {0}")]
132 #[diagnostic(code("Qsc.PartialEval.Unexpected"))]
133 #[diagnostic(help(
134 "this is probably a bug, please consider reporting this as an issue to the development team"
135 ))]
136 Unexpected(String, #[label] PackageSpan),
137
138 #[error("failed to evaluate: {0} is not supported")]
139 #[diagnostic(code("Qsc.PartialEval.Unimplemented"))]
140 Unimplemented(String, #[label] PackageSpan),
141
142 #[error("unsupported call into test callable")]
143 #[diagnostic(code("Qsc.PartialEval.UnsupportedTestCallable"))]
144 #[diagnostic(help(
145 "callables with the `@Test` annotation should not be called from non-test code."
146 ))]
147 UnsupportedTestCallable(#[label] PackageSpan),
148
149 #[error("unsupported use of simulation-only intrinsic `{0}`")]
150 #[diagnostic(code("Qsc.PartialEval.UnsupportedSimulationIntrinsic"))]
151 UnsupportedSimulationIntrinsic(String, #[label] PackageSpan),
152}
153
154impl From<EvalError> for Error {
155 fn from(e: EvalError) -> Self {
156 Error::EvaluationFailed(e.to_string(), *e.span())
157 }
158}
159
160impl Error {
161 #[must_use]
162 pub fn span(&self) -> Option<PackageSpan> {
163 match self {
164 Self::CapabilityError(_) => None,
165 Self::UnexpectedDynamicValue(span)
166 | Self::UnsupportedCustomIntrinsicType(_, span)
167 | Self::EvaluationFailed(_, span)
168 | Self::OutputResultLiteral(span)
169 | Self::Unexpected(_, span)
170 | Self::Unimplemented(_, span)
171 | Self::UnsupportedTestCallable(span)
172 | Self::UnsupportedSimulationIntrinsic(_, span) => Some(*span),
173 }
174 }
175}
176
177/// An entry to the program to be partially evaluated.
178pub struct ProgramEntry {
179 /// The execution graph that corresponds to the entry expression.
180 pub exec_graph: ExecGraph,
181 /// The entry expression unique identifier within a package store.
182 pub expr: fir::StoreExprId,
183}
184
185struct PartialEvaluator<'a> {
186 package_store: &'a PackageStore,
187 compute_properties: &'a PackageStoreComputeProperties,
188 resource_manager: ResourceManager,
189 backend: QuantumIntrinsicsChecker,
190 callables_map: FxHashMap<Rc<str>, CallableId>,
191 eval_context: EvaluationContext,
192 program: Program,
193 entry: Option<&'a ProgramEntry>,
194 config: PartialEvalConfig,
195 dbg_context: DbgContext,
196}
197
198#[derive(Clone, Copy)]
199pub struct PartialEvalConfig {
200 pub generate_debug_metadata: bool,
201}
202
203impl<'a> PartialEvaluator<'a> {
204 fn new(
205 package_store: &'a PackageStore,
206 compute_properties: &'a PackageStoreComputeProperties,
207 entry: &'a ProgramEntry,
208 capabilities: TargetCapabilityFlags,
209 config: PartialEvalConfig,
210 ) -> Self {
211 Self::new_internal(
212 package_store,
213 compute_properties,
214 capabilities,
215 Some(entry),
216 None,
217 config,
218 )
219 }
220
221 fn new_from_package_id(
222 package_store: &'a PackageStore,
223 compute_properties: &'a PackageStoreComputeProperties,
224 package_id: PackageId,
225 capabilities: TargetCapabilityFlags,
226 config: PartialEvalConfig,
227 ) -> Self {
228 Self::new_internal(
229 package_store,
230 compute_properties,
231 capabilities,
232 None,
233 Some(package_id),
234 config,
235 )
236 }
237
238 fn new_internal(
239 package_store: &'a PackageStore,
240 compute_properties: &'a PackageStoreComputeProperties,
241 capabilities: TargetCapabilityFlags,
242 entry: Option<&'a ProgramEntry>,
243 package_id: Option<PackageId>,
244 config: PartialEvalConfig,
245 ) -> Self {
246 // Create the entry-point callable.
247 let mut resource_manager = ResourceManager::default();
248 let mut program = Program::new();
249 program.config.capabilities = capabilities;
250 let entry_block_id = resource_manager.next_block();
251 program.blocks.insert(entry_block_id, rir::Block::default());
252 let entry_point_id = resource_manager.next_callable();
253 let entry_point = rir::Callable {
254 name: "main".into(),
255 input_type: Vec::new(),
256 output_type: Some(rir::Ty::Integer),
257 body: Some(entry_block_id),
258 call_type: CallableType::Regular,
259 };
260 program.callables.insert(entry_point_id, entry_point);
261 program.entry = entry_point_id;
262
263 // Add the required call to the initialization function.
264 let init_func = initialize_decl();
265 let init_id = resource_manager.next_callable();
266 program.callables.insert(init_id, init_func);
267 program
268 .get_block_mut(entry_block_id)
269 .0
270 .push(Instruction::Call(
271 init_id,
272 vec![Operand::Literal(Literal::Pointer)],
273 None,
274 None,
275 ));
276
277 // Initialize the evaluation context and create a new partial evaluator.
278 let context = EvaluationContext::new(
279 package_id.unwrap_or_else(|| {
280 entry
281 .expect("program entry should be provided when package id is None")
282 .expr
283 .package
284 }),
285 entry_block_id,
286 );
287 Self {
288 package_store,
289 compute_properties,
290 eval_context: context,
291 resource_manager,
292 backend: QuantumIntrinsicsChecker::default(),
293 callables_map: FxHashMap::default(),
294 program,
295 entry,
296 config,
297 dbg_context: Default::default(),
298 }
299 }
300
301 fn bind_value_to_pat(&mut self, mutability: Mutability, pat_id: PatId, value: Value) {
302 let pat = self.get_pat(pat_id);
303 match &pat.kind {
304 PatKind::Bind(ident) => {
305 self.bind_value_to_ident(mutability, ident, value);
306 }
307 PatKind::Tuple(pats) => {
308 let tuple = value.unwrap_tuple();
309 assert!(pats.len() == tuple.len());
310 for (pat_id, value) in pats.iter().zip(tuple.iter()) {
311 self.bind_value_to_pat(mutability, *pat_id, value.clone());
312 }
313 }
314 PatKind::Discard => {
315 // Nothing to bind to.
316 }
317 }
318 }
319
320 fn bind_value_to_ident(&mut self, mutability: Mutability, ident: &Ident, value: Value) {
321 // We do slightly different things depending on the mutability of the identifier.
322 match mutability {
323 Mutability::Mutable => self.bind_value_to_mutable_ident(ident, value),
324 Mutability::Immutable => {
325 let current_scope = self.eval_context.get_current_scope();
326 if matches!(value, Value::Var(var) if current_scope.get_static_value(var.id.into()).is_none())
327 {
328 // An immutable identifier is being bound to a dynamic value, so treat the identifier as mutable.
329 // This allows it to represent a point-in-time copy of the mutable value during evaluation.
330 self.bind_value_to_mutable_ident(ident, value);
331 } else {
332 // The value is static, so bind it to the classical map.
333 self.bind_value_to_immutable_ident(ident, value);
334 }
335 }
336 }
337 }
338
339 fn bind_value_to_immutable_ident(&mut self, ident: &Ident, value: Value) {
340 // If the value is not a variable, bind it to the classical map.
341 if !matches!(value, Value::Var(_)) {
342 self.bind_value_in_classical_map(ident, &value);
343 }
344
345 // Always bind the value to the hybrid map.
346 self.bind_value_in_hybrid_map(ident, value);
347 }
348
349 fn bind_value_to_mutable_ident(&mut self, ident: &Ident, value: Value) {
350 // If the value is not a variable, bind it to the classical map.
351 if !matches!(value, Value::Var(_)) {
352 self.bind_value_in_classical_map(ident, &value);
353 }
354
355 // Always bind the value to the hybrid map but do it differently depending of the value type.
356 if let Some((var_id, literal)) = self.try_create_mutable_variable(ident.id, &value) {
357 // If the variable maps to a know static literal, track that mapping.
358 if let Some(literal) = literal {
359 self.eval_context
360 .get_current_scope_mut()
361 .insert_static_var_mapping(var_id, literal);
362 }
363 } else {
364 self.bind_value_in_hybrid_map(ident, value);
365 }
366 }
367
368 fn bind_value_in_classical_map(&mut self, ident: &Ident, value: &Value) {
369 // Create a variable and bind it to the classical environment.
370 let var = Variable {
371 name: ident.name.clone(),
372 value: value.clone(),
373 span: ident.span,
374 };
375 let scope = self.eval_context.get_current_scope_mut();
376 scope.env.bind_variable_in_top_frame(ident.id, var);
377 }
378
379 fn bind_value_in_hybrid_map(&mut self, ident: &Ident, value: Value) {
380 // Insert the value into the hybrid vars map.
381 self.eval_context
382 .get_current_scope_mut()
383 .insert_hybrid_local_value(ident.id, value);
384 }
385
386 fn create_intrinsic_callable(
387 &self,
388 store_item_id: StoreItemId,
389 callable_decl: &CallableDecl,
390 call_type: CallableType,
391 ) -> Result<Callable, Error> {
392 let callable_package = self.package_store.get(store_item_id.package);
393 let name = callable_decl.name.name.to_string();
394 let mut input_type: Vec<rir::Ty> = Vec::new();
395 for input_param in &callable_package.derive_callable_input_params(callable_decl) {
396 input_type.push(map_fir_type_to_rir_type(&input_param.ty).map_err(|msg| {
397 Error::UnsupportedCustomIntrinsicType(
398 msg,
399 PackageSpan {
400 package: map_fir_package_to_hir(store_item_id.package),
401 span: self
402 .package_store
403 .get_pat((store_item_id.package, input_param.pat).into())
404 .span,
405 },
406 )
407 })?);
408 }
409 let output_type = if callable_decl.output == Ty::UNIT {
410 None
411 } else {
412 Some(
413 map_fir_type_to_rir_type(&callable_decl.output).map_err(|msg| {
414 Error::UnsupportedCustomIntrinsicType(
415 msg,
416 PackageSpan {
417 package: map_fir_package_to_hir(self.get_current_package_id()),
418 span: callable_decl.span,
419 },
420 )
421 })?,
422 )
423 };
424 let body = None;
425 let call_type = if name.eq("__quantum__qis__reset__body") {
426 CallableType::Reset
427 } else {
428 call_type
429 };
430 Ok(Callable {
431 name,
432 input_type,
433 output_type,
434 body,
435 call_type,
436 })
437 }
438
439 fn create_program_block(&mut self) -> rir::BlockId {
440 let block_id = self.resource_manager.next_block();
441 self.program.blocks.insert(block_id, rir::Block::default());
442 block_id
443 }
444
445 fn entry_expr_output_span(&self) -> PackageSpan {
446 let expr = self.get_expr(
447 self.entry
448 .expect("should have entry when getting entry expr span")
449 .expr
450 .expr,
451 );
452 let local_span = match &expr.kind {
453 // Special handling for compiler generated entry expressions that come from the `@EntryPoint`
454 // attributed callable.
455 ExprKind::Call(callee, _) if expr.span == Span::default() => {
456 self.get_expr(*callee).span
457 }
458 _ => expr.span,
459 };
460 let hir_package_id = map_fir_package_to_hir(
461 self.entry
462 .expect("should have entry when getting entry expr span")
463 .expr
464 .package,
465 );
466 PackageSpan {
467 package: hir_package_id,
468 span: local_span,
469 }
470 }
471
472 fn extract_program(
473 mut self,
474 ret_val: Value,
475 output_ty: &Ty,
476 output_span: PackageSpan,
477 ) -> Result<Program, Error> {
478 let output_recording: Vec<Instruction> = self
479 .generate_output_recording_instructions(ret_val, output_ty, "")
480 .map_err(|()| Error::OutputResultLiteral(output_span))?;
481
482 // Insert the return expression and return the generated program.
483 let current_block = self.get_current_rir_block_mut();
484 current_block.0.extend(output_recording);
485 current_block.0.push(Instruction::Return);
486
487 // Set the number of qubits and results used by the program.
488 self.program.num_qubits = self
489 .resource_manager
490 .qubit_count()
491 .try_into()
492 .expect("qubits count should fit into a u32");
493 self.program.num_results = self
494 .resource_manager
495 .result_register_count()
496 .try_into()
497 .expect("results count should fit into a u32");
498
499 self.program.dbg_info.remove_unused_dbg_metadata();
500
501 Ok(self.program)
502 }
503
504 fn eval(mut self) -> Result<Program, Error> {
505 // Evaluate the entry-point expression.
506 let ret_val = self
507 .try_eval_expr(
508 self.entry
509 .expect("should have program entry on call to eval")
510 .expr
511 .expr,
512 )?
513 .into_value();
514 let output_ty = &self
515 .get_expr(
516 self.entry
517 .expect("should have program entry on call to eval")
518 .expr
519 .expr,
520 )
521 .ty;
522 let output_span = self.entry_expr_output_span();
523 self.extract_program(ret_val, output_ty, output_span)
524 }
525
526 fn invoke(mut self, callable: StoreItemId, args: Value) -> Result<Program, Error> {
527 // Evaluate the callalbe.
528 let ret_val = self.eval_global_call(callable, args)?.into_value();
529 let global = self
530 .package_store
531 .get_global(callable)
532 .expect("global not present");
533 let Global::Callable(callable_decl) = global else {
534 // Instruction generation for UDTs is not supported.
535 panic!("global is not a callable");
536 };
537 let output_ty = &callable_decl.output;
538 self.extract_program(
539 ret_val,
540 output_ty,
541 PackageSpan {
542 package: map_fir_package_to_hir(callable.package),
543 span: callable_decl.span,
544 },
545 )
546 }
547
548 fn eval_array_update_index(
549 &mut self,
550 array: &[Value],
551 index_expr_id: ExprId,
552 update_expr_id: ExprId,
553 ) -> Result<Value, Error> {
554 // Try to evaluate the index and update expressions to get their value, short-circuiting execution if any of the
555 // expressions is a return.
556 let index_expr_package_span = self.get_expr_package_span(index_expr_id);
557 let index_control_flow = self.try_eval_expr(index_expr_id)?;
558 let EvalControlFlow::Continue(index_value) = index_control_flow else {
559 return Err(Error::Unexpected(
560 "embedded return in index expression".to_string(),
561 index_expr_package_span,
562 ));
563 };
564 let update_control_flow = self.try_eval_expr(update_expr_id)?;
565 let EvalControlFlow::Continue(update_value) = update_control_flow else {
566 return Err(Error::Unexpected(
567 "embedded return in update expression".to_string(),
568 self.get_expr_package_span(update_expr_id),
569 ));
570 };
571
572 // Set the value at the specified index or range.
573 let update_result = match index_value {
574 Value::Int(index) => {
575 update_index_single(array, index, update_value, index_expr_package_span)
576 }
577 Value::Range(range) => update_index_range(
578 array,
579 range.start,
580 range.step,
581 range.end,
582 update_value,
583 index_expr_package_span,
584 ),
585 _ => panic!("invalid kind of value for index"),
586 };
587 let updated_array = update_result.map_err(Error::from)?;
588 Ok(updated_array)
589 }
590
591 fn eval_bin_op(
592 &mut self,
593 bin_op: BinOp,
594 lhs_value: Value,
595 rhs_expr_id: ExprId,
596 lhs_span: PackageSpan, // For diagnostic purposes only.
597 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
598 ) -> Result<EvalControlFlow, Error> {
599 // Evaluate the binary operation differently depending on the LHS value variant.
600 match lhs_value {
601 Value::Array(lhs_array) => self.eval_bin_op_with_lhs_array_operand(
602 bin_op,
603 &lhs_array,
604 rhs_expr_id,
605 bin_op_expr_span,
606 ),
607 Value::Result(lhs_result) => self.eval_bin_op_with_lhs_result_operand(
608 bin_op,
609 lhs_result,
610 rhs_expr_id,
611 bin_op_expr_span,
612 ),
613 Value::Bool(lhs_bool) => {
614 self.eval_bin_op_with_lhs_classical_bool_operand(bin_op, lhs_bool, rhs_expr_id)
615 }
616 Value::Int(lhs_int) => {
617 let lhs_operand = Operand::Literal(Literal::Integer(lhs_int));
618 self.eval_bin_op_with_lhs_integer_operand(
619 bin_op,
620 lhs_operand,
621 rhs_expr_id,
622 bin_op_expr_span,
623 )
624 }
625 Value::Double(lhs_double) => {
626 let lhs_operand = Operand::Literal(Literal::Double(lhs_double));
627 self.eval_bin_op_with_lhs_double_operand(
628 bin_op,
629 lhs_operand,
630 rhs_expr_id,
631 bin_op_expr_span,
632 )
633 }
634 Value::Var(lhs_eval_var) => {
635 self.eval_bin_op_with_lhs_var(bin_op, lhs_eval_var, rhs_expr_id, bin_op_expr_span)
636 }
637 Value::String(_) => {
638 // Strings are a special case that we always treat as empty string during partial evaluation,
639 // but we still need to evaluate the RHS expression in case it contains side effects.
640 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
641 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
642 return Err(Error::Unexpected(
643 "embedded return in RHS expression".to_string(),
644 self.get_expr_package_span(rhs_expr_id),
645 ));
646 };
647 Ok(EvalControlFlow::Continue(rhs_value))
648 }
649 _ => Err(Error::Unexpected(
650 format!("unsupported LHS value: {lhs_value}"),
651 lhs_span,
652 )),
653 }
654 }
655
656 fn eval_bin_op_with_lhs_array_operand(
657 &mut self,
658 bin_op: BinOp,
659 lhs_array: &Rc<Vec<Value>>,
660 rhs_expr_id: ExprId,
661 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
662 ) -> Result<EvalControlFlow, Error> {
663 // Check that the binary operation is currently supported.
664 if matches!(bin_op, BinOp::Eq | BinOp::Neq) {
665 return Err(Error::Unimplemented(
666 "array comparison".to_string(),
667 bin_op_expr_span,
668 ));
669 }
670
671 // The only possible binary operation with array operands at this point is addition.
672 assert!(
673 matches!(bin_op, BinOp::Add),
674 "expected array addition operation, got {bin_op:?}"
675 );
676
677 // Try to evaluate the RHS array expression to get its value.
678 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
679 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
680 return Err(Error::Unexpected(
681 "embedded return in RHS expression".to_string(),
682 self.get_expr_package_span(rhs_expr_id),
683 ));
684 };
685 let Value::Array(rhs_array) = rhs_value else {
686 panic!("expected array value from RHS expression");
687 };
688
689 // Concatenate the arrays.
690 let concatenated_array: Vec<Value> =
691 lhs_array.iter().chain(rhs_array.iter()).cloned().collect();
692 let array_value = Value::Array(concatenated_array.into());
693 Ok(EvalControlFlow::Continue(array_value))
694 }
695
696 fn eval_bin_op_with_lhs_result_operand(
697 &mut self,
698 bin_op: BinOp,
699 lhs_result: val::Result,
700 rhs_expr_id: ExprId,
701 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
702 ) -> Result<EvalControlFlow, Error> {
703 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
704 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
705 return Err(Error::Unexpected(
706 "embedded return in RHS expression".to_string(),
707 self.get_expr_package_span(rhs_expr_id),
708 ));
709 };
710 let Value::Result(rhs_result) = rhs_value else {
711 panic!("expected result value from RHS expression");
712 };
713
714 // Even though to get to this path, an expression would have to be categorized as hybrid by RCA, it is
715 // possible that the expression is in fact purely classical.
716 // This can happen in cases where a data structure such an array, tuple or UDT contains a mix of static and
717 // dynamic values. In such instances, RCA identifies all the contents of the data structure as dynamic even if
718 // some values are static.
719 // Here we handle this case and if both operands are purely classical we evaluate them.
720 if let (val::Result::Val(lhs_result_value), val::Result::Val(rhs_result_value)) =
721 (lhs_result, rhs_result)
722 {
723 let bool_value = match bin_op {
724 BinOp::Eq => lhs_result_value == rhs_result_value,
725 BinOp::Neq => lhs_result_value != rhs_result_value,
726 _ => {
727 return Err(Error::Unexpected(
728 format!("invalid binary operator for Result operands: {bin_op:?})"),
729 bin_op_expr_span,
730 ));
731 }
732 };
733 return Ok(EvalControlFlow::Continue(Value::Bool(bool_value)));
734 }
735
736 // Get the operands to use when generating the binary operation instruction.
737 let lhs_operand = self.eval_result_as_bool_operand(lhs_result);
738 let rhs_operand = self.eval_result_as_bool_operand(rhs_result);
739
740 // Create a variable to store the result of the expression.
741 let variable_id = self.resource_manager.next_var();
742 let rir_variable = rir::Variable {
743 variable_id,
744 ty: rir::Ty::Boolean, // Binary operations between results are always Boolean.
745 };
746
747 // Create the binary operation instruction and add it to the current block.
748 let condition_code = match bin_op {
749 BinOp::Eq => ConditionCode::Eq,
750 BinOp::Neq => ConditionCode::Ne,
751 _ => {
752 return Err(Error::Unexpected(
753 format!("invalid binary operator for Result operands: {bin_op:?})"),
754 bin_op_expr_span,
755 ));
756 }
757 };
758
759 let instruction = match (bin_op, lhs_operand, rhs_operand) {
760 (BinOp::Eq, Operand::Literal(Literal::Bool(true)), operand)
761 | (BinOp::Eq, operand, Operand::Literal(Literal::Bool(true)))
762 | (BinOp::Neq, Operand::Literal(Literal::Bool(false)), operand)
763 | (BinOp::Neq, operand, Operand::Literal(Literal::Bool(false))) => {
764 // One of the operands is a literal so we just need a store instruction.
765 Instruction::Store(operand, rir_variable)
766 }
767 // Both operators are non-literals so we need the comparison instruction.
768 _ => Instruction::Icmp(condition_code, lhs_operand, rhs_operand, rir_variable),
769 };
770 self.get_current_rir_block_mut().0.push(instruction);
771
772 // Return the variable as a value.
773 let value = Value::Var(map_rir_var_to_eval_var(rir_variable).map_err(|()| {
774 Error::Unexpected(
775 format!("{} type in binop", rir_variable.ty),
776 bin_op_expr_span,
777 )
778 })?);
779 Ok(EvalControlFlow::Continue(value))
780 }
781
782 fn eval_bin_op_with_lhs_classical_bool_operand(
783 &mut self,
784 bin_op: BinOp,
785 lhs_bool: bool,
786 rhs_expr_id: ExprId,
787 ) -> Result<EvalControlFlow, Error> {
788 let value = match (bin_op, lhs_bool) {
789 // Handle short-circuiting for logical AND and logical OR.
790 (BinOp::AndL, false) => Value::Bool(false),
791 (BinOp::OrL, true) => Value::Bool(true),
792 // Cases for which just returning the RHS value is sufficient.
793 (BinOp::AndL | BinOp::Eq, true) | (BinOp::OrL | BinOp::Neq, false) => {
794 // Try to evaluate the RHS expression to get its value.
795 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
796 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
797 return Err(Error::Unexpected(
798 "embedded return in RHS expression".to_string(),
799 self.get_expr_package_span(rhs_expr_id),
800 ));
801 };
802 rhs_value
803 }
804 // The other possible cases.
805 (BinOp::Eq | BinOp::Neq, _) => {
806 // Try to evaluate the RHS expression to get its value.
807 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
808 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
809 return Err(Error::Unexpected(
810 "embedded return in RHS expression".to_string(),
811 self.get_expr_package_span(rhs_expr_id),
812 ));
813 };
814
815 // Create the operands.
816 let lhs_operand = Operand::Literal(Literal::Bool(lhs_bool));
817 let rhs_operand = self.map_eval_value_to_rir_operand(&rhs_value);
818
819 // If both operands are literals, evaluate the binary operation and return its value.
820 if let (Operand::Literal(lhs_literal), Operand::Literal(rhs_literal)) =
821 (lhs_operand, rhs_operand)
822 {
823 let value = eval_bin_op_with_bool_literals(bin_op, lhs_literal, rhs_literal);
824 return Ok(EvalControlFlow::Continue(value));
825 }
826
827 // Generate the specific instruction depending on the operand.
828 let bin_op_variable_id = self.resource_manager.next_var();
829 let bin_op_rir_variable = rir::Variable {
830 variable_id: bin_op_variable_id,
831 ty: rir::Ty::Boolean,
832 };
833 let bin_op_ins = match bin_op {
834 BinOp::AndL => {
835 Instruction::LogicalAnd(lhs_operand, rhs_operand, bin_op_rir_variable)
836 }
837 BinOp::OrL => {
838 Instruction::LogicalOr(lhs_operand, rhs_operand, bin_op_rir_variable)
839 }
840 BinOp::Eq => Instruction::Icmp(
841 ConditionCode::Eq,
842 lhs_operand,
843 rhs_operand,
844 bin_op_rir_variable,
845 ),
846 BinOp::Neq => Instruction::Icmp(
847 ConditionCode::Ne,
848 lhs_operand,
849 rhs_operand,
850 bin_op_rir_variable,
851 ),
852 _ => panic!("unsupported binary operation for bools: {bin_op:?}"),
853 };
854 self.get_current_rir_block_mut().0.push(bin_op_ins);
855 Value::Var(map_rir_var_to_eval_var(bin_op_rir_variable).map_err(|()| {
856 Error::Unexpected(
857 format!("{} type in binop", bin_op_rir_variable.ty),
858 self.get_expr_package_span(rhs_expr_id),
859 )
860 })?)
861 }
862 _ => panic!("unsupported binary operation for bools: {bin_op:?}"),
863 };
864 Ok(EvalControlFlow::Continue(value))
865 }
866
867 fn eval_bin_op_with_lhs_dynamic_bool_operand(
868 &mut self,
869 bin_op: BinOp,
870 lhs_eval_var: Var,
871 rhs_expr_id: ExprId,
872 ) -> Result<EvalControlFlow, Error> {
873 let result_var = match bin_op {
874 BinOp::Eq | BinOp::Neq => {
875 self.eval_comparison_bool_bin_op(bin_op, lhs_eval_var, rhs_expr_id)?
876 }
877 BinOp::AndL => {
878 // Logical AND Boolean operations short-circuit on false.
879 let lhs_rir_var = map_eval_var_to_rir_var(lhs_eval_var);
880 self.eval_logical_bool_bin_op(false, lhs_rir_var, rhs_expr_id)?
881 }
882 BinOp::OrL => {
883 // Logical OR Boolean operations short-circuit on true.
884 let lhs_rir_var = map_eval_var_to_rir_var(lhs_eval_var);
885 self.eval_logical_bool_bin_op(true, lhs_rir_var, rhs_expr_id)?
886 }
887 _ => panic!("invalid Boolean operator {bin_op:?}"),
888 };
889 Ok(EvalControlFlow::Continue(Value::Var(result_var)))
890 }
891
892 fn eval_comparison_bool_bin_op(
893 &mut self,
894 bin_op: BinOp,
895 lhs_eval_var: Var,
896 rhs_expr_id: ExprId,
897 ) -> Result<Var, Error> {
898 // Try to evaluate the RHS expression to get its value and create a RHS operand.
899 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
900 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
901 return Err(Error::Unexpected(
902 "embedded return in RHS expression".to_string(),
903 self.get_expr_package_span(rhs_expr_id),
904 ));
905 };
906 let rhs_operand = self.map_eval_value_to_rir_operand(&rhs_value);
907
908 // Get the comparison result depending on the operator and the RHS value.
909 let result_var = match (bin_op, rhs_operand) {
910 // If the RHS value is a literal, depending on the operand, the result of the Boolean comparison is just the
911 // LHS value.
912 (BinOp::Neq, Operand::Literal(Literal::Bool(false)))
913 | (BinOp::Eq, Operand::Literal(Literal::Bool(true))) => lhs_eval_var,
914 // In other cases we have to actually generate the comparison instruction.
915 (BinOp::Eq | BinOp::Neq, _) => {
916 let rir_variable = rir::Variable::new_boolean(self.resource_manager.next_var());
917 let lhs_operand = Operand::Variable(map_eval_var_to_rir_var(lhs_eval_var));
918 let condition_code = match bin_op {
919 BinOp::Eq => ConditionCode::Eq,
920 BinOp::Neq => ConditionCode::Ne,
921 _ => panic!("invalid Boolean comparison operator {bin_op:?}"),
922 };
923 let cmp_inst =
924 Instruction::Icmp(condition_code, lhs_operand, rhs_operand, rir_variable);
925 self.get_current_rir_block_mut().0.push(cmp_inst);
926 map_rir_var_to_eval_var(rir_variable).map_err(|()| {
927 Error::Unexpected(
928 format!("{} type in comparison binop", rir_variable.ty),
929 self.get_expr_package_span(rhs_expr_id),
930 )
931 })?
932 }
933 (_, _) => panic!("invalid Boolean comparison operator {bin_op:?}"),
934 };
935 Ok(result_var)
936 }
937
938 fn eval_logical_bool_bin_op(
939 &mut self,
940 short_circuit_on_true: bool,
941 lhs_rir_var: rir::Variable,
942 rhs_expr_id: ExprId,
943 ) -> Result<Var, Error> {
944 // Create the variable where we will store the result of the Boolean operation and store a default value in it,
945 // which will only be changed inside the conditional block where the RHS expression is evaluated.
946 let result_var_id = self.resource_manager.next_var();
947 let result_rir_var = rir::Variable {
948 variable_id: result_var_id,
949 ty: rir::Ty::Boolean,
950 };
951 let init_var_ins = Instruction::Store(
952 Operand::Literal(Literal::Bool(short_circuit_on_true)),
953 result_rir_var,
954 );
955 self.get_current_rir_block_mut().0.push(init_var_ins);
956
957 // Pop the current block and insert the continuation block.
958 let current_block_node = self.eval_context.pop_block_node();
959 let continuation_block_id = self.create_program_block();
960 let continuation_block_node = BlockNode {
961 id: continuation_block_id,
962 successor: current_block_node.successor,
963 };
964 self.eval_context.push_block_node(continuation_block_node);
965
966 // Now insert the conditional block.
967 let rhs_eval_block_id = self.create_program_block();
968 let rhs_eval_block_node = BlockNode {
969 id: rhs_eval_block_id,
970 successor: Some(continuation_block_id),
971 };
972 self.eval_context.push_block_node(rhs_eval_block_node);
973
974 // Evaluate the RHS expression
975 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
976 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
977 return Err(Error::Unexpected(
978 "embedded return in RHS expression".to_string(),
979 self.get_expr_package_span(rhs_expr_id),
980 ));
981 };
982 let rhs_operand = self.map_eval_value_to_rir_operand(&rhs_value);
983
984 // Store the RHS value into the the variable that represents the result of the Boolean operation.
985 let store_ins = Instruction::Store(rhs_operand, result_rir_var);
986 self.get_current_rir_block_mut().0.push(store_ins);
987 let jump_ins = Instruction::Jump(continuation_block_id);
988 self.get_current_rir_block_mut().0.push(jump_ins);
989 let _ = self.eval_context.pop_block_node();
990
991 // Now that we have constructed both the conditional and continuation blocks, insert the jump instruction and
992 // return the variable that stores the result of the Boolean operation.
993 // The branching blocks depend on whether we short-circuit on true or false.
994 let (true_block_id, false_block_id) = if short_circuit_on_true {
995 (continuation_block_id, rhs_eval_block_id)
996 } else {
997 (rhs_eval_block_id, continuation_block_id)
998 };
999
1000 let branch_metadata = self.metadata_from_expr(rhs_expr_id);
1001 let branch_ins =
1002 Instruction::Branch(lhs_rir_var, true_block_id, false_block_id, branch_metadata);
1003 self.get_program_block_mut(current_block_node.id)
1004 .0
1005 .push(branch_ins);
1006 let result_eval_var = map_rir_var_to_eval_var(result_rir_var).map_err(|()| {
1007 Error::Unexpected(
1008 format!("{} type in logical binop", result_rir_var.ty),
1009 self.get_expr_package_span(rhs_expr_id),
1010 )
1011 })?;
1012 Ok(result_eval_var)
1013 }
1014
1015 fn eval_bin_op_with_lhs_double_operand(
1016 &mut self,
1017 bin_op: BinOp,
1018 lhs_operand: Operand,
1019 rhs_expr_id: ExprId,
1020 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
1021 ) -> Result<EvalControlFlow, Error> {
1022 assert!(
1023 matches!(lhs_operand.get_type(), rir::Ty::Double),
1024 "LHS is expected to be of double type"
1025 );
1026
1027 // Try to evaluate the RHS expression to get its value and construct its operand.
1028 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
1029 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
1030 return Err(Error::Unexpected(
1031 "embedded return in RHS expression".to_string(),
1032 self.get_expr_package_span(rhs_expr_id),
1033 ));
1034 };
1035 let rhs_operand = self.map_eval_value_to_rir_operand(&rhs_value);
1036 assert!(
1037 matches!(rhs_operand.get_type(), rir::Ty::Double),
1038 "LHS value is expected to be of double type"
1039 );
1040
1041 // If both operands are literals, evaluate the binary operation and return its value.
1042 if let (Operand::Literal(lhs_literal), Operand::Literal(rhs_literal)) =
1043 (lhs_operand, rhs_operand)
1044 {
1045 let value = eval_bin_op_with_double_literals(
1046 bin_op,
1047 lhs_literal,
1048 rhs_literal,
1049 bin_op_expr_span,
1050 )?;
1051 return Ok(EvalControlFlow::Continue(value));
1052 }
1053
1054 // Generate the instructions.
1055 let bin_op_rir_variable = self
1056 .generate_instructions_for_binary_operation_with_double_operands(
1057 bin_op,
1058 lhs_operand,
1059 rhs_operand,
1060 bin_op_expr_span,
1061 )?;
1062 let value = Value::Var(map_rir_var_to_eval_var(bin_op_rir_variable).map_err(|()| {
1063 Error::Unexpected(
1064 format!("{} type in binop", bin_op_rir_variable.ty),
1065 bin_op_expr_span,
1066 )
1067 })?);
1068 Ok(EvalControlFlow::Continue(value))
1069 }
1070
1071 fn eval_bin_op_with_lhs_integer_operand(
1072 &mut self,
1073 bin_op: BinOp,
1074 lhs_operand: Operand,
1075 rhs_expr_id: ExprId,
1076 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
1077 ) -> Result<EvalControlFlow, Error> {
1078 assert!(
1079 matches!(lhs_operand.get_type(), rir::Ty::Integer),
1080 "LHS is expected to be of integer type"
1081 );
1082
1083 // Try to evaluate the RHS expression to get its value and construct its operand.
1084 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
1085 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
1086 return Err(Error::Unexpected(
1087 "embedded return in RHS expression".to_string(),
1088 self.get_expr_package_span(rhs_expr_id),
1089 ));
1090 };
1091 let rhs_operand = self.map_eval_value_to_rir_operand(&rhs_value);
1092 assert!(
1093 matches!(rhs_operand.get_type(), rir::Ty::Integer),
1094 "LHS value is expected to be of integer type"
1095 );
1096
1097 // If both operands are literals, evaluate the binary operation and return its value.
1098 if let (Operand::Literal(lhs_literal), Operand::Literal(rhs_literal)) =
1099 (lhs_operand, rhs_operand)
1100 {
1101 let value = eval_bin_op_with_integer_literals(
1102 bin_op,
1103 lhs_literal,
1104 rhs_literal,
1105 bin_op_expr_span,
1106 )?;
1107 return Ok(EvalControlFlow::Continue(value));
1108 }
1109
1110 // Generate the instructions.
1111 let bin_op_rir_variable = self
1112 .generate_instructions_for_binary_operation_with_integer_operands(
1113 bin_op,
1114 lhs_operand,
1115 rhs_operand,
1116 bin_op_expr_span,
1117 )?;
1118 let value = Value::Var(map_rir_var_to_eval_var(bin_op_rir_variable).map_err(|()| {
1119 Error::Unexpected(
1120 format!("{} type in binop", bin_op_rir_variable.ty),
1121 bin_op_expr_span,
1122 )
1123 })?);
1124 Ok(EvalControlFlow::Continue(value))
1125 }
1126
1127 fn eval_bin_op_with_lhs_var(
1128 &mut self,
1129 bin_op: BinOp,
1130 lhs_eval_var: Var,
1131 rhs_expr_id: ExprId,
1132 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
1133 ) -> Result<EvalControlFlow, Error> {
1134 match lhs_eval_var.ty {
1135 VarTy::Boolean => {
1136 self.eval_bin_op_with_lhs_dynamic_bool_operand(bin_op, lhs_eval_var, rhs_expr_id)
1137 }
1138 VarTy::Integer => {
1139 let lhs_rir_var = map_eval_var_to_rir_var(lhs_eval_var);
1140 let lhs_operand = Operand::Variable(lhs_rir_var);
1141 self.eval_bin_op_with_lhs_integer_operand(
1142 bin_op,
1143 lhs_operand,
1144 rhs_expr_id,
1145 bin_op_expr_span,
1146 )
1147 }
1148 VarTy::Double => {
1149 let lhs_rir_var = map_eval_var_to_rir_var(lhs_eval_var);
1150 let lhs_operand = Operand::Variable(lhs_rir_var);
1151 self.eval_bin_op_with_lhs_double_operand(
1152 bin_op,
1153 lhs_operand,
1154 rhs_expr_id,
1155 bin_op_expr_span,
1156 )
1157 }
1158 }
1159 }
1160
1161 fn eval_static_expr(&mut self, expr_id: ExprId) -> Result<EvalControlFlow, Error> {
1162 let current_package_id = self.get_current_package_id();
1163 let store_expr_id = StoreExprId::from((current_package_id, expr_id));
1164 let expr = self.package_store.get_expr(store_expr_id);
1165 let scope_exec_graph = self.get_current_scope_exec_graph().clone();
1166 let scope = self.eval_context.get_current_scope_mut();
1167 let exec_graph = scope_exec_graph.get_range(&expr.exec_graph_range);
1168 let mut state = State::new(
1169 current_package_id,
1170 exec_graph,
1171 ExecGraphConfig::NoDebug,
1172 None,
1173 ErrorBehavior::FailOnError,
1174 );
1175 let classical_result = state.eval(
1176 self.package_store,
1177 &mut scope.env,
1178 &mut TracingBackend::no_tracer(&mut self.backend),
1179 &mut GenericReceiver::new(&mut std::io::sink()),
1180 &[],
1181 StepAction::Continue,
1182 );
1183 let eval_result = match classical_result {
1184 Ok(step_result) => {
1185 let StepResult::Return(value) = step_result else {
1186 panic!("evaluating a classical expression should always return a value");
1187 };
1188
1189 // Figure out the control flow kind.
1190 let scope = self.eval_context.get_current_scope();
1191 let eval_control_flow = if scope.has_classical_evaluator_returned() {
1192 EvalControlFlow::Return(value)
1193 } else {
1194 EvalControlFlow::Continue(value)
1195 };
1196 Ok(eval_control_flow)
1197 }
1198 Err((error, _)) => Err(Error::from(error)),
1199 };
1200
1201 // If this was an assign expression, update the bindings in the hybrid side to keep them in sync and to insert
1202 // store instructions for variables of type `Bool`, `Int` or `Double`.
1203 if let Ok(EvalControlFlow::Continue(_)) = eval_result {
1204 let expr = self.get_expr(expr_id);
1205 if let ExprKind::Assign(lhs_expr_id, _)
1206 | ExprKind::AssignField(lhs_expr_id, _, _)
1207 | ExprKind::AssignIndex(lhs_expr_id, _, _)
1208 | ExprKind::AssignOp(_, lhs_expr_id, _) = &expr.kind
1209 {
1210 self.update_hybrid_bindings_from_classical_bindings(*lhs_expr_id)?;
1211 }
1212 }
1213
1214 eval_result
1215 }
1216
1217 fn eval_dynamic_expr(&mut self, expr_id: ExprId) -> Result<EvalControlFlow, Error> {
1218 let expr = self.get_expr(expr_id);
1219 let expr_package_span = self.get_expr_package_span(expr_id);
1220 match &expr.kind {
1221 ExprKind::Array(exprs) => self.eval_expr_array(exprs),
1222 ExprKind::ArrayLit(_) => Err(Error::Unexpected(
1223 "array literal should have been classically evaluated".to_string(),
1224 expr_package_span,
1225 )),
1226 ExprKind::ArrayRepeat(value_expr_id, size_expr_id) => {
1227 self.eval_expr_array_repeat(*value_expr_id, *size_expr_id)
1228 }
1229 ExprKind::Assign(lhs_expr_id, rhs_expr_id) => {
1230 self.eval_expr_assign(*lhs_expr_id, *rhs_expr_id)
1231 }
1232 ExprKind::AssignField(_, _, _) => Err(Error::Unexpected(
1233 "assigning a dynamic value to a field of a user-defined type is invalid"
1234 .to_string(),
1235 expr_package_span,
1236 )),
1237 ExprKind::AssignIndex(array_expr_id, index_expr_id, replace_expr_id) => {
1238 self.eval_expr_assign_index(*array_expr_id, *index_expr_id, *replace_expr_id)
1239 }
1240 ExprKind::AssignOp(bin_op, lhs_expr_id, rhs_expr_id) => {
1241 self.eval_expr_assign_op(*bin_op, *lhs_expr_id, *rhs_expr_id, expr_package_span)
1242 }
1243 ExprKind::BinOp(bin_op, lhs_expr_id, rhs_expr_id) => {
1244 self.eval_expr_bin_op(*bin_op, *lhs_expr_id, *rhs_expr_id, expr_package_span)
1245 }
1246 ExprKind::Block(block_id) => self.try_eval_block(*block_id),
1247 ExprKind::Call(callee_expr_id, args_expr_id) => {
1248 self.eval_expr_call(expr_id, *callee_expr_id, *args_expr_id)
1249 }
1250 ExprKind::Closure(args, callable) => {
1251 let closure = resolve_closure(
1252 &self.eval_context.get_current_scope().env,
1253 self.get_current_package_id(),
1254 expr.span,
1255 args,
1256 *callable,
1257 )
1258 .map_err(Error::from)?;
1259 Ok(EvalControlFlow::Continue(closure))
1260 }
1261 ExprKind::Fail(_) => Err(Error::Unexpected(
1262 "using a dynamic value in a fail statement is invalid".to_string(),
1263 expr_package_span,
1264 )),
1265 ExprKind::Field(expr_id, field) => self.eval_expr_field(*expr_id, field.clone()),
1266 ExprKind::Hole => Err(Error::Unexpected(
1267 "hole expressions are not expected during partial evaluation".to_string(),
1268 expr_package_span,
1269 )),
1270 ExprKind::If(condition_expr_id, body_expr_id, otherwise_expr_id) => self.eval_expr_if(
1271 expr_id,
1272 *condition_expr_id,
1273 *body_expr_id,
1274 *otherwise_expr_id,
1275 ),
1276 ExprKind::Index(array_expr_id, index_expr_id) => {
1277 self.eval_expr_index(*array_expr_id, *index_expr_id)
1278 }
1279 ExprKind::Lit(_) => Err(Error::Unexpected(
1280 "literal should have been classically evaluated".to_string(),
1281 expr_package_span,
1282 )),
1283 ExprKind::Range(_, _, _) => Err(Error::Unexpected(
1284 "dynamic ranges are invalid".to_string(),
1285 expr_package_span,
1286 )),
1287 ExprKind::Return(expr_id) => self.eval_expr_return(*expr_id),
1288 ExprKind::Struct(..) => Err(Error::Unexpected(
1289 "instruction generation for struct constructor expressions is invalid".to_string(),
1290 expr_package_span,
1291 )),
1292 ExprKind::String(components) => self.eval_expr_string(components),
1293 ExprKind::Tuple(exprs) => self.eval_expr_tuple(exprs),
1294 ExprKind::UnOp(un_op, value_expr_id) => {
1295 self.eval_expr_unary(*un_op, *value_expr_id, expr_package_span)
1296 }
1297 ExprKind::UpdateField(_, _, _) => Err(Error::Unexpected(
1298 "updating a field of a dynamic user-defined type is invalid".to_string(),
1299 expr_package_span,
1300 )),
1301 ExprKind::UpdateIndex(array_expr_id, index_expr_id, update_expr_id) => {
1302 self.eval_expr_update_index(*array_expr_id, *index_expr_id, *update_expr_id)
1303 }
1304 ExprKind::Var(res, _) => Ok(EvalControlFlow::Continue(self.eval_expr_var(res))),
1305 ExprKind::While(condition_expr_id, body_block_id) => {
1306 self.eval_expr_while(expr_id, *condition_expr_id, *body_block_id)
1307 }
1308 }
1309 }
1310
1311 fn eval_expr_string(
1312 &mut self,
1313 components: &Vec<StringComponent>,
1314 ) -> Result<EvalControlFlow, Error> {
1315 // To ensure any dynamic nested expressions are evaluated, we loop through them here.
1316 for component in components {
1317 match component {
1318 StringComponent::Lit(_) => (),
1319 StringComponent::Expr(expr_id) => {
1320 let control_flow = self.try_eval_expr(*expr_id)?;
1321 if control_flow.is_return() {
1322 return Err(Error::Unexpected(
1323 "embedded return in string expression".to_string(),
1324 self.get_expr_package_span(*expr_id),
1325 ));
1326 }
1327 }
1328 }
1329 }
1330 // All dynamic strings are treated as the empty string for the purpose of partial evaluation since RCA prevents
1331 // any dynamic string from affecting control flow.
1332 Ok(EvalControlFlow::Continue(Value::String("".into())))
1333 }
1334
1335 fn eval_expr_array_repeat(
1336 &mut self,
1337 value_expr_id: ExprId,
1338 size_expr_id: ExprId,
1339 ) -> Result<EvalControlFlow, Error> {
1340 // Try to evaluate both the value and size expressions to get their value, short-circuiting execution if any of the
1341 // expressions is a return.
1342 let value_control_flow = self.try_eval_expr(value_expr_id)?;
1343 let EvalControlFlow::Continue(value) = value_control_flow else {
1344 return Err(Error::Unexpected(
1345 "embedded return in array".to_string(),
1346 self.get_expr_package_span(value_expr_id),
1347 ));
1348 };
1349 let size_control_flow = self.try_eval_expr(size_expr_id)?;
1350 let EvalControlFlow::Continue(size) = size_control_flow else {
1351 return Err(Error::Unexpected(
1352 "embedded return in array size".to_string(),
1353 self.get_expr_package_span(size_expr_id),
1354 ));
1355 };
1356
1357 // We assume the size of the array is a classical value because otherwise it would have been rejected before
1358 // getting to the partial evaluation stage.
1359 let size = size.unwrap_int();
1360 let values = vec![value; TryFrom::try_from(size).expect("could not convert size value")];
1361 Ok(EvalControlFlow::Continue(Value::Array(values.into())))
1362 }
1363
1364 fn eval_expr_assign(
1365 &mut self,
1366 lhs_expr_id: ExprId,
1367 rhs_expr_id: ExprId,
1368 ) -> Result<EvalControlFlow, Error> {
1369 let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?;
1370 let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else {
1371 return Err(Error::Unexpected(
1372 "embedded return in assign expression".to_string(),
1373 self.get_expr_package_span(rhs_expr_id),
1374 ));
1375 };
1376
1377 self.update_bindings(lhs_expr_id, rhs_value)?;
1378 Ok(EvalControlFlow::Continue(Value::unit()))
1379 }
1380
1381 fn eval_expr_assign_index(
1382 &mut self,
1383 array_expr_id: ExprId,
1384 index_expr_id: ExprId,
1385 update_expr_id: ExprId,
1386 ) -> Result<EvalControlFlow, Error> {
1387 // Get the value of the array to use it as the basis to perform the update.
1388 let array_expr = self.get_expr(array_expr_id);
1389 let ExprKind::Var(Res::Local(array_loc_id), _) = &array_expr.kind else {
1390 panic!("array expression in assign index expression is expected to be a variable");
1391 };
1392 let array = self
1393 .eval_context
1394 .get_current_scope()
1395 .get_classical_local_value(*array_loc_id)
1396 .clone()
1397 .unwrap_array();
1398
1399 // Evaluate the updated array and update the corresponding bindings.
1400 let new_array_value =
1401 self.eval_array_update_index(&array, index_expr_id, update_expr_id)?;
1402 self.update_bindings(array_expr_id, new_array_value)?;
1403 Ok(EvalControlFlow::Continue(Value::unit()))
1404 }
1405
1406 fn eval_expr_assign_op(
1407 &mut self,
1408 bin_op: BinOp,
1409 lhs_expr_id: ExprId,
1410 rhs_expr_id: ExprId,
1411 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
1412 ) -> Result<EvalControlFlow, Error> {
1413 // Consider optimization of array in-place operations instead of reusing the general binary operation
1414 // evaluation.
1415 let lhs_expr = self.get_expr(lhs_expr_id);
1416 let lhs_expr_package_span = self.get_expr_package_span(lhs_expr_id);
1417 let lhs_value = if matches!(lhs_expr.ty, Ty::Array(_)) {
1418 let ExprKind::Var(Res::Local(lhs_loc_id), _) = &lhs_expr.kind else {
1419 panic!("array expression in assign op expression is expected to be a variable");
1420 };
1421 self.eval_context
1422 .get_current_scope()
1423 .get_classical_local_value(*lhs_loc_id)
1424 .clone()
1425 } else {
1426 let lhs_control_flow = self.try_eval_expr(lhs_expr_id)?;
1427 if lhs_control_flow.is_return() {
1428 return Err(Error::Unexpected(
1429 "embedded return in assign op LHS expression".to_string(),
1430 lhs_expr_package_span,
1431 ));
1432 }
1433 lhs_control_flow.into_value()
1434 };
1435 let bin_op_control_flow = self.eval_bin_op(
1436 bin_op,
1437 lhs_value,
1438 rhs_expr_id,
1439 lhs_expr_package_span,
1440 bin_op_expr_span,
1441 )?;
1442 let EvalControlFlow::Continue(bin_op_value) = bin_op_control_flow else {
1443 panic!(
1444 "evaluating a binary operation is expected to result in an error or a continue, but never in a return"
1445 );
1446 };
1447 self.update_bindings(lhs_expr_id, bin_op_value)?;
1448 Ok(EvalControlFlow::Continue(Value::unit()))
1449 }
1450
1451 #[allow(clippy::similar_names)]
1452 fn eval_expr_bin_op(
1453 &mut self,
1454 bin_op: BinOp,
1455 lhs_expr_id: ExprId,
1456 rhs_expr_id: ExprId,
1457 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
1458 ) -> Result<EvalControlFlow, Error> {
1459 // Try to evaluate the LHS expression and get its value, short-circuiting execution if it is a return.
1460 let lhs_control_flow = self.try_eval_expr(lhs_expr_id)?;
1461 let EvalControlFlow::Continue(lhs_value) = lhs_control_flow else {
1462 return Err(Error::Unexpected(
1463 "embedded return in binary operation".to_string(),
1464 self.get_expr_package_span(lhs_expr_id),
1465 ));
1466 };
1467
1468 // Now that we have a LHS value, evaluate the binary operation, which will properly consider short-circuiting
1469 // logic in the case of Boolean operations.
1470 let lhs_span = self.get_expr_package_span(lhs_expr_id);
1471 self.eval_bin_op(bin_op, lhs_value, rhs_expr_id, lhs_span, bin_op_expr_span)
1472 }
1473
1474 fn eval_expr_call(
1475 &mut self,
1476 call_expr_id: ExprId,
1477 callee_expr_id: ExprId,
1478 args_expr_id: ExprId,
1479 ) -> Result<EvalControlFlow, Error> {
1480 let args_span = self.get_expr_package_span(args_expr_id);
1481 let (callee_control_flow, args_control_flow) =
1482 self.try_eval_callee_and_args(callee_expr_id, args_expr_id)?;
1483
1484 // Get the callable.
1485 let (store_item_id, functor_app, fixed_args) = match callee_control_flow.into_value() {
1486 Value::Closure(inner) => (inner.id, inner.functor, Some(inner.fixed_args)),
1487 Value::Global(id, functor) => (id, functor, None),
1488 _ => panic!("value is not callable"),
1489 };
1490 let global = self
1491 .package_store
1492 .get_global(store_item_id)
1493 .expect("global not present");
1494 let Global::Callable(callable_decl) = global else {
1495 // Instruction generation for UDTs is not supported.
1496 panic!("global is not a callable");
1497 };
1498
1499 self.reject_test_callables(callee_expr_id, callable_decl)?;
1500
1501 // Set up the scope for the call, which allows additional error checking if the callable was
1502 // previously unresolved.
1503 let spec_decl = if let CallableImpl::Spec(spec_impl) = &callable_decl.implementation {
1504 Some(get_spec_decl(spec_impl, functor_app))
1505 } else {
1506 None
1507 };
1508
1509 let args_value = args_control_flow.into_value();
1510 let ctls = if let Some(Some(ctls_pat_id)) = spec_decl.map(|spec_decl| spec_decl.input) {
1511 assert!(
1512 functor_app.controlled > 0,
1513 "control qubits count was expected to be greater than zero"
1514 );
1515 Some((
1516 StorePatId::from((store_item_id.package, ctls_pat_id)),
1517 functor_app.controlled,
1518 ))
1519 } else {
1520 assert!(
1521 functor_app.controlled == 0,
1522 "control qubits count was expected to be zero"
1523 );
1524 None
1525 };
1526 let (args, ctls_arg) = self.resolve_args(
1527 (store_item_id.package, callable_decl.input).into(),
1528 args_value.clone(),
1529 Some(args_span),
1530 ctls,
1531 fixed_args,
1532 )?;
1533 let call_scope = Scope::new(
1534 store_item_id.package,
1535 Some((store_item_id.item, functor_app)),
1536 args,
1537 ctls_arg,
1538 );
1539
1540 self.check_unresolved_call_capabilities(call_expr_id, callee_expr_id, &call_scope)?;
1541 self.assign_current_dbg_location(call_expr_id);
1542
1543 // We generate instructions differently depending on whether we are calling an intrinsic or a specialization
1544 // with an implementation.
1545 let value = match spec_decl {
1546 None => {
1547 let callee_expr_span = self.get_expr_package_span(callee_expr_id);
1548 self.eval_expr_call_to_intrinsic(
1549 store_item_id,
1550 callable_decl,
1551 args_value,
1552 args_span,
1553 callee_expr_span,
1554 )?
1555 }
1556 Some(spec_decl) => {
1557 self.eval_expr_call_to_spec(call_scope, store_item_id, functor_app, spec_decl)?
1558 }
1559 };
1560 Ok(EvalControlFlow::Continue(value))
1561 }
1562
1563 fn reject_test_callables(
1564 &mut self,
1565 callee_expr_id: ExprId,
1566 callable_decl: &CallableDecl,
1567 ) -> Result<(), Error> {
1568 // If the callable has the test attribute, it's not safe to generate QIR, so we return an error.
1569 if callable_decl
1570 .attrs
1571 .iter()
1572 .any(|attr| attr == &fir::Attr::Test)
1573 {
1574 Err(Error::UnsupportedTestCallable(
1575 self.get_expr_package_span(callee_expr_id),
1576 ))
1577 } else {
1578 // If the callable is not a test, we can proceed with generating QIR.
1579 Ok(())
1580 }
1581 }
1582
1583 fn check_unresolved_call_capabilities(
1584 &mut self,
1585 call_expr_id: ExprId,
1586 callee_expr_id: ExprId,
1587 call_scope: &Scope,
1588 ) -> Result<(), Error> {
1589 // If the call has the unresolved flag, it tells us that RCA could not perform static analysis on this call site.
1590 // Now that we are in evaluation, we have a distinct callable resolved and can perform runtime capability check
1591 // ahead of performing the actual call and return the appropriate capabilities error if this call is not supported
1592 // by the target.
1593 if self.is_unresolved_callee_expr(callee_expr_id) {
1594 let call_compute_kind = self.get_call_compute_kind(call_scope);
1595 if let ComputeKind::Dynamic {
1596 runtime_features,
1597 value_kind,
1598 } = call_compute_kind
1599 {
1600 let missing_features = get_missing_runtime_features(
1601 runtime_features,
1602 self.program.config.capabilities,
1603 ) & !RuntimeFeatureFlags::CallToUnresolvedCallee;
1604 if !missing_features.is_empty()
1605 && let Some(error) = generate_errors_from_runtime_features(
1606 missing_features,
1607 self.get_expr(call_expr_id).span,
1608 )
1609 .drain(..)
1610 .next()
1611 {
1612 return Err(Error::CapabilityError(error));
1613 }
1614
1615 // If the call produces a variable value, we treat it as an error because we know that later
1616 // analysis has not taken that variable into account and further partial evaluation may fail
1617 // when it encounters that value.
1618 if value_kind == ValueKind::Variable {
1619 return Err(Error::UnexpectedDynamicValue(
1620 self.get_expr_package_span(call_expr_id),
1621 ));
1622 }
1623 }
1624 }
1625 Ok(())
1626 }
1627
1628 fn eval_global_call(
1629 &mut self,
1630 store_item_id: StoreItemId,
1631 args: Value,
1632 ) -> Result<EvalControlFlow, Error> {
1633 let global = self
1634 .package_store
1635 .get_global(store_item_id)
1636 .expect("global not present");
1637 let Global::Callable(callable_decl) = global else {
1638 // Instruction generation for UDTs is not supported.
1639 panic!("global is not a callable");
1640 };
1641
1642 // Set up the scope for the call, which allows additional error checking if the callable was
1643 // previously unresolved.
1644 let spec_decl = if let CallableImpl::Spec(spec_impl) = &callable_decl.implementation {
1645 get_spec_decl(spec_impl, FunctorApp::default())
1646 } else {
1647 panic!("global call to intrinsic function not supported");
1648 };
1649
1650 let (args, ctls_arg) = self.resolve_args(
1651 (store_item_id.package, callable_decl.input).into(),
1652 args,
1653 None,
1654 None,
1655 None,
1656 )?;
1657 let call_scope = Scope::new(
1658 store_item_id.package,
1659 Some((store_item_id.item, FunctorApp::default())),
1660 args,
1661 ctls_arg,
1662 );
1663
1664 // We generate instructions differently depending on whether we are calling an intrinsic or a specialization
1665 // with an implementation.
1666 let value = self.eval_expr_call_to_spec(
1667 call_scope,
1668 store_item_id,
1669 FunctorApp::default(),
1670 spec_decl,
1671 )?;
1672 Ok(EvalControlFlow::Continue(value))
1673 }
1674
1675 fn try_eval_callee_and_args(
1676 &mut self,
1677 callee_expr_id: ExprId,
1678 args_expr_id: ExprId,
1679 ) -> Result<(EvalControlFlow, EvalControlFlow), Error> {
1680 let callee_control_flow = self.try_eval_expr(callee_expr_id)?;
1681 if callee_control_flow.is_return() {
1682 return Err(Error::Unexpected(
1683 "embedded return in callee".to_string(),
1684 self.get_expr_package_span(callee_expr_id),
1685 ));
1686 }
1687 let args_control_flow = self.try_eval_expr(args_expr_id)?;
1688 if args_control_flow.is_return() {
1689 return Err(Error::Unexpected(
1690 "embedded return in call arguments".to_string(),
1691 self.get_expr_package_span(args_expr_id),
1692 ));
1693 }
1694 Ok((callee_control_flow, args_control_flow))
1695 }
1696
1697 fn eval_expr_call_to_intrinsic(
1698 &mut self,
1699 store_item_id: StoreItemId,
1700 callable_decl: &CallableDecl,
1701 args_value: Value,
1702 args_span: PackageSpan, // For diagnostic purposes only.
1703 callee_expr_span: PackageSpan, // For diagnostic purposes only.
1704 ) -> Result<Value, Error> {
1705 // Check if any qubits passed as arguments have been released.
1706 let qubits = args_value.qubits();
1707 let qubits_len = qubits.len();
1708 if qubits_len > 0 {
1709 let qubits = qubits
1710 .iter()
1711 .filter_map(|q| q.try_deref().map(|q| q.0))
1712 .collect::<Vec<_>>();
1713 if qubits.len() != qubits_len {
1714 return if callable_decl.name.name.as_ref() == "__quantum__rt__qubit_release" {
1715 Err(EvalError::QubitDoubleRelease(args_span).into())
1716 } else {
1717 Err(EvalError::QubitUsedAfterRelease(args_span).into())
1718 };
1719 }
1720 }
1721
1722 if callable_decl.attrs.contains(&fir::Attr::Measurement) {
1723 return Ok(self.measure_qubits(callable_decl, args_value));
1724 }
1725 if callable_decl.attrs.contains(&fir::Attr::Reset) {
1726 return self.eval_expr_call_to_intrinsic_qis(
1727 store_item_id,
1728 callable_decl,
1729 args_value,
1730 callee_expr_span,
1731 CallableType::Reset,
1732 );
1733 }
1734 if callable_decl.attrs.contains(&fir::Attr::NoiseIntrinsic) {
1735 self.program.attrs |= qsc_data_structures::attrs::Attributes::QdkNoise;
1736 return self.eval_expr_call_to_intrinsic_qis(
1737 store_item_id,
1738 callable_decl,
1739 args_value,
1740 callee_expr_span,
1741 CallableType::NoiseIntrinsic,
1742 );
1743 }
1744
1745 // There are a few special cases regarding intrinsic callables. Identify them and handle them properly.
1746 match callable_decl.name.name.as_ref() {
1747 // Qubit allocations and measurements have special handling.
1748 "__quantum__rt__qubit_allocate" | "__quantum__rt__qubit_borrow" => {
1749 Ok(self.allocate_qubit())
1750 }
1751 "__quantum__rt__qubit_release" => Ok(self.release_qubit(args_value)),
1752 "PermuteLabels" => {
1753 if self.eval_context.is_currently_evaluating_any_branch() {
1754 // If we are in a dynamic branch anywhere up the call stack, we cannot support relabel,
1755 // as later qubit usage would need to be dynamic on whether the branch was taken.
1756 return Err(Error::CapabilityError(CapabilityError::UseOfDynamicQubit(
1757 callee_expr_span.span,
1758 )));
1759 }
1760 qubit_relabel(args_value, args_span, |q0, q1| {
1761 self.resource_manager.swap_qubit_ids(q0, q1);
1762 })
1763 }
1764 .map_err(std::convert::Into::into),
1765 "__quantum__qis__m__body" => Ok(self.measure_qubit(builder::m_decl(), args_value)),
1766 "__quantum__qis__mresetz__body" => {
1767 Ok(self.measure_qubit(builder::mresetz_decl(), args_value))
1768 }
1769 "IsResourceEstimating" => Ok(Value::Bool(false)),
1770 // The following intrinsic operations and functions are no-ops.
1771 "BeginEstimateCaching" => Ok(Value::Bool(true)),
1772 "DumpRegister"
1773 | "DumpOperation"
1774 | "AccountForEstimatesInternal"
1775 | "BeginRepeatEstimatesInternal"
1776 | "EndRepeatEstimatesInternal"
1777 | "EnableMemoryComputeArchitecture"
1778 | "ApplyIdleNoise"
1779 | "GlobalPhase"
1780 | "Message"
1781 | "PostSelectZ"
1782 | "Fact" => Ok(Value::unit()),
1783 "CheckZero" => Err(Error::UnsupportedSimulationIntrinsic(
1784 "CheckZero".to_string(),
1785 callee_expr_span,
1786 )),
1787 // The following intrinsic functions and operations should never make it past conditional compilation and
1788 // the capabilities check pass.
1789 "DrawRandomInt" | "DrawRandomDouble" | "DrawRandomBool" | "Length" => {
1790 Err(Error::Unexpected(
1791 format!(
1792 "`{}` is not a supported by partial evaluation",
1793 callable_decl.name.name
1794 ),
1795 callee_expr_span,
1796 ))
1797 }
1798 "IntAsDouble" => {
1799 let variable_id = self.resource_manager.next_var();
1800 self.convert_value(&args_value, rir::Variable::new_double(variable_id))
1801 }
1802 "Truncate" => {
1803 let variable_id = self.resource_manager.next_var();
1804 self.convert_value(&args_value, rir::Variable::new_integer(variable_id))
1805 }
1806 _ => self.eval_expr_call_to_intrinsic_qis(
1807 store_item_id,
1808 callable_decl,
1809 args_value,
1810 callee_expr_span,
1811 CallableType::Regular,
1812 ),
1813 }
1814 }
1815
1816 fn eval_expr_call_to_intrinsic_qis(
1817 &mut self,
1818 store_item_id: StoreItemId,
1819 callable_decl: &CallableDecl,
1820 args_value: Value,
1821 callee_expr_span: PackageSpan,
1822 call_type: CallableType,
1823 ) -> Result<Value, Error> {
1824 // Check if the callable is already in the program, and if not add it.
1825 let callable = self.create_intrinsic_callable(store_item_id, callable_decl, call_type)?;
1826 let output_var = callable.output_type.map(|output_ty| {
1827 let variable_id = self.resource_manager.next_var();
1828 rir::Variable {
1829 variable_id,
1830 ty: output_ty,
1831 }
1832 });
1833
1834 let callable_id = self.get_or_insert_callable(callable);
1835
1836 // Resolve the call arguments, create the call instruction and insert it to the current block.
1837 let (args, ctls_arg) = self
1838 .resolve_args(
1839 (store_item_id.package, callable_decl.input).into(),
1840 args_value,
1841 None,
1842 None,
1843 None,
1844 )
1845 .expect("no controls to verify");
1846 assert!(
1847 ctls_arg.is_none(),
1848 "intrinsic operations cannot have controls"
1849 );
1850 let args_operands = args
1851 .into_iter()
1852 .map(|arg| self.map_eval_value_to_rir_operand(&arg.into_value()))
1853 .collect();
1854
1855 // Current debug location should be set to the call expression currently being evaluated.
1856 let metadata = self.metadata_from_current_dbg_location();
1857 let instruction = Instruction::Call(callable_id, args_operands, output_var, metadata);
1858 let current_block = self.get_current_rir_block_mut();
1859 current_block.0.push(instruction);
1860 let ret_val = match output_var {
1861 None => Value::unit(),
1862 Some(output_var) => {
1863 let rir_var = map_rir_var_to_eval_var(output_var).map_err(|()| {
1864 Error::UnsupportedCustomIntrinsicType(
1865 callable_decl.output.to_string(),
1866 callee_expr_span,
1867 )
1868 })?;
1869 Value::Var(rir_var)
1870 }
1871 };
1872 Ok(ret_val)
1873 }
1874
1875 fn eval_expr_call_to_spec(
1876 &mut self,
1877 call_scope: Scope,
1878 global_callable_id: StoreItemId,
1879 functor_app: FunctorApp,
1880 spec_decl: &SpecDecl,
1881 ) -> Result<Value, Error> {
1882 self.eval_context.push_scope(call_scope);
1883 let block_value = self.try_eval_block(spec_decl.block)?.into_value();
1884 let popped_scope = self.eval_context.pop_scope();
1885 assert!(
1886 popped_scope.package_id == global_callable_id.package,
1887 "scope package ID mismatch"
1888 );
1889 let (popped_callable_id, popped_functor_app) = popped_scope
1890 .callable
1891 .expect("callable in scope is not specified");
1892 assert!(
1893 popped_callable_id == global_callable_id.item,
1894 "scope callable ID mismatch"
1895 );
1896 assert!(popped_functor_app == functor_app, "scope functor mismatch");
1897 Ok(block_value)
1898 }
1899
1900 fn eval_expr_if(
1901 &mut self,
1902 if_expr_id: ExprId,
1903 condition_expr_id: ExprId,
1904 body_expr_id: ExprId,
1905 otherwise_expr_id: Option<ExprId>,
1906 ) -> Result<EvalControlFlow, Error> {
1907 // Visit the the condition expression to get its value.
1908 let condition_control_flow = self.try_eval_expr(condition_expr_id)?;
1909 if condition_control_flow.is_return() {
1910 return Err(Error::Unexpected(
1911 "embedded return in if condition".to_string(),
1912 self.get_expr_package_span(condition_expr_id),
1913 ));
1914 }
1915
1916 // If the condition value is a Boolean literal, use the value to decide which branch to
1917 // evaluate.
1918 let condition_value = condition_control_flow.into_value();
1919 if let Value::Bool(condition_bool) = condition_value {
1920 return self.eval_expr_if_with_classical_condition(
1921 condition_bool,
1922 body_expr_id,
1923 otherwise_expr_id,
1924 );
1925 }
1926
1927 // At this point the condition value is not classical, so we need to generate a branching instruction.
1928 // First, we pop the current block node and generate a new one which the new branches will jump to when their
1929 // instructions end.
1930 let current_block_node = self.eval_context.pop_block_node();
1931 let continuation_block_node_id = self.create_program_block();
1932 let continuation_block_node = BlockNode {
1933 id: continuation_block_node_id,
1934 successor: current_block_node.successor,
1935 };
1936 self.eval_context.push_block_node(continuation_block_node);
1937
1938 // Since the if expression can represent a dynamic value, create a variable to store it if the expression is
1939 // non-unit.
1940 let if_expr = self.get_expr(if_expr_id);
1941 let maybe_if_expr_var =
1942 if if_expr.ty == Ty::UNIT || matches!(if_expr.ty, Ty::Prim(Prim::String)) {
1943 None
1944 } else {
1945 let variable_id = self.resource_manager.next_var();
1946 let variable_ty = map_fir_type_to_rir_type(&if_expr.ty).map_err(|msg| {
1947 Error::Unexpected(
1948 format!("unsupported if-expression output type `{msg}`"),
1949 self.get_expr_package_span(if_expr_id),
1950 )
1951 })?;
1952 Some(rir::Variable {
1953 variable_id,
1954 ty: variable_ty,
1955 })
1956 };
1957
1958 // Evaluate the body expression.
1959 // First, we cache the current static variable mappings so that we can restore them later.
1960 let cached_mappings = self.clone_current_static_var_map();
1961 let if_true_block_id =
1962 self.eval_expr_if_branch(body_expr_id, continuation_block_node_id, maybe_if_expr_var)?;
1963
1964 // Evaluate the otherwise expression (if any), and determine the block to branch to if the condition is false.
1965 let if_false_block_id = if let Some(otherwise_expr_id) = otherwise_expr_id {
1966 // Cache the mappings after the true block so we can compare afterwards.
1967 let post_if_true_mappings = self.clone_current_static_var_map();
1968 // Restore the cached mappings from before evaluating the true block.
1969 self.overwrite_current_static_var_map(cached_mappings);
1970 let if_false_block_id = self.eval_expr_if_branch(
1971 otherwise_expr_id,
1972 continuation_block_node_id,
1973 maybe_if_expr_var,
1974 )?;
1975 // Only keep the static mappings that are the same in both blocks; when they are different,
1976 // the variable is no longer static across the if expression.
1977 self.keep_matching_static_var_mappings(&post_if_true_mappings);
1978 if_false_block_id
1979 } else {
1980 // Only keep the static mappings that are the same after the true block as before; when they are different,
1981 // the variable is no longer static across the if expression.
1982 self.keep_matching_static_var_mappings(&cached_mappings);
1983
1984 // Since there is no otherwise block, we branch to the continuation block.
1985 continuation_block_node_id
1986 };
1987
1988 // Finally, we insert the branch instruction.
1989 let condition_value_var = condition_value.unwrap_var();
1990 let condition_rir_var = map_eval_var_to_rir_var(condition_value_var);
1991 let metadata = self.metadata_from_expr(if_expr_id);
1992 let branch_ins = Instruction::Branch(
1993 condition_rir_var,
1994 if_true_block_id,
1995 if_false_block_id,
1996 metadata,
1997 );
1998 self.get_program_block_mut(current_block_node.id)
1999 .0
2000 .push(branch_ins);
2001
2002 // Return the value of the if expression.
2003 let if_expr_value = if let Some(if_expr_var) = maybe_if_expr_var {
2004 Value::Var(map_rir_var_to_eval_var(if_expr_var).map_err(|()| {
2005 Error::Unexpected(
2006 format!(
2007 "dynamic value of type {} in conditional expression",
2008 if_expr_var.ty
2009 ),
2010 self.get_expr_package_span(if_expr_id),
2011 )
2012 })?)
2013 } else if matches!(if_expr.ty, Ty::Prim(Prim::String)) {
2014 // Dynamic strings are treated as the empty string for the purpose of partial evaluation since RCA prevents
2015 // any dynamic string from affecting control flow.
2016 Value::String("".into())
2017 } else {
2018 Value::unit()
2019 };
2020 Ok(EvalControlFlow::Continue(if_expr_value))
2021 }
2022
2023 fn eval_expr_if_branch(
2024 &mut self,
2025 branch_body_expr_id: ExprId,
2026 continuation_block_id: rir::BlockId,
2027 if_expr_var: Option<rir::Variable>,
2028 ) -> Result<rir::BlockId, Error> {
2029 // Create the block node that corresponds to the branch body and push it as the active one.
2030 let block_node_id = self.create_program_block();
2031 let block_node = BlockNode {
2032 id: block_node_id,
2033 successor: Some(continuation_block_id),
2034 };
2035 self.eval_context.push_block_node(block_node);
2036
2037 // Evaluate the branch body expression.
2038 let body_control = self.try_eval_expr(branch_body_expr_id)?;
2039 if body_control.is_return() {
2040 let body_span = self.get_expr_package_span(branch_body_expr_id);
2041 return Err(Error::Unimplemented("early return".to_string(), body_span));
2042 }
2043
2044 // If there is a variable to save the value of the if expression to, add a store instruction.
2045 if let Some(if_expr_var) = if_expr_var {
2046 let body_operand = self.map_eval_value_to_rir_operand(&body_control.into_value());
2047 let store_ins = Instruction::Store(body_operand, if_expr_var);
2048 self.get_current_rir_block_mut().0.push(store_ins);
2049 }
2050
2051 // Finally, jump to the continuation block and pop the current block node.
2052 let jump_ins = Instruction::Jump(continuation_block_id);
2053 self.get_current_rir_block_mut().0.push(jump_ins);
2054 let _ = self.eval_context.pop_block_node();
2055 Ok(block_node_id)
2056 }
2057
2058 fn eval_expr_if_with_classical_condition(
2059 &mut self,
2060 condition_bool: bool,
2061 body_expr_id: ExprId,
2062 otherwise_expr_id: Option<ExprId>,
2063 ) -> Result<EvalControlFlow, Error> {
2064 if condition_bool {
2065 self.try_eval_expr(body_expr_id)
2066 } else if let Some(otherwise_expr_id) = otherwise_expr_id {
2067 self.try_eval_expr(otherwise_expr_id)
2068 } else {
2069 // The classical condition evaluated to false, but there is not otherwise block so there is nothing to
2070 // evaluate.
2071 // Return unit since it is the only possibility for if expressions with no otherwise block.
2072 Ok(EvalControlFlow::Continue(Value::unit()))
2073 }
2074 }
2075
2076 fn eval_expr_index(
2077 &mut self,
2078 array_expr_id: ExprId,
2079 index_expr_id: ExprId,
2080 ) -> Result<EvalControlFlow, Error> {
2081 // Get the value of the array expression to use it as the basis to perform a replacement on.
2082 let array_control_flow = self.try_eval_expr(array_expr_id)?;
2083 let EvalControlFlow::Continue(array_value) = array_control_flow else {
2084 return Err(Error::Unexpected(
2085 "embedded return in index expression".to_string(),
2086 self.get_expr_package_span(array_expr_id),
2087 ));
2088 };
2089
2090 // Try to evaluate the index and replace expressions to get their value, short-circuiting execution if any of
2091 // the expressions is a return.
2092 let index_control_flow = self.try_eval_expr(index_expr_id)?;
2093 let EvalControlFlow::Continue(index_value) = index_control_flow else {
2094 return Err(Error::Unexpected(
2095 "embedded return in index expression".to_string(),
2096 self.get_expr_package_span(index_expr_id),
2097 ));
2098 };
2099
2100 // Get the value at the specified index.
2101 let array = array_value.unwrap_array();
2102 let index_expr = self.get_expr(index_expr_id);
2103 let hir_package_id = map_fir_package_to_hir(self.get_current_package_id());
2104 let index_package_span = PackageSpan {
2105 package: hir_package_id,
2106 span: index_expr.span,
2107 };
2108 let value_result = match index_value {
2109 Value::Int(index) => index_array(&array, index, index_package_span),
2110 Value::Range(range) => slice_array(
2111 &array,
2112 range.start,
2113 range.step,
2114 range.end,
2115 index_package_span,
2116 ),
2117 _ => panic!("invalid kind of value for index"),
2118 };
2119 let value = value_result.map_err(Error::from)?;
2120 Ok(EvalControlFlow::Continue(value))
2121 }
2122
2123 fn eval_expr_field(
2124 &mut self,
2125 record_id: ExprId,
2126 field: Field,
2127 ) -> Result<EvalControlFlow, Error> {
2128 let control_flow = self.try_eval_expr(record_id)?;
2129 let EvalControlFlow::Continue(record) = control_flow else {
2130 return Err(Error::Unexpected(
2131 "embedded return in field access expression".to_string(),
2132 self.get_expr_package_span(record_id),
2133 ));
2134 };
2135
2136 let field_value = match (record, field) {
2137 (Value::Range(inner), Field::Prim(PrimField::Start)) => Value::Int(
2138 inner
2139 .start
2140 .expect("range access should be validated by compiler"),
2141 ),
2142 (Value::Range(inner), Field::Prim(PrimField::Step)) => Value::Int(inner.step),
2143 (Value::Range(inner), Field::Prim(PrimField::End)) => Value::Int(
2144 inner
2145 .end
2146 .expect("range access should be validated by compiler"),
2147 ),
2148 (mut record, Field::Path(path)) => {
2149 for index in path.indices {
2150 let Value::Tuple(items, _) = record else {
2151 panic!("invalid tuple access");
2152 };
2153 record = items[index].clone();
2154 }
2155 record
2156 }
2157 (ref value, ref field) => {
2158 panic!("invalid field access. value: {value:?}, field: {field:?}")
2159 }
2160 };
2161 Ok(EvalControlFlow::Continue(field_value))
2162 }
2163
2164 fn eval_expr_return(&mut self, expr_id: ExprId) -> Result<EvalControlFlow, Error> {
2165 let control_flow = self.try_eval_expr(expr_id)?;
2166 Ok(EvalControlFlow::Return(control_flow.into_value()))
2167 }
2168
2169 fn eval_expr_array(&mut self, exprs: &Vec<ExprId>) -> Result<EvalControlFlow, Error> {
2170 let mut values = Vec::with_capacity(exprs.len());
2171 for expr_id in exprs {
2172 let control_flow = self.try_eval_expr(*expr_id)?;
2173 if control_flow.is_return() {
2174 return Err(Error::Unexpected(
2175 "embedded return in array".to_string(),
2176 self.get_expr_package_span(*expr_id),
2177 ));
2178 }
2179 values.push(control_flow.into_value());
2180 }
2181 Ok(EvalControlFlow::Continue(Value::Array(values.into())))
2182 }
2183
2184 fn eval_expr_tuple(&mut self, exprs: &Vec<ExprId>) -> Result<EvalControlFlow, Error> {
2185 let mut values = Vec::with_capacity(exprs.len());
2186 for expr_id in exprs {
2187 let control_flow = self.try_eval_expr(*expr_id)?;
2188 if control_flow.is_return() {
2189 return Err(Error::Unexpected(
2190 "embedded return in tuple".to_string(),
2191 self.get_expr_package_span(*expr_id),
2192 ));
2193 }
2194 values.push(control_flow.into_value());
2195 }
2196 Ok(EvalControlFlow::Continue(Value::Tuple(values.into(), None)))
2197 }
2198
2199 fn eval_expr_unary(
2200 &mut self,
2201 un_op: UnOp,
2202 value_expr_id: ExprId,
2203 unary_expr_span: PackageSpan, // For diagnostic purposes only.
2204 ) -> Result<EvalControlFlow, Error> {
2205 let value_expr_package_span = self.get_expr_package_span(value_expr_id);
2206 let value_control_flow = self.try_eval_expr(value_expr_id)?;
2207 let EvalControlFlow::Continue(value) = value_control_flow else {
2208 return Err(Error::Unexpected(
2209 "embedded return in unary operation expression".to_string(),
2210 value_expr_package_span,
2211 ));
2212 };
2213
2214 // Get the variable type corresponding to the value the unary operator acts upon.
2215 let Some(eval_variable_type) = try_get_eval_var_type(&value) else {
2216 return Err(Error::Unexpected(
2217 format!("invalid type for unary operation value: {value}"),
2218 value_expr_package_span,
2219 ));
2220 };
2221
2222 // The leading positive operator is a no-op.
2223 if matches!(un_op, UnOp::Pos) {
2224 let control_flow = EvalControlFlow::Continue(value);
2225 return Ok(control_flow);
2226 }
2227
2228 // If the variable is a literal, we can evaluate the unary operation directly.
2229 if !matches!(value, Value::Var(_)) {
2230 let result = eval_un_op_with_literals(un_op, value);
2231 return Ok(EvalControlFlow::Continue(result));
2232 }
2233
2234 // For all the other supported unary operations we have to generate an instruction, so create a variable to
2235 // store the result.
2236 let variable_id = self.resource_manager.next_var();
2237 let rir_variable_type = map_eval_var_type_to_rir_type(eval_variable_type);
2238 let rir_variable = rir::Variable {
2239 variable_id,
2240 ty: rir_variable_type,
2241 };
2242
2243 // Generate the instruction depending on the unary operator.
2244 let value_operand = self.map_eval_value_to_rir_operand(&value);
2245 let instruction = match un_op {
2246 UnOp::Neg => match rir_variable_type {
2247 rir::Ty::Integer => {
2248 let constant = Operand::Literal(Literal::Integer(-1));
2249 Instruction::Mul(constant, value_operand, rir_variable)
2250 }
2251 rir::Ty::Double => {
2252 let constant = Operand::Literal(Literal::Double(-1.0));
2253 Instruction::Fmul(constant, value_operand, rir_variable)
2254 }
2255 _ => panic!("invalid type for negation operator {rir_variable_type}"),
2256 },
2257 UnOp::NotB => {
2258 assert!(matches!(rir_variable_type, rir::Ty::Integer));
2259 Instruction::BitwiseNot(value_operand, rir_variable)
2260 }
2261 UnOp::NotL => {
2262 assert!(matches!(rir_variable_type, rir::Ty::Boolean));
2263 Instruction::LogicalNot(value_operand, rir_variable)
2264 }
2265 UnOp::Functor(_) | UnOp::Unwrap => {
2266 return Err(Error::Unexpected(
2267 format!("invalid unary operator: {un_op}"),
2268 unary_expr_span,
2269 ));
2270 }
2271 UnOp::Pos => panic!("the leading positive operator should have been a no-op"),
2272 };
2273
2274 // Insert the instruction and return the corresponding evaluator variable.
2275 self.get_current_rir_block_mut().0.push(instruction);
2276 let eval_variable = map_rir_var_to_eval_var(rir_variable).map_err(|()| {
2277 Error::Unexpected(
2278 format!("{} type in unop", rir_variable.ty),
2279 self.get_expr_package_span(value_expr_id),
2280 )
2281 })?;
2282 Ok(EvalControlFlow::Continue(Value::Var(eval_variable)))
2283 }
2284
2285 fn eval_expr_update_index(
2286 &mut self,
2287 array_expr_id: ExprId,
2288 index_expr_id: ExprId,
2289 update_expr_id: ExprId,
2290 ) -> Result<EvalControlFlow, Error> {
2291 // Get the value of the array expression to use it as the basis to perform a replacement on.
2292 let array_control_flow = self.try_eval_expr(array_expr_id)?;
2293 let EvalControlFlow::Continue(array_value) = array_control_flow else {
2294 return Err(Error::Unexpected(
2295 "embedded return in index expression".to_string(),
2296 self.get_expr_package_span(array_expr_id),
2297 ));
2298 };
2299 let array = array_value.unwrap_array();
2300 let updated_array = self.eval_array_update_index(&array, index_expr_id, update_expr_id)?;
2301 Ok(EvalControlFlow::Continue(updated_array))
2302 }
2303
2304 fn eval_expr_var(&mut self, res: &Res) -> Value {
2305 match res {
2306 Res::Err => panic!("resolution error"),
2307 Res::Item(item) => Value::Global(
2308 StoreItemId {
2309 package: item.package,
2310 item: item.item,
2311 },
2312 FunctorApp::default(),
2313 ),
2314 Res::Local(local_var_id) => {
2315 let bound_value = self
2316 .eval_context
2317 .get_current_scope()
2318 .get_hybrid_local_value(*local_var_id);
2319
2320 // Check whether the bound value is a mutable variable and we are not currently evaluating a branch.
2321 // If so, return its value directly rather than the variable if it is static at this moment.
2322 if let Value::Var(var) = bound_value {
2323 let current_scope = self.eval_context.get_current_scope();
2324 if let Some(literal) = current_scope.get_static_value(var.id.into())
2325 && (!current_scope.is_currently_evaluating_branch()
2326 || !self
2327 .program
2328 .config
2329 .capabilities
2330 .contains(TargetCapabilityFlags::BackwardsBranching))
2331 {
2332 map_rir_literal_to_eval_value(*literal)
2333 } else {
2334 bound_value.clone()
2335 }
2336 } else {
2337 bound_value.clone()
2338 }
2339 }
2340 }
2341 }
2342
2343 fn eval_expr_while(
2344 &mut self,
2345 loop_expr_id: ExprId,
2346 condition_expr_id: ExprId,
2347 body_block_id: BlockId,
2348 ) -> Result<EvalControlFlow, Error> {
2349 if self
2350 .program
2351 .config
2352 .capabilities
2353 .contains(TargetCapabilityFlags::BackwardsBranching)
2354 && !self.is_static_expr(condition_expr_id)
2355 {
2356 // If backwards branching is supported and the loop condition is not static,
2357 // we can generate a while loop structure in RIR without unrolling the loop.
2358 return self.eval_expr_emit_while(loop_expr_id, condition_expr_id, body_block_id);
2359 }
2360
2361 // Verify assumptions: the condition expression must either static (such that it can be fully evaluated) or
2362 // dynamic but constant at runtime (such that it can be partially evaluated to a known value).
2363 assert!(
2364 !self
2365 .get_expr_compute_kind(condition_expr_id)
2366 .is_variable_value_kind(),
2367 "loop conditions must be known at code generation time."
2368 );
2369
2370 // Evaluate the block until the loop condition is false.
2371 let condition_expr_span = self.get_expr_package_span(condition_expr_id);
2372 let mut condition_control_flow = self.try_eval_expr(condition_expr_id)?;
2373 if condition_control_flow.is_return() {
2374 return Err(Error::Unexpected(
2375 "embedded return in loop condition".to_string(),
2376 condition_expr_span,
2377 ));
2378 }
2379 let mut condition_boolean = condition_control_flow.into_value().unwrap_bool();
2380
2381 let dbg_location_id = self.new_dbg_location(loop_expr_id);
2382 if let Some(dbg_location_id) = dbg_location_id {
2383 self.dbg_push_loop_iteration_scope(loop_expr_id, dbg_location_id);
2384 }
2385
2386 while condition_boolean {
2387 if dbg_location_id.is_some() {
2388 self.dbg_increment_loop_iteration_count();
2389 }
2390 // Evaluate the loop block.
2391 let block_control_flow = self.try_eval_block(body_block_id)?;
2392 if block_control_flow.is_return() {
2393 if dbg_location_id.is_some() {
2394 self.dbg_pop_loop_iteration_scope();
2395 }
2396 return Ok(block_control_flow);
2397 }
2398
2399 // Re-evaluate the condition now that the block evaluation is done
2400 condition_control_flow = self.try_eval_expr(condition_expr_id)?;
2401 if condition_control_flow.is_return() {
2402 return Err(Error::Unexpected(
2403 "embedded return in loop condition".to_string(),
2404 condition_expr_span,
2405 ));
2406 }
2407 condition_boolean = condition_control_flow.into_value().unwrap_bool();
2408 }
2409 if dbg_location_id.is_some() {
2410 self.dbg_pop_loop_iteration_scope();
2411 }
2412
2413 // We have evaluated the loop so just return unit as the value of this loop expression.
2414 Ok(EvalControlFlow::Continue(Value::unit()))
2415 }
2416
2417 fn eval_expr_emit_while(
2418 &mut self,
2419 loop_expr_id: ExprId,
2420 condition_expr_id: ExprId,
2421 body_block_id: BlockId,
2422 ) -> Result<EvalControlFlow, Error> {
2423 // Pop the current block node and create the necessary block nodes for the loop structure.
2424 let current_block_node = self.eval_context.pop_block_node();
2425 let conditional_block_node_id = self.create_program_block();
2426 let conditional_block_node = BlockNode {
2427 id: conditional_block_node_id,
2428 successor: current_block_node.successor,
2429 };
2430 let continuation_block_node_id = self.create_program_block();
2431 let continuation_block_node = BlockNode {
2432 id: continuation_block_node_id,
2433 successor: current_block_node.successor,
2434 };
2435 self.eval_context.push_block_node(continuation_block_node);
2436
2437 // Insert the jump instruction to the conditional block from the current block.
2438 let jump_to_condition_ins = Instruction::Jump(conditional_block_node_id);
2439 self.get_program_block_mut(current_block_node.id)
2440 .0
2441 .push(jump_to_condition_ins);
2442
2443 // In the conditional block, evaluate the condition expression and generate the branch instruction.
2444 self.eval_context.push_block_node(conditional_block_node);
2445 let condition_control_flow = self.try_eval_expr(condition_expr_id)?;
2446 if condition_control_flow.is_return() {
2447 return Err(Error::Unexpected(
2448 "embedded return in loop condition".to_string(),
2449 self.get_expr_package_span(condition_expr_id),
2450 ));
2451 }
2452 let condition_value = condition_control_flow.into_value();
2453
2454 if let Value::Bool(false) = condition_value {
2455 // If the condition is statically false, jump directly to the continuation block.
2456 let jump_to_continuation_ins = Instruction::Jump(continuation_block_node_id);
2457 self.get_current_rir_block_mut()
2458 .0
2459 .push(jump_to_continuation_ins);
2460 let _ = self.eval_context.pop_block_node();
2461 return Ok(EvalControlFlow::Continue(Value::unit()));
2462 }
2463
2464 // Otherwise, branch to either the body block or the continuation block.
2465 let body_block_node_id = self.create_program_block();
2466 let body_block_node = BlockNode {
2467 id: body_block_node_id,
2468 successor: Some(conditional_block_node_id),
2469 };
2470 let condition_value_var = condition_value.unwrap_var();
2471 let condition_rir_var = map_eval_var_to_rir_var(condition_value_var);
2472 let metadata = self.metadata_from_expr(loop_expr_id);
2473 let branch_ins = Instruction::Branch(
2474 condition_rir_var,
2475 body_block_node_id,
2476 continuation_block_node_id,
2477 metadata,
2478 );
2479 self.get_current_rir_block_mut().0.push(branch_ins);
2480 let _ = self.eval_context.pop_block_node();
2481
2482 // In the body block, evaluate the loop body and jump back to the conditional block.
2483 self.eval_context.push_block_node(body_block_node);
2484 let body_control_flow = self.try_eval_block(body_block_id)?;
2485 if body_control_flow.is_return() {
2486 return Err(Error::Unexpected(
2487 "embedded return in loop body".to_string(),
2488 self.get_expr_package_span(condition_expr_id),
2489 ));
2490 }
2491 let jump_to_condition_ins = Instruction::Jump(conditional_block_node_id);
2492 self.get_current_rir_block_mut()
2493 .0
2494 .push(jump_to_condition_ins);
2495 let _ = self.eval_context.pop_block_node();
2496
2497 Ok(EvalControlFlow::Continue(Value::unit()))
2498 }
2499
2500 fn eval_result_as_bool_operand(&mut self, result: val::Result) -> Operand {
2501 match result {
2502 val::Result::Id(id) => {
2503 // If this is a result ID, generate the instruction to read it.
2504 let result_operand = Operand::Literal(Literal::Result(
2505 id.try_into().expect("could not convert result ID to u32"),
2506 ));
2507 let read_result_callable_id =
2508 self.get_or_insert_callable(builder::read_result_decl());
2509 let variable_id = self.resource_manager.next_var();
2510 let variable_ty = rir::Ty::Boolean;
2511 let variable = rir::Variable {
2512 variable_id,
2513 ty: variable_ty,
2514 };
2515 // Current debug location should be set to the call expression currently being evaluated.
2516 let metadata = self.metadata_from_current_dbg_location();
2517 let current_block = self.get_current_rir_block_mut();
2518 let instruction = Instruction::Call(
2519 read_result_callable_id,
2520 vec![result_operand],
2521 Some(variable),
2522 metadata,
2523 );
2524 current_block.0.push(instruction);
2525 Operand::Variable(variable)
2526 }
2527 val::Result::Val(bool) => Operand::Literal(Literal::Bool(bool)),
2528 val::Result::Loss => panic!("loss result should not occur in partial evaluation"),
2529 }
2530 }
2531
2532 fn generate_instructions_for_binary_operation_with_double_operands(
2533 &mut self,
2534 bin_op: BinOp,
2535 lhs_operand: Operand,
2536 rhs_operand: Operand,
2537 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
2538 ) -> Result<rir::Variable, Error> {
2539 let bin_op_variable_id = self.resource_manager.next_var();
2540
2541 let bin_op_rir_variable = match bin_op {
2542 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
2543 rir::Variable::new_double(bin_op_variable_id)
2544 }
2545 BinOp::Eq | BinOp::Neq | BinOp::Gt | BinOp::Gte | BinOp::Lt | BinOp::Lte => {
2546 rir::Variable::new_boolean(bin_op_variable_id)
2547 }
2548 _ => panic!("unsupported binary operation for double: {bin_op:?}"),
2549 };
2550
2551 let bin_op_rir_ins = match bin_op {
2552 BinOp::Add => Instruction::Fadd(lhs_operand, rhs_operand, bin_op_rir_variable),
2553 BinOp::Sub => Instruction::Fsub(lhs_operand, rhs_operand, bin_op_rir_variable),
2554 BinOp::Mul => Instruction::Fmul(lhs_operand, rhs_operand, bin_op_rir_variable),
2555 BinOp::Div => {
2556 // Validate that the RHS is not a zero.
2557 if let Operand::Literal(Literal::Double(0.0)) = rhs_operand {
2558 let error = EvalError::DivZero(bin_op_expr_span).into();
2559 return Err(error);
2560 }
2561
2562 Instruction::Fdiv(lhs_operand, rhs_operand, bin_op_rir_variable)
2563 }
2564 BinOp::Eq => Instruction::Fcmp(
2565 FcmpConditionCode::OrderedAndEqual,
2566 lhs_operand,
2567 rhs_operand,
2568 bin_op_rir_variable,
2569 ),
2570 BinOp::Neq => Instruction::Fcmp(
2571 FcmpConditionCode::OrderedAndNotEqual,
2572 lhs_operand,
2573 rhs_operand,
2574 bin_op_rir_variable,
2575 ),
2576 BinOp::Gt => Instruction::Fcmp(
2577 FcmpConditionCode::OrderedAndGreaterThan,
2578 lhs_operand,
2579 rhs_operand,
2580 bin_op_rir_variable,
2581 ),
2582 BinOp::Gte => Instruction::Fcmp(
2583 FcmpConditionCode::OrderedAndGreaterThanOrEqual,
2584 lhs_operand,
2585 rhs_operand,
2586 bin_op_rir_variable,
2587 ),
2588 BinOp::Lt => Instruction::Fcmp(
2589 FcmpConditionCode::OrderedAndLessThan,
2590 lhs_operand,
2591 rhs_operand,
2592 bin_op_rir_variable,
2593 ),
2594 BinOp::Lte => Instruction::Fcmp(
2595 FcmpConditionCode::OrderedAndLessThanOrEqual,
2596 lhs_operand,
2597 rhs_operand,
2598 bin_op_rir_variable,
2599 ),
2600 _ => panic!("unsupported binary operation for double: {bin_op:?}"),
2601 };
2602 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2603 Ok(bin_op_rir_variable)
2604 }
2605
2606 #[allow(clippy::too_many_lines)]
2607 fn generate_instructions_for_binary_operation_with_integer_operands(
2608 &mut self,
2609 bin_op: BinOp,
2610 lhs_operand: Operand,
2611 rhs_operand: Operand,
2612 bin_op_expr_span: PackageSpan, // For diagnostic purposes only.
2613 ) -> Result<rir::Variable, Error> {
2614 let rir_variable = match bin_op {
2615 BinOp::Add => {
2616 let bin_op_variable_id = self.resource_manager.next_var();
2617 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2618 let bin_op_rir_ins =
2619 Instruction::Add(lhs_operand, rhs_operand, bin_op_rir_variable);
2620 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2621 bin_op_rir_variable
2622 }
2623 BinOp::Sub => {
2624 let bin_op_variable_id = self.resource_manager.next_var();
2625 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2626 let bin_op_rir_ins =
2627 Instruction::Sub(lhs_operand, rhs_operand, bin_op_rir_variable);
2628 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2629 bin_op_rir_variable
2630 }
2631 BinOp::Mul => {
2632 let bin_op_variable_id = self.resource_manager.next_var();
2633 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2634 let bin_op_rir_ins =
2635 Instruction::Mul(lhs_operand, rhs_operand, bin_op_rir_variable);
2636 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2637 bin_op_rir_variable
2638 }
2639 BinOp::Div => {
2640 // Validate that the RHS is not a zero.
2641 if let Operand::Literal(Literal::Integer(0)) = rhs_operand {
2642 let error = EvalError::DivZero(bin_op_expr_span).into();
2643 return Err(error);
2644 }
2645 let bin_op_variable_id = self.resource_manager.next_var();
2646 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2647 let bin_op_rir_ins =
2648 Instruction::Sdiv(lhs_operand, rhs_operand, bin_op_rir_variable);
2649 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2650 bin_op_rir_variable
2651 }
2652 BinOp::Mod => {
2653 let bin_op_variable_id = self.resource_manager.next_var();
2654 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2655 let bin_op_rir_ins =
2656 Instruction::Srem(lhs_operand, rhs_operand, bin_op_rir_variable);
2657 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2658 bin_op_rir_variable
2659 }
2660 BinOp::Exp => {
2661 // Validate the exponent.
2662 let Operand::Literal(Literal::Integer(exponent)) = rhs_operand else {
2663 let error = Error::Unexpected(
2664 "exponent must be a classical integer".to_string(),
2665 bin_op_expr_span,
2666 );
2667 return Err(error);
2668 };
2669 if exponent < 0 {
2670 let error = EvalError::InvalidNegativeInt(exponent, bin_op_expr_span).into();
2671 return Err(error);
2672 }
2673
2674 // Generate a series of multiplication instructions that represent the exponentiation.
2675 let mut current_rir_variable =
2676 rir::Variable::new_integer(self.resource_manager.next_var());
2677 let init_ins =
2678 Instruction::Store(Operand::Literal(Literal::Integer(1)), current_rir_variable);
2679 self.get_current_rir_block_mut().0.push(init_ins);
2680 for _ in 0..exponent {
2681 let mult_variable =
2682 rir::Variable::new_integer(self.resource_manager.next_var());
2683 let mult_ins = Instruction::Mul(
2684 Operand::Variable(current_rir_variable),
2685 lhs_operand,
2686 mult_variable,
2687 );
2688 self.get_current_rir_block_mut().0.push(mult_ins);
2689 current_rir_variable = mult_variable;
2690 }
2691 current_rir_variable
2692 }
2693 BinOp::AndB => {
2694 let bin_op_variable_id = self.resource_manager.next_var();
2695 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2696 let bin_op_rir_ins =
2697 Instruction::BitwiseAnd(lhs_operand, rhs_operand, bin_op_rir_variable);
2698 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2699 bin_op_rir_variable
2700 }
2701 BinOp::OrB => {
2702 let bin_op_variable_id = self.resource_manager.next_var();
2703 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2704 let bin_op_rir_ins =
2705 Instruction::BitwiseOr(lhs_operand, rhs_operand, bin_op_rir_variable);
2706 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2707 bin_op_rir_variable
2708 }
2709 BinOp::XorB => {
2710 let bin_op_variable_id = self.resource_manager.next_var();
2711 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2712 let bin_op_rir_ins =
2713 Instruction::BitwiseXor(lhs_operand, rhs_operand, bin_op_rir_variable);
2714 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2715 bin_op_rir_variable
2716 }
2717 BinOp::Shl => {
2718 let bin_op_variable_id = self.resource_manager.next_var();
2719 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2720 let bin_op_rir_ins =
2721 Instruction::Shl(lhs_operand, rhs_operand, bin_op_rir_variable);
2722 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2723 bin_op_rir_variable
2724 }
2725 BinOp::Shr => {
2726 let bin_op_variable_id = self.resource_manager.next_var();
2727 let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id);
2728 let bin_op_rir_ins =
2729 Instruction::Ashr(lhs_operand, rhs_operand, bin_op_rir_variable);
2730 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2731 bin_op_rir_variable
2732 }
2733 BinOp::Eq => {
2734 let bin_op_variable_id = self.resource_manager.next_var();
2735 let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id);
2736 let bin_op_rir_ins = Instruction::Icmp(
2737 ConditionCode::Eq,
2738 lhs_operand,
2739 rhs_operand,
2740 bin_op_rir_variable,
2741 );
2742 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2743 bin_op_rir_variable
2744 }
2745 BinOp::Neq => {
2746 let bin_op_variable_id = self.resource_manager.next_var();
2747 let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id);
2748 let bin_op_rir_ins = Instruction::Icmp(
2749 ConditionCode::Ne,
2750 lhs_operand,
2751 rhs_operand,
2752 bin_op_rir_variable,
2753 );
2754 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2755 bin_op_rir_variable
2756 }
2757 BinOp::Gt => {
2758 let bin_op_variable_id = self.resource_manager.next_var();
2759 let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id);
2760 let bin_op_rir_ins = Instruction::Icmp(
2761 ConditionCode::Sgt,
2762 lhs_operand,
2763 rhs_operand,
2764 bin_op_rir_variable,
2765 );
2766 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2767 bin_op_rir_variable
2768 }
2769 BinOp::Gte => {
2770 let bin_op_variable_id = self.resource_manager.next_var();
2771 let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id);
2772 let bin_op_rir_ins = Instruction::Icmp(
2773 ConditionCode::Sge,
2774 lhs_operand,
2775 rhs_operand,
2776 bin_op_rir_variable,
2777 );
2778 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2779 bin_op_rir_variable
2780 }
2781 BinOp::Lt => {
2782 let bin_op_variable_id = self.resource_manager.next_var();
2783 let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id);
2784 let bin_op_rir_ins = Instruction::Icmp(
2785 ConditionCode::Slt,
2786 lhs_operand,
2787 rhs_operand,
2788 bin_op_rir_variable,
2789 );
2790 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2791 bin_op_rir_variable
2792 }
2793 BinOp::Lte => {
2794 let bin_op_variable_id = self.resource_manager.next_var();
2795 let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id);
2796 let bin_op_rir_ins = Instruction::Icmp(
2797 ConditionCode::Sle,
2798 lhs_operand,
2799 rhs_operand,
2800 bin_op_rir_variable,
2801 );
2802 self.get_current_rir_block_mut().0.push(bin_op_rir_ins);
2803 bin_op_rir_variable
2804 }
2805 _ => panic!("unsupported binary operation for integers: {bin_op:?}"),
2806 };
2807 Ok(rir_variable)
2808 }
2809
2810 fn get_block(&self, id: BlockId) -> &'a Block {
2811 let block_id = StoreBlockId::from((self.get_current_package_id(), id));
2812 self.package_store.get_block(block_id)
2813 }
2814
2815 fn get_expr(&self, id: ExprId) -> &'a Expr {
2816 let expr_id = StoreExprId::from((self.get_current_package_id(), id));
2817 self.package_store.get_expr(expr_id)
2818 }
2819
2820 #[allow(clippy::similar_names)]
2821 fn get_expr_package_span(&self, id: ExprId) -> PackageSpan {
2822 let fir_package_id = self.get_current_package_id();
2823 let expr = self.package_store.get_expr((fir_package_id, id).into());
2824 let hir_package_id = map_fir_package_to_hir(fir_package_id);
2825 PackageSpan {
2826 package: hir_package_id,
2827 span: expr.span,
2828 }
2829 }
2830
2831 fn get_pat(&self, id: PatId) -> &'a Pat {
2832 let pat_id = StorePatId::from((self.get_current_package_id(), id));
2833 self.package_store.get_pat(pat_id)
2834 }
2835
2836 fn get_stmt(&self, id: StmtId) -> &'a Stmt {
2837 let stmt_id = StoreStmtId::from((self.get_current_package_id(), id));
2838 self.package_store.get_stmt(stmt_id)
2839 }
2840
2841 fn get_current_package_id(&self) -> PackageId {
2842 self.eval_context.get_current_scope().package_id
2843 }
2844
2845 fn get_current_rir_block_mut(&mut self) -> &mut rir::Block {
2846 self.get_program_block_mut(self.eval_context.get_current_block_id())
2847 }
2848
2849 fn get_current_scope_exec_graph(&self) -> &ExecGraph {
2850 if let Some(spec_decl) = self.get_current_scope_spec_decl() {
2851 &spec_decl.exec_graph
2852 } else {
2853 &self
2854 .entry
2855 .expect("entry expression must be present when not in scope")
2856 .exec_graph
2857 }
2858 }
2859
2860 fn get_current_scope_spec_decl(&self) -> Option<&SpecDecl> {
2861 let current_scope = self.eval_context.get_current_scope();
2862 let (local_item_id, functor_app) = current_scope.callable?;
2863 let store_item_id = StoreItemId::from((current_scope.package_id, local_item_id));
2864 let global = self
2865 .package_store
2866 .get_global(store_item_id)
2867 .expect("global does not exist");
2868 let Global::Callable(callable_decl) = global else {
2869 panic!("global is not a callable");
2870 };
2871
2872 let CallableImpl::Spec(spec_impl) = &callable_decl.implementation else {
2873 panic!("callable does not implement specializations");
2874 };
2875
2876 let spec_decl = get_spec_decl(spec_impl, functor_app);
2877 Some(spec_decl)
2878 }
2879
2880 fn get_expr_compute_kind(&self, expr_id: ExprId) -> ComputeKind {
2881 let current_package_id = self.get_current_package_id();
2882 let store_expr_id = StoreExprId::from((current_package_id, expr_id));
2883 let expr_generator_set = self.compute_properties.get_expr(store_expr_id);
2884 let callable_scope = self.eval_context.get_current_scope();
2885 expr_generator_set.generate_application_compute_kind(&callable_scope.args_compute_kind)
2886 }
2887
2888 fn is_unresolved_callee_expr(&self, expr_id: ExprId) -> bool {
2889 let current_package_id = self.get_current_package_id();
2890 let store_expr_id = StoreExprId::from((current_package_id, expr_id));
2891 self.compute_properties
2892 .is_unresolved_callee_expr(store_expr_id)
2893 }
2894
2895 fn get_call_compute_kind(&self, callable_scope: &Scope) -> ComputeKind {
2896 let store_item_id = StoreItemId::from((
2897 callable_scope.package_id,
2898 callable_scope
2899 .callable
2900 .expect("callable should be present")
2901 .0,
2902 ));
2903 let ItemComputeProperties::Callable(callable_compute_properties) =
2904 self.compute_properties.get_item(store_item_id)
2905 else {
2906 panic!("item compute properties not found");
2907 };
2908 let callable_generator_set = match &callable_scope.callable {
2909 Some((_, functor_app)) => match (functor_app.adjoint, functor_app.controlled) {
2910 (false, 0) => &callable_compute_properties.body,
2911 (false, _) => callable_compute_properties
2912 .ctl
2913 .as_ref()
2914 .expect("controlled should be supported"),
2915 (true, 0) => callable_compute_properties
2916 .adj
2917 .as_ref()
2918 .expect("adjoint should be supported"),
2919 (true, _) => callable_compute_properties
2920 .ctl_adj
2921 .as_ref()
2922 .expect("controlled adjoint should be supported"),
2923 },
2924 None => panic!("call compute kind should have callable"),
2925 };
2926 callable_generator_set.generate_application_compute_kind(&callable_scope.args_compute_kind)
2927 }
2928
2929 fn try_create_mutable_variable(
2930 &mut self,
2931 local_var_id: LocalVarId,
2932 value: &Value,
2933 ) -> Option<(rir::VariableId, Option<Literal>)> {
2934 // Check if we can create a mutable variable for this value.
2935 let var_ty = try_get_eval_var_type(value)?;
2936
2937 // Create an evaluator variable and insert it.
2938 let var_id = self.resource_manager.next_var();
2939 let eval_var = Var {
2940 id: var_id.into(),
2941 ty: var_ty,
2942 };
2943 self.eval_context
2944 .get_current_scope_mut()
2945 .insert_hybrid_local_value(local_var_id, Value::Var(eval_var));
2946
2947 // Insert a store instruction.
2948 let value_operand = self.map_eval_value_to_rir_operand(value);
2949 let rir_var = map_eval_var_to_rir_var(eval_var);
2950 let store_ins = Instruction::Store(value_operand, rir_var);
2951 self.get_current_rir_block_mut().0.push(store_ins);
2952
2953 // Create a mutable variable, mapping it to the static value if any.
2954 let static_value = match value_operand {
2955 Operand::Literal(literal) => Some(literal),
2956 Operand::Variable(_) => None,
2957 };
2958
2959 Some((var_id, static_value))
2960 }
2961
2962 fn get_or_insert_callable(&mut self, callable: Callable) -> CallableId {
2963 // Check if the callable is already in the program, and if not add it.
2964 let callable_name = callable.name.clone();
2965 if let Entry::Vacant(entry) = self.callables_map.entry(callable_name.clone().into()) {
2966 let callable_id = self.resource_manager.next_callable();
2967 entry.insert(callable_id);
2968 self.program.callables.insert(callable_id, callable);
2969 }
2970
2971 *self
2972 .callables_map
2973 .get(callable_name.as_str())
2974 .expect("callable not present")
2975 }
2976
2977 fn get_program_block_mut(&mut self, id: rir::BlockId) -> &mut rir::Block {
2978 self.program
2979 .blocks
2980 .get_mut(id)
2981 .expect("program block does not exist")
2982 }
2983
2984 fn is_static_expr(&self, expr_id: ExprId) -> bool {
2985 let compute_kind = self.get_expr_compute_kind(expr_id);
2986 matches!(compute_kind, ComputeKind::Static)
2987 }
2988
2989 fn allocate_qubit(&mut self) -> Value {
2990 let qubit = self.resource_manager.allocate_qubit();
2991 Value::Qubit(qubit)
2992 }
2993
2994 fn measure_qubits(&mut self, callable_decl: &CallableDecl, args_value: Value) -> Value {
2995 let mut input_type = Vec::new();
2996 let mut operands = Vec::new();
2997 let mut results_values = Vec::new();
2998
2999 match args_value {
3000 Value::Qubit(qubit) => {
3001 input_type.push(qsc_rir::rir::Ty::Qubit);
3002 operands.push(self.map_eval_value_to_rir_operand(&Value::Qubit(qubit)));
3003 }
3004 Value::Tuple(values, _) => {
3005 for value in &*values {
3006 let Value::Qubit(qubit) = value else {
3007 panic!(
3008 "by this point a qsc_pass should have checked that all arguments are Qubits"
3009 )
3010 };
3011 input_type.push(qsc_rir::rir::Ty::Qubit);
3012 operands.push(self.map_eval_value_to_rir_operand(&Value::Qubit(qubit.clone())));
3013 }
3014 }
3015 _ => {
3016 panic!("by this point a qsc_pass should have checked that all arguments are Qubits")
3017 }
3018 }
3019
3020 match &callable_decl.output {
3021 qsc_fir::ty::Ty::Prim(qsc_fir::ty::Prim::Result) => {
3022 input_type.push(qsc_rir::rir::Ty::Result);
3023 let result_value = Value::Result(self.resource_manager.next_result_register());
3024 let result_operand = self.map_eval_value_to_rir_operand(&result_value);
3025 operands.push(result_operand);
3026 results_values.push(result_value);
3027 }
3028 qsc_fir::ty::Ty::Tuple(outputs) => {
3029 for output in outputs {
3030 if matches!(output, qsc_fir::ty::Ty::Prim(qsc_fir::ty::Prim::Result)) {
3031 input_type.push(qsc_rir::rir::Ty::Result);
3032 let result_value =
3033 Value::Result(self.resource_manager.next_result_register());
3034 let result_operand = self.map_eval_value_to_rir_operand(&result_value);
3035 operands.push(result_operand);
3036 results_values.push(result_value);
3037 } else {
3038 panic!(
3039 "by this point a qsc_pass should have checked that all outputs are Results"
3040 )
3041 }
3042 }
3043 }
3044 _ => {
3045 panic!("by this point a qsc_pass should have checked that all outputs are Results")
3046 }
3047 }
3048
3049 let measurement_callable = Callable {
3050 name: callable_decl.name.name.to_string(),
3051 input_type,
3052 output_type: None,
3053 body: None,
3054 call_type: CallableType::Measurement,
3055 };
3056
3057 // Check if the callable has already been added to the program and if not do so now.
3058 let measure_callable_id = self.get_or_insert_callable(measurement_callable);
3059 // Current debug location should be set to the call expression currently being evaluated.
3060 let metadata = self.metadata_from_current_dbg_location();
3061 let instruction = Instruction::Call(measure_callable_id, operands, None, metadata);
3062 let current_block = self.get_current_rir_block_mut();
3063 current_block.0.push(instruction);
3064
3065 match results_values.len() {
3066 0 => panic!("unexpected unitary measurement"),
3067 1 => results_values[0].clone(),
3068 2.. => Value::Tuple(results_values.into(), None),
3069 }
3070 }
3071
3072 fn measure_qubit(&mut self, measure_callable: Callable, args_value: Value) -> Value {
3073 // Get the qubit and result IDs to use in the qubit measure instruction.
3074 let qubit = args_value.unwrap_qubit();
3075 let qubit_value = Value::Qubit(qubit);
3076 let qubit_operand = self.map_eval_value_to_rir_operand(&qubit_value);
3077 let result_value = Value::Result(self.resource_manager.next_result_register());
3078 let result_operand = self.map_eval_value_to_rir_operand(&result_value);
3079
3080 // Check if the callable has already been added to the program and if not do so now.
3081 let measure_callable_id = self.get_or_insert_callable(measure_callable);
3082 let args = vec![qubit_operand, result_operand];
3083 // Current debug location should be set to the call expression currently being evaluated.
3084 let metadata = self.metadata_from_current_dbg_location();
3085 let current_block = self.get_current_rir_block_mut();
3086 let instruction = Instruction::Call(measure_callable_id, args, None, metadata);
3087 current_block.0.push(instruction);
3088
3089 // Return the result value.
3090 result_value
3091 }
3092
3093 fn release_qubit(&mut self, args_value: Value) -> Value {
3094 let qubit = args_value.unwrap_qubit();
3095 self.resource_manager.release_qubit(&qubit);
3096
3097 // The value of a qubit release is unit.
3098 Value::unit()
3099 }
3100
3101 fn resolve_args(
3102 &self,
3103 store_pat_id: StorePatId,
3104 value: Value,
3105 args_span: Option<PackageSpan>,
3106 ctls: Option<(StorePatId, u8)>,
3107 fixed_args: Option<Rc<[Value]>>,
3108 ) -> Result<(Vec<Arg>, Option<Arg>), Error> {
3109 let mut value = value;
3110 let ctls_arg = if let Some((ctls_pat_id, ctls_count)) = ctls {
3111 let mut ctls = vec![];
3112 for _ in 0..ctls_count {
3113 let [c, rest] = &*value.unwrap_tuple() else {
3114 panic!("controls + arguments tuple should be arity 2");
3115 };
3116 ctls.extend_from_slice(&c.clone().unwrap_array());
3117 value = rest.clone();
3118 }
3119 if !are_ctls_unique(&ctls, &value) {
3120 let span = args_span.expect("span should be present");
3121 return Err(EvalError::QubitUniqueness(span).into());
3122 }
3123 let ctls_pat = self.package_store.get_pat(ctls_pat_id);
3124 let ctls_value = Value::Array(ctls.into());
3125 match &ctls_pat.kind {
3126 PatKind::Discard => Some(Arg::Discard(ctls_value)),
3127 PatKind::Bind(ident) => {
3128 let variable = Variable {
3129 name: ident.name.clone(),
3130 value: ctls_value,
3131 span: ident.span,
3132 };
3133 let ctl_arg = Arg::Var(ident.id, variable);
3134 Some(ctl_arg)
3135 }
3136 PatKind::Tuple(_) => panic!("control qubits pattern is not expected to be a tuple"),
3137 }
3138 } else {
3139 None
3140 };
3141
3142 let value = if let Some(fixed_args) = fixed_args {
3143 let mut fixed_args = fixed_args.to_vec();
3144 fixed_args.push(value);
3145 Value::Tuple(fixed_args.into(), None)
3146 } else {
3147 value
3148 };
3149
3150 let pat = self.package_store.get_pat(store_pat_id);
3151 let args = match &pat.kind {
3152 PatKind::Discard => vec![Arg::Discard(value)],
3153 PatKind::Bind(ident) => {
3154 let variable = Variable {
3155 name: ident.name.clone(),
3156 value,
3157 span: ident.span,
3158 };
3159 vec![Arg::Var(ident.id, variable)]
3160 }
3161 PatKind::Tuple(pats) => {
3162 let values = value.unwrap_tuple();
3163 assert_eq!(
3164 pats.len(),
3165 values.len(),
3166 "pattern tuple and value tuple have different arity"
3167 );
3168 let mut args = Vec::new();
3169 let pat_value_tuples = pats.iter().zip(values.to_vec());
3170 for (pat_id, value) in pat_value_tuples {
3171 // At this point we should no longer have control qubits so pass None.
3172 let (mut element_args, None) = self
3173 .resolve_args(
3174 (store_pat_id.package, *pat_id).into(),
3175 value,
3176 None,
3177 None,
3178 None,
3179 )
3180 .expect("no controls to verify")
3181 else {
3182 panic!("no control qubits are expected");
3183 };
3184 args.append(&mut element_args);
3185 }
3186 args
3187 }
3188 };
3189 Ok((args, ctls_arg))
3190 }
3191
3192 fn try_eval_block(&mut self, block_id: BlockId) -> Result<EvalControlFlow, Error> {
3193 let block = self.get_block(block_id);
3194 let mut return_stmt_id = None;
3195 let mut last_control_flow = EvalControlFlow::Continue(Value::unit());
3196
3197 // Iterate through the statements until we hit a return or reach the last statement.
3198 let mut stmts_iter = block.stmts.iter();
3199 for stmt_id in stmts_iter.by_ref() {
3200 last_control_flow = self.try_eval_stmt(*stmt_id)?;
3201 if last_control_flow.is_return() {
3202 return_stmt_id = Some(*stmt_id);
3203 break;
3204 }
3205 }
3206
3207 // While we support multiple returns within a callable, disallow situations in which statements are left
3208 // unprocessed when we are evaluating a branch within a callable scope.
3209 let remaining_stmt_count = stmts_iter.count();
3210 let current_scope = self.eval_context.get_current_scope();
3211 if remaining_stmt_count > 0 && current_scope.is_currently_evaluating_branch() {
3212 let return_stmt =
3213 self.get_stmt(return_stmt_id.expect("a return statement ID must have been set"));
3214 let hir_package_id = map_fir_package_to_hir(self.get_current_package_id());
3215 let return_stmt_package_span = PackageSpan {
3216 package: hir_package_id,
3217 span: return_stmt.span,
3218 };
3219 Err(Error::Unimplemented(
3220 "early return".to_string(),
3221 return_stmt_package_span,
3222 ))
3223 } else {
3224 Ok(last_control_flow)
3225 }
3226 }
3227
3228 fn try_eval_expr(&mut self, expr_id: ExprId) -> Result<EvalControlFlow, Error> {
3229 // An expression is evaluated differently depending on whether it is purely static or dynamic,
3230 // since static expressions can be fully evaluated and do not need to generate any instructions,
3231 // while dynamic expressions may need to generate instructions and map their value to a variable.
3232 if self.is_static_expr(expr_id) {
3233 self.eval_static_expr(expr_id)
3234 } else {
3235 self.eval_dynamic_expr(expr_id)
3236 }
3237 }
3238
3239 fn try_eval_stmt(&mut self, stmt_id: StmtId) -> Result<EvalControlFlow, Error> {
3240 let stmt = self.get_stmt(stmt_id);
3241 match stmt.kind {
3242 StmtKind::Expr(expr_id) => {
3243 // Since non-semi expressions are the only ones whose value is non-unit (their value is the same as the
3244 // value of the expression), they do not need to map their control flow to be unit on continue.
3245 self.try_eval_expr(expr_id)
3246 }
3247 StmtKind::Semi(expr_id) => {
3248 let control_flow = self.try_eval_expr(expr_id)?;
3249 match control_flow {
3250 EvalControlFlow::Continue(_) => Ok(EvalControlFlow::Continue(Value::unit())),
3251 EvalControlFlow::Return(_) => Ok(control_flow),
3252 }
3253 }
3254 StmtKind::Local(mutability, pat_id, expr_id) => {
3255 let control_flow = self.try_eval_expr(expr_id)?;
3256 match control_flow {
3257 EvalControlFlow::Continue(value) => {
3258 self.bind_value_to_pat(mutability, pat_id, value);
3259 Ok(EvalControlFlow::Continue(Value::unit()))
3260 }
3261 EvalControlFlow::Return(_) => Ok(control_flow),
3262 }
3263 }
3264 StmtKind::Item(_) => {
3265 // Do nothing and return a continue unit value.
3266 Ok(EvalControlFlow::Continue(Value::unit()))
3267 }
3268 }
3269 }
3270
3271 fn convert_value(
3272 &mut self,
3273 args_value: &Value,
3274 variable: rir::Variable,
3275 ) -> Result<Value, Error> {
3276 let instruction =
3277 Instruction::Convert(self.map_eval_value_to_rir_operand(args_value), variable);
3278 let current_block = self.get_current_rir_block_mut();
3279 current_block.0.push(instruction);
3280 Ok(Value::Var(
3281 map_rir_var_to_eval_var(variable).expect("variable should convert"),
3282 ))
3283 }
3284
3285 fn update_bindings(&mut self, lhs_expr_id: ExprId, rhs_value: Value) -> Result<(), Error> {
3286 let lhs_expr = self.get_expr(lhs_expr_id);
3287 match (&lhs_expr.kind, rhs_value) {
3288 (ExprKind::Hole, _) => {}
3289 (ExprKind::Var(Res::Local(local_var_id), _), value) => {
3290 // We update both the hybrid and classical bindings because there are some cases where an expression is
3291 // classified as classical by RCA, but some elements of the expression are non-classical.
3292 //
3293 // For example, the output of the `Length` intrinsic function is only considered non-classical when used
3294 // on a dynamically-sized array. However, it can be used on arrays that are considered non-classical,
3295 // such as arrays of Qubits or Results.
3296 //
3297 // Since expressions call expressions to the `Length` intrinsic will be offloaded to the evaluator,
3298 // the evaluator environment also needs to track some non-classical variables.
3299 self.update_hybrid_local(lhs_expr, *local_var_id, value.clone())?;
3300 self.update_classical_local(*local_var_id, value);
3301 }
3302 (ExprKind::Tuple(exprs), Value::Tuple(values, _)) => {
3303 for (expr_id, value) in exprs.iter().zip(values.iter()) {
3304 self.update_bindings(*expr_id, value.clone())?;
3305 }
3306 }
3307 _ => unreachable!("unassignable pattern should be disallowed by compiler"),
3308 }
3309 Ok(())
3310 }
3311
3312 fn update_classical_local(&mut self, local_var_id: LocalVarId, value: Value) {
3313 // Classical values are not updated when we are within a dynamic branch.
3314 if self
3315 .eval_context
3316 .get_current_scope()
3317 .is_currently_evaluating_branch()
3318 {
3319 return;
3320 }
3321
3322 // Variable values are not updated on the classical locals either.
3323 if matches!(value, Value::Var(_)) {
3324 return;
3325 }
3326
3327 // Create a variable and bind it to the classical environment.
3328 self.eval_context
3329 .get_current_scope_mut()
3330 .env
3331 .update_variable_in_top_frame(local_var_id, value);
3332 }
3333
3334 fn update_hybrid_local(
3335 &mut self,
3336 local_expr: &Expr,
3337 local_var_id: LocalVarId,
3338 value: Value,
3339 ) -> Result<(), Error> {
3340 let bound_value = self
3341 .eval_context
3342 .get_current_scope()
3343 .get_hybrid_local_value(local_var_id);
3344 if let Value::Var(var) = bound_value {
3345 // Insert a store instruction when the value of a variable is updated.
3346 let rhs_operand = self.map_eval_value_to_rir_operand(&value);
3347 let rir_var = map_eval_var_to_rir_var(*var);
3348 let store_ins = Instruction::Store(rhs_operand, rir_var);
3349 self.get_current_rir_block_mut().0.push(store_ins);
3350
3351 // If this is a mutable variable, make sure to update whether it is static or dynamic.
3352 let current_scope = self.eval_context.get_current_scope_mut();
3353 match rhs_operand {
3354 Operand::Literal(literal) => {
3355 // The variable maps to a static literal here, so track that literal value.
3356 current_scope.insert_static_var_mapping(rir_var.variable_id, literal);
3357 }
3358 Operand::Variable(_) => {
3359 // The variable is not known to be some literal value, so remove the static mapping.
3360 current_scope.remove_static_value(rir_var.variable_id);
3361 }
3362 }
3363 } else {
3364 // Verify that we are not updating a value that does not have a backing variable from a dynamic branch
3365 // because it is unsupported.
3366 if self
3367 .eval_context
3368 .get_current_scope()
3369 .is_currently_evaluating_branch()
3370 {
3371 let error_message = format!(
3372 "re-assignment within a dynamic branch is unsupported for type {}",
3373 local_expr.ty
3374 );
3375 let error =
3376 Error::Unexpected(error_message, self.get_expr_package_span(local_expr.id));
3377 return Err(error);
3378 }
3379 self.eval_context
3380 .get_current_scope_mut()
3381 .update_hybrid_local_value(local_var_id, value);
3382 }
3383 Ok(())
3384 }
3385
3386 fn update_hybrid_bindings_from_classical_bindings(
3387 &mut self,
3388 lhs_expr_id: ExprId,
3389 ) -> Result<(), Error> {
3390 let lhs_expr = &self.get_expr(lhs_expr_id);
3391 match &lhs_expr.kind {
3392 ExprKind::Hole => {
3393 // Nothing to bind to.
3394 }
3395 ExprKind::Var(Res::Local(local_var_id), _) => {
3396 let classical_value = self
3397 .eval_context
3398 .get_current_scope()
3399 .get_classical_local_value(*local_var_id)
3400 .clone();
3401 self.update_hybrid_local(lhs_expr, *local_var_id, classical_value)?;
3402 }
3403 ExprKind::Tuple(exprs) => {
3404 for expr_id in exprs {
3405 self.update_hybrid_bindings_from_classical_bindings(*expr_id)?;
3406 }
3407 }
3408 _ => unreachable!("unassignable pattern should be disallowed by compiler"),
3409 }
3410 Ok(())
3411 }
3412
3413 fn generate_output_recording_instructions(
3414 &mut self,
3415 ret_val: Value,
3416 ty: &Ty,
3417 tag_root: &str,
3418 ) -> Result<Vec<Instruction>, ()> {
3419 let mut instrs = Vec::new();
3420
3421 match ret_val {
3422 Value::Result(val::Result::Val(_)) => return Err(()),
3423
3424 Value::Array(vals) => self.record_array(ty, &mut instrs, &vals, tag_root)?,
3425 Value::Tuple(vals, _) => self.record_tuple(ty, &mut instrs, &vals, tag_root)?,
3426 Value::Result(res) => self.record_result(&mut instrs, res, tag_root),
3427 Value::Var(var) => self.record_variable(ty, &mut instrs, var, tag_root),
3428 Value::Bool(val) => self.record_bool(&mut instrs, val, tag_root),
3429 Value::Int(val) => self.record_int(&mut instrs, val, tag_root),
3430 Value::Double(val) => self.record_double(&mut instrs, val, tag_root),
3431
3432 Value::BigInt(_)
3433 | Value::Closure(_)
3434 | Value::Global(_, _)
3435 | Value::Pauli(_)
3436 | Value::Qubit(_)
3437 | Value::Range(_)
3438 | Value::String(_) => panic!("unsupported value type in output recording"),
3439 }
3440
3441 Ok(instrs)
3442 }
3443
3444 fn record_int(&mut self, instrs: &mut Vec<Instruction>, val: i64, tag_root: &str) {
3445 let idx = self.program.tags.len();
3446 let tag = format!("{idx}_{tag_root}i");
3447 let len = tag.len();
3448 self.program.tags.push(tag);
3449 let int_record_callable_id = self.get_int_record_callable();
3450 instrs.push(Instruction::Call(
3451 int_record_callable_id,
3452 vec![
3453 Operand::Literal(Literal::Integer(val)),
3454 Operand::Literal(Literal::Tag(idx, len)),
3455 ],
3456 None,
3457 None,
3458 ));
3459 }
3460
3461 fn record_double(&mut self, instrs: &mut Vec<Instruction>, val: f64, tag_root: &str) {
3462 let idx = self.program.tags.len();
3463 let tag = format!("{idx}_{tag_root}d");
3464 let len = tag.len();
3465 self.program.tags.push(tag);
3466 let double_record_callable_id = self.get_double_record_callable();
3467 instrs.push(Instruction::Call(
3468 double_record_callable_id,
3469 vec![
3470 Operand::Literal(Literal::Double(val)),
3471 Operand::Literal(Literal::Tag(idx, len)),
3472 ],
3473 None,
3474 None,
3475 ));
3476 }
3477
3478 fn record_bool(&mut self, instrs: &mut Vec<Instruction>, val: bool, tag_root: &str) {
3479 let idx = self.program.tags.len();
3480 let tag = format!("{idx}_{tag_root}b");
3481 let len = tag.len();
3482 self.program.tags.push(tag);
3483 let bool_record_callable_id = self.get_bool_record_callable();
3484 instrs.push(Instruction::Call(
3485 bool_record_callable_id,
3486 vec![
3487 Operand::Literal(Literal::Bool(val)),
3488 Operand::Literal(Literal::Tag(idx, len)),
3489 ],
3490 None,
3491 None,
3492 ));
3493 }
3494
3495 fn record_variable(
3496 &mut self,
3497 ty: &Ty,
3498 instrs: &mut Vec<Instruction>,
3499 var: Var,
3500 tag_root: &str,
3501 ) {
3502 let idx = self.program.tags.len();
3503 let (record_callable_id, tag_ty) = match ty {
3504 Ty::Prim(Prim::Bool) => (self.get_bool_record_callable(), "b"),
3505 Ty::Prim(Prim::Int) => (self.get_int_record_callable(), "i"),
3506 Ty::Prim(Prim::Double) => (self.get_double_record_callable(), "d"),
3507 _ => panic!("unsupported variable type in output recording"),
3508 };
3509 let tag = format!("{idx}_{tag_root}{tag_ty}");
3510 let len = tag.len();
3511 self.program.tags.push(tag);
3512 instrs.push(Instruction::Call(
3513 record_callable_id,
3514 vec![
3515 Operand::Variable(map_eval_var_to_rir_var(var)),
3516 Operand::Literal(Literal::Tag(idx, len)),
3517 ],
3518 None,
3519 None,
3520 ));
3521 }
3522
3523 fn record_result(&mut self, instrs: &mut Vec<Instruction>, res: val::Result, tag_root: &str) {
3524 let idx = self.program.tags.len();
3525 let result_record_callable_id = self.get_result_record_callable();
3526 let tag = format!("{idx}_{tag_root}r");
3527 let len = tag.len();
3528 self.program.tags.push(tag);
3529 instrs.push(Instruction::Call(
3530 result_record_callable_id,
3531 vec![
3532 Operand::Literal(Literal::Result(
3533 res.unwrap_id()
3534 .try_into()
3535 .expect("result id should fit into u32"),
3536 )),
3537 Operand::Literal(Literal::Tag(idx, len)),
3538 ],
3539 None,
3540 None,
3541 ));
3542 }
3543
3544 fn record_tuple(
3545 &mut self,
3546 ty: &Ty,
3547 instrs: &mut Vec<Instruction>,
3548 vals: &Rc<[Value]>,
3549 tag_root: &str,
3550 ) -> Result<(), ()> {
3551 let Ty::Tuple(elem_tys) = ty else {
3552 panic!("expected tuple type for tuple value");
3553 };
3554 let new_tag_root = format!("{tag_root}t");
3555 let idx = self.program.tags.len();
3556 let tag = format!("{idx}_{new_tag_root}");
3557 let len = tag.len();
3558 self.program.tags.push(tag);
3559 let tuple_record_callable_id = self.get_tuple_record_callable();
3560 instrs.push(Instruction::Call(
3561 tuple_record_callable_id,
3562 vec![
3563 Operand::Literal(Literal::Integer(
3564 vals.len()
3565 .try_into()
3566 .expect("tuple length should fit into u32"),
3567 )),
3568 Operand::Literal(Literal::Tag(idx, len)),
3569 ],
3570 None,
3571 None,
3572 ));
3573 for (idx, (val, elem_ty)) in vals.iter().zip(elem_tys.iter()).enumerate() {
3574 let new_tag_root = format!("{new_tag_root}{idx}");
3575 instrs.extend(self.generate_output_recording_instructions(
3576 val.clone(),
3577 elem_ty,
3578 &new_tag_root,
3579 )?);
3580 }
3581
3582 Ok(())
3583 }
3584
3585 fn record_array(
3586 &mut self,
3587 ty: &Ty,
3588 instrs: &mut Vec<Instruction>,
3589 vals: &Rc<Vec<Value>>,
3590 tag_root: &str,
3591 ) -> Result<(), ()> {
3592 let Ty::Array(elem_ty) = ty else {
3593 panic!("expected array type for array value");
3594 };
3595 let new_tag_root = format!("{tag_root}a");
3596 let idx = self.program.tags.len();
3597 let tag = format!("{idx}_{new_tag_root}");
3598 let len = tag.len();
3599 self.program.tags.push(tag);
3600 let array_record_callable_id = self.get_array_record_callable();
3601 instrs.push(Instruction::Call(
3602 array_record_callable_id,
3603 vec![
3604 Operand::Literal(Literal::Integer(
3605 vals.len()
3606 .try_into()
3607 .expect("array length should fit into u32"),
3608 )),
3609 Operand::Literal(Literal::Tag(idx, len)),
3610 ],
3611 None,
3612 None,
3613 ));
3614 for (idx, val) in vals.iter().enumerate() {
3615 let new_tag_root = format!("{new_tag_root}{idx}");
3616 instrs.extend(self.generate_output_recording_instructions(
3617 val.clone(),
3618 elem_ty,
3619 &new_tag_root,
3620 )?);
3621 }
3622
3623 Ok(())
3624 }
3625
3626 fn get_array_record_callable(&mut self) -> CallableId {
3627 if let Some(id) = self.callables_map.get("__quantum__rt__array_record_output") {
3628 return *id;
3629 }
3630
3631 let callable = builder::array_record_decl();
3632 let callable_id = self.resource_manager.next_callable();
3633 self.callables_map
3634 .insert("__quantum__rt__array_record_output".into(), callable_id);
3635 self.program.callables.insert(callable_id, callable);
3636 callable_id
3637 }
3638
3639 fn get_tuple_record_callable(&mut self) -> CallableId {
3640 if let Some(id) = self.callables_map.get("__quantum__rt__tuple_record_output") {
3641 return *id;
3642 }
3643
3644 let callable = builder::tuple_record_decl();
3645 let callable_id = self.resource_manager.next_callable();
3646 self.callables_map
3647 .insert("__quantum__rt__tuple_record_output".into(), callable_id);
3648 self.program.callables.insert(callable_id, callable);
3649 callable_id
3650 }
3651
3652 fn get_result_record_callable(&mut self) -> CallableId {
3653 if let Some(id) = self
3654 .callables_map
3655 .get("__quantum__rt__result_record_output")
3656 {
3657 return *id;
3658 }
3659
3660 let callable = builder::result_record_decl();
3661 let callable_id = self.resource_manager.next_callable();
3662 self.callables_map
3663 .insert("__quantum__rt__result_record_output".into(), callable_id);
3664 self.program.callables.insert(callable_id, callable);
3665 callable_id
3666 }
3667
3668 fn get_bool_record_callable(&mut self) -> CallableId {
3669 if let Some(id) = self.callables_map.get("__quantum__rt__bool_record_output") {
3670 return *id;
3671 }
3672
3673 let callable = builder::bool_record_decl();
3674 let callable_id = self.resource_manager.next_callable();
3675 self.callables_map
3676 .insert("__quantum__rt__bool_record_output".into(), callable_id);
3677 self.program.callables.insert(callable_id, callable);
3678 callable_id
3679 }
3680
3681 fn get_double_record_callable(&mut self) -> CallableId {
3682 if let Some(id) = self
3683 .callables_map
3684 .get("__quantum__rt__double_record_output")
3685 {
3686 return *id;
3687 }
3688
3689 let callable = builder::double_record_decl();
3690 let callable_id = self.resource_manager.next_callable();
3691 self.callables_map
3692 .insert("__quantum__rt__double_record_output".into(), callable_id);
3693 self.program.callables.insert(callable_id, callable);
3694 callable_id
3695 }
3696
3697 fn get_int_record_callable(&mut self) -> CallableId {
3698 if let Some(id) = self.callables_map.get("__quantum__rt__int_record_output") {
3699 return *id;
3700 }
3701
3702 let callable = builder::int_record_decl();
3703 let callable_id = self.resource_manager.next_callable();
3704 self.callables_map
3705 .insert("__quantum__rt__int_record_output".into(), callable_id);
3706 self.program.callables.insert(callable_id, callable);
3707 callable_id
3708 }
3709
3710 fn map_eval_value_to_rir_operand(&self, value: &Value) -> Operand {
3711 match value {
3712 Value::Bool(b) => Operand::Literal(Literal::Bool(*b)),
3713 Value::Double(d) => Operand::Literal(Literal::Double(*d)),
3714 Value::Int(i) => Operand::Literal(Literal::Integer(*i)),
3715 Value::Qubit(q) => Operand::Literal(Literal::Qubit(
3716 self.resource_manager
3717 .map_qubit(q)
3718 .try_into()
3719 .expect("could not convert qubit ID to u32"),
3720 )),
3721 Value::Result(r) => match r {
3722 val::Result::Id(id) => Operand::Literal(Literal::Result(
3723 (*id)
3724 .try_into()
3725 .expect("could not convert result ID to u32"),
3726 )),
3727 val::Result::Val(bool) => Operand::Literal(Literal::Bool(*bool)),
3728 val::Result::Loss => panic!("loss result should not occur in partial evaluation"),
3729 },
3730 Value::Var(var) => Operand::Variable(map_eval_var_to_rir_var(*var)),
3731 _ => panic!("{value} cannot be mapped to a RIR operand"),
3732 }
3733 }
3734
3735 fn clone_current_static_var_map(&self) -> FxHashMap<VariableId, Literal> {
3736 self.eval_context
3737 .get_current_scope()
3738 .clone_static_var_mappings()
3739 }
3740
3741 fn overwrite_current_static_var_map(&mut self, static_vars: FxHashMap<VariableId, Literal>) {
3742 self.eval_context
3743 .get_current_scope_mut()
3744 .set_static_var_mappings(static_vars);
3745 }
3746
3747 fn keep_matching_static_var_mappings(
3748 &mut self,
3749 other_mappings: &FxHashMap<VariableId, Literal>,
3750 ) {
3751 self.eval_context
3752 .get_current_scope_mut()
3753 .keep_matching_static_var_mappings(other_mappings);
3754 }
3755
3756 fn new_dbg_location(&mut self, expr_id: ExprId) -> Option<DbgLocationId> {
3757 if !self.config.generate_debug_metadata {
3758 return None;
3759 }
3760
3761 let scope_id = self.get_current_dbg_scope();
3762
3763 if let Some(current_scope_id) = scope_id {
3764 let expr_location = self.expr_start_source_location(expr_id);
3765 let inlined_at = self.caller_dbg_location_id();
3766 let new_location = DbgLocation {
3767 location: expr_location,
3768 scope: current_scope_id,
3769 inlined_at,
3770 };
3771 let dbg_location_id = self.program.dbg_info.add_location(new_location);
3772
3773 return Some(dbg_location_id);
3774 }
3775 None
3776 }
3777
3778 fn assign_current_dbg_location(&mut self, call_expr_id: ExprId) {
3779 if !self.config.generate_debug_metadata {
3780 return;
3781 }
3782
3783 if let Some(dbg_location_id) = self.new_dbg_location(call_expr_id) {
3784 self.eval_context
3785 .get_current_scope_mut()
3786 .dbg_context
3787 .current_call_location = Some(dbg_location_id);
3788 }
3789 }
3790
3791 fn get_current_dbg_scope(&mut self) -> Option<DbgScopeId> {
3792 if !self.config.generate_debug_metadata {
3793 return None;
3794 }
3795
3796 let scope = self.eval_context.get_current_scope();
3797
3798 if let Some(LoopScope {
3799 loop_expr,
3800 iteration_count,
3801 ..
3802 }) = scope.dbg_context.loop_iterations.last()
3803 {
3804 let s = self
3805 .dbg_context
3806 .dbg_loop_expr_to_scope
3807 .get(&(*loop_expr, *iteration_count))
3808 .copied();
3809 if let Some(s) = s {
3810 Some(s)
3811 } else {
3812 let loop_expr_location = self.expr_start_source_location(*loop_expr);
3813 let scope = DbgScope::LexicalBlockFile {
3814 discriminator: *iteration_count,
3815 location: loop_expr_location,
3816 };
3817
3818 let i = self.program.dbg_info.add_scope(scope);
3819 self.dbg_context
3820 .dbg_loop_expr_to_scope
3821 .insert((*loop_expr, *iteration_count), i);
3822 Some(i)
3823 }
3824 } else {
3825 let (callable_id, functor_app) = scope.callable?;
3826 let item_id = StoreItemId {
3827 package: scope.package_id,
3828 item: callable_id,
3829 };
3830 let s = self
3831 .dbg_context
3832 .dbg_callable_to_scope
3833 .get(&(item_id, functor_app.adjoint))
3834 .copied();
3835
3836 if let Some(s) = s {
3837 Some(s)
3838 } else {
3839 let fir::ItemKind::Callable(callable_decl) =
3840 &self.package_store.get_item(item_id).kind
3841 else {
3842 panic!("expected callable");
3843 };
3844 let name = if functor_app.adjoint {
3845 format!("{}'", callable_decl.name.name).into()
3846 } else {
3847 callable_decl.name.name.clone()
3848 };
3849 let current_package_id = self.get_current_package_id();
3850 let package_id = current_package_id.into();
3851 let scope = DbgScope::SubProgram {
3852 name,
3853 location: DbgPackageOffset {
3854 package_id,
3855 offset: callable_decl.span.lo,
3856 },
3857 };
3858 let i = self.program.dbg_info.add_scope(scope);
3859 self.dbg_context
3860 .dbg_callable_to_scope
3861 .insert((item_id, functor_app.adjoint), i);
3862 Some(i)
3863 }
3864 }
3865 }
3866
3867 fn metadata_from_expr(&mut self, expr_id: ExprId) -> Option<Box<InstructionDbgMetadata>> {
3868 if self.config.generate_debug_metadata {
3869 let dbg_location_id = self.new_dbg_location(expr_id);
3870 dbg_location_id.map(|dbg_location| {
3871 self.program.dbg_info.mark_location_used(dbg_location);
3872 Box::new(InstructionDbgMetadata { dbg_location })
3873 })
3874 } else {
3875 None
3876 }
3877 }
3878
3879 fn metadata_from_current_dbg_location(&mut self) -> Option<Box<InstructionDbgMetadata>> {
3880 if self.config.generate_debug_metadata {
3881 self.eval_context
3882 .get_current_scope()
3883 .dbg_context
3884 .current_call_location
3885 .map(|dbg_location| {
3886 self.program.dbg_info.mark_location_used(dbg_location);
3887 Box::new(InstructionDbgMetadata { dbg_location })
3888 })
3889 } else {
3890 None
3891 }
3892 }
3893
3894 fn caller_dbg_location_id(&mut self) -> Option<DbgLocationId> {
3895 if let Some(LoopScope {
3896 location_id: loop_location_id,
3897 ..
3898 }) = self
3899 .eval_context
3900 .get_current_scope()
3901 .dbg_context
3902 .loop_iterations
3903 .last()
3904 {
3905 Some(*loop_location_id)
3906 } else if let Some(scope) = self.eval_context.get_caller_scope() {
3907 scope.dbg_context.current_call_location
3908 } else {
3909 None
3910 }
3911 }
3912
3913 fn expr_start_source_location(&self, expr_id: ExprId) -> DbgPackageOffset {
3914 let package_id = self.get_current_package_id();
3915 let package = self.package_store.get(package_id);
3916 DbgPackageOffset {
3917 package_id: package_id.into(),
3918 offset: package
3919 .exprs
3920 .get(expr_id)
3921 .expect("current expr id not found")
3922 .span
3923 .lo,
3924 }
3925 }
3926
3927 fn dbg_push_loop_iteration_scope(&mut self, expr_id: ExprId, dbg_location_id: DbgLocationId) {
3928 self.eval_context
3929 .get_current_scope_mut()
3930 .dbg_context
3931 .loop_iterations
3932 .push(LoopScope {
3933 loop_expr: expr_id,
3934 iteration_count: 0,
3935 location_id: dbg_location_id,
3936 });
3937 }
3938
3939 fn dbg_pop_loop_iteration_scope(&mut self) {
3940 if self.config.generate_debug_metadata {
3941 self.eval_context
3942 .get_current_scope_mut()
3943 .dbg_context
3944 .loop_iterations
3945 .pop();
3946 }
3947 }
3948
3949 fn dbg_increment_loop_iteration_count(&mut self) {
3950 if self.config.generate_debug_metadata {
3951 self.eval_context
3952 .get_current_scope_mut()
3953 .dbg_context
3954 .loop_iterations
3955 .last_mut()
3956 .expect("there should be a loop iteration in the stack")
3957 .iteration_count += 1;
3958 }
3959 }
3960}
3961
3962#[derive(Default)]
3963pub(crate) struct DbgContext {
3964 /// (`CallableId`, isAdjoint) -> Scope index
3965 pub(crate) dbg_callable_to_scope: FxHashMap<(StoreItemId, bool), DbgScopeId>,
3966 /// (Loop `ExprId`, iteration) -> Scope index
3967 pub(crate) dbg_loop_expr_to_scope: FxHashMap<(ExprId, usize), DbgScopeId>,
3968}
3969
3970#[derive(Default)]
3971struct ScopeDbgContext {
3972 /// The distinct debug location of the call expression currently being evaluated.
3973 pub(crate) current_call_location: Option<DbgLocationId>,
3974 pub(crate) loop_iterations: Vec<LoopScope>,
3975}
3976
3977#[derive(Clone, Copy)]
3978struct LoopScope {
3979 loop_expr: ExprId,
3980 iteration_count: usize,
3981 location_id: DbgLocationId,
3982}
3983
3984fn eval_un_op_with_literals(un_op: UnOp, value: Value) -> Value {
3985 match un_op {
3986 UnOp::Neg => match value {
3987 Value::Int(i) => Value::Int(-i),
3988 Value::Double(d) => Value::Double(-d),
3989 Value::BigInt(b) => Value::BigInt(-b),
3990 _ => panic!("invalid type for negation operator {}", value.type_name()),
3991 },
3992 UnOp::NotB => match value {
3993 Value::Int(i) => Value::Int(!i),
3994 Value::BigInt(b) => Value::BigInt(!b),
3995 _ => panic!(
3996 "invalid type for bitwise negation operator {}",
3997 value.type_name()
3998 ),
3999 },
4000 UnOp::NotL => match value {
4001 Value::Bool(b) => Value::Bool(!b),
4002 _ => panic!(
4003 "invalid type for logical negation operator {}",
4004 value.type_name()
4005 ),
4006 },
4007 UnOp::Functor(functor) => match value {
4008 Value::Closure(inner) => Value::Closure(
4009 val::Closure {
4010 functor: update_functor_app(functor, inner.functor),
4011 ..*inner
4012 }
4013 .into(),
4014 ),
4015 Value::Global(id, app) => Value::Global(id, update_functor_app(functor, app)),
4016 _ => panic!("value should be callable"),
4017 },
4018 UnOp::Pos | UnOp::Unwrap => value,
4019 }
4020}
4021
4022fn eval_bin_op_with_bool_literals(
4023 bin_op: BinOp,
4024 lhs_literal: Literal,
4025 rhs_literal: Literal,
4026) -> Value {
4027 let (Literal::Bool(lhs_bool), Literal::Bool(rhs_bool)) = (lhs_literal, rhs_literal) else {
4028 panic!("at least one literal is not bool: {lhs_literal}, {rhs_literal}");
4029 };
4030
4031 let bin_op_result = match bin_op {
4032 BinOp::Eq => lhs_bool == rhs_bool,
4033 BinOp::Neq => lhs_bool != rhs_bool,
4034 BinOp::AndL => lhs_bool && rhs_bool,
4035 BinOp::OrL => lhs_bool || rhs_bool,
4036 _ => panic!("invalid bool operator: {bin_op:?}"),
4037 };
4038 Value::Bool(bin_op_result)
4039}
4040
4041fn eval_bin_op_with_double_literals(
4042 bin_op: BinOp,
4043 lhs_literal: Literal,
4044 rhs_literal: Literal,
4045 bin_op_expr_span: PackageSpan, // For diagnostic purposes only
4046) -> Result<Value, Error> {
4047 fn eval_double_div(lhs: f64, rhs: f64, span: PackageSpan) -> Result<Value, Error> {
4048 match (lhs, rhs) {
4049 (_, 0.0) => Err(EvalError::DivZero(span).into()),
4050 (lhs, rhs) => Ok(Value::Double(lhs / rhs)),
4051 }
4052 }
4053
4054 // Validate that both literals are doubles.
4055 let (Literal::Double(lhs), Literal::Double(rhs)) = (lhs_literal, rhs_literal) else {
4056 panic!("at least one literal is not an double: {lhs_literal}, {rhs_literal}");
4057 };
4058
4059 match bin_op {
4060 BinOp::Eq => {
4061 // matching simulator behavior
4062 #[allow(clippy::float_cmp)]
4063 Ok(Value::Bool(lhs == rhs))
4064 }
4065 BinOp::Neq => {
4066 // matching simulator behavior
4067 #[allow(clippy::float_cmp)]
4068 Ok(Value::Bool(lhs != rhs))
4069 }
4070 BinOp::Gt => Ok(Value::Bool(lhs > rhs)),
4071 BinOp::Gte => Ok(Value::Bool(lhs >= rhs)),
4072 BinOp::Lt => Ok(Value::Bool(lhs < rhs)),
4073 BinOp::Lte => Ok(Value::Bool(lhs <= rhs)),
4074 BinOp::Add => Ok(Value::Double(lhs + rhs)),
4075 BinOp::Sub => Ok(Value::Double(lhs - rhs)),
4076 BinOp::Mul => Ok(Value::Double(lhs * rhs)),
4077 BinOp::Div => eval_double_div(lhs, rhs, bin_op_expr_span),
4078 _ => panic!("invalid double operator: {bin_op:?}"),
4079 }
4080}
4081
4082fn eval_bin_op_with_integer_literals(
4083 bin_op: BinOp,
4084 lhs_literal: Literal,
4085 rhs_literal: Literal,
4086 bin_op_expr_span: PackageSpan, // For diagnostic purposes only
4087) -> Result<Value, Error> {
4088 fn eval_integer_div(lhs_int: i64, rhs_int: i64, span: PackageSpan) -> Result<Value, Error> {
4089 match (lhs_int, rhs_int) {
4090 (_, 0) => Err(EvalError::DivZero(span).into()),
4091 (lhs, rhs) => Ok(Value::Int(lhs / rhs)),
4092 }
4093 }
4094
4095 fn eval_integer_mod(lhs_int: i64, rhs_int: i64, span: PackageSpan) -> Result<Value, Error> {
4096 match (lhs_int, rhs_int) {
4097 (_, 0) => Err(EvalError::DivZero(span).into()),
4098 (lhs, rhs) => Ok(Value::Int(lhs % rhs)),
4099 }
4100 }
4101
4102 fn eval_integer_exp(lhs_int: i64, rhs_int: i64, span: PackageSpan) -> Result<Value, Error> {
4103 let Ok(rhs_int_as_u32) = u32::try_from(rhs_int) else {
4104 return Err(EvalError::IntTooLarge(rhs_int, span).into());
4105 };
4106
4107 Ok(Value::Int(lhs_int.pow(rhs_int_as_u32)))
4108 }
4109
4110 // Validate that both literals are integers.
4111 let (Literal::Integer(lhs_int), Literal::Integer(rhs_int)) = (lhs_literal, rhs_literal) else {
4112 panic!("at least one literal is not an integer: {lhs_literal}, {rhs_literal}");
4113 };
4114
4115 match bin_op {
4116 BinOp::Eq => Ok(Value::Bool(lhs_int == rhs_int)),
4117 BinOp::Neq => Ok(Value::Bool(lhs_int != rhs_int)),
4118 BinOp::Gt => Ok(Value::Bool(lhs_int > rhs_int)),
4119 BinOp::Gte => Ok(Value::Bool(lhs_int >= rhs_int)),
4120 BinOp::Lt => Ok(Value::Bool(lhs_int < rhs_int)),
4121 BinOp::Lte => Ok(Value::Bool(lhs_int <= rhs_int)),
4122 BinOp::Add => Ok(Value::Int(lhs_int + rhs_int)),
4123 BinOp::Sub => Ok(Value::Int(lhs_int - rhs_int)),
4124 BinOp::Mul => Ok(Value::Int(lhs_int * rhs_int)),
4125 BinOp::Div => eval_integer_div(lhs_int, rhs_int, bin_op_expr_span),
4126 BinOp::Mod => eval_integer_mod(lhs_int, rhs_int, bin_op_expr_span),
4127 BinOp::Exp => eval_integer_exp(lhs_int, rhs_int, bin_op_expr_span),
4128 BinOp::AndB => Ok(Value::Int(lhs_int & rhs_int)),
4129 BinOp::OrB => Ok(Value::Int(lhs_int | rhs_int)),
4130 BinOp::XorB => Ok(Value::Int(lhs_int ^ rhs_int)),
4131 BinOp::Shl => Ok(Value::Int(lhs_int << rhs_int)),
4132 BinOp::Shr => Ok(Value::Int(lhs_int >> rhs_int)),
4133 _ => panic!("invalid integer operator: {bin_op:?}"),
4134 }
4135}
4136
4137fn get_spec_decl(spec_impl: &SpecImpl, functor_app: FunctorApp) -> &SpecDecl {
4138 if !functor_app.adjoint && functor_app.controlled == 0 {
4139 &spec_impl.body
4140 } else if functor_app.adjoint && functor_app.controlled == 0 {
4141 spec_impl
4142 .adj
4143 .as_ref()
4144 .expect("adjoint specialization does not exist")
4145 } else if !functor_app.adjoint && functor_app.controlled > 0 {
4146 spec_impl
4147 .ctl
4148 .as_ref()
4149 .expect("controlled specialization does not exist")
4150 } else {
4151 spec_impl
4152 .ctl_adj
4153 .as_ref()
4154 .expect("controlled adjoint specialization does not exits")
4155 }
4156}
4157
4158fn map_eval_var_to_rir_var(var: Var) -> rir::Variable {
4159 rir::Variable {
4160 variable_id: var.id.into(),
4161 ty: map_eval_var_type_to_rir_type(var.ty),
4162 }
4163}
4164
4165fn map_eval_var_type_to_rir_type(var_ty: VarTy) -> rir::Ty {
4166 match var_ty {
4167 VarTy::Boolean => rir::Ty::Boolean,
4168 VarTy::Integer => rir::Ty::Integer,
4169 VarTy::Double => rir::Ty::Double,
4170 }
4171}
4172
4173fn map_fir_type_to_rir_type(ty: &Ty) -> Result<rir::Ty, String> {
4174 match ty {
4175 Ty::Prim(Prim::Bool) => Ok(rir::Ty::Boolean),
4176 Ty::Prim(Prim::Double) => Ok(rir::Ty::Double),
4177 Ty::Prim(Prim::Int) => Ok(rir::Ty::Integer),
4178 Ty::Prim(Prim::Qubit) => Ok(rir::Ty::Qubit),
4179 Ty::Prim(Prim::Result) => Ok(rir::Ty::Result),
4180 _ => Err(format!("{ty}")),
4181 }
4182}
4183
4184fn map_rir_literal_to_eval_value(literal: rir::Literal) -> Value {
4185 match literal {
4186 rir::Literal::Bool(b) => Value::Bool(b),
4187 rir::Literal::Double(d) => Value::Double(d),
4188 rir::Literal::Integer(i) => Value::Int(i),
4189 _ => panic!("{literal:?} RIR literal cannot be mapped to evaluator value"),
4190 }
4191}
4192
4193fn map_rir_var_to_eval_var(var: rir::Variable) -> Result<Var, ()> {
4194 Ok(Var {
4195 id: var.variable_id.into(),
4196 ty: map_rir_type_to_eval_var_type(var.ty)?,
4197 })
4198}
4199
4200fn map_rir_type_to_eval_var_type(ty: rir::Ty) -> Result<VarTy, ()> {
4201 match ty {
4202 rir::Ty::Boolean => Ok(VarTy::Boolean),
4203 rir::Ty::Integer => Ok(VarTy::Integer),
4204 rir::Ty::Double => Ok(VarTy::Double),
4205 _ => Err(()),
4206 }
4207}
4208
4209fn try_get_eval_var_type(value: &Value) -> Option<VarTy> {
4210 match value {
4211 Value::Bool(_) => Some(VarTy::Boolean),
4212 Value::Int(_) => Some(VarTy::Integer),
4213 Value::Double(_) => Some(VarTy::Double),
4214 Value::Var(var) => Some(var.ty),
4215 _ => None,
4216 }
4217}
4218