forked from M-Labs/nac3
core/object: add ListObject and TupleObject
Needed for implementing other ndarray utils.
This commit is contained in:
parent
ec61b50486
commit
1a61a000b2
|
@ -0,0 +1,75 @@
|
||||||
|
use super::any::AnyObject;
|
||||||
|
use crate::{
|
||||||
|
codegen::{model::*, CodeGenContext, CodeGenerator},
|
||||||
|
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Fields of [`List`]
|
||||||
|
pub struct ListFields<'ctx, F: FieldTraversal<'ctx>, Item: Model<'ctx>> {
|
||||||
|
/// Array pointer to content
|
||||||
|
pub items: F::Output<Ptr<Item>>,
|
||||||
|
/// Number of items in the array
|
||||||
|
pub len: F::Output<Int<SizeT>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list in NAC3.
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct List<Item> {
|
||||||
|
/// Model of the list items
|
||||||
|
pub item: Item,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, Item: Model<'ctx>> StructKind<'ctx> for List<Item> {
|
||||||
|
type Fields<F: FieldTraversal<'ctx>> = ListFields<'ctx, F, Item>;
|
||||||
|
|
||||||
|
fn iter_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> {
|
||||||
|
Self::Fields {
|
||||||
|
items: traversal.add("items", Ptr(self.item)),
|
||||||
|
len: traversal.add_auto("len"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A NAC3 Python List object.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ListObject<'ctx> {
|
||||||
|
/// Typechecker type of the list items
|
||||||
|
pub item_type: Type,
|
||||||
|
pub instance: Instance<'ctx, Ptr<Struct<List<Any<'ctx>>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> ListObject<'ctx> {
|
||||||
|
/// Create a [`ListObject`] from an LLVM value and its typechecker [`Type`].
|
||||||
|
pub fn from_object<G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
object: AnyObject<'ctx>,
|
||||||
|
) -> Self {
|
||||||
|
// Check typechecker type and extract `item_type`
|
||||||
|
let item_type = match &*ctx.unifier.get_ty(object.ty) {
|
||||||
|
TypeEnum::TObj { obj_id, params, .. }
|
||||||
|
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
||||||
|
{
|
||||||
|
iter_type_vars(params).next().unwrap().ty // Extract `item_type`
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Expecting type to be a list, but got {}", ctx.unifier.stringify(object.ty))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let plist = Ptr(Struct(List { item: Any(ctx.get_llvm_type(generator, item_type)) }));
|
||||||
|
|
||||||
|
// Create object
|
||||||
|
let value = plist.check_value(generator, ctx.ctx, object.value).unwrap();
|
||||||
|
ListObject { item_type, instance: value }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `len()` of this list.
|
||||||
|
pub fn len<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
) -> Instance<'ctx, Int<SizeT>> {
|
||||||
|
self.instance.get(generator, ctx, |f| f.len)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,4 @@
|
||||||
pub mod any;
|
pub mod any;
|
||||||
|
pub mod list;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
|
pub mod tuple;
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
use inkwell::values::StructValue;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use super::any::AnyObject;
|
||||||
|
use crate::{
|
||||||
|
codegen::{model::*, CodeGenContext, CodeGenerator},
|
||||||
|
typecheck::typedef::{Type, TypeEnum},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A NAC3 tuple object.
|
||||||
|
///
|
||||||
|
/// NOTE: This struct has no copy trait.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TupleObject<'ctx> {
|
||||||
|
/// The type of the tuple.
|
||||||
|
pub tys: Vec<Type>,
|
||||||
|
/// The underlying LLVM struct value of this tuple.
|
||||||
|
pub value: StructValue<'ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> TupleObject<'ctx> {
|
||||||
|
pub fn from_object(ctx: &mut CodeGenContext<'ctx, '_>, object: AnyObject<'ctx>) -> Self {
|
||||||
|
// TODO: Keep `is_vararg_ctx` from TTuple?
|
||||||
|
|
||||||
|
// Sanity check on object type.
|
||||||
|
let TypeEnum::TTuple { ty: tys, .. } = &*ctx.unifier.get_ty(object.ty) else {
|
||||||
|
panic!(
|
||||||
|
"Expected type to be a TypeEnum::TTuple, got {}",
|
||||||
|
ctx.unifier.stringify(object.ty)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check number of fields
|
||||||
|
let value = object.value.into_struct_value();
|
||||||
|
let value_num_fields = value.get_type().count_fields() as usize;
|
||||||
|
assert!(
|
||||||
|
value_num_fields == tys.len(),
|
||||||
|
"Tuple type has {} item(s), but the LLVM struct value has {} field(s)",
|
||||||
|
tys.len(),
|
||||||
|
value_num_fields
|
||||||
|
);
|
||||||
|
|
||||||
|
TupleObject { tys: tys.clone(), value }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function. Create a [`TupleObject`] from an iterator of objects.
|
||||||
|
pub fn from_objects<I, G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
objects: I,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = AnyObject<'ctx>>,
|
||||||
|
{
|
||||||
|
let (values, tys): (Vec<_>, Vec<_>) =
|
||||||
|
objects.into_iter().map(|object| (object.value, object.ty)).unzip();
|
||||||
|
|
||||||
|
let llvm_tys = tys.iter().map(|ty| ctx.get_llvm_type(generator, *ty)).collect_vec();
|
||||||
|
let llvm_tuple_ty = ctx.ctx.struct_type(&llvm_tys, false);
|
||||||
|
|
||||||
|
let pllvm_tuple = ctx.builder.build_alloca(llvm_tuple_ty, "tuple").unwrap();
|
||||||
|
for (i, val) in values.into_iter().enumerate() {
|
||||||
|
let pval = ctx.builder.build_struct_gep(pllvm_tuple, i as u32, "value").unwrap();
|
||||||
|
ctx.builder.build_store(pval, val).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = ctx.builder.build_load(pllvm_tuple, "").unwrap().into_struct_value();
|
||||||
|
TupleObject { tys, value }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn num_elements(&self) -> usize {
|
||||||
|
self.tys.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `len()` of this tuple.
|
||||||
|
#[must_use]
|
||||||
|
pub fn len<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
) -> Instance<'ctx, Int<SizeT>> {
|
||||||
|
Int(SizeT).const_int(generator, ctx.ctx, self.num_elements() as u64, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `i`-th (0-based) object in this tuple.
|
||||||
|
pub fn index(&self, ctx: &mut CodeGenContext<'ctx, '_>, i: usize) -> AnyObject<'ctx> {
|
||||||
|
assert!(
|
||||||
|
i < self.num_elements(),
|
||||||
|
"Tuple object with length {} have index {i}",
|
||||||
|
self.num_elements()
|
||||||
|
);
|
||||||
|
|
||||||
|
let value = ctx.builder.build_extract_value(self.value, i as u32, "tuple[{i}]").unwrap();
|
||||||
|
let ty = self.tys[i];
|
||||||
|
AnyObject { ty, value }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue