vj-codegen 0.1.0

LLVM code generator of the vj SystemVerilog simulator.
//! Lowering errors: every unhandled AST shape must produce one of these,
//! never panic!/unimplemented!/a silent no-op.
use std::fmt;

#[derive(Debug, Clone)]
pub struct LoweringError {
    pub message: String,
    pub at: String,
}

impl LoweringError {
    pub fn unsupported(what: &str, at: &str) -> Self {
        Self {
            message: format!("unsupported: {what}"),
            at: at.into(),
        }
    }

    pub fn unsupported_at(what: &str, sym: &sv_lang::Symbol) -> Self {
        Self {
            message: format!("unsupported: {what}"),
            at: sym.hierarchical_path(),
        }
    }
}

impl fmt::Display for LoweringError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} (at {})", self.message, self.at)
    }
}

impl std::error::Error for LoweringError {}

/// Task 4 (A2-part2; ledger CG-127, RT-003): every failure mode the AOT
/// backend (`crate::aot::AotBackend`, `crate::pipeline::compile_to_object`)
/// can hit -- a distinct type from [`LoweringError`] (which is specifically
/// "this AST shape has no lowering yet"): every `CodegenError` variant here
/// is instead about turning an ALREADY-lowered `Module` into a real,
/// loadable artifact (object emission, system-linker invocation, and the
/// post-link `dlopen`/`dlsym` probe `AotBackend::link` itself performs to
/// populate `AotKernelHandle`) -- never a silent guess, never a panic.
#[derive(Debug)]
pub enum CodegenError {
    /// A plan failed to lower to LLVM IR at all (reuses [`LoweringError`]
    /// unchanged -- `compile_to_object` hits this at the exact same call
    /// site `build_pipeline` does, just wrapped in this crate's own AOT-path
    /// error type).
    Lowering(LoweringError),
    /// `AotBackend::emit_object`: target-machine initialization/creation or
    /// the `write_to_file` emission step itself failed.
    ObjectEmission(String),
    /// `AotBackend::link`: the system linker could not be spawned, or exited
    /// non-zero.
    Link(String),
    /// `AotBackend::link`'s own post-link probe: the just-produced `.so`
    /// could not be `dlopen`'d, or a required symbol (`sim_abi_version`/
    /// `sim_process_count`) could not be `dlsym`'d from it -- a `.so` that
    /// links but fails this probe is never reported as `Ok`.
    Load(String),
    Io(std::io::Error),
}

impl From<LoweringError> for CodegenError {
    fn from(e: LoweringError) -> Self {
        CodegenError::Lowering(e)
    }
}

impl fmt::Display for CodegenError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CodegenError::Lowering(e) => write!(f, "lowering error: {e}"),
            CodegenError::ObjectEmission(msg) => write!(f, "object emission failed: {msg}"),
            CodegenError::Link(msg) => write!(f, "link failed: {msg}"),
            CodegenError::Load(msg) => write!(f, "AOT .so load probe failed: {msg}"),
            CodegenError::Io(e) => write!(f, "I/O error: {e}"),
        }
    }
}

impl std::error::Error for CodegenError {}