From 1a61a000b274a3c4787f0c25da0bffac12bc4020 Mon Sep 17 00:00:00 2001 From: lyken Date: Tue, 20 Aug 2024 12:08:10 +0800 Subject: [PATCH] core/object: add ListObject and TupleObject Needed for implementing other ndarray utils. --- nac3core/src/codegen/object/list.rs | 75 +++++++++++++++++++++ nac3core/src/codegen/object/mod.rs | 2 + nac3core/src/codegen/object/tuple.rs | 98 ++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 nac3core/src/codegen/object/list.rs create mode 100644 nac3core/src/codegen/object/tuple.rs diff --git a/nac3core/src/codegen/object/list.rs b/nac3core/src/codegen/object/list.rs new file mode 100644 index 00000000..36a864d5 --- /dev/null +++ b/nac3core/src/codegen/object/list.rs @@ -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>, + /// Number of items in the array + pub len: F::Output>, +} + +/// A list in NAC3. +#[derive(Debug, Clone, Copy, Default)] +pub struct List { + /// Model of the list items + pub item: Item, +} + +impl<'ctx, Item: Model<'ctx>> StructKind<'ctx> for List { + type Fields> = ListFields<'ctx, F, Item>; + + fn iter_fields>(&self, traversal: &mut F) -> Self::Fields { + 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>>>>, +} + +impl<'ctx> ListObject<'ctx> { + /// Create a [`ListObject`] from an LLVM value and its typechecker [`Type`]. + pub fn from_object( + 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( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> Instance<'ctx, Int> { + self.instance.get(generator, ctx, |f| f.len) + } +} diff --git a/nac3core/src/codegen/object/mod.rs b/nac3core/src/codegen/object/mod.rs index f9abd433..9466d70b 100644 --- a/nac3core/src/codegen/object/mod.rs +++ b/nac3core/src/codegen/object/mod.rs @@ -1,2 +1,4 @@ pub mod any; +pub mod list; pub mod ndarray; +pub mod tuple; diff --git a/nac3core/src/codegen/object/tuple.rs b/nac3core/src/codegen/object/tuple.rs new file mode 100644 index 00000000..376f89a5 --- /dev/null +++ b/nac3core/src/codegen/object/tuple.rs @@ -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, + /// 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( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + objects: I, + ) -> Self + where + I: IntoIterator>, + { + 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( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> Instance<'ctx, Int> { + 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 } + } +}