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}