Skip to content

Commit

Permalink
test: adds test to test abi compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed May 27, 2020
1 parent 8a3600a commit 9352194
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 17 deletions.
2 changes: 1 addition & 1 deletion crates/mun_codegen/src/code_gen/symbols.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::ir::ir_types as ir;
use crate::ir::types as ir;
use crate::ir::{
dispatch_table::{DispatchTable, DispatchableFunction},
function,
Expand Down
2 changes: 1 addition & 1 deletion crates/mun_codegen/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pub mod file;
pub(crate) mod file_group;
pub mod function;
mod intrinsics;
pub mod ir_types;
pub mod ty;
pub(crate) mod type_table;
pub mod types;

/// Try to down cast an `AnyTypeEnum` into a `BasicTypeEnum`.
fn try_convert_any_to_basic(ty: AnyTypeEnum) -> Option<BasicTypeEnum> {
Expand Down
2 changes: 1 addition & 1 deletion crates/mun_codegen/src/ir/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use inkwell::{
};
use std::{collections::HashMap, sync::Arc};

use crate::ir::ir_types as ir;
use crate::ir::types as ir;
use crate::value::Global;
use hir::ResolveBitness;
use inkwell::basic_block::BasicBlock;
Expand Down
4 changes: 2 additions & 2 deletions crates/mun_codegen/src/ir/type_table.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::ir_types as ir;
use super::types as ir;
use crate::ir::dispatch_table::{DispatchTable, FunctionPrototype};
use crate::type_info::{TypeGroup, TypeInfo};
use crate::value::{AsValue, CanInternalize, Global, IrValueContext, IterAsIrValue, Value};
Expand Down Expand Up @@ -192,7 +192,7 @@ impl<'a, 'ctx, 'm, D: IrDatabase> TypeTableBuilder<'a, 'ctx, 'm, D> {
.as_value(self.value_context),
size_in_bits: type_info.size.bit_size as u32,
alignment: type_info.size.alignment as u8,
type_group: type_info.group.to_abi_type(),
group: type_info.group.to_abi_type(),
}
.as_value(self.value_context);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,40 +32,45 @@ impl TransparentValue for abi::StructMemoryKind {
}
}

#[derive(AsValue)]
#[derive(AsValue, TestIsAbiCompatible)]
#[ir_name = "struct.MunTypeInfo"]
#[abi_type(abi::TypeInfo)]
pub struct TypeInfo {
pub guid: abi::Guid,
pub name: Value<*const u8>,
pub size_in_bits: u32,
pub alignment: u8,
pub type_group: abi::TypeGroup,
pub group: abi::TypeGroup,
}

#[derive(AsValue)]
#[derive(AsValue, TestIsAbiCompatible)]
#[ir_name = "struct.MunFunctionSignature"]
#[abi_type(abi::FunctionSignature)]
pub struct FunctionSignature {
pub arg_types: Value<*const *const TypeInfo>,
pub return_type: Value<*const TypeInfo>,
pub num_arg_types: u16,
}

#[derive(AsValue)]
#[derive(AsValue, TestIsAbiCompatible)]
#[ir_name = "struct.MunFunctionPrototype"]
#[abi_type(abi::FunctionPrototype)]
pub struct FunctionPrototype {
pub name: Value<*const u8>,
pub signature: FunctionSignature,
}

#[derive(AsValue)]
#[derive(AsValue, TestIsAbiCompatible)]
#[ir_name = "struct.MunFunctionDefinition"]
#[abi_type(abi::FunctionDefinition)]
pub struct FunctionDefinition {
pub prototype: FunctionPrototype,
pub fn_ptr: Value<*const fn()>,
}

#[derive(AsValue)]
#[derive(AsValue, TestIsAbiCompatible)]
#[ir_name = "struct.MunStructInfo"]
#[abi_type(abi::StructInfo)]
pub struct StructInfo {
pub field_names: Value<*const *const u8>,
pub field_types: Value<*const *const TypeInfo>,
Expand All @@ -74,8 +79,9 @@ pub struct StructInfo {
pub memory_kind: abi::StructMemoryKind,
}

#[derive(AsValue)]
#[derive(AsValue, TestIsAbiCompatible)]
#[ir_name = "struct.MunModuleInfo"]
#[abi_type(abi::ModuleInfo)]
pub struct ModuleInfo {
pub path: Value<*const u8>,
pub functions: Value<*const FunctionDefinition>,
Expand All @@ -84,19 +90,24 @@ pub struct ModuleInfo {
pub num_types: u32,
}

#[derive(AsValue)]
#[derive(AsValue, TestIsAbiCompatible)]
#[ir_name = "struct.MunDispatchTable"]
#[abi_type(abi::DispatchTable)]
pub struct DispatchTable {
pub prototypes: Value<*const FunctionPrototype>,
pub fn_ptrs: Value<*const *mut fn()>,
pub fn_ptrs: Value<*mut *const fn()>,
pub num_entries: u32,
}

#[derive(AsValue)]
#[derive(AsValue, TestIsAbiCompatible)]
#[ir_name = "struct.MunAssemblyInfo"]
#[abi_type(abi::AssemblyInfo)]
pub struct AssemblyInfo {
pub symbols: ModuleInfo,
pub dispatch_table: DispatchTable,
pub dependencies: Value<*const *const u8>,
pub num_dependencies: u32,
}

#[cfg(test)]
mod test;
192 changes: 192 additions & 0 deletions crates/mun_codegen/src/ir/types/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
use crate::value::{ConcreteValueType, Value};

/// A trait implemented by a type to check if its values match its corresponding abi type (T). This
/// trait can be derived. e.g.:
///
/// ```ignore,rust
/// #[derive(AsValue, TestIsAbiCompatible)]
/// #[ir_name = "struct.MunTypeInfo"]
/// #[abi_type(abi::TypeInfo)]
/// pub struct TypeInfo { }
/// ```
///
/// The procedural macro calls for every field:
///
/// ```ignore,rust
/// self::test::AbiTypeHelper::from_value(&abi_value.<field_name>)
/// .ir_type::<<field_type>>()
/// .assert_compatible(<struct_type_name>, <abi_type_name>, <name>);
/// ```
pub trait TestIsAbiCompatible<T> {
/// Runs a test to see if the implementor is compatible with the specified abi type.
fn test(abi_value: &T);
}

/// A trait that is implemented if a type is ABI compatible with with T.
pub trait IsAbiCompatible<T> {}

/// A trait indicating that a type is *not* compatible. This is a helper trait used to determine
/// if a type is ABI compatible at runtime. By default this trait is implemented for all types.
pub trait IsNotAbiCompatible {
fn assert_compatible(&self, ir_type: &str, abi_type: &str, field_name: &str) {
panic!(
"the field '{}' on type '{}' is not compatible with the ABI version of the struct ({})",
field_name, ir_type, abi_type
);
}
}
impl<T> IsNotAbiCompatible for T {}

/// Helper structs that enables extracting the type of a value.
pub struct AbiTypeHelper<T>(std::marker::PhantomData<T>);
pub struct AbiAndIrTypeHelper<T, S>(std::marker::PhantomData<T>, std::marker::PhantomData<S>);

impl<T> AbiTypeHelper<T> {
pub fn from_value(_: &T) -> Self {
Self(Default::default())
}

pub fn ir_type<S>(&self) -> AbiAndIrTypeHelper<S, T> {
AbiAndIrTypeHelper(Default::default(), Default::default())
}
}

impl<S, T: IsAbiCompatible<S>> AbiAndIrTypeHelper<T, S> {
pub fn assert_compatible(&self, _ir_type: &str, _abi_type: &str, _field_name: &str) {}
}

impl IsAbiCompatible<u8> for u8 {}
impl IsAbiCompatible<u16> for u16 {}
impl IsAbiCompatible<u32> for u32 {}
impl IsAbiCompatible<abi::Guid> for abi::Guid {}
impl IsAbiCompatible<abi::TypeGroup> for abi::TypeGroup {}
impl IsAbiCompatible<abi::StructMemoryKind> for abi::StructMemoryKind {}
impl IsAbiCompatible<*const ::std::os::raw::c_char> for *const u8 {}
impl IsAbiCompatible<*const ::std::os::raw::c_void> for *const fn() {}
impl<S, T: IsAbiCompatible<S>> IsAbiCompatible<*const S> for *const T {}
impl<S, T: IsAbiCompatible<S>> IsAbiCompatible<*mut S> for *mut T {}
impl<S, T: ConcreteValueType> IsAbiCompatible<S> for Value<T> where T: IsAbiCompatible<S> {}

#[test]
#[cfg(test)]
fn test_type_info_abi_compatible() {
let abi_type = abi::TypeInfo {
guid: abi::Guid {
b: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
},
name: std::ptr::null(),
size_in_bits: 0,
alignment: 0,
group: abi::TypeGroup::FundamentalTypes,
};

super::TypeInfo::test(&abi_type);
}

#[test]
#[cfg(test)]
fn test_function_signature_abi_compatible() {
let abi_type = abi::FunctionSignature {
arg_types: std::ptr::null(),
return_type: std::ptr::null(),
num_arg_types: 0,
};

super::FunctionSignature::test(&abi_type);
}

#[test]
#[cfg(test)]
fn test_function_prototype_abi_compatible() {
let abi_type = abi::FunctionPrototype {
name: std::ptr::null(),
signature: abi::FunctionSignature {
arg_types: std::ptr::null(),
return_type: std::ptr::null(),
num_arg_types: 0,
},
};

super::FunctionPrototype::test(&abi_type);
}

#[test]
#[cfg(test)]
fn test_function_definition_abi_compatible() {
let abi_type = abi::FunctionDefinition {
prototype: abi::FunctionPrototype {
name: std::ptr::null(),
signature: abi::FunctionSignature {
arg_types: std::ptr::null(),
return_type: std::ptr::null(),
num_arg_types: 0,
},
},
fn_ptr: std::ptr::null(),
};

super::FunctionDefinition::test(&abi_type);
}

#[test]
#[cfg(test)]
fn test_struct_info_abi_compatible() {
let abi_type = abi::StructInfo {
field_names: std::ptr::null(),
field_types: std::ptr::null(),
field_offsets: std::ptr::null(),
num_fields: 0,
memory_kind: abi::StructMemoryKind::Value,
};

super::StructInfo::test(&abi_type);
}

#[test]
#[cfg(test)]
fn test_module_info_abi_compatible() {
let abi_type = abi::ModuleInfo {
path: std::ptr::null(),
functions: std::ptr::null(),
num_functions: 0,
types: std::ptr::null(),
num_types: 0,
};

super::ModuleInfo::test(&abi_type);
}

#[test]
#[cfg(test)]
fn test_dispatch_table_abi_compatible() {
let abi_type = abi::DispatchTable {
prototypes: std::ptr::null(),
fn_ptrs: std::ptr::null_mut(),
num_entries: 0,
};

super::DispatchTable::test(&abi_type);
}

#[test]
#[cfg(test)]
fn test_assembly_info_abi_compatible() {
let abi_type = abi::AssemblyInfo {
symbols: abi::ModuleInfo {
path: std::ptr::null(),
functions: std::ptr::null(),
num_functions: 0,
types: std::ptr::null(),
num_types: 0,
},
dispatch_table: abi::DispatchTable {
prototypes: std::ptr::null(),
fn_ptrs: std::ptr::null_mut(),
num_entries: 0,
},
dependencies: std::ptr::null(),
num_dependencies: 0,
};

super::AssemblyInfo::test(&abi_type);
}
Loading

0 comments on commit 9352194

Please sign in to comment.