Skip to main content

miden_assembly_syntax/sema/
errors.rs

1// Allow unused assignments - required by miette::Diagnostic derive macro
2#![allow(unused_assignments)]
3
4use alloc::{boxed::Box, sync::Arc, vec::Vec};
5use core::fmt;
6
7use miden_debug_types::{SourceFile, SourceSpan};
8use miden_utils_diagnostics::{Diagnostic, miette};
9
10use crate::{
11    Path,
12    ast::{SymbolResolutionError, constants::ConstEvalError},
13};
14
15/// The high-level error type for all semantic analysis errors.
16///
17/// This rolls up multiple errors into a single one, and as such, can emit many
18/// diagnostics at once. You should prefer to gather up errors and continue with
19/// analysis for as long as possible before returning an error of this type, so
20/// as to provide as much information to the user as possible, but there are cases
21/// where doing so would not be profitable - it is a judgement call.
22///
23/// The semantic analyzer does this though, and you can examine its implementation
24/// to see how we approach this during the semantic analysis phase specifically.
25#[derive(Debug, thiserror::Error, Diagnostic)]
26#[error("syntax error")]
27#[diagnostic(help("see emitted diagnostics for details"))]
28pub struct SyntaxError {
29    #[source_code]
30    pub source_file: Arc<SourceFile>,
31    #[related]
32    pub errors: Vec<SemanticAnalysisError>,
33}
34
35/// This type is used when emitting advice/warnings, when those are not being treated as errors.
36///
37/// Like [SyntaxError], this rolls up all such notices into a single batch, and emits them all
38/// at once. The difference is that we never return this as an error from any API, it simply
39/// exists to leverage the diagnostic infrastructure of `miette`.
40#[derive(Debug, thiserror::Error, Diagnostic)]
41#[error("one or more warnings were emitted")]
42#[diagnostic(help("see below for details"))]
43#[cfg_attr(not(feature = "std"), expect(unused))]
44pub struct SyntaxWarning {
45    #[source_code]
46    pub source_file: Arc<SourceFile>,
47    #[related]
48    pub errors: Vec<SemanticAnalysisError>,
49}
50
51/// Identifies the exported surface where a private type was found.
52#[derive(Clone, Copy)]
53pub enum ExportedTypeUse {
54    ProcedureSignature,
55    TypeDeclaration,
56}
57
58impl ExportedTypeUse {
59    pub fn private_type_error(
60        self,
61        span: SourceSpan,
62        defined: SourceSpan,
63    ) -> SemanticAnalysisError {
64        match self {
65            Self::ProcedureSignature => {
66                SemanticAnalysisError::PrivateTypeInExportedSignature { span, defined }
67            },
68            Self::TypeDeclaration => {
69                SemanticAnalysisError::PrivateTypeInExportedType { span, defined }
70            },
71        }
72    }
73}
74
75/// Represents an error that occurs during semantic analysis
76#[derive(Debug, thiserror::Error, Diagnostic)]
77pub enum SemanticAnalysisError {
78    #[error(
79        "conflicting module namespace specification: expected '{expected}', but got '{actual}'"
80    )]
81    NamespaceConflict {
82        expected: Arc<Path>,
83        actual: Arc<Path>,
84        #[label("this declaration conflicts with the expected namespace")]
85        span: SourceSpan,
86    },
87    #[error("invalid namespace '{path}': {err}")]
88    InvalidNamespacePath { path: Arc<Path>, err: crate::PathError },
89    #[error("invalid namespace declaration: must be placed before any other item in the module")]
90    MisplacedNamespaceDeclaration {
91        #[label("make sure this declaration precedes other declarations in this module")]
92        span: SourceSpan,
93    },
94    #[error("invalid module: no namespace declared or explicitly provided")]
95    #[diagnostic(help(
96        "ensure you declare this module's namespace with a `namespace` declaration, or by providing it to the parser"
97    ))]
98    MissingNamespace,
99    #[error("invalid program: no entrypoint defined")]
100    #[diagnostic(help(
101        "ensure you define an entrypoint somewhere in the body with `begin`..`end`"
102    ))]
103    MissingEntrypoint,
104    #[error("invalid module: unexpected entrypoint definition")]
105    #[diagnostic(help("library modules cannot contain `begin`..`end` blocks"))]
106    UnexpectedEntrypoint {
107        #[label]
108        span: SourceSpan,
109    },
110    #[error("invalid module: multiple conflicting entrypoints defined")]
111    #[diagnostic(help("an executable module can only have a single `begin`..`end` block"))]
112    MultipleEntrypoints {
113        #[label]
114        span: SourceSpan,
115        #[label]
116        prev_span: SourceSpan,
117    },
118    #[error("invalid invocation target")]
119    #[diagnostic(help("path contains an invalid component"))]
120    InvalidInvokePath {
121        #[label]
122        span: SourceSpan,
123    },
124    #[error("invalid program: procedure exports are not allowed")]
125    #[diagnostic(help("perhaps you meant to use `proc` instead of `export`?"))]
126    UnexpectedExport {
127        #[label]
128        span: SourceSpan,
129    },
130    #[error("invalid enum type representation: underlying type must be an integral or felt type")]
131    #[diagnostic()]
132    InvalidEnumRepr {
133        #[label]
134        span: SourceSpan,
135    },
136    #[error("invalid enum discriminant: value is not a valid instance of the {repr} type")]
137    #[diagnostic()]
138    InvalidEnumDiscriminant {
139        #[label]
140        span: SourceSpan,
141        repr: crate::ast::types::Type,
142    },
143    #[error("invalid enum discriminant: value conflicts with another variant of the same enum")]
144    #[diagnostic()]
145    EnumDiscriminantConflict {
146        #[label("this discriminant value conflicts with a previous variant")]
147        span: SourceSpan,
148        #[label("discriminant previously observed here")]
149        prev: SourceSpan,
150    },
151    #[error("symbol conflict: found duplicate definitions of the same name")]
152    #[diagnostic()]
153    SymbolConflict {
154        #[label("conflict occurs here")]
155        span: SourceSpan,
156        #[label("previously defined here")]
157        prev_span: SourceSpan,
158    },
159    #[error("dependency conflict: found duplicate 'extern package' declarations")]
160    #[diagnostic()]
161    ExternPackageConflict {
162        #[label("conflict occurs here")]
163        span: SourceSpan,
164        #[label("previously defined here")]
165        prev_span: SourceSpan,
166    },
167    #[error("private type in exported procedure signature")]
168    #[diagnostic(help(
169        "exported procedure signatures may only reference public types, including nested type dependencies"
170    ))]
171    PrivateTypeInExportedSignature {
172        #[label("this exported procedure signature references a private type")]
173        span: SourceSpan,
174        #[label("this type is private")]
175        defined: SourceSpan,
176    },
177    #[error("private type in exported type declaration")]
178    #[diagnostic(help(
179        "exported type declarations may only reference public types, including nested type dependencies"
180    ))]
181    PrivateTypeInExportedType {
182        #[label("this exported type declaration references a private type")]
183        span: SourceSpan,
184        #[label("this type is private")]
185        defined: SourceSpan,
186    },
187    #[error("unused import")]
188    #[diagnostic(severity(Warning), help("this import is never used and can be safely removed"))]
189    UnusedImport {
190        #[label]
191        span: SourceSpan,
192    },
193    #[error("missing import: the referenced module has not been imported")]
194    #[diagnostic()]
195    MissingImport {
196        #[label("this reference is invalid without a corresponding import")]
197        span: SourceSpan,
198    },
199    #[error("symbol conflict: import would shadow a previous import of the same name")]
200    #[diagnostic(help(
201        "imports must have unique names within a module, \
202        try aliasing one of the imports if both are needed"
203    ))]
204    ImportConflict {
205        #[label("caused by this import")]
206        span: SourceSpan,
207        #[label("previously imported here")]
208        prev_span: SourceSpan,
209    },
210    #[error("invalid re-exported module: you may only re-export items")]
211    #[diagnostic()]
212    ReexportedModule {
213        #[label]
214        span: SourceSpan,
215    },
216    #[error("invalid re-export target: kernel procedures may not be re-exported")]
217    #[diagnostic()]
218    ReexportedKernelProcedure {
219        #[label]
220        span: SourceSpan,
221    },
222    #[error(
223        "invalid re-exported procedure: kernel modules may not re-export procedures from other modules"
224    )]
225    #[diagnostic()]
226    ReexportFromKernel {
227        #[label]
228        span: SourceSpan,
229    },
230    #[error("invalid syscall: callee must be resolvable to kernel module")]
231    #[diagnostic()]
232    InvalidSyscallTarget {
233        #[label]
234        span: SourceSpan,
235    },
236    #[error("invalid procedure path: not an item")]
237    #[diagnostic()]
238    InvalidInvokeTargetViaImport {
239        #[label("call occurs here")]
240        span: SourceSpan,
241        #[label("expected this to resolve to a module, but got a module item")]
242        import: SourceSpan,
243    },
244    #[error("invalid recursive procedure call")]
245    #[diagnostic(help(
246        "this call induces a cycle that returns back to the caller, you must break that cycle"
247    ))]
248    InvalidRecursiveCall {
249        #[label("caused by this call")]
250        span: SourceSpan,
251    },
252    #[error("invalid recursive procedure call")]
253    #[diagnostic(help("this call is self-recursive, which is not allowed"))]
254    SelfRecursive {
255        #[label]
256        span: SourceSpan,
257    },
258    #[error("invalid repeat count")]
259    #[diagnostic(help("repeat count must be in the range {min}..={max}"))]
260    InvalidRepeatCount {
261        #[label]
262        span: SourceSpan,
263        min: u32,
264        max: u32,
265    },
266    #[error("invalid immediate: value is larger than expected range")]
267    #[diagnostic()]
268    ImmediateOverflow {
269        #[label]
270        span: SourceSpan,
271    },
272    #[error("invalid module: {}", kind)]
273    #[diagnostic(help("try breaking this module up into submodules"))]
274    LimitExceeded {
275        #[label]
276        span: SourceSpan,
277        kind: LimitKind,
278    },
279    #[error("unused docstring")]
280    #[diagnostic(
281        severity(Warning),
282        help(
283            "this docstring is immediately followed by at least one empty line, then another docstring, \
284            if you intended these to be a single docstring, you should remove the empty lines"
285        )
286    )]
287    UnusedDocstring {
288        #[label]
289        span: SourceSpan,
290    },
291    #[error("unused docstring")]
292    #[diagnostic(severity(Warning), help("trailing docstrings are useless"))]
293    TrailingDocstring {
294        #[label]
295        span: SourceSpan,
296    },
297    #[error("unused docstring")]
298    #[diagnostic(
299        severity(Warning),
300        help(
301            "imports and re-exports cannot have docstrings, you should use line comment syntax here instead"
302        )
303    )]
304    ImportDocstring {
305        #[label]
306        span: SourceSpan,
307    },
308    #[error("advmap key already defined")]
309    #[diagnostic()]
310    AdvMapKeyAlreadyDefined {
311        #[label]
312        span: SourceSpan,
313    },
314    #[error(transparent)]
315    #[diagnostic(transparent)]
316    ConstEvalError(#[from] ConstEvalError),
317    #[error(transparent)]
318    #[diagnostic(transparent)]
319    SymbolResolutionError(#[from] Box<SymbolResolutionError>),
320}
321
322impl From<SymbolResolutionError> for SemanticAnalysisError {
323    fn from(value: SymbolResolutionError) -> Self {
324        Self::SymbolResolutionError(Box::new(value))
325    }
326}
327
328/// Represents a system limit that was exceeded
329#[derive(Debug, Copy, Clone, PartialEq, Eq)]
330pub enum LimitKind {
331    /// The total number of items in a module
332    Items,
333    /// The total number of procedures in a module
334    Procedures,
335    /// The total number of procedure locals
336    Locals,
337    /// The total number of imports in a module
338    Imports,
339    /// The total number of calls to imports
340    ///
341    /// TODO(pauls): Is this even a limit anymore?
342    CalledImports,
343    /// The total number of instructions in a procedure.
344    Instructions,
345}
346
347impl fmt::Display for LimitKind {
348    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
349        match self {
350            Self::Items => f.write_str("too many items in module"),
351            Self::Procedures => f.write_str("too many procedures in module"),
352            Self::Locals => f.write_str("too many procedure locals"),
353            Self::Imports => f.write_str("too many imported procedures"),
354            Self::CalledImports => f.write_str("too many calls to imported procedures"),
355            Self::Instructions => f.write_str("too many instructions in block"),
356        }
357    }
358}