microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/qiskit2-explore

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/compiler/qsc/src/interpret.rs

1575lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod circuit_tests;
6mod debug;
7#[cfg(test)]
8mod debugger_tests;
9#[cfg(test)]
10mod package_tests;
11#[cfg(test)]
12mod tests;
13
14use std::{cell::RefCell, rc::Rc};
15
16pub use qsc_eval::{
17 StepAction, StepResult,
18 debug::Frame,
19 noise::PauliNoise,
20 output::{self, GenericReceiver},
21 val::Closure,
22 val::Range as ValueRange,
23 val::Result,
24 val::Value,
25};
26use qsc_hir::{global, ty};
27use qsc_linter::{HirLint, Lint, LintKind, LintLevel};
28use qsc_lowerer::{
29 map_fir_local_item_to_hir, map_fir_package_to_hir, map_hir_local_item_to_fir,
30 map_hir_package_to_fir,
31};
32use qsc_partial_eval::ProgramEntry;
33use qsc_rca::PackageStoreComputeProperties;
34
35use crate::{
36 error::{self, WithStack},
37 incremental::Compiler,
38 location::Location,
39};
40use debug::format_call_stack;
41use miette::Diagnostic;
42use num_bigint::BigUint;
43use num_complex::Complex;
44use qsc_circuit::{
45 Builder as CircuitBuilder, Circuit, Config as CircuitConfig,
46 operations::entry_expr_for_qubit_operation,
47};
48use qsc_codegen::qir::{fir_to_qir, fir_to_qir_from_callable};
49use qsc_data_structures::{
50 functors::FunctorApp,
51 language_features::LanguageFeatures,
52 line_column::{Encoding, Range},
53 span::Span,
54 target::TargetCapabilityFlags,
55};
56use qsc_eval::{
57 Env, ErrorBehavior, State, VariableInfo,
58 backend::{Backend, Chain as BackendChain, SparseSim},
59 output::Receiver,
60 val,
61};
62use qsc_fir::fir::{self, ExecGraph, Global, PackageStoreLookup};
63use qsc_fir::{
64 fir::{Block, BlockId, Expr, ExprId, Package, PackageId, Pat, PatId, Stmt, StmtId},
65 visit::{self, Visitor},
66};
67use qsc_frontend::{
68 compile::{CompileUnit, Dependencies, PackageStore, Source, SourceMap},
69 error::WithSource,
70 incremental::Increment,
71};
72use qsc_passes::{PackageType, PassContext};
73use rustc_hash::FxHashSet;
74use thiserror::Error;
75
76impl Error {
77 #[must_use]
78 pub fn stack_trace(&self) -> Option<&String> {
79 match &self {
80 Error::Eval(err) => err.stack_trace(),
81 _ => None,
82 }
83 }
84}
85
86#[derive(Clone, Debug, Diagnostic, Error)]
87pub enum Error {
88 #[error(transparent)]
89 #[diagnostic(transparent)]
90 Compile(#[from] crate::compile::Error),
91 #[error(transparent)]
92 #[diagnostic(transparent)]
93 Pass(#[from] WithSource<qsc_passes::Error>),
94 #[error("runtime error")]
95 #[diagnostic(transparent)]
96 Eval(#[from] WithStack<WithSource<qsc_eval::Error>>),
97 #[error("circuit error")]
98 #[diagnostic(transparent)]
99 Circuit(#[from] qsc_circuit::Error),
100 #[error("entry point not found")]
101 #[diagnostic(code("Qsc.Interpret.NoEntryPoint"))]
102 NoEntryPoint,
103 #[error("unsupported runtime capabilities for code generation")]
104 #[diagnostic(code("Qsc.Interpret.UnsupportedRuntimeCapabilities"))]
105 UnsupportedRuntimeCapabilities,
106 #[error("expression does not evaluate to an operation")]
107 #[diagnostic(code("Qsc.Interpret.NotAnOperation"))]
108 #[diagnostic(help("provide the name of a callable or a lambda expression"))]
109 NotAnOperation,
110 #[error("value is not a global callable")]
111 #[diagnostic(code("Qsc.Interpret.NotACallable"))]
112 NotACallable,
113 #[error("partial evaluation error")]
114 #[diagnostic(transparent)]
115 PartialEvaluation(#[from] WithSource<qsc_partial_eval::Error>),
116}
117
118/// A Q# interpreter.
119pub struct Interpreter {
120 /// The incremental Q# compiler.
121 compiler: Compiler,
122 /// The target capabilities used for compilation.
123 capabilities: TargetCapabilityFlags,
124 /// The number of lines that have so far been compiled.
125 /// This field is used to generate a unique label
126 /// for each line evaluated with `eval_fragments`.
127 lines: u32,
128 // The FIR store
129 fir_store: fir::PackageStore,
130 /// FIR lowerer
131 lowerer: qsc_lowerer::Lowerer,
132 /// The execution graph for the last expression evaluated.
133 expr_graph: Option<ExecGraph>,
134 /// Checking if an `ItemId` corresponds to the `Std.OpenQASM.Angle.Angle` UDT
135 /// is an expensive operation. So, we cache the id to avoid incurring that cost.
136 angle_ty_cache: RefCell<Option<crate::hir::ItemId>>,
137 /// Checking if an `ItemId` corresponds to the `Std.Math.Complex` UDT
138 /// is an expensive operation. So, we cache the id to avoid incurring that cost.
139 complex_ty_cache: RefCell<Option<crate::hir::ItemId>>,
140 /// The ID of the current package.
141 /// This ID is valid both for the FIR store and the `PackageStore`.
142 package: PackageId,
143 /// The ID of the source package. The source package
144 /// is made up of the initial sources passed in when creating the interpreter.
145 /// This ID is valid both for the FIR store and the `PackageStore`.
146 source_package: PackageId,
147 /// The default simulator backend.
148 sim: BackendChain<SparseSim, CircuitBuilder>,
149 /// The quantum seed, if any. This is cached here so that it can be used in calls to
150 /// `run_internal` which use a passed instance of the simulator instead of the one above.
151 quantum_seed: Option<u64>,
152 /// The classical seed, if any. This needs to be passed to the evaluator for use in intrinsic
153 /// calls that produce classical random numbers.
154 classical_seed: Option<u64>,
155 /// The evaluator environment.
156 env: Env,
157}
158
159pub type InterpretResult = std::result::Result<Value, Vec<Error>>;
160
161/// Indicates whether an UDT is an `OpenQASM` `Angle` or a `Complex` number.
162/// This information is needed in the Python interop layer to give special
163/// treatment to the instances of these UDTs.
164pub enum UdtKind {
165 /// `Std.OpenQASM.Angle.Angle`
166 Angle,
167 /// `Std.Math.Complex`
168 Complex,
169 /// A normal UDT, see the other variants for the special cases.
170 Udt,
171}
172
173/// An item tagged with its name and the namespace it was defined in.
174pub struct TaggedItem {
175 pub item_id: qsc_hir::hir::ItemId,
176 pub name: Rc<str>,
177 pub namespace: Vec<Rc<str>>,
178}
179
180impl Interpreter {
181 /// Creates a new incremental compiler, compiling the passed in sources.
182 /// # Errors
183 /// If compiling the sources fails, compiler errors are returned.
184 pub fn new(
185 sources: SourceMap,
186 package_type: PackageType,
187 capabilities: TargetCapabilityFlags,
188 language_features: LanguageFeatures,
189 store: PackageStore,
190 dependencies: &Dependencies,
191 ) -> std::result::Result<Self, Vec<Error>> {
192 Self::new_internal(
193 false,
194 sources,
195 package_type,
196 capabilities,
197 language_features,
198 store,
199 dependencies,
200 )
201 }
202
203 /// Creates a new incremental compiler with debugging stmts enabled, compiling the passed in sources.
204 /// # Errors
205 /// If compiling the sources fails, compiler errors are returned.
206 pub fn new_with_debug(
207 sources: SourceMap,
208 package_type: PackageType,
209 capabilities: TargetCapabilityFlags,
210 language_features: LanguageFeatures,
211 store: PackageStore,
212 dependencies: &Dependencies,
213 ) -> std::result::Result<Self, Vec<Error>> {
214 Self::new_internal(
215 true,
216 sources,
217 package_type,
218 capabilities,
219 language_features,
220 store,
221 dependencies,
222 )
223 }
224
225 fn new_internal(
226 dbg: bool,
227 sources: SourceMap,
228 package_type: PackageType,
229 capabilities: TargetCapabilityFlags,
230 language_features: LanguageFeatures,
231 store: PackageStore,
232 dependencies: &Dependencies,
233 ) -> std::result::Result<Self, Vec<Error>> {
234 let compiler = Compiler::new(
235 sources,
236 package_type,
237 capabilities,
238 language_features,
239 store,
240 dependencies,
241 )
242 .map_err(into_errors)?;
243
244 let mut fir_store = fir::PackageStore::new();
245 for (id, unit) in compiler.package_store() {
246 let pkg = qsc_lowerer::Lowerer::new()
247 .with_debug(dbg)
248 .lower_package(&unit.package, &fir_store);
249 fir_store.insert(map_hir_package_to_fir(id), pkg);
250 }
251
252 let source_package_id = compiler.source_package_id();
253 let package_id = compiler.package_id();
254
255 let package = map_hir_package_to_fir(package_id);
256 if capabilities != TargetCapabilityFlags::all() {
257 let _ = PassContext::run_fir_passes_on_fir(
258 &fir_store,
259 map_hir_package_to_fir(source_package_id),
260 capabilities,
261 )
262 .map_err(|caps_errors| {
263 let source_package = compiler
264 .package_store()
265 .get(source_package_id)
266 .expect("package should exist in the package store");
267
268 caps_errors
269 .into_iter()
270 .map(|error| Error::Pass(WithSource::from_map(&source_package.sources, error)))
271 .collect::<Vec<_>>()
272 })?;
273 }
274
275 Ok(Self {
276 compiler,
277 lines: 0,
278 capabilities,
279 fir_store,
280 lowerer: qsc_lowerer::Lowerer::new().with_debug(dbg),
281 expr_graph: None,
282 angle_ty_cache: None.into(),
283 complex_ty_cache: None.into(),
284 env: Env::default(),
285 sim: sim_circuit_backend(),
286 quantum_seed: None,
287 classical_seed: None,
288 package,
289 source_package: map_hir_package_to_fir(source_package_id),
290 })
291 }
292
293 pub fn from(
294 dbg: bool,
295 store: PackageStore,
296 source_package_id: qsc_hir::hir::PackageId,
297 capabilities: TargetCapabilityFlags,
298 language_features: LanguageFeatures,
299 dependencies: &Dependencies,
300 ) -> std::result::Result<Self, Vec<Error>> {
301 let compiler = Compiler::from(
302 store,
303 source_package_id,
304 capabilities,
305 language_features,
306 dependencies,
307 )
308 .map_err(into_errors)?;
309
310 let mut fir_store = fir::PackageStore::new();
311 for (id, unit) in compiler.package_store() {
312 let mut lowerer = qsc_lowerer::Lowerer::new().with_debug(dbg);
313 let pkg = lowerer.lower_package(&unit.package, &fir_store);
314 fir_store.insert(map_hir_package_to_fir(id), pkg);
315 }
316
317 let source_package_id = compiler.source_package_id();
318 let package_id = compiler.package_id();
319
320 let package = map_hir_package_to_fir(package_id);
321 if capabilities != TargetCapabilityFlags::all() {
322 let _ = PassContext::run_fir_passes_on_fir(
323 &fir_store,
324 map_hir_package_to_fir(source_package_id),
325 capabilities,
326 )
327 .map_err(|caps_errors| {
328 let source_package = compiler
329 .package_store()
330 .get(source_package_id)
331 .expect("package should exist in the package store");
332
333 caps_errors
334 .into_iter()
335 .map(|error| Error::Pass(WithSource::from_map(&source_package.sources, error)))
336 .collect::<Vec<_>>()
337 })?;
338 }
339
340 Ok(Self {
341 compiler,
342 lines: 0,
343 capabilities,
344 fir_store,
345 lowerer: qsc_lowerer::Lowerer::new().with_debug(dbg),
346 expr_graph: None,
347 angle_ty_cache: None.into(),
348 complex_ty_cache: None.into(),
349 env: Env::default(),
350 sim: sim_circuit_backend(),
351 quantum_seed: None,
352 classical_seed: None,
353 package,
354 source_package: map_hir_package_to_fir(source_package_id),
355 })
356 }
357
358 /// Given a package ID, returns all the global items in the package.
359 /// Note this does not currently include re-exports.
360 fn package_globals(&self, package_id: PackageId) -> Vec<(Vec<Rc<str>>, Rc<str>, Value)> {
361 let mut exported_items = Vec::new();
362 let package = &self
363 .compiler
364 .package_store()
365 .get(map_fir_package_to_hir(package_id))
366 .expect("package should exist in the package store")
367 .package;
368 for global in global::iter_package(Some(map_fir_package_to_hir(package_id)), package) {
369 if let global::Kind::Callable(term) = global.kind {
370 let store_item_id = fir::StoreItemId {
371 package: package_id,
372 item: fir::LocalItemId::from(usize::from(term.id.item)),
373 };
374 exported_items.push((
375 global.namespace,
376 global.name,
377 Value::Global(store_item_id, FunctorApp::default()),
378 ));
379 }
380 }
381 exported_items
382 }
383
384 /// Get the global callables defined in the user source passed into initialization of the interpreter as `Value` instances.
385 pub fn source_globals(&self) -> Vec<(Vec<Rc<str>>, Rc<str>, Value)> {
386 self.package_globals(self.source_package)
387 }
388
389 /// Get the global callables defined in the open package being interpreted as `Value` instances, which will include any items
390 /// defined by calls to `eval_fragments` and the like.
391 pub fn user_globals(&self) -> Vec<(Vec<Rc<str>>, Rc<str>, Value)> {
392 self.package_globals(self.package)
393 }
394
395 /// Get the input and output types of a given value representing a global item.
396 /// # Panics
397 /// Panics if the item is not callable or a type that can be invoked as a callable.
398 pub fn global_callable_ty(&self, item_id: &Value) -> Option<(ty::Ty, ty::Ty)> {
399 let Value::Global(item_id, _) = item_id else {
400 panic!("value is not a global callable");
401 };
402 let package_id = map_fir_package_to_hir(item_id.package);
403 let unit = self
404 .compiler
405 .package_store()
406 .get(package_id)
407 .expect("package should exist in the package store");
408 let item = unit
409 .package
410 .items
411 .get(qsc_hir::hir::LocalItemId::from(usize::from(item_id.item)))?;
412 match &item.kind {
413 qsc_hir::hir::ItemKind::Callable(decl) => {
414 Some((decl.input.ty.clone(), decl.output.clone()))
415 }
416 qsc_hir::hir::ItemKind::Ty(_, udt) => {
417 // We don't handle UDTs, so we return an error type that prevents later code from processing this item.
418 Some((udt.get_pure_ty(), ty::Ty::Err))
419 }
420 _ => panic!("item is not callable"),
421 }
422 }
423
424 /// Given a package ID, returns all the types in the package.
425 /// Note this does not currently include re-exports.
426 fn package_types(&self, package_id: PackageId) -> Vec<TaggedItem> {
427 let mut exported_items = Vec::new();
428 let package = &self
429 .compiler
430 .package_store()
431 .get(map_fir_package_to_hir(package_id))
432 .expect("package should exist in the package store")
433 .package;
434 for global in global::iter_package(Some(map_fir_package_to_hir(package_id)), package) {
435 if let global::Kind::Ty(ty) = global.kind {
436 exported_items.push(TaggedItem {
437 item_id: ty.id,
438 name: global.name,
439 namespace: global.namespace,
440 });
441 }
442 }
443 exported_items
444 }
445
446 /// Get the global UDTs defined in the user source passed into initialization of the interpreter.
447 pub fn source_types(&self) -> Vec<TaggedItem> {
448 self.package_types(self.source_package)
449 }
450
451 /// Get the global UDTs defined in the open package being interpreted, which will include any items
452 /// defined by calls to `eval_fragments` and the like.
453 pub fn user_types(&self) -> Vec<TaggedItem> {
454 self.package_types(self.package)
455 }
456
457 pub fn udt_ty_from_store_item_id(
458 &self,
459 store_item_id: crate::fir::StoreItemId,
460 ) -> (&ty::Udt, UdtKind) {
461 self.udt_ty_from_item_id(&crate::hir::ItemId {
462 package: Some(map_fir_package_to_hir(store_item_id.package)),
463 item: map_fir_local_item_to_hir(store_item_id.item),
464 })
465 }
466
467 /// Get the type of a UDT given its `item_id`.
468 /// # Panics
469 /// Panics if the item is not a UDT.
470 pub fn udt_ty_from_item_id(&self, item_id: &crate::hir::ItemId) -> (&ty::Udt, UdtKind) {
471 let crate::hir::ItemId {
472 package: package_id_opt,
473 item: local_item_id,
474 } = item_id;
475
476 let package_id = if let Some(package_id) = package_id_opt {
477 package_id
478 } else {
479 &self.compiler.package_id()
480 };
481
482 let unit = self
483 .compiler
484 .package_store()
485 .get(*package_id)
486 .expect("package should exist in the package store");
487
488 let item = unit
489 .package
490 .items
491 .get(*local_item_id)
492 .expect("item should be in this package");
493
494 let parent = item.parent.map(|parent| {
495 &unit
496 .package
497 .items
498 .get(parent)
499 .expect("parent should exist")
500 .kind
501 });
502
503 let qsc_hir::hir::ItemKind::Ty(_, udt) = &item.kind else {
504 panic!("item is not a UDT")
505 };
506
507 let kind = if let Some(id) = &*self.angle_ty_cache.borrow()
508 && id == item_id
509 {
510 UdtKind::Angle
511 } else if let Some(id) = &*self.complex_ty_cache.borrow()
512 && id == item_id
513 {
514 UdtKind::Complex
515 } else if let Some(qsc_hir::hir::ItemKind::Namespace(namespace, _)) = parent {
516 let namespace: Vec<_> = namespace.into();
517 let namespace: Vec<&str> = namespace.iter().map(|ident| &**ident).collect();
518 if matches!(&namespace[..], &["Std", "OpenQASM", "Angle"]) && &*udt.name == "Angle" {
519 *self.angle_ty_cache.borrow_mut() = Some(*item_id);
520 UdtKind::Angle
521 } else if matches!(&namespace[..], &["Std", "Math"]) && &*udt.name == "Complex" {
522 *self.complex_ty_cache.borrow_mut() = Some(*item_id);
523 UdtKind::Complex
524 } else {
525 UdtKind::Udt
526 }
527 } else {
528 UdtKind::Udt
529 };
530
531 (udt, kind)
532 }
533
534 /// Returns the [`fir::StoreItemId`] for the `Std.OpenQASM.Angle.Angle` UDT.
535 ///
536 /// This function intended to be used from
537 /// `source/pip/src/interpreter/data_interop.rs::pyobj_to_value`
538 /// to tag the angles coming from Python with the correct `StoreItemId`.
539 pub fn get_angle_id(&self) -> fir::StoreItemId {
540 if let Some(id) = &*self.angle_ty_cache.borrow() {
541 let crate::hir::ItemId {
542 package: hir_package_id_opt,
543 item: hir_local_item_id,
544 } = id;
545 let package_id = if let Some(package_id) = hir_package_id_opt {
546 package_id
547 } else {
548 &self.compiler.package_id()
549 };
550 let fir_package_id = map_hir_package_to_fir(*package_id);
551 let fir_local_item_id = map_hir_local_item_to_fir(*hir_local_item_id);
552 crate::fir::StoreItemId {
553 package: fir_package_id,
554 item: fir_local_item_id,
555 }
556 } else {
557 // SAFETY: This function is intended to be used when receiving Python objects
558 // in the interop layer. The only way to send a Python object to Q# is
559 // as the argument of a function call. When performing type checking
560 // for this function call in the interop layer, there are two cases:
561 //
562 // 1. The input type is not `Std.OpenQASM.Angle.Angle` and we return
563 // an error.
564 // 2. The input type is `Std.OpenQASM.Angle.Angle`. To verify that
565 // the input type is indeed `Angle`, we call `udt_ty_from_item_id`,
566 // which caches the `Angle` UDT's `LocalItemId`.
567 //
568 // So, if we proceed to execute the function's body, it's guaranteed
569 // that we have already cached `Std.OpenQASM.Angle.Angle`'s `LocalItemId`.
570 // Therefore, this else-branch is unreachable.
571 unreachable!("`self.angle_ty_cache` should be set by `udt_ty_from_item_id`")
572 }
573 }
574
575 /// Returns the [`fir::StoreItemId`] for the `Std.Math.Complex` UDT.
576 ///
577 /// This function intended to be used from
578 /// `source/pip/src/interpreter/data_interop.rs::pyobj_to_value`
579 /// to tag the complex numbers coming from Python with the correct
580 /// `StoreItemId`.
581 pub fn get_complex_id(&self) -> crate::fir::StoreItemId {
582 if let Some(id) = &*self.complex_ty_cache.borrow() {
583 let crate::hir::ItemId {
584 package: hir_package_id_opt,
585 item: hir_local_item_id,
586 } = id;
587
588 let package_id = if let Some(package_id) = hir_package_id_opt {
589 package_id
590 } else {
591 &self.compiler.package_id()
592 };
593
594 let fir_package_id = map_hir_package_to_fir(*package_id);
595 let fir_local_item_id = map_hir_local_item_to_fir(*hir_local_item_id);
596
597 crate::fir::StoreItemId {
598 package: fir_package_id,
599 item: fir_local_item_id,
600 }
601 } else {
602 // SAFETY: This function is intended to be used when receiving Python objects
603 // in the interop layer. The only way to send a Python object to Q# is
604 // as the argument of a function call. When performing type checking
605 // for this function call in the interop layer, there are two cases:
606 //
607 // 1. The input type is not `Std.Math.Complex` and we return an error.
608 // 2. The input type is `Std.Math.Complex`. To verify that the input
609 // type is indeed `Complex`, we call `udt_ty_from_item_id`, which
610 // caches the `Complex` UDT's `LocalItemId`.
611 //
612 // So, if we proceed to execute the function's body, it's guaranteed
613 // that we have already cached `Std.Math.Complex`'s `LocalItemId`.
614 // Therefore, this else-branch is unreachable.
615 unreachable!("`self.complex_ty_cache` should be set by `udt_ty_from_item_id`")
616 }
617 }
618
619 pub fn set_quantum_seed(&mut self, seed: Option<u64>) {
620 self.quantum_seed = seed;
621 self.sim.set_seed(seed);
622 }
623
624 pub fn set_classical_seed(&mut self, seed: Option<u64>) {
625 self.classical_seed = seed;
626 }
627
628 pub fn check_source_lints(&self) -> Vec<Lint> {
629 if let Some(compile_unit) = self
630 .compiler
631 .package_store()
632 .get(self.compiler.source_package_id())
633 {
634 qsc_linter::run_lints(
635 self.compiler.package_store(),
636 compile_unit,
637 // see https://github.com/microsoft/qsharp/pull/1627 for context
638 // on why we override this config
639 Some(&[qsc_linter::LintOrGroupConfig::Lint(
640 qsc_linter::LintConfig {
641 kind: LintKind::Hir(HirLint::NeedlessOperation),
642 level: LintLevel::Warn,
643 },
644 )]),
645 )
646 } else {
647 Vec::new()
648 }
649 }
650
651 /// Executes the entry expression until the end of execution.
652 /// # Errors
653 /// Returns a vector of errors if evaluating the entry point fails.
654 pub fn eval_entry(&mut self, receiver: &mut impl Receiver) -> InterpretResult {
655 let graph = self.get_entry_exec_graph()?;
656 self.expr_graph = Some(graph.clone());
657 eval(
658 self.source_package,
659 self.classical_seed,
660 graph,
661 self.compiler.package_store(),
662 &self.fir_store,
663 &mut Env::default(),
664 &mut self.sim,
665 receiver,
666 )
667 }
668
669 /// Executes the entry expression until the end of execution, using the given simulator backend
670 /// and a new instance of the environment.
671 pub fn eval_entry_with_sim(
672 &mut self,
673 sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
674 receiver: &mut impl Receiver,
675 ) -> InterpretResult {
676 let graph = self.get_entry_exec_graph()?;
677 self.expr_graph = Some(graph.clone());
678 if self.quantum_seed.is_some() {
679 sim.set_seed(self.quantum_seed);
680 }
681 eval(
682 self.source_package,
683 self.classical_seed,
684 graph,
685 self.compiler.package_store(),
686 &self.fir_store,
687 &mut Env::default(),
688 sim,
689 receiver,
690 )
691 }
692
693 fn get_entry_exec_graph(&self) -> std::result::Result<ExecGraph, Vec<Error>> {
694 let unit = self.fir_store.get(self.source_package);
695 if unit.entry.is_some() {
696 return Ok(unit.entry_exec_graph.clone());
697 }
698 Err(vec![Error::NoEntryPoint])
699 }
700
701 /// # Errors
702 /// If the parsing of the fragments fails, an error is returned.
703 /// If the compilation of the fragments fails, an error is returned.
704 /// If there is a runtime error when interpreting the fragments, an error is returned.
705 pub fn eval_fragments(
706 &mut self,
707 receiver: &mut impl Receiver,
708 fragments: &str,
709 ) -> InterpretResult {
710 let label = self.next_line_label();
711
712 let mut increment = self
713 .compiler
714 .compile_fragments_fail_fast(&label, fragments)
715 .map_err(into_errors)?;
716
717 // Clear the entry expression, as we are evaluating fragments and a fragment with a `@EntryPoint` attribute
718 // should not change what gets executed.
719 increment.clear_entry();
720
721 self.eval_increment(receiver, increment)
722 }
723
724 /// It is assumed that if there were any parse errors on the fragments, the caller would have
725 /// already handled them. This function is intended to be used in cases where the caller wants
726 /// to handle the parse errors themselves.
727 /// # Errors
728 /// If the compilation of the fragments fails, an error is returned.
729 /// If there is a runtime error when interpreting the fragments, an error is returned.
730 pub fn eval_ast_fragments(
731 &mut self,
732 receiver: &mut impl Receiver,
733 fragments: &str,
734 package: qsc_ast::ast::Package,
735 ) -> InterpretResult {
736 let label = self.next_line_label();
737
738 let increment = self
739 .compiler
740 .compile_ast_fragments_fail_fast(&label, fragments, package)
741 .map_err(into_errors)?;
742
743 self.eval_increment(receiver, increment)
744 }
745
746 fn eval_increment(
747 &mut self,
748 receiver: &mut impl Receiver,
749 increment: Increment,
750 ) -> InterpretResult {
751 let (graph, _) = self.lower(&increment)?;
752 self.expr_graph = Some(graph.clone());
753
754 // Updating the compiler state with the new AST/HIR nodes
755 // is not necessary for the interpreter to function, as all
756 // the state required for evaluation already exists in the
757 // FIR store. It could potentially save some memory
758 // *not* to do hold on to the AST/HIR, but it is done
759 // here to keep the package stores consistent.
760 self.compiler.update(increment);
761
762 eval(
763 self.package,
764 self.classical_seed,
765 graph,
766 self.compiler.package_store(),
767 &self.fir_store,
768 &mut self.env,
769 &mut self.sim,
770 receiver,
771 )
772 }
773
774 /// Invokes the given callable with the given arguments using the current environment, simlator, and compilation.
775 pub fn invoke(
776 &mut self,
777 receiver: &mut impl Receiver,
778 callable: Value,
779 args: Value,
780 ) -> InterpretResult {
781 qsc_eval::invoke(
782 self.package,
783 self.classical_seed,
784 &self.fir_store,
785 &mut self.env,
786 &mut self.sim,
787 receiver,
788 callable,
789 args,
790 )
791 .map_err(|(error, call_stack)| {
792 eval_error(
793 self.compiler.package_store(),
794 &self.fir_store,
795 call_stack,
796 error,
797 )
798 })
799 }
800
801 // Invokes the given callable with the given arguments using the current compilation but with a fresh
802 // environment and simulator configured with the given noise, if any.
803 pub fn invoke_with_noise(
804 &mut self,
805 receiver: &mut impl Receiver,
806 callable: Value,
807 args: Value,
808 noise: Option<PauliNoise>,
809 qubit_loss: Option<f64>,
810 ) -> InterpretResult {
811 let mut sim = match noise {
812 Some(noise) => SparseSim::new_with_noise(&noise),
813 None => SparseSim::new(),
814 };
815 if let Some(loss) = qubit_loss {
816 sim.set_loss(loss);
817 }
818 self.invoke_with_sim(&mut sim, receiver, callable, args)
819 }
820
821 /// Runs the given entry expression on a new instance of the environment and simulator,
822 /// but using the current compilation.
823 pub fn run(
824 &mut self,
825 receiver: &mut impl Receiver,
826 expr: Option<&str>,
827 noise: Option<PauliNoise>,
828 qubit_loss: Option<f64>,
829 ) -> InterpretResult {
830 let mut sim = match noise {
831 Some(noise) => SparseSim::new_with_noise(&noise),
832 None => SparseSim::new(),
833 };
834 if let Some(loss) = qubit_loss {
835 sim.set_loss(loss);
836 }
837 self.run_with_sim(&mut sim, receiver, expr)
838 }
839
840 /// Gets the current quantum state of the simulator.
841 pub fn get_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
842 self.sim.capture_quantum_state()
843 }
844
845 /// Get the current circuit representation of the program.
846 pub fn get_circuit(&self) -> Circuit {
847 self.sim.chained.snapshot()
848 }
849
850 /// Performs QIR codegen using the given entry expression on a new instance of the environment
851 /// and simulator but using the current compilation.
852 pub fn qirgen(&mut self, expr: &str) -> std::result::Result<String, Vec<Error>> {
853 if self.capabilities == TargetCapabilityFlags::all() {
854 return Err(vec![Error::UnsupportedRuntimeCapabilities]);
855 }
856
857 // Compile the expression. This operation will set the expression as
858 // the entry-point in the FIR store.
859 let (graph, compute_properties) = self.compile_entry_expr(expr)?;
860
861 let Some(compute_properties) = compute_properties else {
862 // This can only happen if capability analysis was not run. This would be a bug
863 // and we are in a bad state and can't proceed.
864 panic!("internal error: compute properties not set after lowering entry expression");
865 };
866 let package = self.fir_store.get(self.package);
867 let entry = ProgramEntry {
868 exec_graph: graph,
869 expr: (
870 self.package,
871 package
872 .entry
873 .expect("package must have an entry expression"),
874 )
875 .into(),
876 };
877 // Generate QIR
878 fir_to_qir(
879 &self.fir_store,
880 self.capabilities,
881 Some(compute_properties),
882 &entry,
883 )
884 .map_err(|e| {
885 let hir_package_id = match e.span() {
886 Some(span) => span.package,
887 None => map_fir_package_to_hir(self.package),
888 };
889 let source_package = self
890 .compiler
891 .package_store()
892 .get(hir_package_id)
893 .expect("package should exist in the package store");
894 vec![Error::PartialEvaluation(WithSource::from_map(
895 &source_package.sources,
896 e,
897 ))]
898 })
899 }
900
901 /// Performs QIR codegen using the given callable with the given arguments on a new instance of the environment
902 /// and simulator but using the current compilation.
903 pub fn qirgen_from_callable(
904 &mut self,
905 callable: &Value,
906 args: Value,
907 ) -> std::result::Result<String, Vec<Error>> {
908 if self.capabilities == TargetCapabilityFlags::all() {
909 return Err(vec![Error::UnsupportedRuntimeCapabilities]);
910 }
911
912 let Value::Global(store_item_id, _) = callable else {
913 return Err(vec![Error::NotACallable]);
914 };
915
916 fir_to_qir_from_callable(
917 &self.fir_store,
918 self.capabilities,
919 None,
920 *store_item_id,
921 args,
922 )
923 .map_err(|e| {
924 let hir_package_id = match e.span() {
925 Some(span) => span.package,
926 None => map_fir_package_to_hir(self.package),
927 };
928 let source_package = self
929 .compiler
930 .package_store()
931 .get(hir_package_id)
932 .expect("package should exist in the package store");
933 vec![Error::PartialEvaluation(WithSource::from_map(
934 &source_package.sources,
935 e,
936 ))]
937 })
938 }
939
940 /// Generates a circuit representation for the program.
941 ///
942 /// `entry` can be the current entrypoint, an entry expression, or any operation
943 /// that takes qubits.
944 ///
945 /// An operation can be specified by its name or a lambda expression that only takes qubits.
946 /// e.g. `Sample.Main` , `qs => H(qs[0])`
947 ///
948 /// If `simulate` is specified, the program is simulated and the resulting
949 /// circuit is returned (a.k.a. trace mode). Otherwise, the circuit is generated without
950 /// simulation. In this case circuit generation may fail if the program contains dynamic
951 /// behavior (quantum operations that are dependent on measurement results).
952 pub fn circuit(
953 &mut self,
954 entry: CircuitEntryPoint,
955 simulate: bool,
956 ) -> std::result::Result<Circuit, Vec<Error>> {
957 let (entry_expr, invoke_params) = match entry {
958 CircuitEntryPoint::Operation(operation_expr) => {
959 let (item, functor_app) = self.eval_to_operation(&operation_expr)?;
960 let expr = entry_expr_for_qubit_operation(item, functor_app, &operation_expr)
961 .map_err(|e| vec![e.into()])?;
962 (Some(expr), None)
963 }
964 CircuitEntryPoint::EntryExpr(expr) => (Some(expr), None),
965 CircuitEntryPoint::Callable(call_val, args_val) => (None, Some((call_val, args_val))),
966 CircuitEntryPoint::EntryPoint => (None, None),
967 };
968
969 let circuit = if simulate {
970 let mut sim = sim_circuit_backend();
971
972 match invoke_params {
973 Some((callable, args)) => {
974 let mut sink = std::io::sink();
975 let mut out = GenericReceiver::new(&mut sink);
976
977 self.invoke_with_sim(&mut sim, &mut out, callable, args)?;
978 }
979 None => self.run_with_sim_no_output(entry_expr, &mut sim)?,
980 }
981
982 sim.chained.finish()
983 } else {
984 let mut sim = CircuitBuilder::new(CircuitConfig {
985 max_operations: CircuitConfig::DEFAULT_MAX_OPERATIONS,
986 });
987
988 match invoke_params {
989 Some((callable, args)) => {
990 let mut sink = std::io::sink();
991 let mut out = GenericReceiver::new(&mut sink);
992
993 self.invoke_with_sim(&mut sim, &mut out, callable, args)?;
994 }
995 None => self.run_with_sim_no_output(entry_expr, &mut sim)?,
996 }
997
998 sim.finish()
999 };
1000
1001 Ok(circuit)
1002 }
1003
1004 /// Sets the entry expression for the interpreter.
1005 pub fn set_entry_expr(&mut self, entry_expr: &str) -> std::result::Result<(), Vec<Error>> {
1006 let (graph, _) = self.compile_entry_expr(entry_expr)?;
1007 self.expr_graph = Some(graph);
1008 Ok(())
1009 }
1010
1011 /// Runs the given entry expression on the given simulator with a new instance of the environment
1012 /// but using the current compilation.
1013 pub fn run_with_sim(
1014 &mut self,
1015 sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
1016 receiver: &mut impl Receiver,
1017 expr: Option<&str>,
1018 ) -> InterpretResult {
1019 let graph = if let Some(expr) = expr {
1020 let (graph, _) = self.compile_entry_expr(expr)?;
1021 self.expr_graph = Some(graph.clone());
1022 graph
1023 } else {
1024 self.expr_graph.clone().ok_or(vec![Error::NoEntryPoint])?
1025 };
1026
1027 if self.quantum_seed.is_some() {
1028 sim.set_seed(self.quantum_seed);
1029 }
1030
1031 eval(
1032 self.package,
1033 self.classical_seed,
1034 graph,
1035 self.compiler.package_store(),
1036 &self.fir_store,
1037 &mut Env::default(),
1038 sim,
1039 receiver,
1040 )
1041 }
1042
1043 fn run_with_sim_no_output(
1044 &mut self,
1045 entry_expr: Option<String>,
1046 sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
1047 ) -> std::result::Result<(), Vec<Error>> {
1048 let mut sink = std::io::sink();
1049 let mut out = GenericReceiver::new(&mut sink);
1050
1051 let (package_id, graph) = if let Some(entry_expr) = entry_expr {
1052 // entry expression is provided
1053 let (graph, _) = self.compile_entry_expr(&entry_expr)?;
1054 (self.package, graph)
1055 } else {
1056 // no entry expression, use the entrypoint in the package
1057 (self.source_package, self.get_entry_exec_graph()?)
1058 };
1059
1060 if self.quantum_seed.is_some() {
1061 sim.set_seed(self.quantum_seed);
1062 }
1063
1064 eval(
1065 package_id,
1066 self.classical_seed,
1067 graph,
1068 self.compiler.package_store(),
1069 &self.fir_store,
1070 &mut Env::default(),
1071 sim,
1072 &mut out,
1073 )?;
1074
1075 Ok(())
1076 }
1077
1078 /// Invokes the given callable with the given arguments on the given simulator with a new instance of the environment
1079 /// but using the current compilation.
1080 pub fn invoke_with_sim(
1081 &mut self,
1082 sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
1083 receiver: &mut impl Receiver,
1084 callable: Value,
1085 args: Value,
1086 ) -> InterpretResult {
1087 qsc_eval::invoke(
1088 self.package,
1089 self.classical_seed,
1090 &self.fir_store,
1091 &mut Env::default(),
1092 sim,
1093 receiver,
1094 callable,
1095 args,
1096 )
1097 .map_err(|(error, call_stack)| {
1098 eval_error(
1099 self.compiler.package_store(),
1100 &self.fir_store,
1101 call_stack,
1102 error,
1103 )
1104 })
1105 }
1106
1107 fn compile_entry_expr(
1108 &mut self,
1109 expr: &str,
1110 ) -> std::result::Result<(ExecGraph, Option<PackageStoreComputeProperties>), Vec<Error>> {
1111 let increment = self
1112 .compiler
1113 .compile_entry_expr(expr)
1114 .map_err(into_errors)?;
1115
1116 // `lower` will update the entry expression in the FIR store,
1117 // and it will always return an empty list of statements.
1118 let (graph, compute_properties) = self.lower(&increment)?;
1119
1120 // The AST and HIR packages in `increment` only contain an entry
1121 // expression and no statements. The HIR *can* contain items if the entry
1122 // expression defined any items.
1123 assert!(increment.hir.stmts.is_empty());
1124 assert!(increment.ast.package.nodes.is_empty());
1125
1126 // Updating the compiler state with the new AST/HIR nodes
1127 // is not necessary for the interpreter to function, as all
1128 // the state required for evaluation already exists in the
1129 // FIR store. It could potentially save some memory
1130 // *not* to do hold on to the AST/HIR, but it is done
1131 // here to keep the package stores consistent.
1132 self.compiler.update(increment);
1133
1134 Ok((graph, compute_properties))
1135 }
1136
1137 fn lower(
1138 &mut self,
1139 unit_addition: &qsc_frontend::incremental::Increment,
1140 ) -> core::result::Result<(ExecGraph, Option<PackageStoreComputeProperties>), Vec<Error>> {
1141 if self.capabilities != TargetCapabilityFlags::all() {
1142 return self.run_fir_passes(unit_addition);
1143 }
1144
1145 self.lower_and_update_package(unit_addition);
1146 Ok((self.lowerer.take_exec_graph().into(), None))
1147 }
1148
1149 fn lower_and_update_package(&mut self, unit: &qsc_frontend::incremental::Increment) {
1150 {
1151 let fir_package = self.fir_store.get_mut(self.package);
1152 self.lowerer
1153 .lower_and_update_package(fir_package, &unit.hir);
1154 }
1155 let fir_package: &Package = self.fir_store.get(self.package);
1156 qsc_fir::validate::validate(fir_package, &self.fir_store);
1157 }
1158
1159 fn run_fir_passes(
1160 &mut self,
1161 unit: &qsc_frontend::incremental::Increment,
1162 ) -> std::result::Result<(ExecGraph, Option<PackageStoreComputeProperties>), Vec<Error>> {
1163 self.lower_and_update_package(unit);
1164
1165 let cap_results =
1166 PassContext::run_fir_passes_on_fir(&self.fir_store, self.package, self.capabilities);
1167
1168 let compute_properties = cap_results.map_err(|caps_errors| {
1169 // if there are errors, convert them to interpreter errors
1170 // and revert the update to the lowerer/FIR store.
1171 let fir_package = self.fir_store.get_mut(self.package);
1172 self.lowerer.revert_last_increment(fir_package);
1173
1174 let source_package = self
1175 .compiler
1176 .package_store()
1177 .get(map_fir_package_to_hir(self.package))
1178 .expect("package should exist in the package store");
1179
1180 caps_errors
1181 .into_iter()
1182 .map(|error| Error::Pass(WithSource::from_map(&source_package.sources, error)))
1183 .collect::<Vec<_>>()
1184 })?;
1185
1186 let graph = self.lowerer.take_exec_graph();
1187 Ok((graph.into(), Some(compute_properties)))
1188 }
1189
1190 fn next_line_label(&mut self) -> String {
1191 let label = format!("line_{}", self.lines);
1192 self.lines += 1;
1193 label
1194 }
1195
1196 /// Evaluate the name of an operation, or any expression that evaluates to a callable,
1197 /// and return the Item ID and function application for the callable.
1198 /// Examples: "Microsoft.Quantum.Diagnostics.DumpMachine", "(qs: Qubit[]) => H(qs[0])",
1199 /// "Controlled SWAP"
1200 fn eval_to_operation(
1201 &mut self,
1202 operation_expr: &str,
1203 ) -> std::result::Result<(&qsc_hir::hir::Item, FunctorApp), Vec<Error>> {
1204 let mut sink = std::io::sink();
1205 let mut out = GenericReceiver::new(&mut sink);
1206 let (store_item_id, functor_app) = match self.eval_fragments(&mut out, operation_expr)? {
1207 Value::Closure(b) => (b.id, b.functor),
1208 Value::Global(item_id, functor_app) => (item_id, functor_app),
1209 _ => return Err(vec![Error::NotAnOperation]),
1210 };
1211 let package = map_fir_package_to_hir(store_item_id.package);
1212 let local_item_id = crate::hir::LocalItemId::from(usize::from(store_item_id.item));
1213 let unit = self
1214 .compiler
1215 .package_store()
1216 .get(package)
1217 .expect("package should exist in the package store");
1218 let item = unit
1219 .package
1220 .items
1221 .get(local_item_id)
1222 .expect("item should exist in the package");
1223 Ok((item, functor_app))
1224 }
1225}
1226
1227fn sim_circuit_backend() -> BackendChain<SparseSim, CircuitBuilder> {
1228 BackendChain::new(
1229 SparseSim::new(),
1230 CircuitBuilder::new(CircuitConfig {
1231 max_operations: CircuitConfig::DEFAULT_MAX_OPERATIONS,
1232 }),
1233 )
1234}
1235
1236/// Describes the entry point for circuit generation.
1237pub enum CircuitEntryPoint {
1238 /// An operation. This must be a callable name or a lambda
1239 /// expression that only takes qubits as arguments.
1240 /// The callable name must be visible in the current package.
1241 Operation(String),
1242 /// An explicitly provided entry expression.
1243 EntryExpr(String),
1244 /// A global callable with arguments.
1245 Callable(Value, Value),
1246 /// The entry point for the current package.
1247 EntryPoint,
1248}
1249
1250/// A debugger that enables step-by-step evaluation of code
1251/// and inspecting state in the interpreter.
1252pub struct Debugger {
1253 interpreter: Interpreter,
1254 /// The encoding (utf-8 or utf-16) used for character offsets
1255 /// in line/character positions returned by the Interpreter.
1256 position_encoding: Encoding,
1257 /// The current state of the evaluator.
1258 state: State,
1259}
1260
1261impl Debugger {
1262 pub fn new(
1263 sources: SourceMap,
1264 capabilities: TargetCapabilityFlags,
1265 position_encoding: Encoding,
1266 language_features: LanguageFeatures,
1267 store: PackageStore,
1268 dependencies: &Dependencies,
1269 ) -> std::result::Result<Self, Vec<Error>> {
1270 let interpreter = Interpreter::new_with_debug(
1271 sources,
1272 PackageType::Exe,
1273 capabilities,
1274 language_features,
1275 store,
1276 dependencies,
1277 )?;
1278 let source_package_id = interpreter.source_package;
1279 let unit = interpreter.fir_store.get(source_package_id);
1280 let entry_exec_graph = unit.entry_exec_graph.clone();
1281 Ok(Self {
1282 interpreter,
1283 position_encoding,
1284 state: State::new(
1285 source_package_id,
1286 entry_exec_graph,
1287 None,
1288 ErrorBehavior::StopOnError,
1289 ),
1290 })
1291 }
1292
1293 pub fn from(interpreter: Interpreter, position_encoding: Encoding) -> Self {
1294 let source_package_id = interpreter.source_package;
1295 let unit = interpreter.fir_store.get(source_package_id);
1296 let entry_exec_graph = unit.entry_exec_graph.clone();
1297 Self {
1298 interpreter,
1299 position_encoding,
1300 state: State::new(
1301 source_package_id,
1302 entry_exec_graph,
1303 None,
1304 ErrorBehavior::StopOnError,
1305 ),
1306 }
1307 }
1308
1309 /// Resumes execution with specified `StepAction`.
1310 /// # Errors
1311 /// Returns a vector of errors if evaluating the entry point fails.
1312 pub fn eval_step(
1313 &mut self,
1314 receiver: &mut impl Receiver,
1315 breakpoints: &[StmtId],
1316 step: StepAction,
1317 ) -> std::result::Result<StepResult, Vec<Error>> {
1318 self.state
1319 .eval(
1320 &self.interpreter.fir_store,
1321 &mut self.interpreter.env,
1322 &mut self.interpreter.sim,
1323 receiver,
1324 breakpoints,
1325 step,
1326 )
1327 .map_err(|(error, call_stack)| {
1328 eval_error(
1329 self.interpreter.compiler.package_store(),
1330 &self.interpreter.fir_store,
1331 call_stack,
1332 error,
1333 )
1334 })
1335 }
1336
1337 #[must_use]
1338 pub fn get_stack_frames(&self) -> Vec<StackFrame> {
1339 let frames = self.state.get_stack_frames();
1340
1341 frames
1342 .iter()
1343 .map(|frame| {
1344 let callable = self
1345 .interpreter
1346 .fir_store
1347 .get_global(frame.id)
1348 .expect("frame should exist");
1349 let functor = format!("{}", frame.functor);
1350 let name = match callable {
1351 Global::Callable(decl) => decl.name.name.to_string(),
1352 Global::Udt => "udt".into(),
1353 };
1354
1355 StackFrame {
1356 name,
1357 functor,
1358 location: Location::from(
1359 frame.span,
1360 map_fir_package_to_hir(frame.id.package),
1361 self.interpreter.compiler.package_store(),
1362 self.position_encoding,
1363 ),
1364 }
1365 })
1366 .collect()
1367 }
1368
1369 pub fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
1370 self.interpreter.sim.capture_quantum_state()
1371 }
1372
1373 pub fn circuit(&self) -> Circuit {
1374 self.interpreter.get_circuit()
1375 }
1376
1377 #[must_use]
1378 pub fn get_breakpoints(&self, path: &str) -> Vec<BreakpointSpan> {
1379 let unit = self.source_package();
1380
1381 if let Some(source) = unit.sources.find_by_name(path) {
1382 let package = self
1383 .interpreter
1384 .fir_store
1385 .get(self.interpreter.source_package);
1386 let mut collector = BreakpointCollector::new(
1387 &unit.sources,
1388 source.offset,
1389 package,
1390 self.position_encoding,
1391 );
1392 collector.visit_package(package, &self.interpreter.fir_store);
1393 let mut spans: Vec<_> = collector.statements.into_iter().collect();
1394
1395 // Sort by start position (line first, column next)
1396 spans.sort_by_key(|s| (s.range.start.line, s.range.start.column));
1397 spans
1398 } else {
1399 Vec::new()
1400 }
1401 }
1402
1403 #[must_use]
1404 pub fn get_locals(&self, frame_id: usize) -> Vec<VariableInfo> {
1405 self.interpreter
1406 .env
1407 .get_variables_in_frame(frame_id)
1408 .into_iter()
1409 .filter(|v| !v.name.starts_with('@'))
1410 .collect()
1411 }
1412
1413 fn source_package(&self) -> &CompileUnit {
1414 self.interpreter
1415 .compiler
1416 .package_store()
1417 .get(map_fir_package_to_hir(self.interpreter.source_package))
1418 .expect("Could not load package")
1419 }
1420}
1421
1422/// Wrapper function for `qsc_eval::eval` that handles error conversion.
1423#[allow(clippy::too_many_arguments)]
1424fn eval(
1425 package: PackageId,
1426 classical_seed: Option<u64>,
1427 exec_graph: ExecGraph,
1428 package_store: &PackageStore,
1429 fir_store: &fir::PackageStore,
1430 env: &mut Env,
1431 sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
1432 receiver: &mut impl Receiver,
1433) -> InterpretResult {
1434 qsc_eval::eval(
1435 package,
1436 classical_seed,
1437 exec_graph,
1438 fir_store,
1439 env,
1440 sim,
1441 receiver,
1442 )
1443 .map_err(|(error, call_stack)| eval_error(package_store, fir_store, call_stack, error))
1444}
1445
1446/// Represents a stack frame for debugging.
1447pub struct StackFrame {
1448 /// The name of the callable.
1449 pub name: String,
1450 /// The functor of the callable.
1451 pub functor: String,
1452 /// The source location of the call site.
1453 pub location: Location,
1454}
1455
1456#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1457pub struct BreakpointSpan {
1458 /// The id of the statement representing the breakpoint location.
1459 pub id: u32,
1460 /// The source range of the call site.
1461 pub range: Range,
1462}
1463
1464struct BreakpointCollector<'a> {
1465 statements: FxHashSet<BreakpointSpan>,
1466 sources: &'a SourceMap,
1467 offset: u32,
1468 package: &'a Package,
1469 position_encoding: Encoding,
1470}
1471
1472impl<'a> BreakpointCollector<'a> {
1473 fn new(
1474 sources: &'a SourceMap,
1475 offset: u32,
1476 package: &'a Package,
1477 position_encoding: Encoding,
1478 ) -> Self {
1479 Self {
1480 statements: FxHashSet::default(),
1481 sources,
1482 offset,
1483 package,
1484 position_encoding,
1485 }
1486 }
1487
1488 fn get_source(&self, offset: u32) -> &Source {
1489 self.sources
1490 .find_by_offset(offset)
1491 .expect("Couldn't find source file")
1492 }
1493
1494 fn add_stmt(&mut self, stmt: &fir::Stmt) {
1495 let source: &Source = self.get_source(stmt.span.lo);
1496 if source.offset == self.offset {
1497 let span = stmt.span - source.offset;
1498 if span != Span::default() {
1499 let bps = BreakpointSpan {
1500 id: stmt.id.into(),
1501 range: Range::from_span(self.position_encoding, &source.contents, &span),
1502 };
1503 self.statements.insert(bps);
1504 }
1505 }
1506 }
1507}
1508
1509impl<'a> Visitor<'a> for BreakpointCollector<'a> {
1510 fn visit_stmt(&mut self, stmt: StmtId) {
1511 let stmt_res = self.get_stmt(stmt);
1512 match stmt_res.kind {
1513 fir::StmtKind::Expr(expr) | fir::StmtKind::Local(_, _, expr) => {
1514 self.add_stmt(stmt_res);
1515 visit::walk_expr(self, expr);
1516 }
1517 fir::StmtKind::Item(_) | fir::StmtKind::Semi(_) => {
1518 self.add_stmt(stmt_res);
1519 }
1520 }
1521 }
1522
1523 fn get_block(&self, id: BlockId) -> &'a Block {
1524 self.package
1525 .blocks
1526 .get(id)
1527 .expect("couldn't find block in FIR")
1528 }
1529
1530 fn get_expr(&self, id: ExprId) -> &'a Expr {
1531 self.package
1532 .exprs
1533 .get(id)
1534 .expect("couldn't find expr in FIR")
1535 }
1536
1537 fn get_pat(&self, id: PatId) -> &'a Pat {
1538 self.package.pats.get(id).expect("couldn't find pat in FIR")
1539 }
1540
1541 fn get_stmt(&self, id: StmtId) -> &'a Stmt {
1542 self.package
1543 .stmts
1544 .get(id)
1545 .expect("couldn't find stmt in FIR")
1546 }
1547}
1548
1549fn eval_error(
1550 package_store: &PackageStore,
1551 fir_store: &fir::PackageStore,
1552 call_stack: Vec<Frame>,
1553 error: qsc_eval::Error,
1554) -> Vec<Error> {
1555 let stack_trace = if call_stack.is_empty() {
1556 None
1557 } else {
1558 Some(format_call_stack(
1559 package_store,
1560 fir_store,
1561 call_stack,
1562 &error,
1563 ))
1564 };
1565
1566 vec![error::from_eval(error, package_store, stack_trace).into()]
1567}
1568
1569#[must_use]
1570pub fn into_errors(errors: Vec<crate::compile::Error>) -> Vec<Error> {
1571 errors
1572 .into_iter()
1573 .map(|error| Error::Compile(error.into_with_source()))
1574 .collect::<Vec<_>>()
1575}
1576