ir_lang/error.rs
1//! The errors of declaring, building, and validating IR.
2
3use alloc::string::String;
4use core::fmt;
5
6use crate::entity::{AggId, Block, FuncId, GlobalId, Inst, SigId, StackSlot, Value, Variable};
7use crate::ops::PolicyFields;
8use crate::types::Type;
9
10/// Why a module-level declaration was refused.
11///
12/// Every variant names what was wrong, so a front end can report it against its own
13/// source. The enum is `#[non_exhaustive]`.
14///
15/// # Examples
16///
17/// ```
18/// use ir_lang::{Linkage, Module, ModuleError, Signature};
19///
20/// let mut m = Module::new("m");
21/// let sig = Signature::new(&[], &[]);
22/// m.declare_function("f", &sig, Linkage::Export)?;
23/// let err = m.declare_function("f", &sig, Linkage::Export).unwrap_err();
24/// assert_eq!(err, ModuleError::DuplicateName { name: "f".into() });
25/// # Ok::<(), ModuleError>(())
26/// ```
27#[derive(Clone, PartialEq, Eq, Debug)]
28#[non_exhaustive]
29pub enum ModuleError {
30 /// A function or global with this name already exists. Symbol names are one
31 /// namespace per module.
32 DuplicateName {
33 /// The repeated name.
34 name: String,
35 },
36 /// A symbol name was empty.
37 EmptyName,
38 /// The function handle does not name a declaration of this module.
39 UnknownFunction {
40 /// The handle.
41 func: FuncId,
42 },
43 /// The global handle does not name a declaration of this module.
44 UnknownGlobal {
45 /// The handle.
46 global: GlobalId,
47 },
48 /// An aggregate names an aggregate that is not declared before it.
49 UnknownAggregate {
50 /// The handle.
51 agg: AggId,
52 },
53 /// An aggregate's size exceeds [`MAX_AGGREGATE_SIZE`](crate::MAX_AGGREGATE_SIZE).
54 AggregateTooLarge,
55 /// An alignment is zero, not a power of two, or larger than allowed.
56 BadAlignment {
57 /// The alignment.
58 align: u64,
59 },
60 /// A global's initializer length differs from its declared size.
61 InitSizeMismatch {
62 /// The declared size.
63 size: u64,
64 /// The initializer length.
65 found: u64,
66 },
67 /// A relocation does not fit inside its global (`offset + pointer size > size`).
68 RelocOutOfRange {
69 /// The relocation offset.
70 offset: u64,
71 },
72 /// An imported global was given an initializer or relocations.
73 ImportWithData,
74 /// An imported function cannot have a body.
75 CannotDefineImport {
76 /// The function.
77 func: FuncId,
78 },
79 /// The function already has a body (use `Module::edit` to change it).
80 AlreadyDefined {
81 /// The function.
82 func: FuncId,
83 },
84 /// The function has no body yet (use `Module::build` to create one).
85 NotDefined {
86 /// The function.
87 func: FuncId,
88 },
89 /// A table of the module reached its capacity (`u32::MAX - 1` entries).
90 CapacityExceeded,
91}
92
93impl fmt::Display for ModuleError {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 match self {
96 ModuleError::DuplicateName { name } => write!(f, "symbol `{name}` is already declared"),
97 ModuleError::EmptyName => f.write_str("symbol names must not be empty"),
98 ModuleError::UnknownFunction { func } => write!(f, "unknown function {func}"),
99 ModuleError::UnknownGlobal { global } => write!(f, "unknown global {global}"),
100 ModuleError::UnknownAggregate { agg } => {
101 write!(f, "aggregate {agg} is not declared before its use")
102 }
103 ModuleError::AggregateTooLarge => f.write_str("aggregate size exceeds 2^48 bytes"),
104 ModuleError::BadAlignment { align } => {
105 write!(f, "alignment {align} is not a supported power of two")
106 }
107 ModuleError::InitSizeMismatch { size, found } => {
108 write!(
109 f,
110 "initializer has {found} bytes but the global is {size} bytes"
111 )
112 }
113 ModuleError::RelocOutOfRange { offset } => {
114 write!(
115 f,
116 "relocation at offset {offset} does not fit in its global"
117 )
118 }
119 ModuleError::ImportWithData => {
120 f.write_str("an imported global cannot have an initializer or relocations")
121 }
122 ModuleError::CannotDefineImport { func } => {
123 write!(f, "function {func} is imported and cannot have a body")
124 }
125 ModuleError::AlreadyDefined { func } => write!(f, "function {func} already has a body"),
126 ModuleError::NotDefined { func } => write!(f, "function {func} has no body"),
127 ModuleError::CapacityExceeded => f.write_str("module table capacity exceeded"),
128 }
129 }
130}
131
132impl core::error::Error for ModuleError {}
133
134/// What is wrong, for a [`ValidationError`] (and for a [`BuildError::Invalid`]).
135///
136/// The enum is `#[non_exhaustive]`: new checks may add variants in a minor release.
137#[derive(Clone, PartialEq, Eq, Debug)]
138#[non_exhaustive]
139pub enum ValidationErrorKind {
140 // ----- module level -----
141 /// Two symbols share a name.
142 DuplicateName {
143 /// The name.
144 name: String,
145 },
146 /// A symbol name is empty.
147 EmptyName,
148 /// A signature handle is out of range.
149 UnknownSignature {
150 /// The handle.
151 sig: SigId,
152 },
153 /// A function handle is out of range.
154 UnknownFunction {
155 /// The handle.
156 func: FuncId,
157 },
158 /// A global handle is out of range.
159 UnknownGlobal {
160 /// The handle.
161 global: GlobalId,
162 },
163 /// An aggregate handle is out of range, or names a later aggregate.
164 UnknownAggregate {
165 /// The handle.
166 agg: AggId,
167 },
168 /// An aggregate's recorded layout is inconsistent or too large.
169 BadAggregate {
170 /// The aggregate.
171 agg: AggId,
172 },
173 /// An alignment is not a supported power of two.
174 BadAlignment {
175 /// The alignment.
176 align: u64,
177 },
178 /// A global's initializer or relocations are inconsistent with its size or
179 /// linkage.
180 BadGlobal {
181 /// The global.
182 global: GlobalId,
183 },
184 /// A defined (local or exported) function has no body.
185 MissingBody,
186 /// An imported function has a body.
187 ImportWithBody,
188
189 // ----- function structure -----
190 /// The entry block handle is out of range or the block was removed.
191 BadEntry,
192 /// The entry block's parameters do not match the signature's parameters.
193 EntryParams {
194 /// The signature's parameter types count.
195 expected: usize,
196 /// The entry block's parameter count.
197 found: usize,
198 },
199 /// A block has no terminator.
200 MissingTerminator,
201 /// An edge targets a block that does not exist or was removed.
202 BadBlockTarget {
203 /// The target.
204 target: Block,
205 },
206 /// An edge targets the entry block, which must have no predecessors.
207 EntryBranchTarget,
208 /// The block's instruction list is corrupt (a link is out of range, points
209 /// into another block, or loops).
210 CorruptLayout,
211 /// A value handle is out of range.
212 ValueOutOfRange {
213 /// The handle.
214 value: Value,
215 },
216 /// A value's recorded definition disagrees with the block parameter list or the
217 /// instruction result range that lists it.
218 InconsistentDefinition {
219 /// The value.
220 value: Value,
221 },
222 /// A value is listed as defined at two sites.
223 DefinedTwice {
224 /// The value.
225 value: Value,
226 },
227 /// A value whose definition was removed is still used.
228 UseOfDetached {
229 /// The value.
230 value: Value,
231 },
232 /// A value's forwarding (from `replace_all_uses`) is inconsistent: it loops or
233 /// changes type.
234 BadAlias {
235 /// The value.
236 value: Value,
237 },
238 /// A value is used where its definition does not dominate the use.
239 UseBeforeDef {
240 /// The value.
241 value: Value,
242 },
243 /// A stack slot handle is out of range, or a slot has a bad size or alignment.
244 BadSlot {
245 /// The slot.
246 slot: StackSlot,
247 },
248
249 // ----- typing -----
250 /// A value has the wrong type for its position.
251 TypeMismatch {
252 /// The value (or `None` for a terminator-produced edge argument).
253 value: Option<Value>,
254 /// The required type.
255 expected: Type,
256 /// The actual type.
257 found: Type,
258 },
259 /// An operand's type is not one the operation accepts.
260 BadOperandType {
261 /// The operation's opcode.
262 op: &'static str,
263 /// The operand's type.
264 found: Type,
265 },
266 /// A conversion between these types is not allowed for this conversion op.
267 BadConversion {
268 /// The conversion's opcode.
269 op: &'static str,
270 /// The source type.
271 from: Type,
272 /// The result type.
273 to: Type,
274 },
275 /// An operation instance does not carry exactly the policy fields it needs.
276 PolicyMismatch {
277 /// The operation's opcode.
278 op: &'static str,
279 /// The fields it must carry.
280 required: PolicyFields,
281 },
282 /// An operation that can raise an error appears as a plain instruction; it
283 /// must be the operation of a `check` terminator. Fallible means: a policy
284 /// field is `error`; `char_from_u32`; or, on a signed integer type, `pow`
285 /// under any `overflow` policy (a negative exponent) or `shl`/`shr` under
286 /// `shift = saturate` (a negative amount). [`BinaryOp::can_error`],
287 /// [`UnaryOp::can_error`], and [`ConvOp::can_error`] are the rule.
288 ///
289 /// [`BinaryOp::can_error`]: crate::BinaryOp::can_error
290 /// [`UnaryOp::can_error`]: crate::UnaryOp::can_error
291 /// [`ConvOp::can_error`]: crate::ConvOp::can_error
292 ErrorOutsideCheck {
293 /// The opcode.
294 op: &'static str,
295 },
296 /// A `check` terminator holds an operation that cannot raise an error, or an
297 /// instruction that is not a unary, binary, or conversion operation.
298 NotFallible {
299 /// The opcode.
300 op: &'static str,
301 },
302 /// A constant's bits do not fit its type (or a `ptr`/`ref` constant is not 0).
303 BadConst {
304 /// The type.
305 ty: Type,
306 /// The bits.
307 bits: u64,
308 },
309 /// A struct field index is out of range, or the aggregate is not a struct.
310 BadField {
311 /// The aggregate.
312 agg: AggId,
313 /// The field index.
314 field: u32,
315 },
316 /// An atomic operation has an ordering it may not have.
317 BadOrdering,
318 /// An instruction's recorded result values disagree with what it produces (count
319 /// or types).
320 ResultMismatch,
321 /// A call passes the wrong number of arguments, or a return passes the wrong
322 /// number of values.
323 ArgCount {
324 /// The required count.
325 expected: usize,
326 /// The supplied count.
327 found: usize,
328 },
329 /// An edge passes the wrong number of arguments for its target's parameters.
330 EdgeArgCount {
331 /// The target block.
332 target: Block,
333 /// The target's parameter count.
334 expected: usize,
335 /// The supplied count.
336 found: usize,
337 },
338 /// A switch has two cases with the same value, or a case value does not fit
339 /// the scrutinee's type.
340 BadSwitchCase {
341 /// The case value.
342 value: u64,
343 },
344 /// A tail call's callee returns or convention differ from this function's, or
345 /// a tail call carries a GC list.
346 BadTailCall,
347 /// A GC list entry is not a `ref`, or a `nogc` call carries a GC list.
348 BadGcList,
349
350 // ----- exception edges -----
351 /// An edge argument is a `Result`, `Exception`, or `ErrorCode` where that kind is
352 /// not allowed, or a `Result` index is out of range.
353 BadEdgeArg,
354 /// A landing pad (the target of an unwind edge) also has a non-unwind
355 /// predecessor, or an `invoke`'s normal and unwind edges target one block.
356 LandingPadPredecessor {
357 /// The landing pad.
358 block: Block,
359 },
360 /// A `nounwind` function contains a `resume`, or a call that may unwind
361 /// without an `invoke`.
362 UnwindInNounwind,
363 /// A `noreturn` function contains a `ret`.
364 ReturnInNoreturn,
365 /// A `nogc` function contains a safepoint.
366 SafepointInNogc,
367
368 // ----- safepoints -----
369 /// A `ref` value, or a pointer derived from one, is live across a safepoint
370 /// without being relocated through its GC list.
371 LiveAcrossSafepoint {
372 /// The value.
373 value: Value,
374 },
375 /// A pointer derived from a `ref` escapes tracking: it is stored to memory or
376 /// passed to a call that is a safepoint.
377 DerivedPointerEscapes {
378 /// The value.
379 value: Value,
380 },
381}
382
383impl fmt::Display for ValidationErrorKind {
384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385 use ValidationErrorKind as K;
386 match self {
387 K::DuplicateName { name } => write!(f, "symbol `{name}` is declared twice"),
388 K::EmptyName => f.write_str("a symbol name is empty"),
389 K::UnknownSignature { sig } => write!(f, "unknown signature {sig}"),
390 K::UnknownFunction { func } => write!(f, "unknown function {func}"),
391 K::UnknownGlobal { global } => write!(f, "unknown global {global}"),
392 K::UnknownAggregate { agg } => write!(f, "unknown or forward aggregate {agg}"),
393 K::BadAggregate { agg } => write!(f, "aggregate {agg} has an invalid layout"),
394 K::BadAlignment { align } => write!(f, "alignment {align} is not supported"),
395 K::BadGlobal { global } => write!(f, "global {global} has an invalid initializer"),
396 K::MissingBody => f.write_str("a defined function has no body"),
397 K::ImportWithBody => f.write_str("an imported function has a body"),
398 K::BadEntry => f.write_str("the entry block is missing"),
399 K::EntryParams { expected, found } => write!(
400 f,
401 "the entry block has {found} parameters but the signature has {expected}"
402 ),
403 K::MissingTerminator => f.write_str("block has no terminator"),
404 K::BadBlockTarget { target } => write!(f, "edge targets missing block {target}"),
405 K::EntryBranchTarget => f.write_str("an edge targets the entry block"),
406 K::CorruptLayout => f.write_str("the block's instruction list is corrupt"),
407 K::ValueOutOfRange { value } => write!(f, "reference to nonexistent value {value}"),
408 K::InconsistentDefinition { value } => {
409 write!(f, "value {value} disagrees with its definition site")
410 }
411 K::DefinedTwice { value } => write!(f, "value {value} is defined twice"),
412 K::UseOfDetached { value } => write!(f, "value {value} is used but no longer defined"),
413 K::BadAlias { value } => write!(f, "value {value} has an invalid forwarding"),
414 K::UseBeforeDef { value } => {
415 write!(
416 f,
417 "value {value} is used where its definition does not dominate"
418 )
419 }
420 K::BadSlot { slot } => write!(f, "invalid stack slot {slot}"),
421 K::TypeMismatch {
422 value: Some(v),
423 expected,
424 found,
425 } => write!(f, "value {v} has type {found}, expected {expected}"),
426 K::TypeMismatch {
427 value: None,
428 expected,
429 found,
430 } => write!(f, "edge argument has type {found}, expected {expected}"),
431 K::BadOperandType { op, found } => write!(f, "`{op}` does not accept {found}"),
432 K::BadConversion { op, from, to } => write!(f, "`{op}` cannot convert {from} to {to}"),
433 K::PolicyMismatch { op, required } => {
434 write!(f, "`{op}` must carry exactly the policy fields {required}")
435 }
436 K::ErrorOutsideCheck { op } => write!(
437 f,
438 "`{op}` can raise an error and must be the operation of a `check` terminator"
439 ),
440 K::NotFallible { op } => write!(f, "`check` holds `{op}`, which cannot raise an error"),
441 K::BadConst { ty, bits } => write!(f, "constant {bits:#x} does not fit {ty}"),
442 K::BadField { agg, field } => write!(f, "{agg} has no field {field}"),
443 K::BadOrdering => f.write_str("invalid memory ordering for this atomic operation"),
444 K::ResultMismatch => {
445 f.write_str("the instruction's results disagree with what it produces")
446 }
447 K::ArgCount { expected, found } => {
448 write!(f, "expected {expected} arguments, found {found}")
449 }
450 K::EdgeArgCount {
451 target,
452 expected,
453 found,
454 } => write!(
455 f,
456 "edge to {target} passes {found} arguments for {expected} parameters"
457 ),
458 K::BadSwitchCase { value } => {
459 write!(f, "switch case {value} is repeated or out of range")
460 }
461 K::BadTailCall => {
462 f.write_str("tail call does not match this function's returns and convention")
463 }
464 K::BadGcList => f.write_str("invalid GC list"),
465 K::BadEdgeArg => f.write_str("edge argument not allowed on this edge"),
466 K::LandingPadPredecessor { block } => {
467 write!(f, "landing pad {block} is reached by a non-unwind edge")
468 }
469 K::UnwindInNounwind => f.write_str("a `nounwind` function may unwind"),
470 K::ReturnInNoreturn => f.write_str("a `noreturn` function returns"),
471 K::SafepointInNogc => f.write_str("a `nogc` function contains a safepoint"),
472 K::LiveAcrossSafepoint { value } => write!(
473 f,
474 "value {value} is a GC reference (or derived from one) live across a safepoint"
475 ),
476 K::DerivedPointerEscapes { value } => {
477 write!(f, "pointer {value} derived from a GC reference escapes")
478 }
479 }
480 }
481}
482
483/// Why a module is not well-formed: what is wrong, and where.
484///
485/// [`Module::validate`](crate::Module::validate) returns the first problem it finds
486/// in a fixed order (module declarations, then each function in handle order;
487/// within a function: structure, instructions, terminators, exception edges,
488/// dominance, safepoints).
489///
490/// # Examples
491///
492/// ```
493/// use ir_lang::{Linkage, Module, Signature, Type, ValidationErrorKind};
494///
495/// let mut m = Module::new("m");
496/// let f = m.declare_function("f", &Signature::new(&[], &[Type::I32]), Linkage::Export)?;
497/// // Exported but never given a body.
498/// let err = m.validate().unwrap_err();
499/// assert_eq!(err.func(), Some(f));
500/// assert_eq!(err.kind(), &ValidationErrorKind::MissingBody);
501/// # Ok::<(), Box<dyn std::error::Error>>(())
502/// ```
503#[derive(Clone, PartialEq, Eq, Debug)]
504pub struct ValidationError {
505 pub(crate) func: Option<FuncId>,
506 pub(crate) block: Option<Block>,
507 pub(crate) inst: Option<Inst>,
508 pub(crate) kind: ValidationErrorKind,
509}
510
511impl ValidationError {
512 pub(crate) fn module(kind: ValidationErrorKind) -> ValidationError {
513 ValidationError {
514 func: None,
515 block: None,
516 inst: None,
517 kind,
518 }
519 }
520
521 /// The function the problem is in, if it is inside one.
522 ///
523 /// See [`ValidationError`] for an example.
524 #[must_use]
525 pub fn func(&self) -> Option<FuncId> {
526 self.func
527 }
528
529 /// The block the problem is in, if any.
530 ///
531 /// # Examples
532 ///
533 /// ```
534 /// use ir_lang::{Linkage, Module, Signature};
535 ///
536 /// let mut m = Module::new("m");
537 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Export)?;
538 /// let _ = m.build(f)?; // the entry block is never terminated
539 /// let err = m.validate().unwrap_err();
540 /// assert_eq!(err.block().map(|b| b.index()), Some(0));
541 /// # Ok::<(), Box<dyn std::error::Error>>(())
542 /// ```
543 #[must_use]
544 pub fn block(&self) -> Option<Block> {
545 self.block
546 }
547
548 /// The instruction the problem is in, if it is in one (`None` for a terminator
549 /// or a block-level problem).
550 ///
551 /// # Examples
552 ///
553 /// ```
554 /// use ir_lang::{Linkage, Module, Signature};
555 ///
556 /// let mut m = Module::new("m");
557 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Export)?;
558 /// let _ = m.build(f)?;
559 /// assert_eq!(m.validate().unwrap_err().inst(), None);
560 /// # Ok::<(), Box<dyn std::error::Error>>(())
561 /// ```
562 #[must_use]
563 pub fn inst(&self) -> Option<Inst> {
564 self.inst
565 }
566
567 /// What is wrong.
568 ///
569 /// See [`ValidationError`] for an example.
570 #[must_use]
571 pub fn kind(&self) -> &ValidationErrorKind {
572 &self.kind
573 }
574}
575
576impl fmt::Display for ValidationError {
577 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
578 let mut sep = "";
579 if let Some(func) = self.func {
580 write!(f, "{sep}{func}")?;
581 sep = " ";
582 }
583 if let Some(block) = self.block {
584 write!(f, "{sep}{block}")?;
585 sep = " ";
586 }
587 if let Some(inst) = self.inst {
588 write!(f, "{sep}{inst}")?;
589 sep = " ";
590 }
591 if sep.is_empty() {
592 write!(f, "{}", self.kind)
593 } else {
594 write!(f, ": {}", self.kind)
595 }
596 }
597}
598
599impl core::error::Error for ValidationError {}
600
601/// Why a [`Builder`](crate::Builder) operation was refused.
602///
603/// The builder checks each instruction and terminator as it is added, so a
604/// lowering bug is reported at the call that caused it. Nothing is ever changed by
605/// a refused operation.
606///
607/// # Examples
608///
609/// ```
610/// use ir_lang::{BuildError, Linkage, Module, Signature};
611///
612/// let mut m = Module::new("m");
613/// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Export)?;
614/// let mut b = m.build(f)?;
615/// b.ret(&[])?;
616/// // A terminator is never silently overwritten.
617/// let entry = b.func().entry();
618/// assert_eq!(b.unreachable(), Err(BuildError::AlreadyTerminated { block: entry }));
619/// # Ok::<(), Box<dyn std::error::Error>>(())
620/// ```
621#[derive(Clone, PartialEq, Eq, Debug)]
622#[non_exhaustive]
623pub enum BuildError {
624 /// The block already has a terminator. Remove it first
625 /// ([`Builder::remove_terminator`](crate::Builder::remove_terminator)) to replace it.
626 AlreadyTerminated {
627 /// The block.
628 block: Block,
629 },
630 /// The block handle is unknown or the block was removed.
631 UnknownBlock {
632 /// The block.
633 block: Block,
634 },
635 /// The instruction handle is unknown or the instruction was removed.
636 UnknownInst {
637 /// The instruction.
638 inst: Inst,
639 },
640 /// The value handle is unknown.
641 UnknownValue {
642 /// The value.
643 value: Value,
644 },
645 /// There is no function parameter at this index.
646 UnknownParam {
647 /// The index.
648 index: usize,
649 },
650 /// The entry block cannot be removed or split off.
651 EntryBlock,
652 /// A block parameter index is out of range.
653 UnknownBlockParam {
654 /// The block.
655 block: Block,
656 /// The index.
657 index: usize,
658 },
659 /// The instruction or terminator is ill-formed.
660 Invalid(ValidationErrorKind),
661 /// A function table reached its capacity (`u32::MAX - 1` entries).
662 CapacityExceeded,
663 /// The variable was not declared by this builder (or the builder's variable
664 /// state was dropped by [`compact`](crate::Builder::compact)).
665 UnknownVariable {
666 /// The variable.
667 var: Variable,
668 },
669 /// An edge targets a block that was sealed: every edge into it was declared
670 /// to exist already ([`Builder::seal_block`](crate::Builder::seal_block)).
671 SealedBlock {
672 /// The sealed block.
673 block: Block,
674 },
675 /// The value is not a parameter of a live block.
676 NotBlockParam {
677 /// The value.
678 value: Value,
679 },
680 /// The operation conflicts with variable construction in progress: splitting
681 /// a block while blocks are unsealed, or adding a parameter to a block the
682 /// construction already added parameters to.
683 VariablesActive,
684 /// A whole-function transformation would exceed its work budget
685 /// ([`Builder::insert_statepoints_within`](crate::Builder::insert_statepoints_within));
686 /// nothing was changed.
687 BudgetExceeded,
688}
689
690impl fmt::Display for BuildError {
691 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
692 match self {
693 BuildError::AlreadyTerminated { block } => {
694 write!(f, "block {block} already has a terminator")
695 }
696 BuildError::UnknownBlock { block } => write!(f, "unknown or removed block {block}"),
697 BuildError::UnknownInst { inst } => write!(f, "unknown or removed instruction {inst}"),
698 BuildError::UnknownValue { value } => write!(f, "unknown value {value}"),
699 BuildError::UnknownParam { index } => write!(f, "no function parameter {index}"),
700 BuildError::EntryBlock => f.write_str("the entry block cannot be removed or split off"),
701 BuildError::UnknownBlockParam { block, index } => {
702 write!(f, "block {block} has no parameter {index}")
703 }
704 BuildError::Invalid(kind) => write!(f, "{kind}"),
705 BuildError::CapacityExceeded => f.write_str("function table capacity exceeded"),
706 BuildError::UnknownVariable { var } => write!(f, "unknown variable {var}"),
707 BuildError::SealedBlock { block } => {
708 write!(f, "block {block} is sealed and cannot get a new edge")
709 }
710 BuildError::NotBlockParam { value } => {
711 write!(f, "value {value} is not a block parameter")
712 }
713 BuildError::VariablesActive => {
714 f.write_str("not allowed while variable construction is in progress")
715 }
716 BuildError::BudgetExceeded => f.write_str("the operation's work budget was exceeded"),
717 }
718 }
719}
720
721impl core::error::Error for BuildError {}
722
723impl From<ValidationErrorKind> for BuildError {
724 fn from(kind: ValidationErrorKind) -> Self {
725 BuildError::Invalid(kind)
726 }
727}
728
729#[cfg(test)]
730mod tests {
731 use super::*;
732 use alloc::string::ToString;
733
734 #[test]
735 fn test_validation_error_display_includes_location() {
736 let e = ValidationError {
737 func: Some(FuncId::from_u32(1)),
738 block: Some(Block::from_u32(2)),
739 inst: None,
740 kind: ValidationErrorKind::MissingTerminator,
741 };
742 assert_eq!(e.to_string(), "f1 b2: block has no terminator");
743 let m = ValidationError::module(ValidationErrorKind::EmptyName);
744 assert_eq!(m.to_string(), "a symbol name is empty");
745 }
746
747 #[test]
748 fn test_every_build_error_displays() {
749 let errs = [
750 BuildError::EntryBlock,
751 BuildError::CapacityExceeded,
752 BuildError::UnknownParam { index: 3 },
753 BuildError::Invalid(ValidationErrorKind::BadOrdering),
754 BuildError::UnknownVariable {
755 var: Variable::from_u32(1),
756 },
757 BuildError::SealedBlock {
758 block: Block::from_u32(1),
759 },
760 BuildError::NotBlockParam {
761 value: Value::from_u32(1),
762 },
763 BuildError::VariablesActive,
764 BuildError::BudgetExceeded,
765 ];
766 for e in errs {
767 assert!(!e.to_string().is_empty());
768 }
769 }
770}