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