an-inkwell 26.0.0

Astery's Narrower Inkwell
use std::ptr::NonNull;

use llvm_sys::{
    core::{
        LLVMConstArray2, LLVMConstInt, LLVMConstNull, LLVMConstReal, LLVMConstStruct,
        LLVMConstVector, LLVMDisposeMessage, LLVMPrintValueToString,
    },
    prelude::LLVMValueRef,
};

use crate::{Context, Error, Type};

pub struct Value<'ctx> {
    context: &'ctx Context,
    raw: NonNull<std::ffi::c_void>,
}

impl<'ctx> Value<'ctx> {
    pub fn integer(integer: &'ctx Type<'ctx>, value: u64, signed: bool) -> Self {
        Self::from_raw(integer.context(), unsafe {
            LLVMConstInt(integer.as_raw(), value, signed as i32)
        })
    }

    pub fn float(float: &'ctx Type<'ctx>, value: f64) -> Self {
        Self::from_raw(float.context(), unsafe {
            LLVMConstReal(float.as_raw(), value)
        })
    }

    pub fn null(pointer: &'ctx Type<'ctx>) -> Result<Self, Error> {
        if pointer.kind() != llvm_sys::LLVMTypeKind::LLVMPointerTypeKind {
            return Err(Error::NotPointerType);
        }

        Ok(Self::from_raw(pointer.context(), unsafe {
            LLVMConstNull(pointer.as_raw())
        }))
    }

    pub fn array(element: &'ctx Type<'ctx>, values: &[Value<'ctx>]) -> Result<Self, Error> {
        for value in values {
            if !std::ptr::eq(element.context(), value.context) {
                return Err(Error::DifferentContext);
            }
        }

        let mut values = values.iter().map(Self::as_raw).collect::<Vec<_>>();
        let raw =
            unsafe { LLVMConstArray2(element.as_raw(), values.as_mut_ptr(), values.len() as u64) };

        Ok(Self::from_raw(element.context(), raw))
    }

    pub fn vector(values: &[Value<'ctx>]) -> Result<Self, Error> {
        let Some(first) = values.first() else {
            return Err(Error::EmptyAggregate);
        };

        for value in values {
            if !std::ptr::eq(first.context, value.context) {
                return Err(Error::DifferentContext);
            }
        }

        let mut values = values.iter().map(Self::as_raw).collect::<Vec<_>>();
        let raw = unsafe { LLVMConstVector(values.as_mut_ptr(), values.len() as u32) };
        Ok(Self::from_raw(first.context, raw))
    }

    pub fn structure(values: &[Value<'ctx>], packed: bool) -> Result<Self, Error> {
        let Some(first) = values.first() else {
            return Err(Error::EmptyAggregate);
        };

        for value in values {
            if !std::ptr::eq(first.context, value.context) {
                return Err(Error::DifferentContext);
            }
        }

        let mut values = values.iter().map(Self::as_raw).collect::<Vec<_>>();
        let raw =
            unsafe { LLVMConstStruct(values.as_mut_ptr(), values.len() as u32, packed as i32) };
        Ok(Self::from_raw(first.context, raw))
    }

    pub fn context(&self) -> &Context {
        self.context
    }

    pub fn as_ir(&self) -> String {
        let value = unsafe { LLVMPrintValueToString(self.as_raw()) };
        if value.is_null() {
            return String::new();
        }

        let text = unsafe { std::ffi::CStr::from_ptr(value) };
        let text = text.to_string_lossy().into_owned();
        unsafe { LLVMDisposeMessage(value) };
        text
    }

    pub(crate) fn from_raw(context: &'ctx Context, raw: LLVMValueRef) -> Self {
        let raw = unsafe { NonNull::new_unchecked(raw.cast()) };
        Self { context, raw }
    }

    pub(crate) fn as_raw(&self) -> LLVMValueRef {
        self.raw.as_ptr().cast()
    }
}