mod array_value;
mod float_value;
mod function_value;
mod global;
mod int_value;
mod pointer_value;
mod string;
mod tuple_value;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::targets::TargetData;
pub use array_value::IterAsIrValue;
pub use global::Global;
pub use string::CanInternalize;
use inkwell::types::{BasicTypeEnum, PointerType, StructType};
use inkwell::values::PointerValue;
use inkwell::AddressSpace;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
pub struct Value<'ink, T: ConcreteValueType<'ink> + ?Sized> {
pub value: T::Value,
}
pub trait AsValue<'ink, T: ConcreteValueType<'ink> + ?Sized> {
fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, T>;
}
pub trait TransparentValue<'ink> {
type Target: ConcreteValueType<'ink> + ?Sized;
fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target>;
fn as_bytes_and_ptrs(&self, context: &IrTypeContext<'ink, '_>) -> Vec<BytesOrPtr<'ink>>;
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BytesOrPtr<'ink> {
Bytes(Vec<u8>),
UntypedPtr(PointerValue<'ink>),
}
impl<'ink> From<Vec<u8>> for BytesOrPtr<'ink> {
fn from(bytes: Vec<u8>) -> Self {
BytesOrPtr::Bytes(bytes)
}
}
impl<'ink> From<PointerValue<'ink>> for BytesOrPtr<'ink> {
fn from(ptr: PointerValue<'ink>) -> Self {
BytesOrPtr::UntypedPtr(ptr)
}
}
pub trait AsBytesAndPtrs<'ink> {
fn as_bytes_and_ptrs(&self, context: &IrTypeContext<'ink, '_>) -> Vec<BytesOrPtr<'ink>>;
}
pub trait HasConstValue {
fn has_const_value() -> bool;
}
pub struct IrTypeContext<'ink, 'a> {
pub context: &'ink Context,
pub target_data: &'a TargetData,
pub struct_types: &'a RefCell<HashMap<&'static str, StructType<'ink>>>,
}
pub struct IrValueContext<'ink, 'a, 'b> {
pub context: &'ink Context,
pub type_context: &'b IrTypeContext<'ink, 'a>,
pub module: &'b Module<'ink>,
}
pub trait ConcreteValueType<'ink> {
type Value: ValueType<'ink>;
}
pub trait AddressableType<'ink, T: ?Sized> {
fn ptr_cast(
value: PointerValue<'ink>,
_context: &IrValueContext<'ink, '_, '_>,
) -> PointerValue<'ink> {
value
}
}
pub trait SizedValueType<'ink>: ConcreteValueType<'ink> + Sized {
fn get_ir_type(
context: &IrTypeContext<'ink, '_>,
) -> <<Self as ConcreteValueType<'ink>>::Value as ValueType<'ink>>::Type;
}
pub trait PointerValueType<'ink> {
fn get_ptr_type(
context: &IrTypeContext<'ink, '_>,
address_space: Option<AddressSpace>,
) -> inkwell::types::PointerType<'ink>;
}
pub trait TypeValue<'ink> {
type Value: inkwell::values::AnyValue<'ink>;
}
pub trait ValueType<'ink>: Clone + Debug + Copy + Eq + PartialEq + Hash {
type Type: inkwell::types::AnyType<'ink>;
fn get_type(&self) -> Self::Type;
}
pub trait AddressableTypeValue<'ink>: TypeValue<'ink> {
fn ptr_type(&self, address_space: AddressSpace) -> inkwell::types::PointerType<'ink>;
}
macro_rules! impl_value_type_value {
($($ty:ty => $val:ty),+) => {
$(
impl<'ink> TypeValue<'ink> for $ty {
type Value = $val;
}
impl<'ink> ValueType<'ink> for $val {
type Type = $ty;
fn get_type(&self) -> Self::Type {
Self::get_type(*self)
}
}
)*
}
}
impl_value_type_value! (
inkwell::types::IntType<'ink> => inkwell::values::IntValue<'ink>,
inkwell::types::FloatType<'ink> => inkwell::values::FloatValue<'ink>,
inkwell::types::ArrayType<'ink> => inkwell::values::ArrayValue<'ink>,
inkwell::types::VectorType<'ink> => inkwell::values::VectorValue<'ink>,
inkwell::types::StructType<'ink> => inkwell::values::StructValue<'ink>,
inkwell::types::PointerType<'ink> => inkwell::values::PointerValue<'ink>,
inkwell::types::FunctionType<'ink> => inkwell::values::FunctionValue<'ink>
);
macro_rules! impl_addressable_type_values {
($($ty:ty),+) => {
$(
impl<'ink> AddressableTypeValue<'ink> for $ty {
fn ptr_type(&self, address_space: AddressSpace) -> inkwell::types::PointerType<'ink> {
Self::ptr_type(*self, address_space)
}
}
)*
}
}
impl_addressable_type_values!(
inkwell::types::IntType<'ink>,
inkwell::types::FloatType<'ink>,
inkwell::types::ArrayType<'ink>,
inkwell::types::VectorType<'ink>,
inkwell::types::StructType<'ink>,
inkwell::types::PointerType<'ink>,
inkwell::types::FunctionType<'ink>
);
impl<'ink> AddressableTypeValue<'ink> for inkwell::types::BasicTypeEnum<'ink> {
fn ptr_type(&self, address_space: AddressSpace) -> PointerType<'ink> {
match self {
BasicTypeEnum::ArrayType(ty) => ty.ptr_type(address_space),
BasicTypeEnum::FloatType(ty) => ty.ptr_type(address_space),
BasicTypeEnum::IntType(ty) => ty.ptr_type(address_space),
BasicTypeEnum::PointerType(ty) => ty.ptr_type(address_space),
BasicTypeEnum::StructType(ty) => ty.ptr_type(address_space),
BasicTypeEnum::VectorType(ty) => ty.ptr_type(address_space),
}
}
}
impl<'ink> TypeValue<'ink> for inkwell::types::BasicTypeEnum<'ink> {
type Value = inkwell::values::BasicValueEnum<'ink>;
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Value<'ink, T> {
pub fn get_type(&self) -> <T::Value as ValueType<'ink>>::Type {
<T::Value as ValueType>::get_type(&self.value)
}
pub(super) fn from_raw(value: T::Value) -> Value<'ink, T> {
Value { value }
}
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Value<'ink, *const T>
where
*const T: SizedValueType<'ink, Value = PointerValue<'ink>>,
<*const T as ConcreteValueType<'ink>>::Value: ValueType<'ink, Type = PointerType<'ink>>,
{
pub fn with_cast(value: PointerValue<'ink>, context: &IrValueContext<'ink, '_, '_>) -> Self {
let target_type = <*const T>::get_ir_type(context.type_context);
Value {
value: if value.get_type() == target_type {
value
} else {
value.const_cast(target_type)
},
}
}
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Value<'ink, *mut T>
where
*mut T: SizedValueType<'ink, Value = PointerValue<'ink>>,
<*mut T as ConcreteValueType<'ink>>::Value: ValueType<'ink, Type = PointerType<'ink>>,
{
pub fn with_cast(value: PointerValue<'ink>, context: &IrValueContext<'ink, '_, '_>) -> Self {
let target_type = <*mut T>::get_ir_type(context.type_context);
Value {
value: if value.get_type() == target_type {
value
} else {
value.const_cast(target_type)
},
}
}
}
impl<'ink, T: SizedValueType<'ink> + ?Sized> Value<'ink, T> {
pub fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> <T::Value as ValueType<'ink>>::Type {
T::get_ir_type(context)
}
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> AsValue<'ink, T> for Value<'ink, T> {
fn as_value(&self, _context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, T> {
*self
}
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Clone for Value<'ink, T> {
fn clone(&self) -> Self {
Value { value: self.value }
}
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Copy for Value<'ink, T> {}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> PartialEq for Value<'ink, T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Eq for Value<'ink, T> {}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Hash for Value<'ink, T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state)
}
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> std::fmt::Debug for Value<'ink, T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self.value)
}
}
impl<'ink, T: ConcreteValueType<'ink> + ?Sized> From<Value<'ink, T>>
for inkwell::values::BasicValueEnum<'ink>
where
T::Value: Into<inkwell::values::BasicValueEnum<'ink>>,
{
fn from(value: Value<'ink, T>) -> Self {
value.value.into()
}
}
pub trait AsValueInto<'ink, T> {
fn as_value_into(&self, context: &IrValueContext<'ink, '_, '_>) -> T;
}
impl<'ink, I, T: ConcreteValueType<'ink>> AsValueInto<'ink, I> for Value<'ink, T>
where
T::Value: Into<I>,
{
fn as_value_into(&self, _context: &IrValueContext<'ink, '_, '_>) -> I {
self.value.into()
}
}
impl<'ink, I, T: ConcreteValueType<'ink> + AsValue<'ink, T>> AsValueInto<'ink, I> for T
where
T::Value: Into<I>,
{
fn as_value_into(&self, context: &IrValueContext<'ink, '_, '_>) -> I {
self.as_value(context).value.into()
}
}
impl<'ink, T: TransparentValue<'ink>> ConcreteValueType<'ink> for T {
type Value = <T::Target as ConcreteValueType<'ink>>::Value;
}
impl<'ink, T: TransparentValue<'ink>> SizedValueType<'ink> for T
where
T::Target: SizedValueType<'ink>,
{
fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> <Self::Value as ValueType<'ink>>::Type {
T::Target::get_ir_type(context)
}
}
impl<'ink, T: TransparentValue<'ink>> PointerValueType<'ink> for T
where
T::Target: PointerValueType<'ink>,
{
fn get_ptr_type(
context: &IrTypeContext<'ink, '_>,
address_space: Option<AddressSpace>,
) -> PointerType<'ink> {
T::Target::get_ptr_type(context, address_space)
}
}
impl<
'ink,
I: ?Sized,
U: ConcreteValueType<'ink> + ?Sized + AddressableType<'ink, I>,
T: TransparentValue<'ink, Target = U>,
> AddressableType<'ink, I> for T
{
fn ptr_cast(
value: PointerValue<'ink>,
context: &IrValueContext<'ink, '_, '_>,
) -> PointerValue<'ink> {
<T::Target as AddressableType<'ink, I>>::ptr_cast(value, context)
}
}
impl<'ink, T> HasConstValue for T
where
T: TransparentValue<'ink>,
{
fn has_const_value() -> bool {
true
}
}
impl<'ink, T> AsValue<'ink, T> for T
where
T: TransparentValue<'ink>,
{
fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, T> {
Value::from_raw(self.as_target_value(context).value)
}
}
#[cfg(test)]
mod tests {
use super::{AsBytesAndPtrs, HasConstValue};
use crate::{code_gen::CodeGenContext, mock::MockDatabase, value::IrTypeContext};
use bytemuck::from_bytes;
use std::mem::size_of;
macro_rules! test_as_bytes_and_ptrs_primitive {
($($ty:ty),+) => {
$(
paste::item! {
#[test]
fn [<test_has_const_value_ $ty>]() {
assert_eq!($ty::has_const_value(), true);
}
}
paste::item! {
#[test]
fn [<test_as_bytes_and_ptrs_ $ty>]() {
let bytes: Vec<u8> = (0..(size_of::<$ty>() as u8)).collect();
let value = from_bytes::<$ty>(&bytes);
let inkwell_context = inkwell::context::Context::create();
let db = MockDatabase::default();
let codegen_context = CodeGenContext::new(&inkwell_context, &db);
let target_data = codegen_context.target_machine.get_target_data();
let type_context = IrTypeContext {
context: &inkwell_context,
target_data: &target_data,
struct_types: &codegen_context.rust_types,
};
assert_eq!(
value.as_bytes_and_ptrs(&type_context),
vec![bytes.into()],
);
}
}
)+
};
}
test_as_bytes_and_ptrs_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
}