microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billti/bloch

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/interpret.rs

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