llvm-native-core 0.1.14

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
//! LLVM-style RTTI casting — isa<>, cast<>, dyn_cast<> equivalents.
//!
//! Clean-room behavioral reconstruction.
//! @llvm_behavior: LLVM uses a custom RTTI system (isa<>, cast<>, dyn_cast<>)
//!   for safe downcasting in class hierarchies. In Rust, we use enums
//!   and pattern matching for the same purpose, but provide isa/cast
//!   convenience traits for LLVM-like coding patterns.

/// Trait for types that support LLVM-style isa<> checking.
pub trait Isa<T: ?Sized> {
    /// Returns true if this value is of type T.
    fn isa(&self) -> bool;
}

/// Trait for types that support LLVM-style cast<> (asserting downcast).
pub trait Cast<T> {
    /// Cast to T, panicking if the cast is invalid.
    fn cast(&self) -> &T;

    /// Mutable cast.
    fn cast_mut(&mut self) -> &mut T;
}

/// Trait for types that support LLVM-style dyn_cast<> (checked downcast).
pub trait DynCast<T> {
    /// Try to cast to T, returning None if the cast is invalid.
    fn dyn_cast(&self) -> Option<&T>;

    /// Mutable checked cast.
    fn dyn_cast_mut(&mut self) -> Option<&mut T>;
}

// === Value Casting Utilities ===

/// Cast a value to a target type via a function.
/// Panics if the conversion function returns None.
/// @llvm_behavior: cast<To>(Val) asserts that the cast is valid.
pub fn cast_val<T, U>(val: T, f: impl FnOnce(T) -> Option<U>) -> U {
    f(val).expect("cast<> failed: invalid type conversion")
}

/// Try to cast a value to a target type.
/// @llvm_behavior: dyn_cast<To>(Val) returns nullptr on failure.
pub fn dyn_cast_val<T, U>(val: T, f: impl FnOnce(T) -> Option<U>) -> Option<U> {
    f(val)
}

/// Check if a value has a specific type.
/// @llvm_behavior: isa<To>(Val) returns true if Val is of type To.
pub fn isa_val<T>(val: &T, f: impl FnOnce(&T) -> bool) -> bool {
    f(val)
}

// === Tag-based casting for enum hierarchies ===

/// A tag indicating the concrete type in a sum-type hierarchy.
/// Used to implement isa<>/cast<> semantics for enums.
pub trait TypeTag: PartialEq + Copy {
    /// The full set of possible tags for this hierarchy.
    fn all() -> &'static [Self];
}

/// A value tagged with its concrete type.
pub struct Tagged<T: TypeTag, V> {
    pub tag: T,
    pub value: V,
}

impl<T: TypeTag + std::fmt::Debug, V> Tagged<T, V> {
    /// Check if the tagged value has a specific type tag.
    pub fn is_a(&self, tag: T) -> bool {
        self.tag == tag
    }

    /// Try to get the value if it matches the tag.
    pub fn as_a(&self, tag: T) -> Option<&V> {
        if self.tag == tag {
            Some(&self.value)
        } else {
            None
        }
    }

    /// Get the value, asserting it matches the tag.
    /// Panics if the tag doesn't match.
    pub fn cast_a(&self, tag: T) -> &V {
        assert_eq!(
            self.tag, tag,
            "cast_a<> failed: expected {:?}, got {:?}",
            tag, self.tag
        );
        &self.value
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    enum MyTag {
        Int,
        Float,
        String,
    }

    impl TypeTag for MyTag {
        fn all() -> &'static [Self] {
            &[MyTag::Int, MyTag::Float, MyTag::String]
        }
    }

    #[test]
    fn test_tagged_is_a() {
        let v = Tagged {
            tag: MyTag::Int,
            value: 42i32,
        };
        assert!(v.is_a(MyTag::Int));
        assert!(!v.is_a(MyTag::Float));
    }

    #[test]
    fn test_tagged_as_a() {
        let v = Tagged {
            tag: MyTag::String,
            value: "hello".to_string(),
        };
        assert!(v.as_a(MyTag::String).is_some());
        assert!(v.as_a(MyTag::Int).is_none());
    }

    #[test]
    fn test_cast_val_success() {
        let result = cast_val(Some(42), |v| v);
        assert_eq!(result, 42);
    }

    #[test]
    #[should_panic]
    fn test_cast_val_failure() {
        let _: i32 = cast_val(None::<i32>, |v| v);
    }
}