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