use std::convert::TryFrom;
use std::fmt::{self, Display};
use crate::types::AsTypeRef;
use crate::values::AsValueRef;
use crate::values::{AnyValue, FunctionValue, PointerValue};
use llvm_sys::LLVMTypeKind;
use llvm_sys::core::{LLVMGetElementType, LLVMGetTypeKind, LLVMTypeOf};
use llvm_sys::prelude::LLVMTypeRef;
use llvm_sys::prelude::LLVMValueRef;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum CallableValueEnum<'ctx> {
Function(FunctionValue<'ctx>),
Pointer(PointerValue<'ctx>),
}
impl<'ctx> CallableValueEnum<'ctx> {
#[allow(unused)]
#[inline]
#[must_use]
pub fn is_function(self) -> bool {
matches!(self, Self::Function(_))
}
#[allow(unused)]
#[inline]
#[must_use]
pub fn is_pointer(self) -> bool {
matches!(self, Self::Pointer(_))
}
#[allow(unused)]
#[inline]
#[must_use]
pub fn function(self) -> Option<FunctionValue<'ctx>> {
match self {
Self::Function(function) => Some(function),
_ => None,
}
}
#[allow(unused)]
#[inline]
#[must_use]
pub fn pointer(self) -> Option<PointerValue<'ctx>> {
match self {
Self::Pointer(pointer) => Some(pointer),
_ => None,
}
}
#[allow(unused)]
#[inline]
#[must_use]
#[track_caller]
pub fn expect_function(self, msg: &str) -> FunctionValue<'ctx> {
match self {
Self::Function(function) => function,
_ => panic!("{msg}"),
}
}
#[allow(unused)]
#[inline]
#[must_use]
#[track_caller]
pub fn expect_pointer(self, msg: &str) -> PointerValue<'ctx> {
match self {
Self::Pointer(pointer) => pointer,
_ => panic!("{msg}"),
}
}
#[allow(unused)]
#[inline]
#[must_use]
#[track_caller]
pub fn unwrap_function(self) -> FunctionValue<'ctx> {
self.expect_function("Called unwrap_function() on CallableValueEnum::Pointer.")
}
#[allow(unused)]
#[inline]
#[must_use]
#[track_caller]
pub fn unwrap_pointer(self) -> PointerValue<'ctx> {
self.expect_pointer("Called unwrap_pointer() on CallableValueEnum::Function.")
}
}
#[derive(Debug)]
pub struct CallableValue<'ctx>(CallableValueEnum<'ctx>);
unsafe impl AsValueRef for CallableValue<'_> {
fn as_value_ref(&self) -> LLVMValueRef {
match self.0 {
CallableValueEnum::Function(function) => function.as_value_ref(),
CallableValueEnum::Pointer(pointer) => pointer.as_value_ref(),
}
}
}
unsafe impl<'ctx> AnyValue<'ctx> for CallableValue<'ctx> {}
unsafe impl AsTypeRef for CallableValue<'_> {
fn as_type_ref(&self) -> LLVMTypeRef {
match self.0 {
CallableValueEnum::Function(function) => function.get_type().as_type_ref(),
CallableValueEnum::Pointer(pointer) => pointer.get_type().get_element_type().as_type_ref(),
}
}
}
impl CallableValue<'_> {
#[llvm_versions(..=14)]
pub(crate) fn returns_void(&self) -> bool {
use llvm_sys::core::LLVMGetReturnType;
let return_type =
unsafe { LLVMGetTypeKind(LLVMGetReturnType(LLVMGetElementType(LLVMTypeOf(self.as_value_ref())))) };
matches!(return_type, LLVMTypeKind::LLVMVoidTypeKind)
}
}
impl<'ctx> From<FunctionValue<'ctx>> for CallableValue<'ctx> {
fn from(value: FunctionValue<'ctx>) -> Self {
Self(CallableValueEnum::Function(value))
}
}
impl<'ctx> TryFrom<PointerValue<'ctx>> for CallableValue<'ctx> {
type Error = ();
fn try_from(value: PointerValue<'ctx>) -> Result<Self, Self::Error> {
let value_ref = value.as_value_ref();
let ty_kind = unsafe { LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(value_ref))) };
let is_a_fn_ptr = matches!(ty_kind, LLVMTypeKind::LLVMFunctionTypeKind);
if is_a_fn_ptr {
Ok(Self(CallableValueEnum::Pointer(value)))
} else {
Err(())
}
}
}
impl Display for CallableValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.print_to_string())
}
}