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), 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<&'a 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.location.as_ref()
490 }
491}
492
493fn label_and_source_file_from_location(
494 location: Option<&Location>,
495 host: &(dyn BaseHost + '_),
496) -> (SourceSpan, Option<Arc<SourceFile>>) {
497 location.map_or_else(
498 || (SourceSpan::UNKNOWN, None),
499 |location| host.get_label_and_source_file(location),
500 )
501}
502
503fn get_label_and_source_file() -> (SourceSpan, Option<Arc<SourceFile>>) {
509 (SourceSpan::UNKNOWN, None)
510}
511
512pub fn advice_error_with_context(err: AdviceError) -> ExecutionError {
517 let (label, source_file) = get_label_and_source_file();
518 ExecutionError::AdviceError { label, source_file, err }
519}
520
521pub fn advice_error_with_package_source_context(
523 err: AdviceError,
524 context: PackageSourceDebugContext<'_>,
525 host: &(dyn BaseHost + '_),
526 op_idx: Option<usize>,
527) -> ExecutionError {
528 let (label, source_file) =
529 label_and_source_file_from_location(context.assembly_location(op_idx), host);
530 ExecutionError::AdviceError { label, source_file, err }
531}
532
533pub fn event_error_with_context(
538 error: EventError,
539 event_id: EventId,
540 event_name: Option<EventName>,
541) -> ExecutionError {
542 let (label, source_file) = get_label_and_source_file();
543 ExecutionError::EventError {
544 label,
545 source_file,
546 event_id,
547 event_name,
548 error,
549 }
550}
551
552pub fn event_error_with_package_source_context(
554 error: EventError,
555 context: PackageSourceDebugContext<'_>,
556 host: &(dyn BaseHost + '_),
557 op_idx: Option<usize>,
558 event_id: EventId,
559 event_name: Option<EventName>,
560) -> ExecutionError {
561 let (label, source_file) =
562 label_and_source_file_from_location(context.assembly_location(op_idx), host);
563 ExecutionError::EventError {
564 label,
565 source_file,
566 event_id,
567 event_name,
568 error,
569 }
570}
571
572pub fn procedure_not_found_with_context(root_digest: Word) -> ExecutionError {
574 let (label, source_file) = get_label_and_source_file();
575 ExecutionError::ProcedureNotFound { label, source_file, root_digest }
576}
577
578pub fn procedure_not_found_with_package_source_context(
580 root_digest: Word,
581 context: PackageSourceDebugContext<'_>,
582 host: &(dyn BaseHost + '_),
583) -> ExecutionError {
584 let (label, source_file) =
585 label_and_source_file_from_location(context.assembly_location(None), host);
586 ExecutionError::ProcedureNotFound { label, source_file, root_digest }
587}
588
589pub fn malformed_mast_forest_with_context(
591 root_digest: Word,
592 context: Option<PackageSourceDebugContext<'_>>,
593 host: &(dyn BaseHost + '_),
594) -> ExecutionError {
595 let err = OperationError::MalformedMastForestInHost { root_digest };
596 match context {
597 Some(context) => err.with_package_source_context(context, host, None),
598 None => err.with_context(),
599 }
600}
601
602pub trait MapExecErr<T> {
615 fn map_exec_err(self) -> Result<T, ExecutionError>;
616}
617
618pub trait MapExecErrWithOpIdx<T> {
623 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError>;
624
625 fn map_exec_err_with_package_source_op_idx(
626 self,
627 context: Option<PackageSourceDebugContext<'_>>,
628 _host: &(dyn BaseHost + '_),
629 _op_idx: usize,
630 ) -> Result<T, ExecutionError>
631 where
632 Self: Sized,
633 {
634 if context.is_some() {
635 return Err(ExecutionError::Internal(
636 "package source context is unsupported for this error type",
637 ));
638 }
639
640 self.map_exec_err_with_op_idx()
641 }
642}
643
644pub trait MapExecErrNoCtx<T> {
649 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError>;
650}
651
652impl<T> MapExecErr<T> for Result<T, OperationError> {
654 #[inline(always)]
655 fn map_exec_err(self) -> Result<T, ExecutionError> {
656 match self {
657 Ok(v) => Ok(v),
658 Err(err) => {
659 let (label, source_file) = get_label_and_source_file();
660 Err(ExecutionError::OperationError { label, source_file, err })
661 },
662 }
663 }
664}
665
666impl<T> MapExecErrWithOpIdx<T> for Result<T, OperationError> {
667 #[inline(always)]
668 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
669 match self {
670 Ok(v) => Ok(v),
671 Err(err) => {
672 let (label, source_file) = get_label_and_source_file();
673 Err(ExecutionError::OperationError { label, source_file, err })
674 },
675 }
676 }
677
678 #[inline(always)]
679 fn map_exec_err_with_package_source_op_idx(
680 self,
681 context: Option<PackageSourceDebugContext<'_>>,
682 host: &(dyn BaseHost + '_),
683 op_idx: usize,
684 ) -> Result<T, ExecutionError> {
685 match (self, context) {
686 (Ok(v), _) => Ok(v),
687 (Err(err), Some(context)) => {
688 Err(err.with_package_source_context(context, host, Some(op_idx)))
689 },
690 (Err(err), None) => {
691 let (label, source_file) = get_label_and_source_file();
692 Err(ExecutionError::OperationError { label, source_file, err })
693 },
694 }
695 }
696}
697
698impl<T> MapExecErrNoCtx<T> for Result<T, OperationError> {
699 #[inline(always)]
700 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
701 match self {
702 Ok(v) => Ok(v),
703 Err(err) => Err(ExecutionError::OperationError {
704 label: SourceSpan::UNKNOWN,
705 source_file: None,
706 err,
707 }),
708 }
709 }
710}
711
712impl<T> MapExecErr<T> for Result<T, AdviceError> {
714 #[inline(always)]
715 fn map_exec_err(self) -> Result<T, ExecutionError> {
716 match self {
717 Ok(v) => Ok(v),
718 Err(err) => Err(advice_error_with_context(err)),
719 }
720 }
721}
722
723impl<T> MapExecErrNoCtx<T> for Result<T, AdviceError> {
724 #[inline(always)]
725 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
726 match self {
727 Ok(v) => Ok(v),
728 Err(err) => Err(ExecutionError::AdviceError {
729 label: SourceSpan::UNKNOWN,
730 source_file: None,
731 err,
732 }),
733 }
734 }
735}
736
737impl<T> MapExecErr<T> for Result<T, MemoryError> {
739 #[inline(always)]
740 fn map_exec_err(self) -> Result<T, ExecutionError> {
741 match self {
742 Ok(v) => Ok(v),
743 Err(err) => {
744 let (label, source_file) = get_label_and_source_file();
745 Err(ExecutionError::MemoryError { label, source_file, err })
746 },
747 }
748 }
749}
750
751impl<T> MapExecErrWithOpIdx<T> for Result<T, MemoryError> {
752 #[inline(always)]
753 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
754 match self {
755 Ok(v) => Ok(v),
756 Err(err) => {
757 let (label, source_file) = get_label_and_source_file();
758 Err(ExecutionError::MemoryError { label, source_file, err })
759 },
760 }
761 }
762
763 #[inline(always)]
764 fn map_exec_err_with_package_source_op_idx(
765 self,
766 context: Option<PackageSourceDebugContext<'_>>,
767 host: &(dyn BaseHost + '_),
768 op_idx: usize,
769 ) -> Result<T, ExecutionError> {
770 match (self, context) {
771 (Ok(v), _) => Ok(v),
772 (Err(err), Some(context)) => {
773 let (label, source_file) = label_and_source_file_from_location(
774 context.assembly_location(Some(op_idx)),
775 host,
776 );
777 Err(ExecutionError::MemoryError { label, source_file, err })
778 },
779 (Err(err), None) => {
780 let (label, source_file) = get_label_and_source_file();
781 Err(ExecutionError::MemoryError { label, source_file, err })
782 },
783 }
784 }
785}
786
787impl<T> MapExecErr<T> for Result<T, SystemEventError> {
789 #[inline(always)]
790 fn map_exec_err(self) -> Result<T, ExecutionError> {
791 match self {
792 Ok(v) => Ok(v),
793 Err(err) => {
794 let (label, source_file) = get_label_and_source_file();
795 Err(match err {
796 SystemEventError::Advice(err) => {
797 ExecutionError::AdviceError { label, source_file, err }
798 },
799 SystemEventError::Operation(err) => {
800 ExecutionError::OperationError { label, source_file, err }
801 },
802 SystemEventError::Memory(err) => {
803 ExecutionError::MemoryError { label, source_file, err }
804 },
805 })
806 },
807 }
808 }
809}
810
811impl<T> MapExecErrWithOpIdx<T> for Result<T, SystemEventError> {
812 #[inline(always)]
813 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
814 match self {
815 Ok(v) => Ok(v),
816 Err(err) => {
817 let (label, source_file) = get_label_and_source_file();
818 Err(match err {
819 SystemEventError::Advice(err) => {
820 ExecutionError::AdviceError { label, source_file, err }
821 },
822 SystemEventError::Operation(err) => {
823 ExecutionError::OperationError { label, source_file, err }
824 },
825 SystemEventError::Memory(err) => {
826 ExecutionError::MemoryError { label, source_file, err }
827 },
828 })
829 },
830 }
831 }
832
833 #[inline(always)]
834 fn map_exec_err_with_package_source_op_idx(
835 self,
836 context: Option<PackageSourceDebugContext<'_>>,
837 host: &(dyn BaseHost + '_),
838 op_idx: usize,
839 ) -> Result<T, ExecutionError> {
840 match (self, context) {
841 (Ok(v), _) => Ok(v),
842 (Err(err), Some(context)) => {
843 let (label, source_file) = label_and_source_file_from_location(
844 context.assembly_location(Some(op_idx)),
845 host,
846 );
847 Err(match err {
848 SystemEventError::Advice(err) => {
849 ExecutionError::AdviceError { label, source_file, err }
850 },
851 SystemEventError::Operation(err) => {
852 ExecutionError::OperationError { label, source_file, err }
853 },
854 SystemEventError::Memory(err) => {
855 ExecutionError::MemoryError { label, source_file, err }
856 },
857 })
858 },
859 (Err(err), None) => {
860 let (label, source_file) = get_label_and_source_file();
861 Err(match err {
862 SystemEventError::Advice(err) => {
863 ExecutionError::AdviceError { label, source_file, err }
864 },
865 SystemEventError::Operation(err) => {
866 ExecutionError::OperationError { label, source_file, err }
867 },
868 SystemEventError::Memory(err) => {
869 ExecutionError::MemoryError { label, source_file, err }
870 },
871 })
872 },
873 }
874 }
875}
876
877impl<T> MapExecErrWithOpIdx<T> for Result<T, IoError> {
879 #[inline(always)]
880 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
881 match self {
882 Ok(v) => Ok(v),
883 Err(err) => {
884 let (label, source_file) = get_label_and_source_file();
885 Err(match err {
886 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
887 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
888 IoError::Operation(err) => {
889 ExecutionError::OperationError { label, source_file, err }
890 },
891 IoError::Execution(boxed_err) => *boxed_err,
893 })
894 },
895 }
896 }
897
898 #[inline(always)]
899 fn map_exec_err_with_package_source_op_idx(
900 self,
901 context: Option<PackageSourceDebugContext<'_>>,
902 host: &(dyn BaseHost + '_),
903 op_idx: usize,
904 ) -> Result<T, ExecutionError> {
905 match (self, context) {
906 (Ok(v), _) => Ok(v),
907 (Err(IoError::Execution(boxed_err)), _) => Err(*boxed_err),
908 (Err(err), Some(context)) => {
909 let (label, source_file) = label_and_source_file_from_location(
910 context.assembly_location(Some(op_idx)),
911 host,
912 );
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(_) => unreachable!("handled above"),
920 })
921 },
922 (Err(err), None) => {
923 let (label, source_file) = get_label_and_source_file();
924 Err(match err {
925 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
926 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
927 IoError::Operation(err) => {
928 ExecutionError::OperationError { label, source_file, err }
929 },
930 IoError::Execution(_) => unreachable!("handled above"),
931 })
932 },
933 }
934 }
935}
936
937impl<T> MapExecErrWithOpIdx<T> for Result<T, CryptoError> {
939 #[inline(always)]
940 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
941 match self {
942 Ok(v) => Ok(v),
943 Err(err) => {
944 let (label, source_file) = get_label_and_source_file();
945 Err(match err {
946 CryptoError::Advice(err) => {
947 ExecutionError::AdviceError { label, source_file, err }
948 },
949 CryptoError::Operation(err) => {
950 ExecutionError::OperationError { label, source_file, err }
951 },
952 })
953 },
954 }
955 }
956
957 #[inline(always)]
958 fn map_exec_err_with_package_source_op_idx(
959 self,
960 context: Option<PackageSourceDebugContext<'_>>,
961 host: &(dyn BaseHost + '_),
962 op_idx: usize,
963 ) -> Result<T, ExecutionError> {
964 match (self, context) {
965 (Ok(v), _) => Ok(v),
966 (Err(err), Some(context)) => {
967 let (label, source_file) = label_and_source_file_from_location(
968 context.assembly_location(Some(op_idx)),
969 host,
970 );
971 Err(match err {
972 CryptoError::Advice(err) => {
973 ExecutionError::AdviceError { label, source_file, err }
974 },
975 CryptoError::Operation(err) => ExecutionError::OperationError {
976 label,
977 source_file,
978 err: err.with_package_debug_info(context.debug_info()),
979 },
980 })
981 },
982 (Err(err), None) => {
983 let (label, source_file) = get_label_and_source_file();
984 Err(match err {
985 CryptoError::Advice(err) => {
986 ExecutionError::AdviceError { label, source_file, err }
987 },
988 CryptoError::Operation(err) => {
989 ExecutionError::OperationError { label, source_file, err }
990 },
991 })
992 },
993 }
994 }
995}
996
997impl<T> MapExecErrWithOpIdx<T> for Result<T, AceEvalError> {
999 #[inline(always)]
1000 fn map_exec_err_with_op_idx(self) -> Result<T, ExecutionError> {
1001 match self {
1002 Ok(v) => Ok(v),
1003 Err(err) => {
1004 let (label, source_file) = get_label_and_source_file();
1005 Err(match err {
1006 AceEvalError::Ace(error) => {
1007 ExecutionError::AceChipError { label, source_file, error }
1008 },
1009 AceEvalError::Memory(err) => {
1010 ExecutionError::MemoryError { label, source_file, err }
1011 },
1012 })
1013 },
1014 }
1015 }
1016
1017 #[inline(always)]
1018 fn map_exec_err_with_package_source_op_idx(
1019 self,
1020 context: Option<PackageSourceDebugContext<'_>>,
1021 host: &(dyn BaseHost + '_),
1022 op_idx: usize,
1023 ) -> Result<T, ExecutionError> {
1024 match (self, context) {
1025 (Ok(v), _) => Ok(v),
1026 (Err(err), Some(context)) => {
1027 let (label, source_file) = label_and_source_file_from_location(
1028 context.assembly_location(Some(op_idx)),
1029 host,
1030 );
1031 Err(match err {
1032 AceEvalError::Ace(error) => {
1033 ExecutionError::AceChipError { label, source_file, error }
1034 },
1035 AceEvalError::Memory(err) => {
1036 ExecutionError::MemoryError { label, source_file, err }
1037 },
1038 })
1039 },
1040 (Err(err), None) => {
1041 let (label, source_file) = get_label_and_source_file();
1042 Err(match err {
1043 AceEvalError::Ace(error) => {
1044 ExecutionError::AceChipError { label, source_file, error }
1045 },
1046 AceEvalError::Memory(err) => {
1047 ExecutionError::MemoryError { label, source_file, err }
1048 },
1049 })
1050 },
1051 }
1052 }
1053}
1054
1055#[cfg(test)]
1059mod error_assertions {
1060 use alloc::sync::Arc;
1061
1062 use miden_debug_types::{ByteIndex, SourceId, Uri};
1063 use miden_mast_package::debug_info::{
1064 DebugErrorMessage, DebugErrorMessagesSection, DebugSourceAsmOp, DebugSourceMapSection,
1065 PackageDebugInfo,
1066 };
1067
1068 use super::*;
1069
1070 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
1072
1073 fn _assert_execution_error_bounds(err: ExecutionError) {
1074 _assert_error_is_send_sync_static(err);
1075 }
1076
1077 struct RecordingHost {
1078 expected_location: Location,
1079 returned_span: SourceSpan,
1080 }
1081
1082 impl BaseHost for RecordingHost {
1083 fn get_label_and_source_file(
1084 &self,
1085 location: &Location,
1086 ) -> (SourceSpan, Option<Arc<SourceFile>>) {
1087 assert_eq!(location, &self.expected_location);
1088 (self.returned_span, None)
1089 }
1090 }
1091
1092 #[test]
1093 fn package_source_context_resolves_by_source_occurrence() {
1094 let source_a = DebugSourceNodeId::from(0);
1095 let source_b = DebugSourceNodeId::from(1);
1096 let location_a = Location::new(
1097 Uri::new("file://pkg/first.masm"),
1098 ByteIndex::new(10),
1099 ByteIndex::new(13),
1100 );
1101 let location_b = Location::new(
1102 Uri::new("file://pkg/second.masm"),
1103 ByteIndex::new(20),
1104 ByteIndex::new(24),
1105 );
1106 let later_location_b = Location::new(
1107 Uri::new("file://pkg/second-later.masm"),
1108 ByteIndex::new(30),
1109 ByteIndex::new(35),
1110 );
1111 let debug_info =
1112 PackageDebugInfo::default().with_source_map(DebugSourceMapSection::from_parts(
1113 vec![
1114 DebugSourceAsmOp::new(
1115 source_a,
1116 0,
1117 Some(location_a),
1118 "first".into(),
1119 "add".into(),
1120 1,
1121 ),
1122 DebugSourceAsmOp::new(
1123 source_b,
1124 2,
1125 Some(later_location_b),
1126 "second_later".into(),
1127 "mul".into(),
1128 1,
1129 ),
1130 DebugSourceAsmOp::new(
1131 source_b,
1132 0,
1133 Some(location_b.clone()),
1134 "second".into(),
1135 "add".into(),
1136 1,
1137 ),
1138 ],
1139 Vec::new(),
1140 ));
1141 let host = RecordingHost {
1142 expected_location: location_b,
1143 returned_span: SourceSpan::new(SourceId::new(7), 20u32..24),
1144 };
1145 let context = PackageSourceDebugContext::new(&debug_info, source_b);
1146
1147 assert_eq!(context.assembly_location(None), Some(&host.expected_location));
1148
1149 let err = OperationError::DivideByZero.with_package_source_context(context, &host, Some(0));
1150
1151 match err {
1152 ExecutionError::OperationError { label, source_file, err } => {
1153 assert_eq!(label, host.returned_span);
1154 assert!(source_file.is_none());
1155 assert!(matches!(err, OperationError::DivideByZero));
1156 },
1157 err => panic!("expected operation error, got {err:?}"),
1158 }
1159 }
1160
1161 #[test]
1162 fn package_source_context_without_location_uses_unknown_span() {
1163 let source_node_id = DebugSourceNodeId::from(0);
1164 let debug_info =
1165 PackageDebugInfo::default().with_source_map(DebugSourceMapSection::from_parts(
1166 vec![DebugSourceAsmOp::new(
1167 source_node_id,
1168 0,
1169 None,
1170 "missing_location".into(),
1171 "add".into(),
1172 1,
1173 )],
1174 Vec::new(),
1175 ));
1176 let host = RecordingHost {
1177 expected_location: Location::new(
1178 Uri::new("file://unused.masm"),
1179 ByteIndex::new(0),
1180 ByteIndex::new(0),
1181 ),
1182 returned_span: SourceSpan::new(SourceId::new(7), 20u32..24),
1183 };
1184 let context = PackageSourceDebugContext::new(&debug_info, source_node_id);
1185
1186 let err = advice_error_with_package_source_context(
1187 AdviceError::StackReadFailed,
1188 context,
1189 &host,
1190 Some(0),
1191 );
1192
1193 match err {
1194 ExecutionError::AdviceError { label, source_file, err } => {
1195 assert_eq!(label, SourceSpan::UNKNOWN);
1196 assert!(source_file.is_none());
1197 assert!(matches!(err, AdviceError::StackReadFailed));
1198 },
1199 err => panic!("expected advice error, got {err:?}"),
1200 }
1201 }
1202
1203 #[test]
1204 fn package_debug_info_without_source_node_restores_error_message() {
1205 let debug_info =
1206 PackageDebugInfo::default().with_error_messages(DebugErrorMessagesSection::from_parts(
1207 vec![DebugErrorMessage::new(7, Arc::from("some error message"))],
1208 ));
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 debug_info =
1245 PackageDebugInfo::default().with_error_messages(DebugErrorMessagesSection::from_parts(
1246 vec![DebugErrorMessage::new(7, Arc::from("some error message"))],
1247 ));
1248 let err = OperationError::MerklePathVerificationFailed {
1249 inner: Box::new(MerklePathVerificationFailedInner {
1250 value: Word::default(),
1251 index: Felt::from_u32(3),
1252 root: Word::default(),
1253 err_code: Felt::from_u32(7),
1254 err_msg: None,
1255 }),
1256 }
1257 .with_package_debug_info(&debug_info);
1258
1259 match err {
1260 OperationError::MerklePathVerificationFailed { inner } => {
1261 assert_eq!(inner.err_msg.as_deref(), Some("some error message"));
1262 },
1263 err => panic!("expected MerklePathVerificationFailed, got {err:?}"),
1264 }
1265 }
1266}