miden_assembly_syntax/sema/
errors.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_debug_types::{SourceFile, SourceSpan};
5use miden_utils_diagnostics::{Diagnostic, miette};
6
7/// The high-level error type for all semantic analysis errors.
8///
9/// This rolls up multiple errors into a single one, and as such, can emit many
10/// diagnostics at once. You should prefer to gather up errors and continue with
11/// analysis for as long as possible before returning an error of this type, so
12/// as to provide as much information to the user as possible, but there are cases
13/// where doing so would not be profitable - it is a judgement call.
14///
15/// The semantic analyzer does this though, and you can examine its implementation
16/// to see how we approach this during the semantic analysis phase specifically.
17#[derive(Debug, thiserror::Error, Diagnostic)]
18#[error("syntax error")]
19#[diagnostic(help("see emitted diagnostics for details"))]
20pub struct SyntaxError {
21    #[source_code]
22    pub source_file: Arc<SourceFile>,
23    #[related]
24    pub errors: Vec<SemanticAnalysisError>,
25}
26
27/// This type is used when emitting advice/warnings, when those are not being treated as errors.
28///
29/// Like [SyntaxError], this rolls up all such notices into a single batch, and emits them all
30/// at once. The difference is that we never return this as an error from any API, it simply
31/// exists to leverage the diagnostic infrastructure of `miette`.
32#[derive(Debug, thiserror::Error, Diagnostic)]
33#[error("one or more warnings were emitted")]
34#[diagnostic(help("see below for details"))]
35#[cfg_attr(not(feature = "std"), allow(unused))]
36pub struct SyntaxWarning {
37    #[source_code]
38    pub source_file: Arc<SourceFile>,
39    #[related]
40    pub errors: Vec<SemanticAnalysisError>,
41}
42
43/// Represents an error that occurs during semantic analysis
44#[derive(Debug, thiserror::Error, Diagnostic)]
45pub enum SemanticAnalysisError {
46    #[error("invalid program: no entrypoint defined")]
47    #[diagnostic(help(
48        "ensure you define an entrypoint somewhere in the body with `begin`..`end`"
49    ))]
50    MissingEntrypoint,
51    #[error("invalid module: unexpected entrypoint definition")]
52    #[diagnostic(help("library modules cannot contain `begin`..`end` blocks"))]
53    UnexpectedEntrypoint {
54        #[label]
55        span: SourceSpan,
56    },
57    #[error("invalid module: multiple conflicting entrypoints defined")]
58    #[diagnostic(help("an executable module can only have a single `begin`..`end` block"))]
59    MultipleEntrypoints {
60        #[label]
61        span: SourceSpan,
62        #[label]
63        prev_span: SourceSpan,
64    },
65    #[error("invalid program: procedure exports are not allowed")]
66    #[diagnostic(help("perhaps you meant to use `proc` instead of `export`?"))]
67    UnexpectedExport {
68        #[label]
69        span: SourceSpan,
70    },
71    #[error("invalid enum type representation: underlying type must be an integral type")]
72    #[diagnostic()]
73    InvalidEnumRepr {
74        #[label]
75        span: SourceSpan,
76    },
77    #[error("invalid enum discriminant: value is not a valid instance of the {repr} type")]
78    #[diagnostic()]
79    InvalidEnumDiscriminant {
80        #[label]
81        span: SourceSpan,
82        repr: crate::ast::types::Type,
83    },
84    #[error("invalid enum discriminant: value conflicts with another variant of the same enum")]
85    #[diagnostic()]
86    EnumDiscriminantConflict {
87        #[label("this discriminant value conflicts with a previous variant")]
88        span: SourceSpan,
89        #[label("discriminant previously observed here")]
90        prev: SourceSpan,
91    },
92    #[error("symbol conflict: found duplicate definitions of the same name")]
93    #[diagnostic()]
94    SymbolConflict {
95        #[label("conflict occurs here")]
96        span: SourceSpan,
97        #[label("previously defined here")]
98        prev_span: SourceSpan,
99    },
100    #[error("symbol undefined: no such name found in scope")]
101    #[diagnostic(help("are you missing an import?"))]
102    SymbolUndefined {
103        #[label]
104        span: SourceSpan,
105    },
106    #[error("unused import")]
107    #[diagnostic(severity(Warning), help("this import is never used and can be safely removed"))]
108    UnusedImport {
109        #[label]
110        span: SourceSpan,
111    },
112    #[error("missing import: the referenced module has not been imported")]
113    #[diagnostic()]
114    MissingImport {
115        #[label("this reference is invalid without a corresponding import")]
116        span: SourceSpan,
117    },
118    #[error("symbol conflict: import would shadow a previous import of the same name")]
119    #[diagnostic(help(
120        "imports must have unique names within a module, \
121        try aliasing one of the imports if both are needed"
122    ))]
123    ImportConflict {
124        #[label("caused by this import")]
125        span: SourceSpan,
126        #[label("previously imported here")]
127        prev_span: SourceSpan,
128    },
129    #[error(
130        "invalid re-exported procedure: kernel modules may not re-export procedures from other modules"
131    )]
132    #[diagnostic()]
133    ReexportFromKernel {
134        #[label]
135        span: SourceSpan,
136    },
137    #[error("invalid syscall: cannot make a syscall from within the kernel")]
138    #[diagnostic(help("syscalls are only valid outside the kernel, you should use exec instead"))]
139    SyscallInKernel {
140        #[label]
141        span: SourceSpan,
142    },
143    #[error("invalid call: kernel modules cannot make calls to external procedures")]
144    #[diagnostic(help(
145        "this call is being made from a kernel module, and may only refer to local procedures"
146    ))]
147    CallInKernel {
148        #[label]
149        span: SourceSpan,
150    },
151    #[error("invalid instruction usage: 'caller' is only valid in kernel modules")]
152    #[diagnostic()]
153    CallerInKernel {
154        #[label]
155        span: SourceSpan,
156    },
157    #[error("invalid syscall: callee must be resolvable to kernel module")]
158    #[diagnostic()]
159    InvalidSyscallTarget {
160        #[label]
161        span: SourceSpan,
162    },
163    #[error("invalid recursive procedure call")]
164    #[diagnostic(help(
165        "this call induces a cycle that returns back to the caller, you must break that cycle"
166    ))]
167    InvalidRecursiveCall {
168        #[label("caused by this call")]
169        span: SourceSpan,
170    },
171    #[error("invalid recursive procedure call")]
172    #[diagnostic(help("this call is self-recursive, which is not allowed"))]
173    SelfRecursive {
174        #[label]
175        span: SourceSpan,
176    },
177    #[error("invalid immediate: value is larger than expected range")]
178    #[diagnostic()]
179    ImmediateOverflow {
180        #[label]
181        span: SourceSpan,
182    },
183    #[error("invalid module: {}", kind)]
184    #[diagnostic(help("try breaking this module up into submodules"))]
185    LimitExceeded {
186        #[label]
187        span: SourceSpan,
188        kind: LimitKind,
189    },
190    #[error("unused docstring")]
191    #[diagnostic(
192        severity(Warning),
193        help(
194            "this docstring is immediately followed by at least one empty line, then another docstring,\
195            if you intended these to be a single docstring, you should remove the empty lines"
196        )
197    )]
198    UnusedDocstring {
199        #[label]
200        span: SourceSpan,
201    },
202    #[error("unused docstring")]
203    #[diagnostic(
204        severity(Warning),
205        help(
206            "module imports cannot have docstrings, you should use line comment syntax here instead"
207        )
208    )]
209    ImportDocstring {
210        #[label]
211        span: SourceSpan,
212    },
213    #[error("invalid constant")]
214    #[diagnostic(help("this constant does not resolve to a value of the right type"))]
215    InvalidConstant {
216        #[label]
217        span: SourceSpan,
218    },
219    #[error("constant evaluation terminated due to infinite recursion")]
220    #[diagnostic(help("dependencies between constants must form an acyclic graph"))]
221    ConstEvalCycle {
222        #[label("occurs while evaluating this expression")]
223        start: SourceSpan,
224        #[label("cycle occurs because we attempt to eval this constant recursively")]
225        detected: SourceSpan,
226    },
227    #[error("advmap key already defined")]
228    AdvMapKeyAlreadyDefined {
229        #[label]
230        span: SourceSpan,
231    },
232}
233
234/// Represents a system limit that was exceeded
235#[derive(Debug, Copy, Clone, PartialEq, Eq)]
236pub enum LimitKind {
237    /// The total number of procedures in a module
238    Procedures,
239    /// The total number of procedure locals
240    Locals,
241    /// The total number of imports in a module
242    Imports,
243    /// The total number of calls to imports
244    ///
245    /// TODO(pauls): Is this even a limit anymore?
246    CalledImports,
247    /// The total number of instructions in a procedure.
248    Instructions,
249}
250
251impl fmt::Display for LimitKind {
252    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
253        match self {
254            Self::Procedures => f.write_str("too many procedures in module"),
255            Self::Locals => f.write_str("too many procedure locals"),
256            Self::Imports => f.write_str("too many imported procedures"),
257            Self::CalledImports => f.write_str("too many calls to imported procedures"),
258            Self::Instructions => f.write_str("too many instructions in block"),
259        }
260    }
261}