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};
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 (a policy field is `error`, or
283 /// `char_from_u32`) appears as a plain instruction; it must be the operation of
284 /// a `check` terminator.
285 ErrorOutsideCheck {
286 /// The opcode.
287 op: &'static str,
288 },
289 /// A `check` terminator holds an operation that cannot raise an error, or an
290 /// instruction that is not a unary, binary, or conversion operation.
291 NotFallible {
292 /// The opcode.
293 op: &'static str,
294 },
295 /// A constant's bits do not fit its type (or a `ptr`/`ref` constant is not 0).
296 BadConst {
297 /// The type.
298 ty: Type,
299 /// The bits.
300 bits: u64,
301 },
302 /// A struct field index is out of range, or the aggregate is not a struct.
303 BadField {
304 /// The aggregate.
305 agg: AggId,
306 /// The field index.
307 field: u32,
308 },
309 /// An atomic operation has an ordering it may not have.
310 BadOrdering,
311 /// An instruction's recorded result values disagree with what it produces (count
312 /// or types).
313 ResultMismatch,
314 /// A call passes the wrong number of arguments, or a return passes the wrong
315 /// number of values.
316 ArgCount {
317 /// The required count.
318 expected: usize,
319 /// The supplied count.
320 found: usize,
321 },
322 /// An edge passes the wrong number of arguments for its target's parameters.
323 EdgeArgCount {
324 /// The target block.
325 target: Block,
326 /// The target's parameter count.
327 expected: usize,
328 /// The supplied count.
329 found: usize,
330 },
331 /// A switch has two cases with the same value, or a case value does not fit
332 /// the scrutinee's type.
333 BadSwitchCase {
334 /// The case value.
335 value: u64,
336 },
337 /// A tail call's callee returns or convention differ from this function's, or
338 /// a tail call carries a GC list.
339 BadTailCall,
340 /// A GC list entry is not a `ref`, or a `nogc` call carries a GC list.
341 BadGcList,
342
343 // ----- exception edges -----
344 /// An edge argument is a `Result`, `Exception`, or `ErrorCode` where that kind is
345 /// not allowed, or a `Result` index is out of range.
346 BadEdgeArg,
347 /// A landing pad (the target of an unwind edge) also has a non-unwind
348 /// predecessor, or an `invoke`'s normal and unwind edges target one block.
349 LandingPadPredecessor {
350 /// The landing pad.
351 block: Block,
352 },
353 /// A `nounwind` function contains a `resume`, or a call that may unwind
354 /// without an `invoke`.
355 UnwindInNounwind,
356 /// A `noreturn` function contains a `ret`.
357 ReturnInNoreturn,
358 /// A `nogc` function contains a safepoint.
359 SafepointInNogc,
360
361 // ----- safepoints -----
362 /// A `ref` value, or a pointer derived from one, is live across a safepoint
363 /// without being relocated through its GC list.
364 LiveAcrossSafepoint {
365 /// The value.
366 value: Value,
367 },
368 /// A pointer derived from a `ref` escapes tracking: it is stored to memory or
369 /// passed to a call that is a safepoint.
370 DerivedPointerEscapes {
371 /// The value.
372 value: Value,
373 },
374}
375
376impl fmt::Display for ValidationErrorKind {
377 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378 use ValidationErrorKind as K;
379 match self {
380 K::DuplicateName { name } => write!(f, "symbol `{name}` is declared twice"),
381 K::EmptyName => f.write_str("a symbol name is empty"),
382 K::UnknownSignature { sig } => write!(f, "unknown signature {sig}"),
383 K::UnknownFunction { func } => write!(f, "unknown function {func}"),
384 K::UnknownGlobal { global } => write!(f, "unknown global {global}"),
385 K::UnknownAggregate { agg } => write!(f, "unknown or forward aggregate {agg}"),
386 K::BadAggregate { agg } => write!(f, "aggregate {agg} has an invalid layout"),
387 K::BadAlignment { align } => write!(f, "alignment {align} is not supported"),
388 K::BadGlobal { global } => write!(f, "global {global} has an invalid initializer"),
389 K::MissingBody => f.write_str("a defined function has no body"),
390 K::ImportWithBody => f.write_str("an imported function has a body"),
391 K::BadEntry => f.write_str("the entry block is missing"),
392 K::EntryParams { expected, found } => write!(
393 f,
394 "the entry block has {found} parameters but the signature has {expected}"
395 ),
396 K::MissingTerminator => f.write_str("block has no terminator"),
397 K::BadBlockTarget { target } => write!(f, "edge targets missing block {target}"),
398 K::EntryBranchTarget => f.write_str("an edge targets the entry block"),
399 K::CorruptLayout => f.write_str("the block's instruction list is corrupt"),
400 K::ValueOutOfRange { value } => write!(f, "reference to nonexistent value {value}"),
401 K::InconsistentDefinition { value } => {
402 write!(f, "value {value} disagrees with its definition site")
403 }
404 K::DefinedTwice { value } => write!(f, "value {value} is defined twice"),
405 K::UseOfDetached { value } => write!(f, "value {value} is used but no longer defined"),
406 K::BadAlias { value } => write!(f, "value {value} has an invalid forwarding"),
407 K::UseBeforeDef { value } => {
408 write!(
409 f,
410 "value {value} is used where its definition does not dominate"
411 )
412 }
413 K::BadSlot { slot } => write!(f, "invalid stack slot {slot}"),
414 K::TypeMismatch {
415 value: Some(v),
416 expected,
417 found,
418 } => write!(f, "value {v} has type {found}, expected {expected}"),
419 K::TypeMismatch {
420 value: None,
421 expected,
422 found,
423 } => write!(f, "edge argument has type {found}, expected {expected}"),
424 K::BadOperandType { op, found } => write!(f, "`{op}` does not accept {found}"),
425 K::BadConversion { op, from, to } => write!(f, "`{op}` cannot convert {from} to {to}"),
426 K::PolicyMismatch { op, required } => {
427 write!(f, "`{op}` must carry exactly the policy fields {required}")
428 }
429 K::ErrorOutsideCheck { op } => write!(
430 f,
431 "`{op}` can raise an error and must be the operation of a `check` terminator"
432 ),
433 K::NotFallible { op } => write!(f, "`check` holds `{op}`, which cannot raise an error"),
434 K::BadConst { ty, bits } => write!(f, "constant {bits:#x} does not fit {ty}"),
435 K::BadField { agg, field } => write!(f, "{agg} has no field {field}"),
436 K::BadOrdering => f.write_str("invalid memory ordering for this atomic operation"),
437 K::ResultMismatch => {
438 f.write_str("the instruction's results disagree with what it produces")
439 }
440 K::ArgCount { expected, found } => {
441 write!(f, "expected {expected} arguments, found {found}")
442 }
443 K::EdgeArgCount {
444 target,
445 expected,
446 found,
447 } => write!(
448 f,
449 "edge to {target} passes {found} arguments for {expected} parameters"
450 ),
451 K::BadSwitchCase { value } => {
452 write!(f, "switch case {value} is repeated or out of range")
453 }
454 K::BadTailCall => {
455 f.write_str("tail call does not match this function's returns and convention")
456 }
457 K::BadGcList => f.write_str("invalid GC list"),
458 K::BadEdgeArg => f.write_str("edge argument not allowed on this edge"),
459 K::LandingPadPredecessor { block } => {
460 write!(f, "landing pad {block} is reached by a non-unwind edge")
461 }
462 K::UnwindInNounwind => f.write_str("a `nounwind` function may unwind"),
463 K::ReturnInNoreturn => f.write_str("a `noreturn` function returns"),
464 K::SafepointInNogc => f.write_str("a `nogc` function contains a safepoint"),
465 K::LiveAcrossSafepoint { value } => write!(
466 f,
467 "value {value} is a GC reference (or derived from one) live across a safepoint"
468 ),
469 K::DerivedPointerEscapes { value } => {
470 write!(f, "pointer {value} derived from a GC reference escapes")
471 }
472 }
473 }
474}
475
476/// Why a module is not well-formed: what is wrong, and where.
477///
478/// [`Module::validate`](crate::Module::validate) returns the first problem it finds
479/// in a fixed order (module declarations, then each function in handle order;
480/// within a function: structure, instructions, terminators, exception edges,
481/// dominance, safepoints).
482///
483/// # Examples
484///
485/// ```
486/// use ir_lang::{Linkage, Module, Signature, Type, ValidationErrorKind};
487///
488/// let mut m = Module::new("m");
489/// let f = m.declare_function("f", &Signature::new(&[], &[Type::I32]), Linkage::Export)?;
490/// // Exported but never given a body.
491/// let err = m.validate().unwrap_err();
492/// assert_eq!(err.func(), Some(f));
493/// assert_eq!(err.kind(), &ValidationErrorKind::MissingBody);
494/// # Ok::<(), Box<dyn std::error::Error>>(())
495/// ```
496#[derive(Clone, PartialEq, Eq, Debug)]
497pub struct ValidationError {
498 pub(crate) func: Option<FuncId>,
499 pub(crate) block: Option<Block>,
500 pub(crate) inst: Option<Inst>,
501 pub(crate) kind: ValidationErrorKind,
502}
503
504impl ValidationError {
505 pub(crate) fn module(kind: ValidationErrorKind) -> ValidationError {
506 ValidationError {
507 func: None,
508 block: None,
509 inst: None,
510 kind,
511 }
512 }
513
514 /// The function the problem is in, if it is inside one.
515 ///
516 /// See [`ValidationError`] for an example.
517 #[must_use]
518 pub fn func(&self) -> Option<FuncId> {
519 self.func
520 }
521
522 /// The block the problem is in, if any.
523 ///
524 /// # Examples
525 ///
526 /// ```
527 /// use ir_lang::{Linkage, Module, Signature};
528 ///
529 /// let mut m = Module::new("m");
530 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Export)?;
531 /// let _ = m.build(f)?; // the entry block is never terminated
532 /// let err = m.validate().unwrap_err();
533 /// assert_eq!(err.block().map(|b| b.index()), Some(0));
534 /// # Ok::<(), Box<dyn std::error::Error>>(())
535 /// ```
536 #[must_use]
537 pub fn block(&self) -> Option<Block> {
538 self.block
539 }
540
541 /// The instruction the problem is in, if it is in one (`None` for a terminator
542 /// or a block-level problem).
543 ///
544 /// # Examples
545 ///
546 /// ```
547 /// use ir_lang::{Linkage, Module, Signature};
548 ///
549 /// let mut m = Module::new("m");
550 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Export)?;
551 /// let _ = m.build(f)?;
552 /// assert_eq!(m.validate().unwrap_err().inst(), None);
553 /// # Ok::<(), Box<dyn std::error::Error>>(())
554 /// ```
555 #[must_use]
556 pub fn inst(&self) -> Option<Inst> {
557 self.inst
558 }
559
560 /// What is wrong.
561 ///
562 /// See [`ValidationError`] for an example.
563 #[must_use]
564 pub fn kind(&self) -> &ValidationErrorKind {
565 &self.kind
566 }
567}
568
569impl fmt::Display for ValidationError {
570 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571 let mut sep = "";
572 if let Some(func) = self.func {
573 write!(f, "{sep}{func}")?;
574 sep = " ";
575 }
576 if let Some(block) = self.block {
577 write!(f, "{sep}{block}")?;
578 sep = " ";
579 }
580 if let Some(inst) = self.inst {
581 write!(f, "{sep}{inst}")?;
582 sep = " ";
583 }
584 if sep.is_empty() {
585 write!(f, "{}", self.kind)
586 } else {
587 write!(f, ": {}", self.kind)
588 }
589 }
590}
591
592impl core::error::Error for ValidationError {}
593
594/// Why a [`Builder`](crate::Builder) operation was refused.
595///
596/// The builder checks each instruction and terminator as it is added, so a
597/// lowering bug is reported at the call that caused it. Nothing is ever changed by
598/// a refused operation.
599///
600/// # Examples
601///
602/// ```
603/// use ir_lang::{BuildError, Linkage, Module, Signature};
604///
605/// let mut m = Module::new("m");
606/// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Export)?;
607/// let mut b = m.build(f)?;
608/// b.ret(&[])?;
609/// // A terminator is never silently overwritten.
610/// let entry = b.func().entry();
611/// assert_eq!(b.unreachable(), Err(BuildError::AlreadyTerminated { block: entry }));
612/// # Ok::<(), Box<dyn std::error::Error>>(())
613/// ```
614#[derive(Clone, PartialEq, Eq, Debug)]
615#[non_exhaustive]
616pub enum BuildError {
617 /// The block already has a terminator. Remove it first
618 /// ([`Builder::remove_terminator`](crate::Builder::remove_terminator)) to replace it.
619 AlreadyTerminated {
620 /// The block.
621 block: Block,
622 },
623 /// The block handle is unknown or the block was removed.
624 UnknownBlock {
625 /// The block.
626 block: Block,
627 },
628 /// The instruction handle is unknown or the instruction was removed.
629 UnknownInst {
630 /// The instruction.
631 inst: Inst,
632 },
633 /// The value handle is unknown.
634 UnknownValue {
635 /// The value.
636 value: Value,
637 },
638 /// There is no function parameter at this index.
639 UnknownParam {
640 /// The index.
641 index: usize,
642 },
643 /// The entry block cannot be removed or split off.
644 EntryBlock,
645 /// A block parameter index is out of range.
646 UnknownBlockParam {
647 /// The block.
648 block: Block,
649 /// The index.
650 index: usize,
651 },
652 /// The instruction or terminator is ill-formed.
653 Invalid(ValidationErrorKind),
654 /// A function table reached its capacity (`u32::MAX - 1` entries).
655 CapacityExceeded,
656}
657
658impl fmt::Display for BuildError {
659 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660 match self {
661 BuildError::AlreadyTerminated { block } => {
662 write!(f, "block {block} already has a terminator")
663 }
664 BuildError::UnknownBlock { block } => write!(f, "unknown or removed block {block}"),
665 BuildError::UnknownInst { inst } => write!(f, "unknown or removed instruction {inst}"),
666 BuildError::UnknownValue { value } => write!(f, "unknown value {value}"),
667 BuildError::UnknownParam { index } => write!(f, "no function parameter {index}"),
668 BuildError::EntryBlock => f.write_str("the entry block cannot be removed or split off"),
669 BuildError::UnknownBlockParam { block, index } => {
670 write!(f, "block {block} has no parameter {index}")
671 }
672 BuildError::Invalid(kind) => write!(f, "{kind}"),
673 BuildError::CapacityExceeded => f.write_str("function table capacity exceeded"),
674 }
675 }
676}
677
678impl core::error::Error for BuildError {}
679
680impl From<ValidationErrorKind> for BuildError {
681 fn from(kind: ValidationErrorKind) -> Self {
682 BuildError::Invalid(kind)
683 }
684}
685
686#[cfg(test)]
687mod tests {
688 use super::*;
689 use alloc::string::ToString;
690
691 #[test]
692 fn test_validation_error_display_includes_location() {
693 let e = ValidationError {
694 func: Some(FuncId::from_u32(1)),
695 block: Some(Block::from_u32(2)),
696 inst: None,
697 kind: ValidationErrorKind::MissingTerminator,
698 };
699 assert_eq!(e.to_string(), "f1 b2: block has no terminator");
700 let m = ValidationError::module(ValidationErrorKind::EmptyName);
701 assert_eq!(m.to_string(), "a symbol name is empty");
702 }
703
704 #[test]
705 fn test_every_build_error_displays() {
706 let errs = [
707 BuildError::EntryBlock,
708 BuildError::CapacityExceeded,
709 BuildError::UnknownParam { index: 3 },
710 BuildError::Invalid(ValidationErrorKind::BadOrdering),
711 ];
712 for e in errs {
713 assert!(!e.to_string().is_empty());
714 }
715 }
716}