bcgen_lang/error.rs
1//! Why a unit did not compile.
2
3use core::fmt;
4
5use bytecode_lang::BuildError;
6use hir_lang::{NodeRef, Span};
7
8/// A HIR form this release does not compile, named precisely.
9///
10/// Every variant says what is missing and where it is planned, so a front end
11/// can report "not supported yet" instead of guessing. Forms that need a
12/// missing LSB feature (not a later bcgen-lang release) say so.
13///
14/// # Examples
15///
16/// ```
17/// use bcgen_lang::Unsupported;
18///
19/// assert!(Unsupported::Coroutine.to_string().contains("0.5"));
20/// ```
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22#[non_exhaustive]
23pub enum Unsupported {
24 /// `yield`, `await`, `spawn`, or a function or closure declared `ASYNC` or
25 /// `YIELD` (coroutines arrive in 0.5, with bvm-lang's coroutine group).
26 Coroutine,
27 /// Classes, impls, interfaces with bodies, mixins, and receiver parameters
28 /// (0.5).
29 Class,
30 /// A record or struct literal or pattern naming a declared type, a sum
31 /// variant, or a constructor pattern (typed data arrives with classes in
32 /// 0.5; anonymous records compile, as maps).
33 Record,
34 /// `*e`, `&e`, a `ref` pattern, or binding by reference: the dynamic
35 /// profile has no reference values.
36 Reference,
37 /// A reference assignment or by-reference argument whose place is not a
38 /// local variable (an array element, a property, a global alias). LSB has
39 /// no reference to a map slot; only whole variables can be shared.
40 RefPlace,
41 /// `$$name`: LSB frames have no names at run time.
42 VarVar,
43 /// An atomic, volatile, or fence intrinsic, or a named intrinsic this
44 /// release does not know.
45 Intrinsic,
46 /// Inline assembly (native backends only).
47 Asm,
48 /// A big-integer literal (no big-integer runtime yet).
49 BigInt,
50 /// A definition of another compilation unit: LSB modules link to host
51 /// functions only, so cross-unit calls wait for loader-lang.
52 ForeignUnit,
53 /// A path resolved only by type (`Vec::new`), or naming a primitive,
54 /// type parameter, or const parameter as a value (typed paths, 0.5).
55 TypeDirected,
56 /// A host function used as a value rather than called: LSB imports have a
57 /// fixed arity, which a first-class value does not know.
58 ExternValue,
59 /// Spread, named, or by-reference arguments to a host function, or a
60 /// bodyless function with defaults: imports take exactly their parameters.
61 ExternArgs,
62 /// A conversion at a width dynamic values do not have (`zext`/`sext` to
63 /// fewer than 64 bits, 32-bit bit casts).
64 Conversion,
65 /// A cast to a type the dynamic profile cannot test or produce.
66 Cast,
67 /// A range pattern bound or type test this release cannot evaluate.
68 Pattern,
69}
70
71impl Unsupported {
72 /// A short description of the form and where it is planned.
73 ///
74 /// # Examples
75 ///
76 /// ```
77 /// use bcgen_lang::Unsupported;
78 ///
79 /// assert_eq!(Unsupported::Asm.describe(), "inline assembly (native backends only)");
80 /// ```
81 #[must_use]
82 pub const fn describe(self) -> &'static str {
83 match self {
84 Self::Coroutine => {
85 "coroutines (yield, await, spawn, async and generator functions) arrive in bcgen-lang 0.5"
86 }
87 Self::Class => {
88 "classes, impls, mixins, and receiver parameters arrive in bcgen-lang 0.5"
89 }
90 Self::Record => {
91 "declared record, struct, and sum types arrive with classes in bcgen-lang 0.5"
92 }
93 Self::Reference => {
94 "dereference, borrow, and by-reference patterns (the dynamic profile has no reference values)"
95 }
96 Self::RefPlace => {
97 "a reference to a place that is not a local variable (LSB has no reference to a map slot)"
98 }
99 Self::VarVar => "variable variables (LSB frames have no names at run time)",
100 Self::Intrinsic => "atomic, volatile, fence, or unknown named intrinsics",
101 Self::Asm => "inline assembly (native backends only)",
102 Self::BigInt => "big-integer literals (no big-integer runtime yet)",
103 Self::ForeignUnit => {
104 "definitions of another unit (LSB modules link to host functions only)"
105 }
106 Self::TypeDirected => {
107 "type-directed paths and type-level values arrive with typed fast paths in bcgen-lang 0.5"
108 }
109 Self::ExternValue => "a host function used as a value (LSB imports have a fixed arity)",
110 Self::ExternArgs => {
111 "spread, named, or by-reference arguments to a host function, or defaults on a bodyless function"
112 }
113 Self::Conversion => "a conversion at a width dynamic values do not have",
114 Self::Cast => "a cast to a type the dynamic profile cannot test or produce",
115 Self::Pattern => "a pattern form the dynamic profile cannot test",
116 }
117 }
118}
119
120impl fmt::Display for Unsupported {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.write_str(self.describe())
123 }
124}
125
126/// Why [`compile`](crate::compile) refused a unit.
127///
128/// Errors about the program name the HIR node and its source span, so a
129/// front end can point at the offending code. Compilation stops at the first
130/// error, in the canonical order of the unit.
131///
132/// # Examples
133///
134/// ```
135/// use bcgen_lang::CompileError;
136/// use hir_lang::{Builder, Effects, Expr, Item, ItemKind, List};
137/// use intern_lang::Interner;
138///
139/// // A module whose body failed to lower: it was reported already, so it
140/// // does not compile.
141/// let mut b = Builder::new();
142/// let body = b.expr(Expr::Err);
143/// let module = ItemKind::Module { items: List::EMPTY, body: Some(body), effects: Effects::NONE };
144/// let root = b.item(Item::new(None, module));
145/// let hir = b.finish(root)?;
146///
147/// let err = bcgen_lang::compile(&hir, &Interner::new()).unwrap_err();
148/// assert!(matches!(err, CompileError::ErrorNode { .. }));
149/// # Ok::<(), hir_lang::HirError>(())
150/// ```
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152#[non_exhaustive]
153pub enum CompileError {
154 /// A form this release does not compile.
155 Unsupported {
156 /// What is missing.
157 feature: Unsupported,
158 /// The node.
159 node: NodeRef,
160 /// Its source span.
161 span: Span,
162 },
163 /// A path the resolver left unresolved (or resolved to `Res::Err`): the
164 /// compiler runs on resolved HIR.
165 Unresolved {
166 /// The path's node.
167 node: NodeRef,
168 /// Its source span.
169 span: Span,
170 },
171 /// An error node (`Expr::Err`, `Stmt::Err`, ...) from lenient lowering:
172 /// broken source was reported already and does not compile.
173 ErrorNode {
174 /// The node.
175 node: NodeRef,
176 /// Its source span.
177 span: Span,
178 },
179 /// An integer literal outside the dynamic integer range (`i64`).
180 LiteralOutOfRange {
181 /// The literal's node.
182 node: NodeRef,
183 /// Its source span.
184 span: Span,
185 },
186 /// An assignment target that is not a writable place (a function, a
187 /// constant, an append read).
188 NotAssignable {
189 /// The target's node.
190 node: NodeRef,
191 /// Its source span.
192 span: Span,
193 },
194 /// A named intrinsic called with the wrong number of operands.
195 IntrinsicArity {
196 /// The intrinsic's node.
197 node: NodeRef,
198 /// Its source span.
199 span: Span,
200 /// The operands it takes.
201 expected: u32,
202 },
203 /// A function or closure with more parameters than the calling convention
204 /// carries (64, the width of the presence mask).
205 TooManyParams {
206 /// The function or closure.
207 node: NodeRef,
208 /// Its source span.
209 span: Span,
210 /// The limit.
211 limit: u32,
212 },
213 /// A `finally` (or `defer`) left by more distinct jump targets than its
214 /// `i8` completion kind can number (124).
215 TooManyJumps {
216 /// The `try` or `defer`.
217 node: NodeRef,
218 /// Its source span.
219 span: Span,
220 },
221 /// The instruction budget ([`Options::with_max_instructions`]) ran out.
222 ///
223 /// [`Options::with_max_instructions`]: crate::Options::with_max_instructions
224 BudgetExceeded {
225 /// The budget, in instructions.
226 limit: u64,
227 },
228 /// The LSB builder refused the module: a format limit (65,536 registers
229 /// in one function, 4 GiB in one section, ...).
230 Build(BuildError),
231}
232
233impl CompileError {
234 /// The node the error is about, when it is about one.
235 ///
236 /// # Examples
237 ///
238 /// ```
239 /// use bcgen_lang::CompileError;
240 ///
241 /// assert_eq!(CompileError::BudgetExceeded { limit: 1 }.node(), None);
242 /// ```
243 #[must_use]
244 pub const fn node(&self) -> Option<NodeRef> {
245 match *self {
246 Self::Unsupported { node, .. }
247 | Self::Unresolved { node, .. }
248 | Self::ErrorNode { node, .. }
249 | Self::LiteralOutOfRange { node, .. }
250 | Self::NotAssignable { node, .. }
251 | Self::IntrinsicArity { node, .. }
252 | Self::TooManyParams { node, .. }
253 | Self::TooManyJumps { node, .. } => Some(node),
254 Self::BudgetExceeded { .. } | Self::Build(_) => None,
255 }
256 }
257
258 /// The source span of the node the error is about, when there is one.
259 ///
260 /// # Examples
261 ///
262 /// ```
263 /// use bcgen_lang::CompileError;
264 ///
265 /// assert_eq!(CompileError::BudgetExceeded { limit: 1 }.span(), None);
266 /// ```
267 #[must_use]
268 pub const fn span(&self) -> Option<Span> {
269 match *self {
270 Self::Unsupported { span, .. }
271 | Self::Unresolved { span, .. }
272 | Self::ErrorNode { span, .. }
273 | Self::LiteralOutOfRange { span, .. }
274 | Self::NotAssignable { span, .. }
275 | Self::IntrinsicArity { span, .. }
276 | Self::TooManyParams { span, .. }
277 | Self::TooManyJumps { span, .. } => Some(span),
278 Self::BudgetExceeded { .. } | Self::Build(_) => None,
279 }
280 }
281}
282
283impl fmt::Display for CompileError {
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 match self {
286 Self::Unsupported { feature, span, .. } => {
287 write!(f, "unsupported at {span:?}: {feature}")
288 }
289 Self::Unresolved { span, .. } => write!(f, "unresolved path at {span:?}"),
290 Self::ErrorNode { span, .. } => write!(f, "error node at {span:?}"),
291 Self::LiteralOutOfRange { span, .. } => {
292 write!(f, "integer literal at {span:?} is outside the i64 range")
293 }
294 Self::NotAssignable { span, .. } => {
295 write!(f, "assignment target at {span:?} is not a writable place")
296 }
297 Self::IntrinsicArity { span, expected, .. } => {
298 write!(f, "intrinsic at {span:?} takes {expected} operands")
299 }
300 Self::TooManyParams { span, limit, .. } => {
301 write!(f, "function at {span:?} has more than {limit} parameters")
302 }
303 Self::TooManyJumps { span, .. } => write!(
304 f,
305 "finally at {span:?} is left by more than 124 distinct jump targets"
306 ),
307 Self::BudgetExceeded { limit } => {
308 write!(f, "the instruction budget of {limit} ran out")
309 }
310 Self::Build(e) => write!(f, "LSB builder: {e}"),
311 }
312 }
313}
314
315impl core::error::Error for CompileError {}
316
317impl From<BuildError> for CompileError {
318 fn from(e: BuildError) -> Self {
319 Self::Build(e)
320 }
321}