an-inkwell 26.0.0

Astery's Narrower Inkwell
use llvm_sys::core::{
    LLVMBuildExtractElement, LLVMBuildExtractValue, LLVMBuildInsertElement, LLVMBuildInsertValue,
};

use crate::{Builder, Error, Value};

impl<'ctx> Builder<'ctx> {
    pub fn extract_value(&self, aggregate: &Value<'ctx>, index: u32) -> Result<Value<'ctx>, Error> {
        self.check_aggregate_context(aggregate)?;
        let raw = unsafe {
            LLVMBuildExtractValue(self.as_raw(), aggregate.as_raw(), index, c"".as_ptr())
        };
        Ok(Value::from_raw(self.context(), raw))
    }

    pub fn insert_value(
        &self,
        aggregate: &Value<'ctx>,
        value: &Value<'ctx>,
        index: u32,
    ) -> Result<Value<'ctx>, Error> {
        self.check_aggregate(aggregate, value)?;
        let raw = unsafe {
            LLVMBuildInsertValue(
                self.as_raw(),
                aggregate.as_raw(),
                value.as_raw(),
                index,
                c"".as_ptr(),
            )
        };
        Ok(Value::from_raw(self.context(), raw))
    }

    pub fn extract_element(
        &self,
        vector: &Value<'ctx>,
        index: &Value<'ctx>,
    ) -> Result<Value<'ctx>, Error> {
        self.check_aggregate(vector, index)?;
        let raw = unsafe {
            LLVMBuildExtractElement(self.as_raw(), vector.as_raw(), index.as_raw(), c"".as_ptr())
        };
        Ok(Value::from_raw(self.context(), raw))
    }

    pub fn insert_element(
        &self,
        vector: &Value<'ctx>,
        element: &Value<'ctx>,
        index: &Value<'ctx>,
    ) -> Result<Value<'ctx>, Error> {
        self.check_aggregate(vector, element)?;
        self.check_aggregate(vector, index)?;
        let raw = unsafe {
            LLVMBuildInsertElement(
                self.as_raw(),
                vector.as_raw(),
                element.as_raw(),
                index.as_raw(),
                c"".as_ptr(),
            )
        };
        Ok(Value::from_raw(self.context(), raw))
    }

    fn check_aggregate_context(&self, value: &Value<'ctx>) -> Result<(), Error> {
        if !std::ptr::eq(self.context(), value.context()) {
            return Err(Error::DifferentContext);
        }
        Ok(())
    }

    fn check_aggregate(&self, left: &Value<'ctx>, right: &Value<'ctx>) -> Result<(), Error> {
        self.check_aggregate_context(left)?;
        self.check_aggregate_context(right)?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::Builder;
    use crate::{Context, Error, Type, Value};

    fn function(context: &Context) -> (crate::Module<'_>, Builder<'_>, crate::Block<'_>) {
        let module = context.module("test").unwrap();
        let builder = context.builder().unwrap();
        let function_type = Type::function(&Type::void(context), &[], false).unwrap();
        let function = module.function("main", &function_type).unwrap();
        let block = function.block("entry").unwrap();
        builder.position(&block).unwrap();
        (module, builder, block)
    }

    #[test]
    fn builds_aggregate_access() {
        let context = Context::create();
        let (_module, builder, _block) = function(&context);
        let integer = Type::i32(&context);
        let value = Value::structure(
            &[
                Value::integer(&integer, 10, false),
                Value::integer(&integer, 20, false),
            ],
            false,
        )
        .unwrap();

        assert!(
            builder
                .extract_value(&value, 1)
                .unwrap()
                .as_ir()
                .contains("i32 20")
        );
        assert!(
            builder
                .insert_value(&value, &Value::integer(&integer, 30, false), 1)
                .unwrap()
                .as_ir()
                .contains("i32 30")
        );
    }

    #[test]
    fn builds_vector_element_access() {
        let context = Context::create();
        let (_module, builder, _block) = function(&context);
        let integer = Type::i32(&context);
        let value = Value::vector(&[
            Value::integer(&integer, 10, false),
            Value::integer(&integer, 20, false),
        ])
        .unwrap();
        let index = Value::integer(&integer, 1, false);

        assert!(
            builder
                .extract_element(&value, &index)
                .unwrap()
                .as_ir()
                .contains("i32 20")
        );
        assert!(
            builder
                .insert_element(&value, &Value::integer(&integer, 30, false), &index)
                .unwrap()
                .as_ir()
                .contains("i32 30")
        );
    }

    #[test]
    fn rejects_aggregate_access_from_different_contexts() {
        let first = Context::create();
        let second = Context::create();
        let (_module, builder, _block) = function(&first);
        let first_type = Type::i32(&first);
        let second_type = Type::i32(&second);
        let value = Value::structure(&[Value::integer(&first_type, 1, false)], false).unwrap();
        let other = Value::integer(&second_type, 2, false);

        assert!(matches!(
            builder.extract_value(&other, 0),
            Err(Error::DifferentContext)
        ));
        assert!(matches!(
            builder.insert_value(&value, &other, 0),
            Err(Error::DifferentContext)
        ));
    }
}