vyre 0.4.0

GPU compute intermediate representation with a standard operation library
Documentation
//! Error types for IR validation, wire-format decoding, and GPU operations.
//!
//! Vyre unifies all failure modes under a single `Error` enum so that
//! frontends, backends, and the conform gate speak the same language.
//! Every variant carries an actionable `Fix:` message that tells the caller
//! exactly what invariant was violated and how to recover.

use thiserror::Error;

/// Shorthand result type used throughout the vyre public API.
///
/// All fallible vyre operations return `Result<T>` so that callers only need
/// to learn one error representation. The unified type ensures that a
/// frontend emitting bad IR, a backend hitting an adapter limit, or a
/// wire-format decoder seeing truncated bytes all produce the same top-level
/// failure.
pub type Result<T> = std::result::Result<T, Error>;

/// The unified failure enum for every vyre operation.
///
/// Each variant represents a distinct invariant violation: a recursive
/// inlining cycle, a missing operation registration, a GPU pipeline
/// failure, or a malformed wire-format payload. The error messages are
/// intentionally prescriptive — every variant includes a `Fix:` hint that
/// names the exact change required to make the program valid.
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[non_exhaustive]
pub enum Error {
    /// A recursive composition cycle was found during operation inlining.
    ///
    /// Category A operations compose by inlining their IR bodies. A cycle
    /// means an operation calls itself (directly or indirectly), which would
    /// expand into infinite shader code. The conform gate rejects such
    /// programs before they ever reach a backend.
    #[error(
        "IR inlining cycle at operation `{op_id}`. Fix: remove the recursive Expr::Call chain or split the recursive algorithm into an explicit bounded Loop."
    )]
    InlineCycle {
        /// The operation identifier that closed the cycle.
        op_id: String,
    },

    /// Operation inlining could not resolve an operation id.
    ///
    /// An `Expr::Call` referenced an operation that is not registered in the
    /// in-core operation registry. Without a registered definition the
    /// inliner cannot substitute the callee's IR body.
    #[error(
        "IR inlining could not resolve operation `{op_id}`. Fix: register a Category A operation with this id before lowering or replace the call with inline IR."
    )]
    InlineUnknownOp {
        /// The missing operation identifier.
        op_id: String,
    },

    /// Operation inlining rejected an operation that must dispatch separately.
    ///
    /// Some operations process buffer inputs and therefore cannot be
    /// inlined into another kernel. They must remain as separate dispatch
    /// boundaries. The inliner rejects them to preserve the buffer-based
    /// execution contract.
    #[error(
        "IR inlining rejected non-inlinable operation `{op_id}`. Fix: this op processes buffer inputs and must be dispatched as a separate kernel, not composed via Expr::Call."
    )]
    InlineNonInlinable {
        /// The operation identifier that cannot be inlined.
        op_id: String,
    },

    /// The number of arguments passed to an inlined operation did not match.
    ///
    /// Each `Expr::Call` must supply exactly one argument for every
    /// read-only or uniform input buffer declared by the callee program.
    /// A mismatch means the caller and callee disagree on the buffer
    /// interface.
    #[error(
        "IR inlining argument count mismatch for operation `{op_id}`: expected {expected}, got {got}. Fix: pass exactly one argument for each ReadOnly or Uniform input buffer declared by the callee program."
    )]
    InlineArgCountMismatch {
        /// The operation identifier being expanded.
        op_id: String,
        /// The number of arguments the callee expects.
        expected: usize,
        /// The number of arguments the caller provided.
        got: usize,
    },

    /// The inlined operation never wrote to its declared output buffer.
    ///
    /// A Category A operation must produce at least one output so that the
    /// caller can observe the result. An op body that omits all output
    /// writes violates the compositional contract.
    #[error(
        "IR inlining found no output write for operation `{op_id}`. Fix: Ensure the op's program() body writes to its output buffer at least once."
    )]
    InlineNoOutput {
        /// The operation identifier being expanded.
        op_id: String,
    },

    /// The inlined operation declared an invalid number of output buffers.
    ///
    /// Exactly one result buffer must be marked with
    /// `BufferDecl::output(...)`. Zero outputs means the op is unobservable;
    /// more than one means the inliner does not know which value to forward.
    #[error(
        "IR inlining found {got} declared output buffers for operation `{op_id}`. Fix: mark exactly one result buffer with BufferDecl::output(...)."
    )]
    InlineOutputCountMismatch {
        /// The operation identifier being expanded.
        op_id: String,
        /// The actual number of buffers marked as outputs.
        got: usize,
    },

    /// Wire-format payload failed validation checks.
    ///
    /// The bytes decoded into a syntactically valid structure, but the
    /// semantics violate vyre invariants (out-of-range node IDs, type
    /// mismatches, excessive nesting, etc.). This error originates in the
    /// validation layer after deserialization.
    #[error(
        "Wire-format validation failed: {message}. Fix: recompile the frontend program set and ensure the compiler only emits valid instructions."
    )]
    WireFormatValidation {
        /// Human-readable description of the validation failure.
        message: String,
    },

    /// WGSL lowering failed before a shader could be emitted.
    ///
    /// The lowering pipeline accepts only validated IR whose buffer layout,
    /// casts, atomics, and expression forms can be represented in WGSL. This
    /// variant carries the same actionable prose as the former lowering-local
    /// error type.
    #[error("vyre WGSL lowering: {message}")]
    Lowering {
        /// Human-readable description of the lowering failure.
        message: String,
    },

    /// Reference interpreter execution failed.
    ///
    /// The pure Rust parity interpreter rejects malformed programs, unknown
    /// names, type mismatches, unsupported IR, and float operations that have
    /// not landed yet. This variant carries the same actionable prose as the
    /// former interpreter-local error type.
    #[error("vyre reference interpreter: {message}")]
    Interp {
        /// Human-readable description of the interpreter failure.
        message: String,
    },

    /// GPU execution failed.
    ///
    /// The backend encountered a fatal condition during pipeline creation,
    /// buffer upload, shader compilation, or dispatch. The message includes
    /// adapter-specific details such as limit exhaustion or missing
    /// extensions.
    #[error(
        "GPU pipeline failed: {message}. Fix: verify wgpu is available and the compiled buffers fit the target adapter limits."
    )]
    Gpu {
        /// Description of the GPU failure.
        message: String,
    },

    /// Decode configuration failed validation.
    ///
    /// A decoder (for example base64, hex, or URL decoding) was initialized
    /// with invalid parameters such as zero thresholds or malformed TOML.
    #[error(
        "Decode configuration failed: {message}. Fix: provide valid TOML and non-zero decode thresholds."
    )]
    DecodeConfig {
        /// Description of the configuration failure.
        message: String,
    },

    /// Decode execution or readback failed validation.
    ///
    /// The decoder ran but produced invalid sizing, out-of-bounds source
    /// regions, or a checksum mismatch. This usually indicates truncated
    /// input or a shader output size bug.
    #[error(
        "Decode pipeline failed: {message}. Fix: inspect shader output sizing and source-region validation."
    )]
    Decode {
        /// Description of the decode failure.
        message: String,
    },

    /// Decompression execution, sizing, or readback failed validation.
    ///
    /// A decompressor (gzip, deflate, LZ4, zstd) encountered corrupt frame
    /// metadata, oversided payloads, or a status-word mismatch. The input
    /// must be split or the frame metadata repaired before retry.
    #[error(
        "Decompression pipeline failed: {message}. Fix: validate frame metadata, split oversized payloads, and inspect GPU decompression status words."
    )]
    Decompress {
        /// Description of the decompression failure.
        message: String,
    },

    /// DFA compilation or scanning failed.
    ///
    /// The deterministic finite automaton used for pattern matching could
    /// not be built from the supplied transition tables, or the scan kernel
    /// encountered an invalid state. Check the table dimensions and output
    /// link consistency.
    #[error(
        "DFA pipeline failed: {message}. Fix: validate DFA transition tables, output links, and target adapter limits."
    )]
    Dfa {
        /// Description of the DFA failure.
        message: String,
    },

    /// Dataflow graph execution failed.
    ///
    /// A dataflow operation encountered invalid graph inputs, mismatched
    /// buffer sizes, or an adapter limit violation. Validate the node
    /// topology and buffer layout before resubmission.
    #[error(
        "Dataflow pipeline failed: {message}. Fix: validate graph inputs, buffer sizing, and target adapter limits."
    )]
    Dataflow {
        /// Description of the dataflow failure.
        message: String,
    },

    /// Prefix-array construction failed before allocation or upload.
    ///
    /// A prefix operation (for example Aho-Corasick or suffix-array
    /// preprocessing) could not allocate or upload its working set.
    /// Reducing the per-file scan size or splitting the input usually
    /// resolves the failure.
    #[error(
        "Prefix construction failed: {message}. Fix: split the input before building prefix arrays or reduce per-file scan size."
    )]
    Prefix {
        /// Description of the prefix construction failure.
        message: String,
    },

    /// CSR graph construction or validation failed.
    ///
    /// A compressed-sparse-row graph had out-of-range edge endpoints,
    /// mismatched column/row lengths, or exceeded memory limits. Cap the
    /// graph size and verify that every edge endpoint is within `node_count`.
    #[error(
        "CSR graph construction failed: {message}. Fix: cap graph size and ensure every edge endpoint is within node_count."
    )]
    Csr {
        /// Description of the CSR failure.
        message: String,
    },

    /// Serialization or deserialization failed.
    ///
    /// The wire-format bytes were truncated, corrupted, or produced by an
    /// incompatible schema version. Verify the payload length and checksum
    /// before retry.
    #[error(
        "Serialization failed: {message}. Fix: verify the wire payload is not truncated or corrupted."
    )]
    Serialization {
        /// Description of the serialization failure.
        message: String,
    },

    /// Rule formula construction or evaluation failed.
    ///
    /// A rule-engine expression referenced an unknown pattern ID, used an
    /// invalid threshold, or wrote past the end of the verdict buffer.
    /// Validate the rule spec and buffer sizing before lowering.
    #[error(
        "Rule evaluation failed: {message}. Fix: validate rule pattern ids, thresholds, and verdict buffer sizing before lowering."
    )]
    RuleEval {
        /// Description of the rule failure.
        message: String,
    },
}

impl Error {
    /// Build a WGSL lowering error with actionable guidance.
    #[must_use]
    pub fn lowering(message: impl Into<String>) -> Self {
        Self::Lowering {
            message: message.into(),
        }
    }

    /// Build a reference-interpreter error with actionable guidance.
    #[must_use]
    pub fn interp(message: impl Into<String>) -> Self {
        Self::Interp {
            message: message.into(),
        }
    }
}