llvm-native-core 0.1.16

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
//! LLVM Error/Expected<T> — error handling with checked invariants.
//!
//! Clean-room behavioral reconstruction.
//! @llvm_behavior: llvm::Error represents either success or a typed error.
//!   Errors MUST be checked before destruction.
//!   Expected<T> is a variant that holds either a T or an Error.

use std::fmt;

/// The base error trait. LLVM errors are type-erased into this.
/// Note: `is_a` is NOT on the trait to keep it dyn-compatible.
pub trait ErrorInfo: fmt::Display + fmt::Debug + Send + Sync {
    /// Get the underlying type ID for isa<>/dyn_cast<> equivalence.
    fn type_id(&self) -> std::any::TypeId;

    /// Convert this error to an Error object.
    fn into_error(self) -> Error
    where
        Self: Sized + 'static,
    {
        Error {
            inner: Some(Box::new(self)),
        }
    }
}

/// Wrapper to store a concrete error as a trait object.
#[allow(dead_code)]
struct ErrorWrapper<E: ErrorInfo + 'static>(E);

impl<E: ErrorInfo + 'static> fmt::Display for ErrorWrapper<E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl<E: ErrorInfo + 'static> fmt::Debug for ErrorWrapper<E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&self.0, f)
    }
}

impl<E: ErrorInfo + 'static> ErrorInfo for ErrorWrapper<E> {
    fn type_id(&self) -> std::any::TypeId {
        std::any::TypeId::of::<E>()
    }
}

/// Check if an ErrorInfo is of a specific type (isa<> equivalent).
/// This is a free function, not a method, to keep the trait dyn-compatible.
pub fn is_error_a<E: ErrorInfo + 'static>(err: &dyn ErrorInfo) -> bool {
    std::any::TypeId::of::<E>() == err.type_id()
}

/// LLVM Error type — either success or a type-erased error.
///
/// @llvm_behavior: Error must be checked. If an Error containing a failure
///   is dropped, the program asserts (warns in debug mode).
#[must_use]
pub struct Error {
    inner: Option<Box<dyn ErrorInfo>>,
}

impl Error {
    pub fn success() -> Self {
        Self { inner: None }
    }

    pub fn is_success(&self) -> bool {
        self.inner.is_none()
    }

    pub fn is_failure(&self) -> bool {
        self.inner.is_some()
    }

    pub fn from_string(msg: impl Into<String>) -> Self {
        #[derive(Debug)]
        struct StringError(String);

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

        impl ErrorInfo for StringError {
            fn type_id(&self) -> std::any::TypeId {
                std::any::TypeId::of::<StringError>()
            }
        }

        StringError(msg.into()).into_error()
    }

    pub fn into_result(&self) -> Result<(), String> {
        match &self.inner {
            None => Ok(()),
            Some(err) => Err(err.to_string()),
        }
    }

    pub fn join(self, other: Error) -> Error {
        if self.is_success() {
            return other;
        }
        if other.is_success() {
            return self;
        }
        let msg = format!(
            "{}; {}",
            self.inner.as_ref().unwrap(),
            other.inner.as_ref().unwrap()
        );
        std::mem::forget(other);
        Error::from_string(msg)
    }

    pub fn join_errors(errors: Vec<Error>) -> Error {
        errors
            .into_iter()
            .fold(Error::success(), |acc, e| acc.join(e))
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.inner {
            None => write!(f, "success"),
            Some(err) => write!(f, "{}", err),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.inner {
            None => write!(f, "Error::success()"),
            Some(err) => write!(f, "Error({})", err),
        }
    }
}

/// Explicitly consume an Error, marking it as checked.
pub fn consume_error(err: Error) {
    if err.is_failure() {
        eprintln!("Warning: consuming unchecked error: {}", err);
    }
    drop(err);
}

/// Sentinel for success.
pub struct Success;

impl From<Success> for Error {
    fn from(_: Success) -> Self {
        Error::success()
    }
}

/// Expected<T> — a value-or-error variant.
#[must_use]
pub struct Expected<T> {
    value: Option<T>,
    error: Error,
}

impl<T> Expected<T> {
    pub fn from_value(value: T) -> Self {
        Self {
            value: Some(value),
            error: Error::success(),
        }
    }

    pub fn from_error(error: Error) -> Self {
        Self { value: None, error }
    }

    pub fn is_valid(&self) -> bool {
        self.value.is_some()
    }
    pub fn get(&self) -> Option<&T> {
        self.value.as_ref()
    }

    pub fn take(self) -> T {
        match self.value {
            Some(v) => {
                drop(self.error);
                v
            }
            None => panic!("Expected::take() called on error: {}", self.error),
        }
    }

    pub fn take_error(self) -> Error {
        self.error
    }

    pub fn into_result(self) -> Result<T, Error> {
        match self.value {
            Some(v) => Ok(v),
            None => Err(self.error),
        }
    }

    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Expected<U> {
        match self.value {
            Some(v) => Expected::from_value(f(v)),
            None => Expected::from_error(self.error),
        }
    }
}

impl<T: fmt::Debug> fmt::Debug for Expected<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.value {
            Some(v) => write!(f, "Expected({:?})", v),
            None => write!(f, "Expected(error: {})", self.error),
        }
    }
}

impl<T> From<Result<T, Error>> for Expected<T> {
    fn from(result: Result<T, Error>) -> Self {
        match result {
            Ok(v) => Expected::from_value(v),
            Err(e) => Expected::from_error(e),
        }
    }
}

impl Drop for Error {
    fn drop(&mut self) {
        if cfg!(debug_assertions) {
            if let Some(err) = &self.inner {
                eprintln!("WARNING: Unchecked LLVM Error dropped: {:?}", err);
            }
        }
    }
}

/// A generic error with a string message.
#[derive(Debug, Clone)]
pub struct GenericError {
    pub message: String,
}

impl GenericError {
    pub fn new(msg: impl Into<String>) -> Self {
        Self {
            message: msg.into(),
        }
    }
}

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

impl ErrorInfo for GenericError {
    fn type_id(&self) -> std::any::TypeId {
        std::any::TypeId::of::<GenericError>()
    }
}