Skip to main content

baedeker_core/validate/
error.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Validation error types.
5//!
6//! Validation runs after binary decoding and reports type/index/control-flow problems
7//! against the decoded module structure.
8
9use alloc::vec::Vec;
10use core::fmt;
11
12use crate::error::{ByteOffset, DecodeContext, DecodeError, DecodeErrorKind};
13use crate::types::{
14    BlockType, DataIdx, ElemIdx, FuncIdx, GlobalIdx, LabelIdx, LocalIdx, MemIdx, RefType, TableIdx,
15    TypeIdx, ValType,
16};
17
18/// A validation error with byte offset and function context.
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct ValidationError {
21    pub offset: ByteOffset,
22    pub function: Option<FuncIdx>,
23    pub kind: ValidationErrorKind,
24}
25
26/// Specific categories of validation errors.
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum ValidationErrorKind {
29    UnknownTypeIdx {
30        idx: TypeIdx,
31    },
32    UnknownFuncIdx {
33        idx: FuncIdx,
34    },
35    UndeclaredFuncRef {
36        idx: FuncIdx,
37    },
38    UnknownLocalIdx {
39        idx: LocalIdx,
40    },
41    UninitializedLocal {
42        idx: LocalIdx,
43    },
44    UnknownGlobalIdx {
45        idx: GlobalIdx,
46        available: u32,
47    },
48    UnknownTableIdx {
49        idx: TableIdx,
50        available: u32,
51    },
52    UnknownMemIdx {
53        idx: MemIdx,
54        available: u32,
55    },
56    UnknownDataIdx {
57        idx: DataIdx,
58        available: u32,
59    },
60    UnknownElemIdx {
61        idx: ElemIdx,
62        available: u32,
63    },
64    InvalidMemArgAlign {
65        op: &'static str,
66        max: u32,
67        found: u32,
68    },
69    InvalidSimdLaneIdx {
70        op: &'static str,
71        max: u8,
72        found: u8,
73    },
74    UnknownLabelIdx {
75        idx: LabelIdx,
76    },
77    Decode {
78        context: DecodeContext,
79        kind: DecodeErrorKind,
80    },
81    InvalidGlobalInitExpr,
82    NonConstantGlobalInitExpr,
83    MutableGlobalInInitExpr {
84        idx: GlobalIdx,
85    },
86    ImmutableGlobalSet {
87        idx: GlobalIdx,
88    },
89    GlobalInitTypeMismatch {
90        expected: ValType,
91        found: ValType,
92    },
93    BranchTypeMismatch {
94        label: LabelIdx,
95        expected: Vec<ValType>,
96        found: Vec<ValType>,
97    },
98    InvalidBrOnNonNullTarget {
99        label: LabelIdx,
100        found: Vec<ValType>,
101    },
102    InconsistentBranchTypes {
103        expected: Vec<ValType>,
104        found: Vec<ValType>,
105    },
106    UnexpectedElse,
107    UnexpectedEnd,
108    UnterminatedControlFrames,
109    ElseOutsideIf,
110    MissingElseForResult,
111    InvalidBlockType {
112        block_type: BlockType,
113    },
114    ControlResultTypeMismatch {
115        expected: Vec<ValType>,
116        found: Vec<ValType>,
117    },
118    InvalidSelectResultArity {
119        found: usize,
120    },
121    SelectOperandTypeMismatch {
122        expected: ValType,
123        found: Vec<ValType>,
124    },
125    StackUnderflow {
126        op: &'static str,
127        expected: Vec<ValType>,
128        available: Vec<ValType>,
129    },
130    TypeMismatch {
131        op: &'static str,
132        expected: ValType,
133        found: ValType,
134    },
135    FunctionResultTypeMismatch {
136        expected: Vec<ValType>,
137        found: Vec<ValType>,
138        full_stack: Vec<ValType>,
139    },
140    ResultTypeMismatch {
141        expected: Vec<ValType>,
142        found: Vec<ValType>,
143    },
144    InvalidStartFunctionType {
145        params: Vec<ValType>,
146        results: Vec<ValType>,
147    },
148    InvalidElementExpr,
149    NonConstantElementExpr,
150    ElementExprTypeMismatch {
151        expected: ValType,
152        found: ValType,
153    },
154    ElementTableTypeMismatch {
155        expected: RefType,
156        found: RefType,
157    },
158    InvalidCallIndirectTableType {
159        expected: RefType,
160        found: RefType,
161    },
162    MissingDataCountSection {
163        op: &'static str,
164    },
165    MemorySizeOutOfRange,
166    MemoryMinExceedsMax,
167    /// A non-nullable-element table without an initializer.
168    TableTypeMismatch,
169    DuplicateExportName {
170        name: alloc::string::String,
171    },
172}
173
174impl fmt::Display for ValidationError {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        match self.function {
177            Some(func) => write!(
178                f,
179                "validation error at byte {} in function {}: {}",
180                self.offset.0, func.0, self.kind
181            ),
182            None => write!(
183                f,
184                "validation error at byte {}: {}",
185                self.offset.0, self.kind
186            ),
187        }
188    }
189}
190
191impl fmt::Display for ValidationErrorKind {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        match self {
194            ValidationErrorKind::UnknownTypeIdx { idx } => {
195                write!(f, "unknown type index {}", idx.0)
196            }
197            ValidationErrorKind::UnknownFuncIdx { idx } => {
198                write!(f, "unknown function index {}", idx.0)
199            }
200            ValidationErrorKind::UndeclaredFuncRef { idx } => {
201                write!(f, "undeclared function reference {}", idx.0)
202            }
203            ValidationErrorKind::UnknownLocalIdx { idx } => {
204                write!(f, "unknown local index {}", idx.0)
205            }
206            ValidationErrorKind::UninitializedLocal { idx } => {
207                write!(f, "uninitialized local {}", idx.0)
208            }
209            ValidationErrorKind::UnknownGlobalIdx { idx, available } => {
210                write!(
211                    f,
212                    "unknown global index {} (available globals: {})",
213                    idx.0, available
214                )
215            }
216            ValidationErrorKind::UnknownTableIdx { idx, available } => {
217                write!(
218                    f,
219                    "unknown table index {} (available tables: {})",
220                    idx.0, available
221                )
222            }
223            ValidationErrorKind::UnknownMemIdx { idx, available } => {
224                write!(
225                    f,
226                    "unknown memory index {} (available memories: {})",
227                    idx.0, available
228                )
229            }
230            ValidationErrorKind::UnknownDataIdx { idx, available } => {
231                write!(
232                    f,
233                    "unknown data index {} (available data segments: {})",
234                    idx.0, available
235                )
236            }
237            ValidationErrorKind::UnknownElemIdx { idx, available } => {
238                write!(
239                    f,
240                    "unknown element index {} (available element segments: {})",
241                    idx.0, available
242                )
243            }
244            ValidationErrorKind::InvalidMemArgAlign { op, max, found } => {
245                write!(
246                    f,
247                    "invalid memarg alignment in {}: found {}, maximum natural alignment exponent {}",
248                    op, found, max
249                )
250            }
251            ValidationErrorKind::InvalidSimdLaneIdx { op, max, found } => {
252                write!(
253                    f,
254                    "invalid SIMD lane index in {}: found {}, maximum lane {}",
255                    op, found, max
256                )
257            }
258            ValidationErrorKind::UnknownLabelIdx { idx } => {
259                write!(f, "unknown label index {}", idx.0)
260            }
261            ValidationErrorKind::Decode { context, kind } => {
262                write!(f, "instruction decode error in {}: {}", context, kind)
263            }
264            ValidationErrorKind::InvalidGlobalInitExpr => {
265                write!(f, "invalid global initializer expression")
266            }
267            ValidationErrorKind::NonConstantGlobalInitExpr => {
268                write!(f, "global initializer must be a constant expression")
269            }
270            ValidationErrorKind::MutableGlobalInInitExpr { idx } => {
271                write!(
272                    f,
273                    "global initializer references mutable imported global {}",
274                    idx.0
275                )
276            }
277            ValidationErrorKind::ImmutableGlobalSet { idx } => {
278                write!(f, "cannot assign to immutable global {}", idx.0)
279            }
280            ValidationErrorKind::GlobalInitTypeMismatch { expected, found } => {
281                write!(
282                    f,
283                    "global initializer type mismatch: expected {:?}, found {:?}",
284                    expected, found
285                )
286            }
287            ValidationErrorKind::BranchTypeMismatch {
288                label,
289                expected,
290                found,
291            } => {
292                write!(
293                    f,
294                    "branch to label {} has type mismatch: expected {:?}, found {:?}",
295                    label.0, expected, found
296                )
297            }
298            ValidationErrorKind::InvalidBrOnNonNullTarget { label, found } => {
299                write!(
300                    f,
301                    "br_on_non_null target label {} must end in a reference type, found {:?}",
302                    label.0, found
303                )
304            }
305            ValidationErrorKind::InconsistentBranchTypes { expected, found } => {
306                write!(
307                    f,
308                    "branch targets have inconsistent types: expected {:?}, found {:?}",
309                    expected, found
310                )
311            }
312            ValidationErrorKind::UnexpectedElse => write!(f, "unexpected else"),
313            ValidationErrorKind::UnexpectedEnd => write!(f, "unexpected end"),
314            ValidationErrorKind::UnterminatedControlFrames => {
315                write!(f, "unterminated control frames")
316            }
317            ValidationErrorKind::ElseOutsideIf => write!(f, "else outside if block"),
318            ValidationErrorKind::MissingElseForResult => {
319                write!(f, "if block with result type requires else branch")
320            }
321            ValidationErrorKind::InvalidBlockType { block_type } => {
322                write!(f, "invalid block type {:?}", block_type)
323            }
324            ValidationErrorKind::ControlResultTypeMismatch { expected, found } => {
325                write!(
326                    f,
327                    "control frame result type mismatch: expected {:?}, found {:?}",
328                    expected, found
329                )
330            }
331            ValidationErrorKind::InvalidSelectResultArity { found } => {
332                write!(
333                    f,
334                    "typed select requires exactly one result type, found {}",
335                    found
336                )
337            }
338            ValidationErrorKind::SelectOperandTypeMismatch { expected, found } => {
339                write!(
340                    f,
341                    "select operands must match {:?}, found {:?}",
342                    expected, found
343                )
344            }
345            ValidationErrorKind::StackUnderflow {
346                op,
347                expected,
348                available,
349            } => {
350                write!(
351                    f,
352                    "operand stack underflow in {}: expected {:?}, available {:?}",
353                    op, expected, available
354                )
355            }
356            ValidationErrorKind::TypeMismatch {
357                op,
358                expected,
359                found,
360            } => {
361                write!(
362                    f,
363                    "type mismatch in {}: expected {:?}, found {:?}",
364                    op, expected, found
365                )
366            }
367            ValidationErrorKind::FunctionResultTypeMismatch {
368                expected,
369                found,
370                full_stack,
371            } => {
372                write!(
373                    f,
374                    "function result type mismatch: expected {:?}, found {:?} at stack top (full stack {:?})",
375                    expected, found, full_stack
376                )
377            }
378            ValidationErrorKind::ResultTypeMismatch { expected, found } => {
379                write!(
380                    f,
381                    "result type mismatch: expected {:?}, found {:?}",
382                    expected, found
383                )
384            }
385            ValidationErrorKind::InvalidStartFunctionType { params, results } => {
386                write!(
387                    f,
388                    "start function must have type [] -> [], found {:?} -> {:?}",
389                    params, results
390                )
391            }
392            ValidationErrorKind::InvalidElementExpr => {
393                write!(f, "invalid element initializer expression")
394            }
395            ValidationErrorKind::NonConstantElementExpr => {
396                write!(f, "element initializer must be a constant expression")
397            }
398            ValidationErrorKind::ElementExprTypeMismatch { expected, found } => {
399                write!(
400                    f,
401                    "element initializer type mismatch: expected {:?}, found {:?}",
402                    expected, found
403                )
404            }
405            ValidationErrorKind::ElementTableTypeMismatch { expected, found } => {
406                write!(
407                    f,
408                    "active element segment table type mismatch: expected {:?}, found {:?}",
409                    expected, found
410                )
411            }
412            ValidationErrorKind::InvalidCallIndirectTableType { expected, found } => {
413                write!(
414                    f,
415                    "call_indirect requires table element type {:?}, found {:?}",
416                    expected, found
417                )
418            }
419            ValidationErrorKind::MissingDataCountSection { op } => {
420                write!(f, "{} requires a data count section", op)
421            }
422            ValidationErrorKind::MemorySizeOutOfRange => write!(f, "memory size"),
423            ValidationErrorKind::MemoryMinExceedsMax => {
424                write!(f, "size minimum must not be greater than maximum")
425            }
426            ValidationErrorKind::TableTypeMismatch => write!(f, "type mismatch"),
427            ValidationErrorKind::DuplicateExportName { name } => {
428                write!(f, "duplicate export name {:?}", name)
429            }
430        }
431    }
432}
433
434impl From<DecodeError> for ValidationErrorKind {
435    fn from(error: DecodeError) -> Self {
436        Self::Decode {
437            context: error.context,
438            kind: error.kind,
439        }
440    }
441}
442
443#[cfg(feature = "std")]
444impl std::error::Error for ValidationError {}
445
446#[cfg(not(feature = "std"))]
447impl core::error::Error for ValidationError {}