Skip to main content

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, Symbol};
7
8use crate::intrinsics::Arity;
9
10/// A HIR form this release does not compile, named precisely.
11///
12/// Every variant says what is missing and where it is planned, so a front end
13/// can report "not supported yet" instead of guessing. Forms that need a
14/// missing LSB feature (not a later bcgen-lang release) say so.
15///
16/// # Examples
17///
18/// ```
19/// use bcgen_lang::Unsupported;
20///
21/// assert!(Unsupported::Class.to_string().contains("0.5"));
22/// ```
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24#[non_exhaustive]
25pub enum Unsupported {
26    /// Classes, impls, interfaces with bodies, mixins, and receiver parameters
27    /// (0.5).
28    Class,
29    /// A record or struct literal or pattern naming a declared type, a sum
30    /// variant, or a constructor pattern (typed data arrives with classes in
31    /// 0.5; anonymous records compile, as maps).
32    Record,
33    /// `*e`, `&e`, a `ref` pattern, or binding by reference: the dynamic
34    /// profile has no pointer values (PHP references are places, see
35    /// [`RefPlace`](Unsupported::RefPlace)).
36    Reference,
37    /// A reference (`&`) to a place LSB cannot reference: an appended
38    /// element (`&$a[]`, whose key is known only after the append), a
39    /// variable variable, or a value that is not a place.
40    RefPlace,
41    /// `$$name`: LSB frames have no names at run time.
42    VarVar,
43    /// An atomic, volatile, or fence intrinsic (native backends only; named
44    /// intrinsics compile through the [`Intrinsics`](crate::Intrinsics)
45    /// table).
46    Intrinsic,
47    /// Inline assembly (native backends only).
48    Asm,
49    /// A big-integer literal (no big-integer runtime yet).
50    BigInt,
51    /// A definition of another compilation unit: LSB modules link to host
52    /// functions only, so cross-unit calls wait for loader-lang.
53    ForeignUnit,
54    /// A path resolved only by type (`Vec::new`), or naming a primitive,
55    /// type parameter, or const parameter as a value (typed paths, 0.5).
56    TypeDirected,
57    /// A host function used as a value rather than called: the compiler does
58    /// not know the host function's parameter list, which a first-class
59    /// value needs (LSB ยง5.15).
60    ExternValue,
61    /// Spread or named arguments to a host function: the compiler does not
62    /// know its parameter names, so imports take exactly their positional
63    /// parameters.
64    ExternArgs,
65    /// A conversion at a width dynamic values do not have (`zext`/`sext` to
66    /// fewer than 64 bits, 32-bit bit casts).
67    Conversion,
68    /// A cast to a type the dynamic profile cannot test or produce.
69    Cast,
70    /// A range pattern bound or type test this release cannot evaluate.
71    Pattern,
72}
73
74impl Unsupported {
75    /// A short description of the form and where it is planned.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use bcgen_lang::Unsupported;
81    ///
82    /// assert_eq!(Unsupported::Asm.describe(), "inline assembly (native backends only)");
83    /// ```
84    #[must_use]
85    pub const fn describe(self) -> &'static str {
86        match self {
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 pointer values)"
95            }
96            Self::RefPlace => {
97                "a reference to an appended element, a variable variable, or a value that is not a place"
98            }
99            Self::VarVar => "variable variables (LSB frames have no names at run time)",
100            Self::Intrinsic => "atomic, volatile, and fence intrinsics (native backends only)",
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 => {
110                "a host function used as a value (its parameter list is unknown to the compiler)"
111            }
112            Self::ExternArgs => "spread or named arguments to a host function",
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 the [`Intrinsics`](crate::Intrinsics) table does
195    /// not have.
196    UnknownIntrinsic {
197        /// The intrinsic's name.
198        name: Symbol,
199        /// The intrinsic's node.
200        node: NodeRef,
201        /// Its source span.
202        span: Span,
203    },
204    /// A named intrinsic called with a number of operands its table entry
205    /// does not accept.
206    IntrinsicArity {
207        /// The intrinsic's node.
208        node: NodeRef,
209        /// Its source span.
210        span: Span,
211        /// The operands it takes.
212        arity: Arity,
213        /// The operands it was given.
214        found: u32,
215    },
216    /// A call with more arguments than an LSB call window carries (255,
217    /// counting a method's receiver).
218    TooManyArgs {
219        /// The call.
220        node: NodeRef,
221        /// Its source span.
222        span: Span,
223        /// The limit.
224        limit: u32,
225    },
226    /// A function or closure with more parameters than an LSB parameter list
227    /// carries: 64 when a parameter has a default (the width of the presence
228    /// mask), else 255 (including a generator closure's captures).
229    TooManyParams {
230        /// The function or closure.
231        node: NodeRef,
232        /// Its source span.
233        span: Span,
234        /// The limit.
235        limit: u32,
236    },
237    /// A `finally` (or `defer`) left by more distinct jump targets than its
238    /// `i8` completion kind can number (124).
239    TooManyJumps {
240        /// The `try` or `defer`.
241        node: NodeRef,
242        /// Its source span.
243        span: Span,
244    },
245    /// The instruction budget ([`Options::with_max_instructions`]) ran out.
246    ///
247    /// [`Options::with_max_instructions`]: crate::Options::with_max_instructions
248    BudgetExceeded {
249        /// The budget, in instructions.
250        limit: u64,
251    },
252    /// The LSB builder refused the module: a format limit (65,536 registers
253    /// in one function, 4 GiB in one section, ...).
254    Build(BuildError),
255}
256
257impl CompileError {
258    /// The node the error is about, when it is about one.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use bcgen_lang::CompileError;
264    ///
265    /// assert_eq!(CompileError::BudgetExceeded { limit: 1 }.node(), None);
266    /// ```
267    #[must_use]
268    pub const fn node(&self) -> Option<NodeRef> {
269        match *self {
270            Self::Unsupported { node, .. }
271            | Self::Unresolved { node, .. }
272            | Self::ErrorNode { node, .. }
273            | Self::LiteralOutOfRange { node, .. }
274            | Self::NotAssignable { node, .. }
275            | Self::UnknownIntrinsic { node, .. }
276            | Self::IntrinsicArity { node, .. }
277            | Self::TooManyArgs { node, .. }
278            | Self::TooManyParams { node, .. }
279            | Self::TooManyJumps { node, .. } => Some(node),
280            Self::BudgetExceeded { .. } | Self::Build(_) => None,
281        }
282    }
283
284    /// The source span of the node the error is about, when there is one.
285    ///
286    /// # Examples
287    ///
288    /// ```
289    /// use bcgen_lang::CompileError;
290    ///
291    /// assert_eq!(CompileError::BudgetExceeded { limit: 1 }.span(), None);
292    /// ```
293    #[must_use]
294    pub const fn span(&self) -> Option<Span> {
295        match *self {
296            Self::Unsupported { span, .. }
297            | Self::Unresolved { span, .. }
298            | Self::ErrorNode { span, .. }
299            | Self::LiteralOutOfRange { span, .. }
300            | Self::NotAssignable { span, .. }
301            | Self::UnknownIntrinsic { span, .. }
302            | Self::IntrinsicArity { span, .. }
303            | Self::TooManyArgs { span, .. }
304            | Self::TooManyParams { span, .. }
305            | Self::TooManyJumps { span, .. } => Some(span),
306            Self::BudgetExceeded { .. } | Self::Build(_) => None,
307        }
308    }
309}
310
311impl fmt::Display for CompileError {
312    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313        match self {
314            Self::Unsupported { feature, span, .. } => {
315                write!(f, "unsupported at {span:?}: {feature}")
316            }
317            Self::Unresolved { span, .. } => write!(f, "unresolved path at {span:?}"),
318            Self::ErrorNode { span, .. } => write!(f, "error node at {span:?}"),
319            Self::LiteralOutOfRange { span, .. } => {
320                write!(f, "integer literal at {span:?} is outside the i64 range")
321            }
322            Self::NotAssignable { span, .. } => {
323                write!(f, "assignment target at {span:?} is not a writable place")
324            }
325            Self::UnknownIntrinsic { span, .. } => {
326                write!(f, "intrinsic at {span:?} is not in the intrinsic table")
327            }
328            Self::IntrinsicArity {
329                span, arity, found, ..
330            } => {
331                write!(
332                    f,
333                    "intrinsic at {span:?} takes {arity} operands, not {found}"
334                )
335            }
336            Self::TooManyArgs { span, limit, .. } => {
337                write!(f, "call at {span:?} has more than {limit} arguments")
338            }
339            Self::TooManyParams { span, limit, .. } => {
340                write!(f, "function at {span:?} has more than {limit} parameters")
341            }
342            Self::TooManyJumps { span, .. } => write!(
343                f,
344                "finally at {span:?} is left by more than 124 distinct jump targets"
345            ),
346            Self::BudgetExceeded { limit } => {
347                write!(f, "the instruction budget of {limit} ran out")
348            }
349            Self::Build(e) => write!(f, "LSB builder: {e}"),
350        }
351    }
352}
353
354impl core::error::Error for CompileError {}
355
356impl From<BuildError> for CompileError {
357    fn from(e: BuildError) -> Self {
358        Self::Build(e)
359    }
360}