microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
e874bfb8e1dbfc3eec7aebe87f45fc0cb4c547b6

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/interpret.rs

897lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4mod debug;
5
6#[cfg(test)]
7mod tests;
8
9#[cfg(test)]
10mod debugger_tests;
11
12#[cfg(test)]
13mod circuit_tests;
14
15use std::rc::Rc;
16
17pub use qsc_eval::{
18 debug::Frame,
19 output::{self, GenericReceiver},
20 val::Closure,
21 val::Range as ValueRange,
22 val::Result,
23 val::Value,
24 StepAction, StepResult,
25};
26use qsc_lowerer::{map_fir_package_to_hir, map_hir_package_to_fir};
27use qsc_partial_eval::ProgramEntry;
28use qsc_rca::PackageStoreComputeProperties;
29
30use crate::{
31 error::{self, WithStack},
32 incremental::Compiler,
33 location::Location,
34};
35use debug::format_call_stack;
36use miette::Diagnostic;
37use num_bigint::BigUint;
38use num_complex::Complex;
39use qsc_circuit::{
40 operations::entry_expr_for_qubit_operation, Builder as CircuitBuilder, Circuit,
41 Config as CircuitConfig,
42};
43use qsc_codegen::{qir::fir_to_qir, qir_base::BaseProfSim};
44use qsc_data_structures::{
45 functors::FunctorApp,
46 language_features::LanguageFeatures,
47 line_column::{Encoding, Range},
48 span::Span,
49};
50use qsc_eval::{
51 backend::{Backend, Chain as BackendChain, SparseSim},
52 output::Receiver,
53 val, Env, State, VariableInfo,
54};
55use qsc_fir::fir::{self, ExecGraphNode, Global, PackageStoreLookup};
56use qsc_fir::{
57 fir::{Block, BlockId, Expr, ExprId, Package, PackageId, Pat, PatId, Stmt, StmtId},
58 visit::{self, Visitor},
59};
60use qsc_frontend::{
61 compile::{CompileUnit, PackageStore, Source, SourceMap, TargetCapabilityFlags},
62 error::WithSource,
63};
64use qsc_passes::{PackageType, PassContext};
65use rustc_hash::FxHashSet;
66use thiserror::Error;
67
68impl Error {
69 #[must_use]
70 pub fn stack_trace(&self) -> &Option<String> {
71 match &self {
72 Error::Eval(err) => err.stack_trace(),
73 _ => &None,
74 }
75 }
76}
77
78#[derive(Clone, Debug, Diagnostic, Error)]
79pub enum Error {
80 #[error(transparent)]
81 #[diagnostic(transparent)]
82 Compile(#[from] crate::compile::Error),
83 #[error(transparent)]
84 #[diagnostic(transparent)]
85 Pass(#[from] WithSource<qsc_passes::Error>),
86 #[error("runtime error")]
87 #[diagnostic(transparent)]
88 Eval(#[from] WithStack<WithSource<qsc_eval::Error>>),
89 #[error("circuit error")]
90 #[diagnostic(transparent)]
91 Circuit(#[from] qsc_circuit::Error),
92 #[error("entry point not found")]
93 #[diagnostic(code("Qsc.Interpret.NoEntryPoint"))]
94 NoEntryPoint,
95 #[error("unsupported runtime capabilities for code generation")]
96 #[diagnostic(code("Qsc.Interpret.UnsupportedRuntimeCapabilities"))]
97 UnsupportedRuntimeCapabilities,
98 #[error("expression does not evaluate to an operation")]
99 #[diagnostic(code("Qsc.Interpret.NotAnOperation"))]
100 #[diagnostic(help("provide the name of a callable or a lambda expression"))]
101 NotAnOperation,
102 #[error("partial evaluation error")]
103 #[diagnostic(transparent)]
104 PartialEvaluation(#[from] WithSource<qsc_partial_eval::Error>),
105}
106
107/// A Q# interpreter.
108pub struct Interpreter {
109 /// The incremental Q# compiler.
110 compiler: Compiler,
111 /// The target capabilities used for compilation.
112 capabilities: TargetCapabilityFlags,
113 /// The number of lines that have so far been compiled.
114 /// This field is used to generate a unique label
115 /// for each line evaluated with `eval_fragments`.
116 lines: u32,
117 // The FIR store
118 fir_store: fir::PackageStore,
119 /// FIR lowerer
120 lowerer: qsc_lowerer::Lowerer,
121 /// The ID of the current package.
122 /// This ID is valid both for the FIR store and the `PackageStore`.
123 package: PackageId,
124 /// The ID of the source package. The source package
125 /// is made up of the initial sources passed in when creating the interpreter.
126 /// This ID is valid both for the FIR store and the `PackageStore`.
127 source_package: PackageId,
128 /// The default simulator backend.
129 sim: BackendChain<SparseSim, CircuitBuilder>,
130 /// The quantum seed, if any. This is cached here so that it can be used in calls to
131 /// `run_internal` which use a passed instance of the simulator instead of the one above.
132 quantum_seed: Option<u64>,
133 /// The classical seed, if any. This needs to be passed to the evaluator for use in intrinsic
134 /// calls that produce classical random numbers.
135 classical_seed: Option<u64>,
136 /// The evaluator environment.
137 env: Env,
138}
139
140pub type InterpretResult = std::result::Result<Value, Vec<Error>>;
141
142impl Interpreter {
143 /// Creates a new incremental compiler, compiling the passed in sources.
144 /// # Errors
145 /// If compiling the sources fails, compiler errors are returned.
146 pub fn new(
147 std: bool,
148 sources: SourceMap,
149 package_type: PackageType,
150 capabilities: TargetCapabilityFlags,
151 language_features: LanguageFeatures,
152 ) -> std::result::Result<Self, Vec<Error>> {
153 Self::new_internal(
154 false,
155 std,
156 sources,
157 package_type,
158 capabilities,
159 language_features,
160 )
161 }
162
163 /// Creates a new incremental compiler with debugging stmts enabled, compiling the passed in sources.
164 /// # Errors
165 /// If compiling the sources fails, compiler errors are returned.
166 pub fn new_with_debug(
167 std: bool,
168 sources: SourceMap,
169 package_type: PackageType,
170 capabilities: TargetCapabilityFlags,
171 language_features: LanguageFeatures,
172 ) -> std::result::Result<Self, Vec<Error>> {
173 Self::new_internal(
174 true,
175 std,
176 sources,
177 package_type,
178 capabilities,
179 language_features,
180 )
181 }
182
183 fn new_internal(
184 dbg: bool,
185 std: bool,
186 sources: SourceMap,
187 package_type: PackageType,
188 capabilities: TargetCapabilityFlags,
189 language_features: LanguageFeatures,
190 ) -> std::result::Result<Self, Vec<Error>> {
191 let compiler = Compiler::new(std, sources, package_type, capabilities, language_features)
192 .map_err(into_errors)?;
193
194 let mut fir_store = fir::PackageStore::new();
195 for (id, unit) in compiler.package_store() {
196 fir_store.insert(
197 map_hir_package_to_fir(id),
198 qsc_lowerer::Lowerer::new()
199 .with_debug(dbg)
200 .lower_package(&unit.package),
201 );
202 }
203
204 let source_package_id = compiler.source_package_id();
205 let package_id = compiler.package_id();
206 Ok(Self {
207 compiler,
208 lines: 0,
209 capabilities,
210 fir_store,
211 lowerer: qsc_lowerer::Lowerer::new().with_debug(dbg),
212 env: Env::default(),
213 sim: BackendChain::new(
214 SparseSim::new(),
215 CircuitBuilder::new(CircuitConfig {
216 // When using in conjunction with the simulator,
217 // the circuit builder should *not* perform base profile
218 // decompositions, in order to match the simulator's behavior.
219 //
220 // Note that conditional compilation (e.g. @Config(Base) attributes)
221 // will still respect the selected profile. This also
222 // matches the behavior of the simulator.
223 base_profile: false,
224 }),
225 ),
226 quantum_seed: None,
227 classical_seed: None,
228 package: map_hir_package_to_fir(package_id),
229 source_package: map_hir_package_to_fir(source_package_id),
230 })
231 }
232
233 pub fn set_quantum_seed(&mut self, seed: Option<u64>) {
234 self.quantum_seed = seed;
235 self.sim.set_seed(seed);
236 }
237
238 pub fn set_classical_seed(&mut self, seed: Option<u64>) {
239 self.classical_seed = seed;
240 }
241 /// Executes the entry expression until the end of execution.
242 /// # Errors
243 /// Returns a vector of errors if evaluating the entry point fails.
244 pub fn eval_entry(
245 &mut self,
246 receiver: &mut impl Receiver,
247 ) -> std::result::Result<Value, Vec<Error>> {
248 let graph = self.get_entry_exec_graph()?;
249 eval(
250 self.source_package,
251 self.classical_seed,
252 graph,
253 self.compiler.package_store(),
254 &self.fir_store,
255 &mut Env::default(),
256 &mut self.sim,
257 receiver,
258 )
259 }
260
261 /// Executes the entry expression until the end of execution, using the given simulator backend
262 /// and a new instance of the environment.
263 pub fn eval_entry_with_sim(
264 &mut self,
265 sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
266 receiver: &mut impl Receiver,
267 ) -> std::result::Result<Value, Vec<Error>> {
268 let graph = self.get_entry_exec_graph()?;
269 if self.quantum_seed.is_some() {
270 sim.set_seed(self.quantum_seed);
271 }
272 eval(
273 self.source_package,
274 self.classical_seed,
275 graph,
276 self.compiler.package_store(),
277 &self.fir_store,
278 &mut Env::default(),
279 sim,
280 receiver,
281 )
282 }
283
284 fn get_entry_exec_graph(&self) -> std::result::Result<Rc<[ExecGraphNode]>, Vec<Error>> {
285 let unit = self.fir_store.get(self.source_package);
286 if unit.entry.is_some() {
287 return Ok(unit.entry_exec_graph.clone());
288 };
289 Err(vec![Error::NoEntryPoint])
290 }
291
292 /// # Errors
293 /// If the parsing of the fragments fails, an error is returned.
294 /// If the compilation of the fragments fails, an error is returned.
295 /// If there is a runtime error when interpreting the fragments, an error is returned.
296 pub fn eval_fragments(
297 &mut self,
298 receiver: &mut impl Receiver,
299 fragments: &str,
300 ) -> InterpretResult {
301 let label = self.next_line_label();
302
303 let increment = self
304 .compiler
305 .compile_fragments_fail_fast(&label, fragments)
306 .map_err(into_errors)?;
307
308 let (graph, _) = self.lower(&increment)?;
309
310 // Updating the compiler state with the new AST/HIR nodes
311 // is not necessary for the interpreter to function, as all
312 // the state required for evaluation already exists in the
313 // FIR store. It could potentially save some memory
314 // *not* to do hold on to the AST/HIR, but it is done
315 // here to keep the package stores consistent.
316 self.compiler.update(increment);
317
318 eval(
319 self.package,
320 self.classical_seed,
321 graph.into(),
322 self.compiler.package_store(),
323 &self.fir_store,
324 &mut self.env,
325 &mut self.sim,
326 receiver,
327 )
328 }
329
330 /// Runs the given entry expression on a new instance of the environment and simulator,
331 /// but using the current compilation.
332 pub fn run(
333 &mut self,
334 receiver: &mut impl Receiver,
335 expr: &str,
336 ) -> std::result::Result<InterpretResult, Vec<Error>> {
337 self.run_with_sim(&mut SparseSim::new(), receiver, expr)
338 }
339
340 /// Gets the current quantum state of the simulator.
341 pub fn get_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
342 self.sim.capture_quantum_state()
343 }
344
345 /// Get the current circuit representation of the program.
346 pub fn get_circuit(&self) -> Circuit {
347 self.sim.chained.snapshot()
348 }
349
350 /// Performs QIR codegen using the given entry expression on a new instance of the environment
351 /// and simulator but using the current compilation.
352 pub fn qirgen(&mut self, expr: &str) -> std::result::Result<String, Vec<Error>> {
353 if self.capabilities == TargetCapabilityFlags::all() {
354 return Err(vec![Error::UnsupportedRuntimeCapabilities]);
355 }
356 if self.capabilities == TargetCapabilityFlags::empty() {
357 let mut sim = BaseProfSim::new();
358 let mut stdout = std::io::sink();
359 let mut out = GenericReceiver::new(&mut stdout);
360
361 let val = self.run_with_sim(&mut sim, &mut out, expr)??;
362
363 Ok(sim.finish(&val))
364 } else {
365 // Compile the expression. This operation will set the expression as
366 // the entry-point in the FIR store.
367 let (graph, compute_properties) = self.compile_entry_expr(expr)?;
368
369 let Some(compute_properties) = compute_properties else {
370 // This can only happen if capability analysis was not run. This would be a bug
371 // and we are in a bad state and can't proceed.
372 panic!(
373 "internal error: compute properties not set after lowering entry expression"
374 );
375 };
376 let package = self.fir_store.get(self.package);
377 let entry = ProgramEntry {
378 exec_graph: graph.into(),
379 expr: (
380 self.package,
381 package
382 .entry
383 .expect("package must have an entry expression"),
384 )
385 .into(),
386 };
387 // Generate QIR
388 fir_to_qir(
389 &self.fir_store,
390 self.capabilities,
391 Some(compute_properties),
392 &entry,
393 )
394 .map_err(|e| {
395 let source_package = self
396 .compiler
397 .package_store()
398 .get(map_fir_package_to_hir(self.package))
399 .expect("package should exist in the package store");
400 vec![Error::PartialEvaluation(WithSource::from_map(
401 &source_package.sources,
402 e,
403 ))]
404 })
405 }
406 }
407
408 /// Generates a circuit representation for the program.
409 ///
410 /// `entry` can be the current entrypoint, an entry expression, or any operation
411 /// that takes qubits.
412 ///
413 /// An operation can be specified by its name or a lambda expression that only takes qubits.
414 /// e.g. `Sample.Main` , `qs => H(qs[0])`
415 pub fn circuit(
416 &mut self,
417 entry: CircuitEntryPoint,
418 ) -> std::result::Result<Circuit, Vec<Error>> {
419 let mut sink = std::io::sink();
420 let mut out = GenericReceiver::new(&mut sink);
421 let mut sim = CircuitBuilder::new(CircuitConfig {
422 base_profile: self.capabilities.is_empty(),
423 });
424
425 let entry_expr = match entry {
426 CircuitEntryPoint::Operation(operation_expr) => {
427 let (item, functor_app) = self.eval_to_operation(&operation_expr)?;
428 let expr = entry_expr_for_qubit_operation(item, functor_app, &operation_expr)
429 .map_err(|e| vec![e.into()])?;
430 Some(expr)
431 }
432 CircuitEntryPoint::EntryExpr(expr) => Some(expr),
433 CircuitEntryPoint::EntryPoint => None,
434 };
435
436 if let Some(entry_expr) = entry_expr {
437 self.run_with_sim(&mut sim, &mut out, &entry_expr)?
438 } else {
439 self.eval_entry_with_sim(&mut sim, &mut out)
440 }?;
441
442 Ok(sim.finish())
443 }
444
445 /// Runs the given entry expression on the given simulator with a new instance of the environment
446 /// but using the current compilation.
447 pub fn run_with_sim(
448 &mut self,
449 sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
450 receiver: &mut impl Receiver,
451 expr: &str,
452 ) -> std::result::Result<InterpretResult, Vec<Error>> {
453 let (graph, _) = self.compile_entry_expr(expr)?;
454
455 if self.quantum_seed.is_some() {
456 sim.set_seed(self.quantum_seed);
457 }
458
459 Ok(eval(
460 self.package,
461 self.classical_seed,
462 graph.into(),
463 self.compiler.package_store(),
464 &self.fir_store,
465 &mut Env::default(),
466 sim,
467 receiver,
468 ))
469 }
470
471 fn compile_entry_expr(
472 &mut self,
473 expr: &str,
474 ) -> std::result::Result<(Vec<ExecGraphNode>, Option<PackageStoreComputeProperties>), Vec<Error>>
475 {
476 let increment = self
477 .compiler
478 .compile_entry_expr(expr)
479 .map_err(into_errors)?;
480
481 // `lower` will update the entry expression in the FIR store,
482 // and it will always return an empty list of statements.
483 let (graph, compute_properties) = self.lower(&increment)?;
484
485 // The AST and HIR packages in `increment` only contain an entry
486 // expression and no statements. The HIR *can* contain items if the entry
487 // expression defined any items.
488 assert!(increment.hir.stmts.is_empty());
489 assert!(increment.ast.package.nodes.is_empty());
490
491 // Updating the compiler state with the new AST/HIR nodes
492 // is not necessary for the interpreter to function, as all
493 // the state required for evaluation already exists in the
494 // FIR store. It could potentially save some memory
495 // *not* to do hold on to the AST/HIR, but it is done
496 // here to keep the package stores consistent.
497 self.compiler.update(increment);
498
499 Ok((graph, compute_properties))
500 }
501
502 fn lower(
503 &mut self,
504 unit_addition: &qsc_frontend::incremental::Increment,
505 ) -> core::result::Result<(Vec<ExecGraphNode>, Option<PackageStoreComputeProperties>), Vec<Error>>
506 {
507 if self.capabilities != TargetCapabilityFlags::all() {
508 return self.run_fir_passes(unit_addition);
509 }
510 let fir_package = self.fir_store.get_mut(self.package);
511 self.lowerer
512 .lower_and_update_package(fir_package, &unit_addition.hir);
513 Ok((self.lowerer.take_exec_graph(), None))
514 }
515
516 fn run_fir_passes(
517 &mut self,
518 unit: &qsc_frontend::incremental::Increment,
519 ) -> std::result::Result<(Vec<ExecGraphNode>, Option<PackageStoreComputeProperties>), Vec<Error>>
520 {
521 let fir_package = self.fir_store.get_mut(self.package);
522 self.lowerer
523 .lower_and_update_package(fir_package, &unit.hir);
524
525 let cap_results =
526 PassContext::run_fir_passes_on_fir(&self.fir_store, self.package, self.capabilities);
527
528 let compute_properties = cap_results.map_err(|caps_errors| {
529 // if there are errors, convert them to interpreter errors
530 // and don't update the lowerer or FIR store.
531 let source_package = self
532 .compiler
533 .package_store()
534 .get(map_fir_package_to_hir(self.package))
535 .expect("package should exist in the package store");
536
537 caps_errors
538 .into_iter()
539 .map(|error| Error::Pass(WithSource::from_map(&source_package.sources, error)))
540 .collect::<Vec<_>>()
541 })?;
542
543 let graph = self.lowerer.take_exec_graph();
544 Ok((graph, Some(compute_properties)))
545 }
546
547 fn next_line_label(&mut self) -> String {
548 let label = format!("line_{}", self.lines);
549 self.lines += 1;
550 label
551 }
552
553 /// Evaluate the name of an operation, or any expression that evaluates to a callable,
554 /// and return the Item ID and function application for the callable.
555 /// Examples: "Microsoft.Quantum.Diagnostics.DumpMachine", "(qs: Qubit[]) => H(qs[0])",
556 /// "Controlled SWAP"
557 fn eval_to_operation(
558 &mut self,
559 operation_expr: &str,
560 ) -> std::result::Result<(&qsc_hir::hir::Item, FunctorApp), Vec<Error>> {
561 let mut sink = std::io::sink();
562 let mut out = GenericReceiver::new(&mut sink);
563 let (store_item_id, functor_app) = match self.eval_fragments(&mut out, operation_expr)? {
564 Value::Closure(b) => (b.id, b.functor),
565 Value::Global(item_id, functor_app) => (item_id, functor_app),
566 _ => return Err(vec![Error::NotAnOperation]),
567 };
568 let package = map_fir_package_to_hir(store_item_id.package);
569 let local_item_id = crate::hir::LocalItemId::from(usize::from(store_item_id.item));
570 let unit = self
571 .compiler
572 .package_store()
573 .get(package)
574 .expect("package should exist in the package store");
575 let item = unit
576 .package
577 .items
578 .get(local_item_id)
579 .expect("item should exist in the package");
580 Ok((item, functor_app))
581 }
582}
583
584/// Describes the entry point for circuit generation.
585pub enum CircuitEntryPoint {
586 /// An operation. This must be a callable name or a lambda
587 /// expression that only takes qubits as arguments.
588 /// The callable name must be visible in the current package.
589 Operation(String),
590 /// An explicitly provided entry expression.
591 EntryExpr(String),
592 /// The entry point for the current package.
593 EntryPoint,
594}
595
596/// A debugger that enables step-by-step evaluation of code
597/// and inspecting state in the interpreter.
598pub struct Debugger {
599 interpreter: Interpreter,
600 /// The encoding (utf-8 or utf-16) used for character offsets
601 /// in line/character positions returned by the Interpreter.
602 position_encoding: Encoding,
603 /// The current state of the evaluator.
604 state: State,
605}
606
607impl Debugger {
608 pub fn new(
609 sources: SourceMap,
610 capabilities: TargetCapabilityFlags,
611 position_encoding: Encoding,
612 language_features: LanguageFeatures,
613 ) -> std::result::Result<Self, Vec<Error>> {
614 let interpreter = Interpreter::new_with_debug(
615 true,
616 sources,
617 PackageType::Exe,
618 capabilities,
619 language_features,
620 )?;
621 let source_package_id = interpreter.source_package;
622 let unit = interpreter.fir_store.get(source_package_id);
623 let entry_exec_graph = unit.entry_exec_graph.clone();
624 Ok(Self {
625 interpreter,
626 position_encoding,
627 state: State::new(source_package_id, entry_exec_graph, None),
628 })
629 }
630
631 /// Resumes execution with specified `StepAction`.
632 /// # Errors
633 /// Returns a vector of errors if evaluating the entry point fails.
634 pub fn eval_step(
635 &mut self,
636 receiver: &mut impl Receiver,
637 breakpoints: &[StmtId],
638 step: StepAction,
639 ) -> std::result::Result<StepResult, Vec<Error>> {
640 self.state
641 .eval(
642 &self.interpreter.fir_store,
643 &mut self.interpreter.env,
644 &mut self.interpreter.sim,
645 receiver,
646 breakpoints,
647 step,
648 )
649 .map_err(|(error, call_stack)| {
650 eval_error(
651 self.interpreter.compiler.package_store(),
652 &self.interpreter.fir_store,
653 call_stack,
654 error,
655 )
656 })
657 }
658
659 #[must_use]
660 pub fn get_stack_frames(&self) -> Vec<StackFrame> {
661 let frames = self.state.get_stack_frames();
662 let stack_frames = frames
663 .iter()
664 .map(|frame| {
665 let callable = self
666 .interpreter
667 .fir_store
668 .get_global(frame.id)
669 .expect("frame should exist");
670 let functor = format!("{}", frame.functor);
671 let name = match callable {
672 Global::Callable(decl) => decl.name.name.to_string(),
673 Global::Udt => "udt".into(),
674 };
675
676 StackFrame {
677 name,
678 functor,
679 location: Location::from(
680 frame.span,
681 map_fir_package_to_hir(frame.id.package),
682 self.interpreter.compiler.package_store(),
683 map_fir_package_to_hir(self.interpreter.source_package),
684 self.position_encoding,
685 ),
686 }
687 })
688 .collect();
689 stack_frames
690 }
691
692 pub fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
693 self.interpreter.sim.capture_quantum_state()
694 }
695
696 pub fn circuit(&self) -> Circuit {
697 self.interpreter.get_circuit()
698 }
699
700 #[must_use]
701 pub fn get_breakpoints(&self, path: &str) -> Vec<BreakpointSpan> {
702 let unit = self.source_package();
703
704 if let Some(source) = unit.sources.find_by_name(path) {
705 let package = self
706 .interpreter
707 .fir_store
708 .get(self.interpreter.source_package);
709 let mut collector = BreakpointCollector::new(
710 &unit.sources,
711 source.offset,
712 package,
713 self.position_encoding,
714 );
715 collector.visit_package(package);
716 let mut spans: Vec<_> = collector.statements.into_iter().collect();
717
718 // Sort by start position (line first, column next)
719 spans.sort_by_key(|s| (s.range.start.line, s.range.start.column));
720 spans
721 } else {
722 Vec::new()
723 }
724 }
725
726 #[must_use]
727 pub fn get_locals(&self) -> Vec<VariableInfo> {
728 self.interpreter
729 .env
730 .get_variables_in_top_frame()
731 .into_iter()
732 .filter(|v| !v.name.starts_with('@'))
733 .collect()
734 }
735
736 fn source_package(&self) -> &CompileUnit {
737 self.interpreter
738 .compiler
739 .package_store()
740 .get(map_fir_package_to_hir(self.interpreter.source_package))
741 .expect("Could not load package")
742 }
743}
744
745/// Wrapper function for `qsc_eval::eval` that handles error conversion.
746#[allow(clippy::too_many_arguments)]
747fn eval(
748 package: PackageId,
749 classical_seed: Option<u64>,
750 exec_graph: Rc<[ExecGraphNode]>,
751 package_store: &PackageStore,
752 fir_store: &fir::PackageStore,
753 env: &mut Env,
754 sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
755 receiver: &mut impl Receiver,
756) -> InterpretResult {
757 qsc_eval::eval(
758 package,
759 classical_seed,
760 exec_graph,
761 fir_store,
762 env,
763 sim,
764 receiver,
765 )
766 .map_err(|(error, call_stack)| eval_error(package_store, fir_store, call_stack, error))
767}
768
769/// Represents a stack frame for debugging.
770pub struct StackFrame {
771 /// The name of the callable.
772 pub name: String,
773 /// The functor of the callable.
774 pub functor: String,
775 /// The source location of the call site.
776 pub location: Location,
777}
778
779#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
780pub struct BreakpointSpan {
781 /// The id of the statement representing the breakpoint location.
782 pub id: u32,
783 /// The source range of the call site.
784 pub range: Range,
785}
786
787struct BreakpointCollector<'a> {
788 statements: FxHashSet<BreakpointSpan>,
789 sources: &'a SourceMap,
790 offset: u32,
791 package: &'a Package,
792 position_encoding: Encoding,
793}
794
795impl<'a> BreakpointCollector<'a> {
796 fn new(
797 sources: &'a SourceMap,
798 offset: u32,
799 package: &'a Package,
800 position_encoding: Encoding,
801 ) -> Self {
802 Self {
803 statements: FxHashSet::default(),
804 sources,
805 offset,
806 package,
807 position_encoding,
808 }
809 }
810
811 fn get_source(&self, offset: u32) -> &Source {
812 self.sources
813 .find_by_offset(offset)
814 .expect("Couldn't find source file")
815 }
816
817 fn add_stmt(&mut self, stmt: &qsc_fir::fir::Stmt) {
818 let source: &Source = self.get_source(stmt.span.lo);
819 if source.offset == self.offset {
820 let span = stmt.span - source.offset;
821 if span != Span::default() {
822 let bps = BreakpointSpan {
823 id: stmt.id.into(),
824 range: Range::from_span(self.position_encoding, &source.contents, &span),
825 };
826 self.statements.insert(bps);
827 }
828 }
829 }
830}
831
832impl<'a> Visitor<'a> for BreakpointCollector<'a> {
833 fn visit_stmt(&mut self, stmt: StmtId) {
834 let stmt_res = self.get_stmt(stmt);
835 match stmt_res.kind {
836 qsc_fir::fir::StmtKind::Expr(expr) | qsc_fir::fir::StmtKind::Local(_, _, expr) => {
837 self.add_stmt(stmt_res);
838 visit::walk_expr(self, expr);
839 }
840 qsc_fir::fir::StmtKind::Item(_) | qsc_fir::fir::StmtKind::Semi(_) => {
841 self.add_stmt(stmt_res);
842 }
843 };
844 }
845
846 fn get_block(&self, id: BlockId) -> &'a Block {
847 self.package
848 .blocks
849 .get(id)
850 .expect("couldn't find block in FIR")
851 }
852
853 fn get_expr(&self, id: ExprId) -> &'a Expr {
854 self.package
855 .exprs
856 .get(id)
857 .expect("couldn't find expr in FIR")
858 }
859
860 fn get_pat(&self, id: PatId) -> &'a Pat {
861 self.package.pats.get(id).expect("couldn't find pat in FIR")
862 }
863
864 fn get_stmt(&self, id: StmtId) -> &'a Stmt {
865 self.package
866 .stmts
867 .get(id)
868 .expect("couldn't find stmt in FIR")
869 }
870}
871
872fn eval_error(
873 package_store: &PackageStore,
874 fir_store: &fir::PackageStore,
875 call_stack: Vec<Frame>,
876 error: qsc_eval::Error,
877) -> Vec<Error> {
878 let stack_trace = if call_stack.is_empty() {
879 None
880 } else {
881 Some(format_call_stack(
882 package_store,
883 fir_store,
884 call_stack,
885 &error,
886 ))
887 };
888
889 vec![error::from_eval(error, package_store, stack_trace).into()]
890}
891
892fn into_errors(errors: Vec<crate::compile::Error>) -> Vec<Error> {
893 errors
894 .into_iter()
895 .map(|error| Error::Compile(error.into_with_source()))
896 .collect::<Vec<_>>()
897}
898