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