an-inkwell 26.0.0

Astery's Narrower Inkwell
use llvm_sys::core::LLVMBuildCall2;

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

impl<'ctx> Builder<'ctx> {
    pub fn call(
        &self,
        function_type: &Type<'ctx>,
        function: &Function<'ctx>,
        arguments: &[Value<'ctx>],
    ) -> Result<Value<'ctx>, Error> {
        if !std::ptr::eq(self.context(), function_type.context())
            || !std::ptr::eq(self.context(), function.context())
        {
            return Err(Error::DifferentContext);
        }

        for argument in arguments {
            if !std::ptr::eq(self.context(), argument.context()) {
                return Err(Error::DifferentContext);
            }
        }

        let mut arguments = arguments.iter().map(Value::as_raw).collect::<Vec<_>>();
        let raw = unsafe {
            LLVMBuildCall2(
                self.as_raw(),
                function_type.as_raw(),
                function.as_raw(),
                arguments.as_mut_ptr(),
                arguments.len() as u32,
                c"".as_ptr(),
            )
        };

        Ok(Value::from_raw(self.context(), raw))
    }
}

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

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

    #[test]
    fn builds_call() {
        let context = Context::create();
        let (module, builder, _block, function, function_type) = function(&context);
        let integer = Type::i32(&context);
        let argument = Value::integer(&integer, 42, false);

        let call = builder
            .call(&function_type, &function, &[argument])
            .unwrap();

        assert!(call.as_ir().contains("call i32 @add"));
        assert!(module.as_ir().contains("call i32 @add"));
    }

    #[test]
    fn rejects_call_from_different_context() {
        let first = Context::create();
        let second = Context::create();
        let (_module, builder, _block, function, function_type) = function(&first);
        let integer = Type::i32(&second);
        let argument = Value::integer(&integer, 42, false);

        assert!(matches!(
            builder.call(&function_type, &function, &[argument]),
            Err(crate::Error::DifferentContext)
        ));
    }
}