1#![allow(unused_assignments)]
3
4use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
5
6use miden_air::trace::chiplets::hasher::MAX_MERKLE_DEPTH;
7use miden_core::{deferred::PrecompileError, program::MIN_STACK_DEPTH};
8use miden_debug_types::{Location, SourceFile, SourceSpan};
9use miden_mast_package::{
10 PackageDebugInfoError,
11 debug_info::{DebugSourceNodeId, PackageDebugInfo},
12};
13use miden_utils_diagnostics::{Diagnostic, miette};
14
15use crate::{
16 BaseHost, ContextId, Felt, Word,
17 advice::AdviceError,
18 event::{EventError, EventId, EventName},
19 fast::SystemEventError,
20 utils::to_hex,
21};
22
23#[non_exhaustive]
27#[derive(Debug, thiserror::Error, Diagnostic)]
28pub enum ExecutionError {
29 #[error("failed to execute arithmetic circuit evaluation operation: {error}")]
30 #[diagnostic()]
31 AceChipError {
32 #[label("this call failed")]
33 label: SourceSpan,
34 #[source_code]
35 source_file: Option<Arc<SourceFile>>,
36 error: AceError,
37 },
38 #[error("{err}")]
39 #[diagnostic(forward(err))]
40 AdviceError {
41 #[label]
42 label: SourceSpan,
43 #[source_code]
44 source_file: Option<Arc<SourceFile>>,
45 err: AdviceError,
46 },
47 #[error("exceeded the allowed number of max cycles {0}")]
48 CycleLimitExceeded(u32),
49 #[error("error during processing of event {}", match event_name {
50 Some(name) => format!("'{name}' (ID: {event_id})"),
51 None => format!("with ID: {event_id}"),
52 })]
53 #[diagnostic()]
54 EventError {
55 #[label]
56 label: SourceSpan,
57 #[source_code]
58 source_file: Option<Arc<SourceFile>>,
59 event_id: EventId,
60 event_name: Option<EventName>,
61 #[source]
62 error: EventError,
63 },
64 #[error("{err}")]
66 #[diagnostic()]
67 DeferredError {
68 #[label]
69 label: SourceSpan,
70 #[source_code]
71 source_file: Option<Arc<SourceFile>>,
72 err: PrecompileError,
73 },
74 #[error("failed to execute the program for internal reason: {0}")]
75 Internal(&'static str),
76 #[error("operand stack depth {depth} exceeds the maximum of {max}")]
77 StackDepthLimitExceeded { depth: usize, max: usize },
78 #[error("trace length exceeded the maximum of {0} rows")]
82 TraceLenExceeded(usize),
83 #[error(
85 "estimated prover memory of {estimated_bytes} bytes exceeds the budget of {budget_bytes} bytes"
86 )]
87 ProverMemoryExceeded { estimated_bytes: u64, budget_bytes: u64 },
88 #[error("{err}")]
92 #[diagnostic(forward(err))]
93 MemoryError {
94 #[label]
95 label: SourceSpan,
96 #[source_code]
97 source_file: Option<Arc<SourceFile>>,
98 err: MemoryError,
99 },
100 #[error(transparent)]
105 #[diagnostic(transparent)]
106 MemoryErrorNoCtx(MemoryError),
107 #[error("{err}")]
108 #[diagnostic(forward(err))]
109 OperationError {
110 #[label]
111 label: SourceSpan,
112 #[source_code]
113 source_file: Option<Arc<SourceFile>>,
114 err: OperationError,
115 },
116 #[error("stack should have at most {MIN_STACK_DEPTH} elements at the end of program execution, but had {} elements", MIN_STACK_DEPTH + .0)]
117 OutputStackOverflow(usize),
118 #[error("procedure with root digest {root_digest} could not be found")]
119 #[diagnostic()]
120 ProcedureNotFound {
121 #[label]
122 label: SourceSpan,
123 #[source_code]
124 source_file: Option<Arc<SourceFile>>,
125 root_digest: Word,
126 },
127 #[error("failed to generate STARK proof: {0}")]
128 ProvingError(String),
129 #[error(transparent)]
130 HostError(#[from] HostError),
131 #[error(transparent)]
132 PackageDebugInfoError(#[from] PackageDebugInfoError),
133}
134
135impl ExecutionError {
136 pub fn advice_error_no_context(err: AdviceError) -> Self {
138 Self::AdviceError {
139 label: SourceSpan::UNKNOWN,
140 source_file: None,
141 err,
142 }
143 }
144}
145
146impl AsRef<dyn Diagnostic> for ExecutionError {
147 fn as_ref(&self) -> &(dyn Diagnostic + 'static) {
148 self
149 }
150}
151
152#[derive(Debug, thiserror::Error)]
156#[error("ace circuit evaluation failed: {0}")]
157pub struct AceError(pub String);
158
159#[derive(Debug, thiserror::Error)]
168pub enum AceEvalError {
169 #[error(transparent)]
170 Ace(#[from] AceError),
171 #[error(transparent)]
172 Memory(#[from] MemoryError),
173}
174
175#[derive(Debug, thiserror::Error)]
180pub enum HostError {
181 #[error("attempted to add event handler for '{event}' (already registered)")]
182 DuplicateEventHandler { event: EventName },
183 #[error("attempted to add event handler for '{event}' (reserved system event)")]
184 ReservedEventNamespace { event: EventName },
185}
186
187#[derive(Debug, thiserror::Error, Diagnostic)]
196pub enum IoError {
197 #[error(transparent)]
198 Advice(#[from] AdviceError),
199 #[error(transparent)]
200 Memory(#[from] MemoryError),
201 #[error(transparent)]
202 #[diagnostic(transparent)]
203 Operation(#[from] OperationError),
204 #[error(transparent)]
209 #[diagnostic(transparent)]
210 Execution(Box<ExecutionError>),
211}
212
213impl From<ExecutionError> for IoError {
214 fn from(err: ExecutionError) -> Self {
215 IoError::Execution(Box::new(err))
216 }
217}
218
219#[derive(Debug, thiserror::Error, Diagnostic)]
228pub enum MemoryError {
229 #[error("memory address cannot exceed 2^32 but was {addr}")]
230 AddressOutOfBounds { addr: u64 },
231 #[error(
232 "memory address {addr} in context {ctx} was read and written, or written twice, in the same clock cycle {clk}"
233 )]
234 IllegalMemoryAccess { ctx: ContextId, addr: u32, clk: Felt },
235 #[error(
236 "memory range start address cannot exceed end address, but was ({start_addr}, {end_addr})"
237 )]
238 InvalidMemoryRange { start_addr: u64, end_addr: u64 },
239 #[error(
240 "word access at memory address {addr} in context {ctx} is unaligned: word accesses require addresses that are multiples of 4"
241 )]
242 UnalignedWordAccess { addr: u32, ctx: ContextId },
243 #[error("failed to read from memory: {0}")]
244 MemoryReadFailed(String),
245 #[error(
246 "writing to memory address {addr} in context {ctx} would exceed the maximum number of memory elements {max}"
247 )]
248 #[diagnostic(help(
249 "increase the limit via `ExecutionOptions::with_max_memory_elements`, or reduce the number of distinct memory addresses the program writes to"
250 ))]
251 MemoryElementLimitExceeded { ctx: ContextId, addr: u32, max: usize },
252}
253
254#[derive(Debug, thiserror::Error, Diagnostic)]
263pub enum CryptoError {
264 #[error(transparent)]
265 Advice(#[from] AdviceError),
266 #[error(transparent)]
267 #[diagnostic(transparent)]
268 Operation(#[from] OperationError),
269}
270
271#[derive(Debug, Clone, thiserror::Error, Diagnostic)]
305pub enum OperationError {
306 #[error("external node with mast root {0} resolved to an external node")]
307 CircularExternalNode(Word),
308 #[error("division by zero: divisor must be non-zero for division or modulo operations")]
309 DivideByZero,
310 #[error(transparent)]
311 Deferred(#[from] PrecompileError),
312 #[error(
313 "assertion failed with error {}",
314 match err_msg {
315 Some(msg) => format!("message: {msg}"),
316 None => format!("code: {err_code}"),
317 }
318 )]
319 FailedAssertion {
320 err_code: Felt,
321 err_msg: Option<Arc<str>>,
322 },
323 #[error(
324 "u32 assertion failed: u32assert2 requires both stack values to be valid 32-bit unsigned integers; error {}; invalid values: {invalid_values:?}",
325 match err_msg {
326 Some(msg) => format!("message: {msg}"),
327 None => format!("code: {err_code}"),
328 }
329 )]
330 U32AssertionFailed {
331 err_code: Felt,
332 err_msg: Option<Arc<str>>,
333 invalid_values: Vec<Felt>,
334 },
335 #[error("FRI operation failed: {0}")]
336 FriError(String),
337 #[error(
338 "Horner evaluation point at memory address {addr} in context {ctx} must be encoded as [alpha0, alpha1, 0, 0]"
339 )]
340 InvalidHornerEvaluationPointWord { ctx: ContextId, addr: u64 },
341 #[error(
342 "invalid crypto operation: Merkle path length {path_len} does not match expected depth {depth}"
343 )]
344 InvalidMerklePathLength { path_len: usize, depth: Felt },
345 #[error("when returning from a call, stack depth must be {MIN_STACK_DEPTH}, but was {depth}")]
346 InvalidStackDepthOnReturn { depth: usize },
347 #[error("ilog2 requires a non-zero argument")]
348 LogArgumentZero,
349 #[error(
350 "MAST forest in host indexed by procedure root {root_digest} doesn't contain that root"
351 )]
352 MalformedMastForestInHost { root_digest: Word },
353 #[error("Merkle tree depth must be in the range 1..={MAX_MERKLE_DEPTH}, but was {depth}")]
354 MerkleDepthOutOfRange { depth: Felt },
355 #[error("merkle path verification failed for value {value} at index {index} in the Merkle tree with root {root} (error {err})",
356 value = to_hex(inner.value.as_bytes()),
357 root = to_hex(inner.root.as_bytes()),
358 index = inner.index,
359 err = match &inner.err_msg {
360 Some(msg) => format!("message: {msg}"),
361 None => format!("code: {}", inner.err_code),
362 }
363 )]
364 MerklePathVerificationFailed {
365 inner: Box<MerklePathVerificationFailedInner>,
366 },
367 #[error("{message}, but got {value}", message = context.message())]
368 NotBinaryValue {
369 context: BinaryValueErrorContext,
370 value: Felt,
371 },
372 #[error("operation expected u32 values, but got values: {values:?}")]
373 NotU32Values { values: Vec<Felt> },
374 #[error("syscall failed: procedure with root {proc_root} was not found in the kernel")]
375 SyscallTargetNotInKernel { proc_root: Word },
376 #[error("failed to execute the operation for internal reason: {0}")]
377 Internal(&'static str),
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq)]
381pub enum BinaryValueErrorContext {
382 Operation,
383 If,
384 Loop,
385}
386
387impl BinaryValueErrorContext {
388 const fn message(self) -> &'static str {
389 match self {
390 Self::Operation => "operation expected a binary value",
391 Self::If => "if statement expected a binary value on top of the stack",
392 Self::Loop => {
393 "loop condition must be a binary value on entry and each subsequent iteration"
394 },
395 }
396 }
397}
398
399impl OperationError {
400 pub fn with_context(self) -> ExecutionError {
405 let (label, source_file) = get_label_and_source_file();
406 ExecutionError::OperationError { label, source_file, err: self }
407 }
408
409 pub fn with_package_source_context(
414 self,
415 context: PackageSourceDebugContext<'_>,
416 host: &(dyn BaseHost + '_),
417 op_idx: Option<usize>,
418 ) -> ExecutionError {
419 let (label, source_file) =
420 label_and_source_file_from_location(context.assembly_location(op_idx).as_ref(), host);
421 ExecutionError::OperationError {
422 label,
423 source_file,
424 err: self.with_package_debug_info(context.debug_info()),
425 }
426 }
427
428 fn with_package_debug_info(self, debug_info: &PackageDebugInfo) -> Self {
429 match self {
430 Self::FailedAssertion { err_code, err_msg: None } => Self::FailedAssertion {
431 err_msg: debug_info.error_message(err_code.as_canonical_u64()),
432 err_code,
433 },
434 Self::U32AssertionFailed { err_code, err_msg: None, invalid_values } => {
435 Self::U32AssertionFailed {
436 err_msg: debug_info.error_message(err_code.as_canonical_u64()),
437 err_code,
438 invalid_values,
439 }
440 },
441 Self::MerklePathVerificationFailed { mut inner } if inner.err_msg.is_none() => {
442 inner.err_msg = debug_info.error_message(inner.err_code.as_canonical_u64());
443 Self::MerklePathVerificationFailed { inner }
444 },
445 err => err,
446 }
447 }
448}
449
450#[derive(Debug, Clone)]
454pub struct MerklePathVerificationFailedInner {
455 pub value: Word,
456 pub index: Felt,
457 pub root: Word,
458 pub err_code: Felt,
459 pub err_msg: Option<Arc<str>>,
460}
461
462#[derive(Clone, Copy, Debug)]
470pub struct PackageSourceDebugContext<'a> {
471 debug_info: &'a PackageDebugInfo,
472 source_node_id: Option<DebugSourceNodeId>,
473}
474
475impl<'a> PackageSourceDebugContext<'a> {
476 pub fn new(debug_info: &'a PackageDebugInfo, source_node_id: DebugSourceNodeId) -> Self {
478 Self {
479 debug_info,
480 source_node_id: Some(source_node_id),
481 }
482 }
483
484 pub(crate) fn new_optional(
486 debug_info: &'a PackageDebugInfo,
487 source_node_id: Option<DebugSourceNodeId>,
488 ) -> Self {
489 Self { debug_info, source_node_id }
490 }
491
492 pub fn source_node_id(&self) -> Option<DebugSourceNodeId> {
494 self.source_node_id
495 }
496
497 pub fn debug_info(&self) -> &'a PackageDebugInfo {
499 self.debug_info
500 }
501
502 pub fn assembly_location(&self, op_idx: Option<usize>) -> Option<Location> {
506 let source_node_id = self.source_node_id?;
507 let assembly_op = match op_idx {
508 Some(op_idx) => u32::try_from(op_idx)
509 .ok()
510 .and_then(|op_idx| self.debug_info.asm_op_for_operation(source_node_id, op_idx)),
511 None => self.debug_info.first_asm_op_for_source_node(source_node_id),
512 }?;
513
514 assembly_op
515 .location_idx
516 .into_option()
517 .and_then(|idx| self.debug_info.get_location(idx))
518 }
519}
520
521fn label_and_source_file_from_location(
522 location: Option<&Location>,
523 host: &(dyn BaseHost + '_),
524) -> (SourceSpan, Option<Arc<SourceFile>>) {
525 location.map_or_else(
526 || (SourceSpan::UNKNOWN, None),
527 |location| host.get_label_and_source_file(location),
528 )
529}
530
531fn get_label_and_source_file() -> (SourceSpan, Option<Arc<SourceFile>>) {
537 (SourceSpan::UNKNOWN, None)
538}
539
540pub fn advice_error_with_context(err: AdviceError) -> ExecutionError {
545 let (label, source_file) = get_label_and_source_file();
546 ExecutionError::AdviceError { label, source_file, err }
547}
548
549pub fn advice_error_with_package_source_context(
551 err: AdviceError,
552 context: PackageSourceDebugContext<'_>,
553 host: &(dyn BaseHost + '_),
554 op_idx: Option<usize>,
555) -> ExecutionError {
556 let (label, source_file) =
557 label_and_source_file_from_location(context.assembly_location(op_idx).as_ref(), host);
558 ExecutionError::AdviceError { label, source_file, err }
559}
560
561pub fn event_error_with_context(
566 error: EventError,
567 event_id: EventId,
568 event_name: Option<EventName>,
569) -> ExecutionError {
570 let (label, source_file) = get_label_and_source_file();
571 ExecutionError::EventError {
572 label,
573 source_file,
574 event_id,
575 event_name,
576 error,
577 }
578}
579
580pub fn event_error_with_package_source_context(
582 error: EventError,
583 context: PackageSourceDebugContext<'_>,
584 host: &(dyn BaseHost + '_),
585 op_idx: Option<usize>,
586 event_id: EventId,
587 event_name: Option<EventName>,
588) -> ExecutionError {
589 let (label, source_file) =
590 label_and_source_file_from_location(context.assembly_location(op_idx).as_ref(), host);
591 ExecutionError::EventError {
592 label,
593 source_file,
594 event_id,
595 event_name,
596 error,
597 }
598}
599
600pub fn procedure_not_found_with_context(root_digest: Word) -> ExecutionError {
602 let (label, source_file) = get_label_and_source_file();
603 ExecutionError::ProcedureNotFound { label, source_file, root_digest }
604}
605
606pub fn procedure_not_found_with_package_source_context(
608 root_digest: Word,
609 context: PackageSourceDebugContext<'_>,
610 host: &(dyn BaseHost + '_),
611) -> ExecutionError {
612 let (label, source_file) =
613 label_and_source_file_from_location(context.assembly_location(None).as_ref(), host);
614 ExecutionError::ProcedureNotFound { label, source_file, root_digest }
615}
616
617pub fn malformed_mast_forest_with_context(
619 root_digest: Word,
620 context: Option<PackageSourceDebugContext<'_>>,
621 host: &(dyn BaseHost + '_),
622) -> ExecutionError {
623 let err = OperationError::MalformedMastForestInHost { root_digest };
624 match context {
625 Some(context) => err.with_package_source_context(context, host, None),
626 None => err.with_context(),
627 }
628}
629
630pub trait MapExecErr<T> {
643 fn map_exec_err(self) -> Result<T, ExecutionError>;
644}
645
646pub trait MapExecErrWithOpIdx<T> {
651 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError>;
652
653 fn map_exec_err_with_package_source_op_idx(
654 self,
655 context: Option<PackageSourceDebugContext<'_>>,
656 _host: &(dyn BaseHost + '_),
657 _op_idx: usize,
658 ) -> Result<T, ExecutionError>
659 where
660 Self: Sized,
661 {
662 if context.is_some() {
663 return Err(ExecutionError::Internal(
664 "package source context is unsupported for this error type",
665 ));
666 }
667
668 self.map_exec_err_with_op_idx()
669 }
670}
671
672pub trait MapExecErrNoCtx<T> {
677 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError>;
678}
679
680impl<T> MapExecErr<T> for Result<T, OperationError> {
682 #[inline(always)]
683 fn map_exec_err(self) -> Result<T, ExecutionError> {
684 match self {
685 Ok(v) => Ok(v),
686 Err(err) => {
687 let (label, source_file) = get_label_and_source_file();
688 Err(ExecutionError::OperationError { label, source_file, err })
689 },
690 }
691 }
692}
693
694impl<T> MapExecErrWithOpIdx<T> for Result<T, OperationError> {
695 #[inline(always)]
696 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
697 match self {
698 Ok(v) => Ok(v),
699 Err(err) => {
700 let (label, source_file) = get_label_and_source_file();
701 Err(ExecutionError::OperationError { label, source_file, err })
702 },
703 }
704 }
705
706 #[inline(always)]
707 fn map_exec_err_with_package_source_op_idx(
708 self,
709 context: Option<PackageSourceDebugContext<'_>>,
710 host: &(dyn BaseHost + '_),
711 op_idx: usize,
712 ) -> Result<T, ExecutionError> {
713 match (self, context) {
714 (Ok(v), _) => Ok(v),
715 (Err(err), Some(context)) => {
716 Err(err.with_package_source_context(context, host, Some(op_idx)))
717 },
718 (Err(err), None) => {
719 let (label, source_file) = get_label_and_source_file();
720 Err(ExecutionError::OperationError { label, source_file, err })
721 },
722 }
723 }
724}
725
726impl<T> MapExecErrNoCtx<T> for Result<T, OperationError> {
727 #[inline(always)]
728 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
729 match self {
730 Ok(v) => Ok(v),
731 Err(err) => Err(ExecutionError::OperationError {
732 label: SourceSpan::UNKNOWN,
733 source_file: None,
734 err,
735 }),
736 }
737 }
738}
739
740impl<T> MapExecErr<T> for Result<T, AdviceError> {
742 #[inline(always)]
743 fn map_exec_err(self) -> Result<T, ExecutionError> {
744 match self {
745 Ok(v) => Ok(v),
746 Err(err) => Err(advice_error_with_context(err)),
747 }
748 }
749}
750
751impl<T> MapExecErrNoCtx<T> for Result<T, AdviceError> {
752 #[inline(always)]
753 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
754 match self {
755 Ok(v) => Ok(v),
756 Err(err) => Err(ExecutionError::AdviceError {
757 label: SourceSpan::UNKNOWN,
758 source_file: None,
759 err,
760 }),
761 }
762 }
763}
764
765impl<T> MapExecErr<T> for Result<T, MemoryError> {
767 #[inline(always)]
768 fn map_exec_err(self) -> Result<T, ExecutionError> {
769 match self {
770 Ok(v) => Ok(v),
771 Err(err) => {
772 let (label, source_file) = get_label_and_source_file();
773 Err(ExecutionError::MemoryError { label, source_file, err })
774 },
775 }
776 }
777}
778
779impl<T> MapExecErrWithOpIdx<T> for Result<T, MemoryError> {
780 #[inline(always)]
781 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
782 match self {
783 Ok(v) => Ok(v),
784 Err(err) => {
785 let (label, source_file) = get_label_and_source_file();
786 Err(ExecutionError::MemoryError { label, source_file, err })
787 },
788 }
789 }
790
791 #[inline(always)]
792 fn map_exec_err_with_package_source_op_idx(
793 self,
794 context: Option<PackageSourceDebugContext<'_>>,
795 host: &(dyn BaseHost + '_),
796 op_idx: usize,
797 ) -> Result<T, ExecutionError> {
798 match (self, context) {
799 (Ok(v), _) => Ok(v),
800 (Err(err), Some(context)) => {
801 let (label, source_file) = label_and_source_file_from_location(
802 context.assembly_location(Some(op_idx)).as_ref(),
803 host,
804 );
805 Err(ExecutionError::MemoryError { label, source_file, err })
806 },
807 (Err(err), None) => {
808 let (label, source_file) = get_label_and_source_file();
809 Err(ExecutionError::MemoryError { label, source_file, err })
810 },
811 }
812 }
813}
814
815impl<T> MapExecErr<T> for Result<T, SystemEventError> {
817 #[inline(always)]
818 fn map_exec_err(self) -> Result<T, ExecutionError> {
819 match self {
820 Ok(v) => Ok(v),
821 Err(err) => {
822 let (label, source_file) = get_label_and_source_file();
823 Err(match err {
824 SystemEventError::Advice(err) => {
825 ExecutionError::AdviceError { label, source_file, err }
826 },
827 SystemEventError::Operation(err) => {
828 ExecutionError::OperationError { label, source_file, err }
829 },
830 SystemEventError::Memory(err) => {
831 ExecutionError::MemoryError { label, source_file, err }
832 },
833 SystemEventError::Deferred(err) => {
834 ExecutionError::DeferredError { label, source_file, err }
835 },
836 })
837 },
838 }
839 }
840}
841
842impl<T> MapExecErrWithOpIdx<T> for Result<T, SystemEventError> {
843 #[inline(always)]
844 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
845 match self {
846 Ok(v) => Ok(v),
847 Err(err) => {
848 let (label, source_file) = get_label_and_source_file();
849 Err(match err {
850 SystemEventError::Advice(err) => {
851 ExecutionError::AdviceError { label, source_file, err }
852 },
853 SystemEventError::Operation(err) => {
854 ExecutionError::OperationError { label, source_file, err }
855 },
856 SystemEventError::Memory(err) => {
857 ExecutionError::MemoryError { label, source_file, err }
858 },
859 SystemEventError::Deferred(err) => {
860 ExecutionError::DeferredError { label, source_file, err }
861 },
862 })
863 },
864 }
865 }
866
867 #[inline(always)]
868 fn map_exec_err_with_package_source_op_idx(
869 self,
870 context: Option<PackageSourceDebugContext<'_>>,
871 host: &(dyn BaseHost + '_),
872 op_idx: usize,
873 ) -> Result<T, ExecutionError> {
874 match (self, context) {
875 (Ok(v), _) => Ok(v),
876 (Err(err), Some(context)) => {
877 let (label, source_file) = label_and_source_file_from_location(
878 context.assembly_location(Some(op_idx)).as_ref(),
879 host,
880 );
881 Err(match err {
882 SystemEventError::Advice(err) => {
883 ExecutionError::AdviceError { label, source_file, err }
884 },
885 SystemEventError::Operation(err) => {
886 ExecutionError::OperationError { label, source_file, err }
887 },
888 SystemEventError::Memory(err) => {
889 ExecutionError::MemoryError { label, source_file, err }
890 },
891 SystemEventError::Deferred(err) => {
892 ExecutionError::DeferredError { label, source_file, err }
893 },
894 })
895 },
896 (Err(err), None) => {
897 let (label, source_file) = get_label_and_source_file();
898 Err(match err {
899 SystemEventError::Advice(err) => {
900 ExecutionError::AdviceError { label, source_file, err }
901 },
902 SystemEventError::Operation(err) => {
903 ExecutionError::OperationError { label, source_file, err }
904 },
905 SystemEventError::Memory(err) => {
906 ExecutionError::MemoryError { label, source_file, err }
907 },
908 SystemEventError::Deferred(err) => {
909 ExecutionError::DeferredError { label, source_file, err }
910 },
911 })
912 },
913 }
914 }
915}
916
917impl<T> MapExecErrWithOpIdx<T> for Result<T, IoError> {
919 #[inline(always)]
920 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
921 match self {
922 Ok(v) => Ok(v),
923 Err(err) => {
924 let (label, source_file) = get_label_and_source_file();
925 Err(match err {
926 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
927 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
928 IoError::Operation(err) => {
929 ExecutionError::OperationError { label, source_file, err }
930 },
931 IoError::Execution(boxed_err) => *boxed_err,
933 })
934 },
935 }
936 }
937
938 #[inline(always)]
939 fn map_exec_err_with_package_source_op_idx(
940 self,
941 context: Option<PackageSourceDebugContext<'_>>,
942 host: &(dyn BaseHost + '_),
943 op_idx: usize,
944 ) -> Result<T, ExecutionError> {
945 match (self, context) {
946 (Ok(v), _) => Ok(v),
947 (Err(IoError::Execution(boxed_err)), _) => Err(*boxed_err),
948 (Err(err), Some(context)) => {
949 let (label, source_file) = label_and_source_file_from_location(
950 context.assembly_location(Some(op_idx)).as_ref(),
951 host,
952 );
953 Err(match err {
954 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
955 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
956 IoError::Operation(err) => {
957 ExecutionError::OperationError { label, source_file, err }
958 },
959 IoError::Execution(_) => unreachable!("handled above"),
960 })
961 },
962 (Err(err), None) => {
963 let (label, source_file) = get_label_and_source_file();
964 Err(match err {
965 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
966 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
967 IoError::Operation(err) => {
968 ExecutionError::OperationError { label, source_file, err }
969 },
970 IoError::Execution(_) => unreachable!("handled above"),
971 })
972 },
973 }
974 }
975}
976
977impl<T> MapExecErrWithOpIdx<T> for Result<T, CryptoError> {
979 #[inline(always)]
980 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
981 match self {
982 Ok(v) => Ok(v),
983 Err(err) => {
984 let (label, source_file) = get_label_and_source_file();
985 Err(match err {
986 CryptoError::Advice(err) => {
987 ExecutionError::AdviceError { label, source_file, err }
988 },
989 CryptoError::Operation(err) => {
990 ExecutionError::OperationError { label, source_file, err }
991 },
992 })
993 },
994 }
995 }
996
997 #[inline(always)]
998 fn map_exec_err_with_package_source_op_idx(
999 self,
1000 context: Option<PackageSourceDebugContext<'_>>,
1001 host: &(dyn BaseHost + '_),
1002 op_idx: usize,
1003 ) -> Result<T, ExecutionError> {
1004 match (self, context) {
1005 (Ok(v), _) => Ok(v),
1006 (Err(err), Some(context)) => {
1007 let (label, source_file) = label_and_source_file_from_location(
1008 context.assembly_location(Some(op_idx)).as_ref(),
1009 host,
1010 );
1011 Err(match err {
1012 CryptoError::Advice(err) => {
1013 ExecutionError::AdviceError { label, source_file, err }
1014 },
1015 CryptoError::Operation(err) => ExecutionError::OperationError {
1016 label,
1017 source_file,
1018 err: err.with_package_debug_info(context.debug_info()),
1019 },
1020 })
1021 },
1022 (Err(err), None) => {
1023 let (label, source_file) = get_label_and_source_file();
1024 Err(match err {
1025 CryptoError::Advice(err) => {
1026 ExecutionError::AdviceError { label, source_file, err }
1027 },
1028 CryptoError::Operation(err) => {
1029 ExecutionError::OperationError { label, source_file, err }
1030 },
1031 })
1032 },
1033 }
1034 }
1035}
1036
1037impl<T> MapExecErrWithOpIdx<T> for Result<T, AceEvalError> {
1039 #[inline(always)]
1040 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
1041 match self {
1042 Ok(v) => Ok(v),
1043 Err(err) => {
1044 let (label, source_file) = get_label_and_source_file();
1045 Err(match err {
1046 AceEvalError::Ace(error) => {
1047 ExecutionError::AceChipError { label, source_file, error }
1048 },
1049 AceEvalError::Memory(err) => {
1050 ExecutionError::MemoryError { label, source_file, err }
1051 },
1052 })
1053 },
1054 }
1055 }
1056
1057 #[inline(always)]
1058 fn map_exec_err_with_package_source_op_idx(
1059 self,
1060 context: Option<PackageSourceDebugContext<'_>>,
1061 host: &(dyn BaseHost + '_),
1062 op_idx: usize,
1063 ) -> Result<T, ExecutionError> {
1064 match (self, context) {
1065 (Ok(v), _) => Ok(v),
1066 (Err(err), Some(context)) => {
1067 let (label, source_file) = label_and_source_file_from_location(
1068 context.assembly_location(Some(op_idx)).as_ref(),
1069 host,
1070 );
1071 Err(match err {
1072 AceEvalError::Ace(error) => {
1073 ExecutionError::AceChipError { label, source_file, error }
1074 },
1075 AceEvalError::Memory(err) => {
1076 ExecutionError::MemoryError { label, source_file, err }
1077 },
1078 })
1079 },
1080 (Err(err), None) => {
1081 let (label, source_file) = get_label_and_source_file();
1082 Err(match err {
1083 AceEvalError::Ace(error) => {
1084 ExecutionError::AceChipError { label, source_file, error }
1085 },
1086 AceEvalError::Memory(err) => {
1087 ExecutionError::MemoryError { label, source_file, err }
1088 },
1089 })
1090 },
1091 }
1092 }
1093}
1094
1095#[cfg(test)]
1099mod error_assertions {
1100 use alloc::sync::Arc;
1101
1102 use miden_core::mast::MastNodeId;
1103 use miden_debug_types::{ByteIndex, SourceId, Uri};
1104 use miden_mast_package::debug_info::{
1105 DebugSourceAsmOp, DebugSourceNode, PackageDebugInfoBuilder,
1106 };
1107
1108 use super::*;
1109
1110 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
1112
1113 fn _assert_execution_error_bounds(err: ExecutionError) {
1114 _assert_error_is_send_sync_static(err);
1115 }
1116
1117 fn debug_asm_op(
1118 builder: &mut PackageDebugInfoBuilder,
1119 op_idx: u32,
1120 location: Option<Location>,
1121 context_name: &str,
1122 op_name: &str,
1123 ) -> DebugSourceAsmOp {
1124 DebugSourceAsmOp::new(
1125 op_idx,
1126 location.map(|location| builder.add_location(location)),
1127 builder.add_string(context_name),
1128 builder.add_string(op_name),
1129 1,
1130 )
1131 }
1132
1133 fn debug_source_node(exec_node: u32, asm_ops: Vec<DebugSourceAsmOp>) -> DebugSourceNode {
1134 let op_end = asm_ops.iter().map(|row| row.op_idx + 1).max().unwrap_or(1);
1135 DebugSourceNode {
1136 exec_node: MastNodeId::from(exec_node),
1137 children: Vec::new(),
1138 op_start: 0,
1139 op_end,
1140 asm_ops,
1141 debug_vars: Vec::new(),
1142 inline_calls: Vec::new(),
1143 }
1144 }
1145
1146 struct RecordingHost {
1147 expected_location: Location,
1148 returned_span: SourceSpan,
1149 }
1150
1151 impl BaseHost for RecordingHost {
1152 fn get_label_and_source_file(
1153 &self,
1154 location: &Location,
1155 ) -> (SourceSpan, Option<Arc<SourceFile>>) {
1156 assert_eq!(location, &self.expected_location);
1157 (self.returned_span, None)
1158 }
1159 }
1160
1161 #[test]
1162 fn package_source_context_resolves_by_source_occurrence() {
1163 let location_a = Location::new(
1164 Uri::new("file://pkg/first.masm"),
1165 ByteIndex::new(10),
1166 ByteIndex::new(13),
1167 );
1168 let location_b = Location::new(
1169 Uri::new("file://pkg/second.masm"),
1170 ByteIndex::new(20),
1171 ByteIndex::new(24),
1172 );
1173 let later_location_b = Location::new(
1174 Uri::new("file://pkg/second-later.masm"),
1175 ByteIndex::new(30),
1176 ByteIndex::new(35),
1177 );
1178 let mut builder = PackageDebugInfoBuilder::default();
1179 let source_a_op = debug_asm_op(&mut builder, 0, Some(location_a), "first", "add");
1180 let _source_a = builder.add_node(debug_source_node(0, vec![source_a_op])).unwrap();
1181 let source_b_later_op =
1182 debug_asm_op(&mut builder, 2, Some(later_location_b), "second_later", "mul");
1183 let source_b_op = debug_asm_op(&mut builder, 0, Some(location_b.clone()), "second", "add");
1184 let source_b = builder
1185 .add_node(debug_source_node(1, vec![source_b_later_op, source_b_op]))
1186 .unwrap();
1187 let debug_info = *builder.build();
1188 let host = RecordingHost {
1189 expected_location: location_b,
1190 returned_span: SourceSpan::new(SourceId::new(7), 20u32..24),
1191 };
1192 let context = PackageSourceDebugContext::new(&debug_info, source_b);
1193
1194 assert_eq!(context.assembly_location(None), Some(host.expected_location.clone()));
1195
1196 let err = OperationError::DivideByZero.with_package_source_context(context, &host, Some(0));
1197
1198 match err {
1199 ExecutionError::OperationError { label, source_file, err } => {
1200 assert_eq!(label, host.returned_span);
1201 assert!(source_file.is_none());
1202 assert!(matches!(err, OperationError::DivideByZero));
1203 },
1204 err => panic!("expected operation error, got {err:?}"),
1205 }
1206 }
1207
1208 #[test]
1209 fn package_source_context_without_location_uses_unknown_span() {
1210 let mut builder = PackageDebugInfoBuilder::default();
1211 let asm_op = debug_asm_op(&mut builder, 0, None, "missing_location", "add");
1212 let source_node_id = builder.add_node(debug_source_node(0, vec![asm_op])).unwrap();
1213 let debug_info = *builder.build();
1214 let host = RecordingHost {
1215 expected_location: Location::new(
1216 Uri::new("file://unused.masm"),
1217 ByteIndex::new(0),
1218 ByteIndex::new(0),
1219 ),
1220 returned_span: SourceSpan::new(SourceId::new(7), 20u32..24),
1221 };
1222 let context = PackageSourceDebugContext::new(&debug_info, source_node_id);
1223
1224 let err = advice_error_with_package_source_context(
1225 AdviceError::StackReadFailed,
1226 context,
1227 &host,
1228 Some(0),
1229 );
1230
1231 match err {
1232 ExecutionError::AdviceError { label, source_file, err } => {
1233 assert_eq!(label, SourceSpan::UNKNOWN);
1234 assert!(source_file.is_none());
1235 assert!(matches!(err, AdviceError::StackReadFailed));
1236 },
1237 err => panic!("expected advice error, got {err:?}"),
1238 }
1239 }
1240
1241 #[test]
1242 fn package_debug_info_without_source_node_restores_error_message() {
1243 let mut builder = PackageDebugInfoBuilder::default();
1244 assert!(builder.add_error_message(7, Arc::from("some error message")));
1245 let debug_info = *builder.build();
1246 let context = PackageSourceDebugContext::new_optional(&debug_info, None);
1247 let err = Err::<(), _>(OperationError::FailedAssertion {
1248 err_code: Felt::from_u32(7),
1249 err_msg: None,
1250 })
1251 .map_exec_err_with_package_source_op_idx(
1252 Some(context),
1253 &RecordingHost {
1254 expected_location: Location::new(
1255 Uri::new("file://unused.masm"),
1256 ByteIndex::new(0),
1257 ByteIndex::new(0),
1258 ),
1259 returned_span: SourceSpan::new(SourceId::new(7), 20u32..24),
1260 },
1261 0,
1262 )
1263 .unwrap_err();
1264
1265 match err {
1266 ExecutionError::OperationError {
1267 label,
1268 source_file,
1269 err: OperationError::FailedAssertion { err_msg, .. },
1270 } => {
1271 assert_eq!(label, SourceSpan::UNKNOWN);
1272 assert!(source_file.is_none());
1273 assert_eq!(err_msg.as_deref(), Some("some error message"));
1274 },
1275 err => panic!("expected failed assertion operation error, got {err:?}"),
1276 }
1277 }
1278
1279 #[test]
1280 fn package_debug_info_restores_merkle_path_error_message() {
1281 let mut builder = PackageDebugInfoBuilder::default();
1282 assert!(builder.add_error_message(7, Arc::from("some error message")));
1283 let debug_info = *builder.build();
1284 let err = OperationError::MerklePathVerificationFailed {
1285 inner: Box::new(MerklePathVerificationFailedInner {
1286 value: Word::default(),
1287 index: Felt::from_u32(3),
1288 root: Word::default(),
1289 err_code: Felt::from_u32(7),
1290 err_msg: None,
1291 }),
1292 }
1293 .with_package_debug_info(&debug_info);
1294
1295 match err {
1296 OperationError::MerklePathVerificationFailed { inner } => {
1297 assert_eq!(inner.err_msg.as_deref(), Some("some error message"));
1298 },
1299 err => panic!("expected MerklePathVerificationFailed, got {err:?}"),
1300 }
1301 }
1302}