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::Impl.to_string().contains("impl"));
22/// ```
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24#[non_exhaustive]
25pub enum Unsupported {
26    /// A receiver parameter outside a member of a class or mixin (a closure
27    /// or a free function cannot take one).
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    /// An `impl` block (methods of a static language's type): typed fast
73    /// paths, 0.5.
74    Impl,
75    /// A class with more than one base (Python): an LSB struct has one
76    /// parent (LSB §3.1).
77    MultipleInheritance,
78    /// A class, interface, or mixin name used as a value outside `new`,
79    /// `instanceof`, a type test, and member access (`Foo::class` and
80    /// class values are the lowering's job).
81    ClassValue,
82    /// A member reached through a mixin (PHP trait) itself, `T::m()`: only
83    /// the classes that use the mixin have its members (PHP 8.1 deprecates
84    /// calling a trait's static members directly).
85    MixinMember,
86    /// A type test (`catch`, `instanceof` by type) against a class or
87    /// interface the unit does not declare: its struct type is not in the
88    /// module.
89    ClassTest,
90    /// A static member path (`C::m`) whose class the compiler cannot pick:
91    /// the unit declares two classes of that name that both reach the
92    /// member.
93    AmbiguousClass,
94    /// Binding a late-bound static property to a reference
95    /// (`static::$x = &$y`): the reference lives in a global the accessor
96    /// only reads.
97    StaticRef,
98}
99
100impl Unsupported {
101    /// A short description of the form and where it is planned.
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use bcgen_lang::Unsupported;
107    ///
108    /// assert_eq!(Unsupported::Asm.describe(), "inline assembly (native backends only)");
109    /// ```
110    #[must_use]
111    pub const fn describe(self) -> &'static str {
112        match self {
113            Self::Class => "a receiver parameter outside a class or mixin member",
114            Self::Record => {
115                "declared record, struct, and sum types arrive with classes in bcgen-lang 0.5"
116            }
117            Self::Reference => {
118                "dereference, borrow, and by-reference patterns (the dynamic profile has no pointer values)"
119            }
120            Self::RefPlace => {
121                "a reference to an appended element, a variable variable, or a value that is not a place"
122            }
123            Self::VarVar => "variable variables (LSB frames have no names at run time)",
124            Self::Intrinsic => "atomic, volatile, and fence intrinsics (native backends only)",
125            Self::Asm => "inline assembly (native backends only)",
126            Self::BigInt => "big-integer literals (no big-integer runtime yet)",
127            Self::ForeignUnit => {
128                "definitions of another unit (LSB modules link to host functions only)"
129            }
130            Self::TypeDirected => {
131                "type-directed paths and type-level values arrive with typed fast paths in bcgen-lang 0.5"
132            }
133            Self::ExternValue => {
134                "a host function used as a value (its parameter list is unknown to the compiler)"
135            }
136            Self::ExternArgs => "spread or named arguments to a host function",
137            Self::Conversion => "a conversion at a width dynamic values do not have",
138            Self::Cast => "a cast to a type the dynamic profile cannot test or produce",
139            Self::Pattern => "a pattern form the dynamic profile cannot test",
140            Self::Impl => {
141                "impl blocks (static-language methods) arrive with typed fast paths in bcgen-lang 0.5"
142            }
143            Self::MultipleInheritance => {
144                "a class with more than one base (an LSB struct has one parent)"
145            }
146            Self::ClassValue => {
147                "a class name used as a value outside new, instanceof, type tests, and member access"
148            }
149            Self::MixinMember => {
150                "a member reached through a mixin itself (only the classes using it have its members)"
151            }
152            Self::ClassTest => "a type test against a class or interface the unit does not declare",
153            Self::AmbiguousClass => {
154                "a static member path naming one of two same-named classes that both reach the member"
155            }
156            Self::StaticRef => "binding a late-bound static property to a reference",
157        }
158    }
159}
160
161/// A class graph or class member LSB cannot hold, or one PHP rejects at
162/// compile time.
163///
164/// # Examples
165///
166/// ```
167/// use bcgen_lang::ClassProblem;
168///
169/// assert!(ClassProblem::Cycle.to_string().contains("cycle"));
170/// assert!(ClassProblem::TooDeep { limit: 256 }.to_string().contains("256"));
171/// ```
172#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
173#[non_exhaustive]
174pub enum ClassProblem {
175    /// A class that is its own ancestor, or a mixin that uses itself.
176    Cycle,
177    /// An inheritance chain longer than `limit` classes (bvm-lang's load-time
178    /// limit, `MAX_INHERITANCE_DEPTH`).
179    TooDeep {
180        /// The deepest chain allowed.
181        limit: u32,
182    },
183    /// A class whose instances would have more than `limit` property slots,
184    /// its ancestors' included (an LSB field slot is 16 bits).
185    TooManyFields {
186        /// The most slots allowed.
187        limit: u32,
188    },
189    /// A base that is not a class of the unit (an interface, a mixin, or
190    /// something that is not a class at all).
191    NotAClass,
192    /// An implemented interface or interface parent that is not an
193    /// interface of the unit.
194    NotAnInterface,
195    /// A used mixin that is not a mixin (PHP trait) of the unit.
196    NotAMixin,
197    /// A destructor (see [`Options::destructor`](crate::Options::destructor))
198    /// that takes parameters or is static: bvm-lang calls it with the
199    /// instance alone.
200    BadDestructor,
201}
202
203impl fmt::Display for ClassProblem {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        match self {
206            Self::Cycle => f.write_str("an inheritance or mixin cycle"),
207            Self::TooDeep { limit } => {
208                write!(f, "an inheritance chain deeper than {limit} classes")
209            }
210            Self::TooManyFields { limit } => {
211                write!(f, "a class with more than {limit} property slots")
212            }
213            Self::NotAClass => f.write_str("a base that is not a class of the unit"),
214            Self::NotAnInterface => {
215                f.write_str("an interface that is not an interface of the unit")
216            }
217            Self::NotAMixin => f.write_str("a used mixin that is not a mixin of the unit"),
218            Self::BadDestructor => f.write_str("a destructor that takes parameters or is static"),
219        }
220    }
221}
222
223impl fmt::Display for Unsupported {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        f.write_str(self.describe())
226    }
227}
228
229/// Why [`compile`](crate::compile) refused a unit.
230///
231/// Errors about the program name the HIR node and its source span, so a
232/// front end can point at the offending code. Compilation stops at the first
233/// error, in the canonical order of the unit.
234///
235/// # Examples
236///
237/// ```
238/// use bcgen_lang::CompileError;
239/// use hir_lang::{Builder, Effects, Expr, Item, ItemKind, List};
240/// use intern_lang::Interner;
241///
242/// // A module whose body failed to lower: it was reported already, so it
243/// // does not compile.
244/// let mut b = Builder::new();
245/// let body = b.expr(Expr::Err);
246/// let module = ItemKind::Module { items: List::EMPTY, body: Some(body), effects: Effects::NONE };
247/// let root = b.item(Item::new(None, module));
248/// let hir = b.finish(root)?;
249///
250/// let err = bcgen_lang::compile(&hir, &Interner::new()).unwrap_err();
251/// assert!(matches!(err, CompileError::ErrorNode { .. }));
252/// # Ok::<(), hir_lang::HirError>(())
253/// ```
254#[derive(Clone, Copy, Debug, PartialEq, Eq)]
255#[non_exhaustive]
256pub enum CompileError {
257    /// A form this release does not compile.
258    Unsupported {
259        /// What is missing.
260        feature: Unsupported,
261        /// The node.
262        node: NodeRef,
263        /// Its source span.
264        span: Span,
265    },
266    /// A path the resolver left unresolved (or resolved to `Res::Err`): the
267    /// compiler runs on resolved HIR.
268    Unresolved {
269        /// The path's node.
270        node: NodeRef,
271        /// Its source span.
272        span: Span,
273    },
274    /// An error node (`Expr::Err`, `Stmt::Err`, ...) from lenient lowering:
275    /// broken source was reported already and does not compile.
276    ErrorNode {
277        /// The node.
278        node: NodeRef,
279        /// Its source span.
280        span: Span,
281    },
282    /// An integer literal outside the dynamic integer range (`i64`).
283    LiteralOutOfRange {
284        /// The literal's node.
285        node: NodeRef,
286        /// Its source span.
287        span: Span,
288    },
289    /// An assignment target that is not a writable place (a function, a
290    /// constant, an append read).
291    NotAssignable {
292        /// The target's node.
293        node: NodeRef,
294        /// Its source span.
295        span: Span,
296    },
297    /// A named intrinsic the [`Intrinsics`](crate::Intrinsics) table does
298    /// not have.
299    UnknownIntrinsic {
300        /// The intrinsic's name.
301        name: Symbol,
302        /// The intrinsic's node.
303        node: NodeRef,
304        /// Its source span.
305        span: Span,
306    },
307    /// A named intrinsic called with a number of operands its table entry
308    /// does not accept.
309    IntrinsicArity {
310        /// The intrinsic's node.
311        node: NodeRef,
312        /// Its source span.
313        span: Span,
314        /// The operands it takes.
315        arity: Arity,
316        /// The operands it was given.
317        found: u32,
318    },
319    /// A call with more arguments than an LSB call window carries (255,
320    /// counting a method's receiver).
321    TooManyArgs {
322        /// The call.
323        node: NodeRef,
324        /// Its source span.
325        span: Span,
326        /// The limit.
327        limit: u32,
328    },
329    /// A function or closure with more parameters than an LSB parameter list
330    /// carries: 64 when a parameter has a default (the width of the presence
331    /// mask), else 255 (including a generator closure's captures).
332    TooManyParams {
333        /// The function or closure.
334        node: NodeRef,
335        /// Its source span.
336        span: Span,
337        /// The limit.
338        limit: u32,
339    },
340    /// A `finally` (or `defer`) left by more distinct jump targets than its
341    /// `i8` completion kind can number (124).
342    TooManyJumps {
343        /// The `try` or `defer`.
344        node: NodeRef,
345        /// Its source span.
346        span: Span,
347    },
348    /// A class graph or member the module cannot hold (see
349    /// [`ClassProblem`]); `node` is the class or member.
350    Class {
351        /// What is wrong.
352        problem: ClassProblem,
353        /// The class or member.
354        node: NodeRef,
355        /// Its span.
356        span: Span,
357    },
358    /// An option names something the compiler cannot use: `option` is the
359    /// option's name (`string_offsets`: the intrinsic is not in the table or
360    /// does not take two operands).
361    InvalidOption {
362        /// The option.
363        option: &'static str,
364    },
365    /// The instruction budget ([`Options::with_max_instructions`]) ran out.
366    ///
367    /// [`Options::with_max_instructions`]: crate::Options::with_max_instructions
368    BudgetExceeded {
369        /// The budget, in instructions.
370        limit: u64,
371    },
372    /// The LSB builder refused the module: a format limit (65,536 registers
373    /// in one function, 4 GiB in one section, ...).
374    Build(BuildError),
375}
376
377impl CompileError {
378    /// The node the error is about, when it is about one.
379    ///
380    /// # Examples
381    ///
382    /// ```
383    /// use bcgen_lang::CompileError;
384    ///
385    /// assert_eq!(CompileError::BudgetExceeded { limit: 1 }.node(), None);
386    /// ```
387    #[must_use]
388    pub const fn node(&self) -> Option<NodeRef> {
389        match *self {
390            Self::Unsupported { node, .. }
391            | Self::Unresolved { node, .. }
392            | Self::ErrorNode { node, .. }
393            | Self::LiteralOutOfRange { node, .. }
394            | Self::NotAssignable { node, .. }
395            | Self::UnknownIntrinsic { node, .. }
396            | Self::IntrinsicArity { node, .. }
397            | Self::TooManyArgs { node, .. }
398            | Self::TooManyParams { node, .. }
399            | Self::TooManyJumps { node, .. }
400            | Self::Class { node, .. } => Some(node),
401            Self::BudgetExceeded { .. } | Self::Build(_) | Self::InvalidOption { .. } => None,
402        }
403    }
404
405    /// The source span of the node the error is about, when there is one.
406    ///
407    /// # Examples
408    ///
409    /// ```
410    /// use bcgen_lang::CompileError;
411    ///
412    /// assert_eq!(CompileError::BudgetExceeded { limit: 1 }.span(), None);
413    /// ```
414    #[must_use]
415    pub const fn span(&self) -> Option<Span> {
416        match *self {
417            Self::Unsupported { span, .. }
418            | Self::Unresolved { span, .. }
419            | Self::ErrorNode { span, .. }
420            | Self::LiteralOutOfRange { span, .. }
421            | Self::NotAssignable { span, .. }
422            | Self::UnknownIntrinsic { span, .. }
423            | Self::IntrinsicArity { span, .. }
424            | Self::TooManyArgs { span, .. }
425            | Self::TooManyParams { span, .. }
426            | Self::TooManyJumps { span, .. }
427            | Self::Class { span, .. } => Some(span),
428            Self::BudgetExceeded { .. } | Self::Build(_) | Self::InvalidOption { .. } => None,
429        }
430    }
431}
432
433impl fmt::Display for CompileError {
434    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435        match self {
436            Self::Unsupported { feature, span, .. } => {
437                write!(f, "unsupported at {span:?}: {feature}")
438            }
439            Self::Unresolved { span, .. } => write!(f, "unresolved path at {span:?}"),
440            Self::ErrorNode { span, .. } => write!(f, "error node at {span:?}"),
441            Self::LiteralOutOfRange { span, .. } => {
442                write!(f, "integer literal at {span:?} is outside the i64 range")
443            }
444            Self::NotAssignable { span, .. } => {
445                write!(f, "assignment target at {span:?} is not a writable place")
446            }
447            Self::UnknownIntrinsic { span, .. } => {
448                write!(f, "intrinsic at {span:?} is not in the intrinsic table")
449            }
450            Self::IntrinsicArity {
451                span, arity, found, ..
452            } => {
453                write!(
454                    f,
455                    "intrinsic at {span:?} takes {arity} operands, not {found}"
456                )
457            }
458            Self::TooManyArgs { span, limit, .. } => {
459                write!(f, "call at {span:?} has more than {limit} arguments")
460            }
461            Self::TooManyParams { span, limit, .. } => {
462                write!(f, "function at {span:?} has more than {limit} parameters")
463            }
464            Self::TooManyJumps { span, .. } => write!(
465                f,
466                "finally at {span:?} is left by more than 124 distinct jump targets"
467            ),
468            Self::Class { problem, span, .. } => write!(f, "class at {span:?}: {problem}"),
469            Self::InvalidOption { option } => {
470                write!(
471                    f,
472                    "option `{option}` names something the compiler cannot use"
473                )
474            }
475            Self::BudgetExceeded { limit } => {
476                write!(f, "the instruction budget of {limit} ran out")
477            }
478            Self::Build(e) => write!(f, "LSB builder: {e}"),
479        }
480    }
481}
482
483impl core::error::Error for CompileError {}
484
485impl From<BuildError> for CompileError {
486    fn from(e: BuildError) -> Self {
487        Self::Build(e)
488    }
489}