1use alloc::{boxed::Box, string::String, sync::Arc};
2
3use vm_core::mast::MastForestError;
4
5use crate::{
6 LibraryNamespace, LibraryPath, SourceSpan,
7 ast::QualifiedProcedureName,
8 diagnostics::{Diagnostic, RelatedError, RelatedLabel, SourceFile},
9};
10
11#[derive(Debug, thiserror::Error, Diagnostic)]
16#[non_exhaustive]
17pub enum AssemblyError {
18 #[error("there are no modules to analyze")]
19 #[diagnostic()]
20 Empty,
21 #[error("assembly failed")]
22 #[diagnostic(help("see diagnostics for details"))]
23 Failed {
24 #[related]
25 labels: Box<[RelatedLabel]>,
26 },
27 #[error("found a cycle in the call graph, involving these procedures: {}", nodes.join(", "))]
28 #[diagnostic()]
29 Cycle { nodes: Box<[String]> },
30 #[error(
31 "two procedures found with same mast root, but conflicting definitions ('{first}' and '{second}')"
32 )]
33 #[diagnostic()]
34 ConflictingDefinitions {
35 first: Box<QualifiedProcedureName>,
36 second: Box<QualifiedProcedureName>,
37 },
38 #[error("duplicate definition found for module '{path}'")]
39 #[diagnostic()]
40 DuplicateModule { path: LibraryPath },
41 #[error("undefined module '{path}'")]
42 #[diagnostic()]
43 UndefinedModule {
44 #[label]
45 span: SourceSpan,
46 #[source_code]
47 source_file: Option<Arc<SourceFile>>,
48 path: LibraryPath,
49 },
50 #[error("module namespace is inconsistent with library ('{actual}' vs '{expected}')")]
51 #[diagnostic()]
52 InconsistentNamespace {
53 expected: LibraryNamespace,
54 actual: LibraryNamespace,
55 },
56 #[error("invalid syscall: '{callee}' is not an exported kernel procedure")]
57 #[diagnostic()]
58 InvalidSysCallTarget {
59 #[label("call occurs here")]
60 span: SourceSpan,
61 #[source_code]
62 source_file: Option<Arc<SourceFile>>,
63 callee: Box<QualifiedProcedureName>,
64 },
65 #[error("invalid local word index: {local_addr}")]
66 #[diagnostic(help("the index to a local word must be a multiple of 4"))]
67 InvalidLocalWordIndex {
68 #[label]
69 span: SourceSpan,
70 #[source_code]
71 source_file: Option<Arc<SourceFile>>,
72 local_addr: u16,
73 },
74 #[error(
75 "word accessed from local memory but the number of procedure locals was only {num_proc_locals}"
76 )]
77 #[diagnostic(help(
78 "when a word is accessed from local memory, the number of procedure locals must be at least 4 (since a word is 4 locals)"
79 ))]
80 InvalidNumProcLocals {
81 #[label("local word accessed here")]
82 span: SourceSpan,
83 #[source_code]
84 source_file: Option<Arc<SourceFile>>,
85 num_proc_locals: u16,
86 },
87 #[error("invalid parameter value: {param}; expected to be between {min} and {max}")]
88 #[diagnostic()]
89 InvalidU8Param {
90 #[label]
91 span: SourceSpan,
92 #[source_code]
93 source_file: Option<Arc<SourceFile>>,
94 param: u8,
95 min: u8,
96 max: u8,
97 },
98 #[error("invalid use of 'caller' instruction outside of kernel")]
99 #[diagnostic(help(
100 "the 'caller' instruction is only allowed in procedures defined in a kernel"
101 ))]
102 CallerOutsideOfKernel {
103 #[label]
104 span: SourceSpan,
105 #[source_code]
106 source_file: Option<Arc<SourceFile>>,
107 },
108
109 #[error("invalid procedure: body must contain at least one instruction if it has decorators")]
110 #[diagnostic()]
111 EmptyProcedureBodyWithDecorators {
112 span: SourceSpan,
113 #[source_code]
114 source_file: Option<Arc<SourceFile>>,
115 },
116 #[error("number of procedure locals was not set (or set to 0), but local memory was accessed")]
117 #[diagnostic(help(
118 "to use procedure locals, the number of procedure locals must be declared at the start of the procedure"
119 ))]
120 ProcLocalsNotDeclared {
121 #[label("procedure local accessed here")]
122 span: SourceSpan,
123 #[source_code]
124 source_file: Option<Arc<SourceFile>>,
125 },
126 #[error(transparent)]
127 #[diagnostic(transparent)]
128 Other(RelatedError),
129 #[error("{0}: {1}")]
133 Forest(&'static str, MastForestError),
134}
135
136impl AssemblyError {
137 pub(super) fn forest_error(message: &'static str, source: MastForestError) -> Self {
138 Self::Forest(message, source)
139 }
140}