1
0
forked from M-Labs/nac3

Compare commits

...

10 Commits

Author SHA1 Message Date
c845924c20
allow ListObject to have TVar item_type 2024-08-24 12:37:25 +08:00
2941e8e865
fixup! fixup! core/ndstrides: implement np_array()
fix binop +
2024-08-23 16:35:43 +08:00
4e3e490b92
fixup! core/object: add ListObject and TupleObject
fix List field items name
2024-08-23 16:27:49 +08:00
aad4fafcba
fixup! core: refactor to use ListObject / List
fix gen_expr list index slice bug
2024-08-23 16:24:18 +08:00
9e005e9b07
core/model: fix Ptr::copy_from int types 2024-08-23 16:23:16 +08:00
7e45c104be
core: remove List proxy 2024-08-23 15:34:26 +08:00
d2650e6979
core: refactor to use ListObject / List 2024-08-23 15:33:00 +08:00
2d799d13e2
core/model: add Int not 2024-08-23 15:28:34 +08:00
2fa3ada445
fixup! core/ndstrides: implement np_array()
fix ListObject::get_opaque_list_ptr comment
2024-08-23 12:10:51 +08:00
787fe23202
fixup! core/ndstrides: implement ndarray indexing
fix index comment
2024-08-23 11:50:45 +08:00
11 changed files with 361 additions and 836 deletions

View File

@ -1,10 +1,9 @@
use nac3core::{ use nac3core::{
codegen::{ codegen::{
classes::{ListValue, UntypedArrayLikeAccessor},
expr::gen_call, expr::gen_call,
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave}, llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
model::*, model::*,
object::{any::AnyObject, ndarray::NDArrayObject, range::RangeObject}, object::{any::AnyObject, list::ListObject, ndarray::NDArrayObject, range::RangeObject},
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with}, stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
@ -1015,14 +1014,16 @@ fn polymorphic_print<'ctx>(
args.extend(&[str_len.into(), str_data.into()]); args.extend(&[str_len.into(), str_data.into()]);
} }
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => { TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
let elem_ty = *params.iter().next().unwrap().1;
fmt.push('['); fmt.push('[');
flush(ctx, generator, &mut fmt, &mut args); flush(ctx, generator, &mut fmt, &mut args);
let val = ListValue::from_ptr_val(value.into_pointer_value(), llvm_usize, None); let list = AnyObject { ty, value };
let len = val.load_size(ctx, None); let list = ListObject::from_object(generator, ctx, list);
let items = list.instance.get(generator, ctx, |f| f.items);
let len = list.instance.get(generator, ctx, |f| f.len).value;
let last = let last =
ctx.builder.build_int_sub(len, llvm_usize.const_int(1, false), "").unwrap(); ctx.builder.build_int_sub(len, llvm_usize.const_int(1, false), "").unwrap();
@ -1033,12 +1034,12 @@ fn polymorphic_print<'ctx>(
llvm_usize.const_zero(), llvm_usize.const_zero(),
(len, false), (len, false),
|generator, ctx, _, i| { |generator, ctx, _, i| {
let elem = unsafe { val.data().get_unchecked(ctx, generator, &i, None) }; let item = items.get_index(generator, ctx, i).value;
polymorphic_print( polymorphic_print(
ctx, ctx,
generator, generator,
&[(elem_ty, elem.into())], &[(list.item_type, item.into())],
"", "",
None, None,
true, true,

View File

@ -68,13 +68,9 @@ namespace indexing
/** /**
* @brief Perform ndarray "basic indexing" (https://numpy.org/doc/stable/user/basics.indexing.html#basic-indexing) * @brief Perform ndarray "basic indexing" (https://numpy.org/doc/stable/user/basics.indexing.html#basic-indexing)
* *
* This is function very similar to performing `dst_ndarray = src_ndarray[indices]` in Python (where the variables * This function is very similar to performing `dst_ndarray = src_ndarray[indices]` in Python.
* can all be found in the parameter of this function).
* *
* In other words, this function takes in an ndarray (`src_ndarray`), index it with `indices`, and return the * This function also does proper assertions on `indices` to check for out of bounds access.
* indexed array (by writing the result to `dst_ndarray`).
*
* This function also does proper assertions on `indices`.
* *
* # Notes on `dst_ndarray` * # Notes on `dst_ndarray`
* The caller is responsible for allocating space for the resulting ndarray. * The caller is responsible for allocating space for the resulting ndarray.

View File

@ -1,11 +1,10 @@
use crate::codegen::{CodeGenContext, CodeGenerator}; use crate::codegen::{CodeGenContext, CodeGenerator};
use inkwell::context::Context; use inkwell::types::BasicType;
use inkwell::types::{BasicType, StructType}; use inkwell::values::BasicValue;
use inkwell::values::{BasicValue, StructValue};
use inkwell::{ use inkwell::{
types::{AnyTypeEnum, BasicTypeEnum, IntType, PointerType}, types::AnyTypeEnum,
values::{BasicValueEnum, IntValue, PointerValue}, values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace, IntPredicate, IntPredicate,
}; };
/// A LLVM type that is used to represent a non-primitive type in NAC3. /// A LLVM type that is used to represent a non-primitive type in NAC3.
@ -502,374 +501,3 @@ impl<'ctx> ArrayLikeIndexer<'ctx> for ArraySliceValue<'ctx> {
impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ArraySliceValue<'ctx> {} impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ArraySliceValue<'ctx> {}
impl<'ctx> UntypedArrayLikeMutator<'ctx> for ArraySliceValue<'ctx> {} impl<'ctx> UntypedArrayLikeMutator<'ctx> for ArraySliceValue<'ctx> {}
/// Proxy type for a `list` type in LLVM.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct ListType<'ctx> {
ty: PointerType<'ctx>,
llvm_usize: IntType<'ctx>,
}
impl<'ctx> ListType<'ctx> {
/// Checks whether `llvm_ty` represents a `list` type, returning [Err] if it does not.
pub fn is_type(llvm_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> {
let llvm_list_ty = llvm_ty.get_element_type();
let AnyTypeEnum::StructType(llvm_list_ty) = llvm_list_ty else {
return Err(format!("Expected struct type for `list` type, got {llvm_list_ty}"));
};
if llvm_list_ty.count_fields() != 2 {
return Err(format!(
"Expected 2 fields in `list`, got {}",
llvm_list_ty.count_fields()
));
}
let list_size_ty = llvm_list_ty.get_field_type_at_index(0).unwrap();
let Ok(_) = PointerType::try_from(list_size_ty) else {
return Err(format!("Expected pointer type for `list.0`, got {list_size_ty}"));
};
let list_data_ty = llvm_list_ty.get_field_type_at_index(1).unwrap();
let Ok(list_data_ty) = IntType::try_from(list_data_ty) else {
return Err(format!("Expected int type for `list.1`, got {list_data_ty}"));
};
if list_data_ty.get_bit_width() != llvm_usize.get_bit_width() {
return Err(format!(
"Expected {}-bit int type for `list.1`, got {}-bit int",
llvm_usize.get_bit_width(),
list_data_ty.get_bit_width()
));
}
Ok(())
}
/// Creates an instance of [`ListType`].
#[must_use]
pub fn new<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
element_type: BasicTypeEnum<'ctx>,
) -> Self {
let llvm_usize = generator.get_size_type(ctx);
let llvm_list = ctx
.struct_type(
&[element_type.ptr_type(AddressSpace::default()).into(), llvm_usize.into()],
false,
)
.ptr_type(AddressSpace::default());
ListType::from_type(llvm_list, llvm_usize)
}
/// Creates an [`ListType`] from a [`PointerType`].
#[must_use]
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::is_type(ptr_ty, llvm_usize).is_ok());
ListType { ty: ptr_ty, llvm_usize }
}
/// Returns the type of the `size` field of this `list` type.
#[must_use]
pub fn size_type(&self) -> IntType<'ctx> {
self.as_base_type()
.get_element_type()
.into_struct_type()
.get_field_type_at_index(1)
.map(BasicTypeEnum::into_int_type)
.unwrap()
}
/// Returns the element type of this `list` type.
#[must_use]
pub fn element_type(&self) -> AnyTypeEnum<'ctx> {
self.as_base_type()
.get_element_type()
.into_struct_type()
.get_field_type_at_index(0)
.map(BasicTypeEnum::into_pointer_type)
.map(PointerType::get_element_type)
.unwrap()
}
}
impl<'ctx> ProxyType<'ctx> for ListType<'ctx> {
type Base = PointerType<'ctx>;
type Underlying = StructType<'ctx>;
type Value = ListValue<'ctx>;
fn new_value<G: CodeGenerator + ?Sized>(
&self,
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> Self::Value {
self.create_value(
generator.gen_var_alloc(ctx, self.as_underlying_type().into(), name).unwrap(),
name,
)
}
fn create_value(
&self,
value: <Self::Value as ProxyValue<'ctx>>::Base,
name: Option<&'ctx str>,
) -> Self::Value {
debug_assert_eq!(value.get_type(), self.as_base_type());
ListValue { value, llvm_usize: self.llvm_usize, name }
}
fn as_base_type(&self) -> Self::Base {
self.ty
}
fn as_underlying_type(&self) -> Self::Underlying {
self.as_base_type().get_element_type().into_struct_type()
}
}
impl<'ctx> From<ListType<'ctx>> for PointerType<'ctx> {
fn from(value: ListType<'ctx>) -> Self {
value.as_base_type()
}
}
/// Proxy type for accessing a `list` value in LLVM.
#[derive(Copy, Clone)]
pub struct ListValue<'ctx> {
value: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
}
impl<'ctx> ListValue<'ctx> {
/// Checks whether `value` is an instance of `list`, returning [Err] if `value` is not an
/// instance.
pub fn is_instance(value: PointerValue<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> {
ListType::is_type(value.get_type(), llvm_usize)
}
/// Creates an [`ListValue`] from a [`PointerValue`].
#[must_use]
pub fn from_ptr_val(
ptr: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
<Self as ProxyValue<'ctx>>::Type::from_type(ptr.get_type(), llvm_usize)
.create_value(ptr, name)
}
/// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr`
/// on the field.
fn pptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
let llvm_i32 = ctx.ctx.i32_type();
let var_name = self.name.map(|v| format!("{v}.data.addr")).unwrap_or_default();
unsafe {
ctx.builder
.build_in_bounds_gep(
self.as_base_value(),
&[llvm_i32.const_zero(), llvm_i32.const_zero()],
var_name.as_str(),
)
.unwrap()
}
}
/// Returns the pointer to the field storing the size of this `list`.
fn ptr_to_size(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
let llvm_i32 = ctx.ctx.i32_type();
let var_name = self.name.map(|v| format!("{v}.size.addr")).unwrap_or_default();
unsafe {
ctx.builder
.build_in_bounds_gep(
self.as_base_value(),
&[llvm_i32.const_zero(), llvm_i32.const_int(1, true)],
var_name.as_str(),
)
.unwrap()
}
}
/// Stores the array of data elements `data` into this instance.
fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) {
ctx.builder.build_store(self.pptr_to_data(ctx), data).unwrap();
}
/// Convenience method for creating a new array storing data elements with the given element
/// type `elem_ty` and `size`.
///
/// If `size` is [None], the size stored in the field of this instance is used instead.
pub fn create_data(
&self,
ctx: &mut CodeGenContext<'ctx, '_>,
elem_ty: BasicTypeEnum<'ctx>,
size: Option<IntValue<'ctx>>,
) {
let size = size.unwrap_or_else(|| self.load_size(ctx, None));
let data = ctx
.builder
.build_select(
ctx.builder
.build_int_compare(IntPredicate::NE, size, self.llvm_usize.const_zero(), "")
.unwrap(),
ctx.builder.build_array_alloca(elem_ty, size, "").unwrap(),
elem_ty.ptr_type(AddressSpace::default()).const_zero(),
"",
)
.map(BasicValueEnum::into_pointer_value)
.unwrap();
self.store_data(ctx, data);
}
/// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr`
/// on the field.
#[must_use]
pub fn data(&self) -> ListDataProxy<'ctx, '_> {
ListDataProxy(self)
}
/// Stores the `size` of this `list` into this instance.
pub fn store_size<G: CodeGenerator + ?Sized>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
generator: &G,
size: IntValue<'ctx>,
) {
debug_assert_eq!(size.get_type(), generator.get_size_type(ctx.ctx));
let psize = self.ptr_to_size(ctx);
ctx.builder.build_store(psize, size).unwrap();
}
/// Returns the size of this `list` as a value.
pub fn load_size(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
let psize = self.ptr_to_size(ctx);
let var_name = name
.map(ToString::to_string)
.or_else(|| self.name.map(|v| format!("{v}.size")))
.unwrap_or_default();
ctx.builder
.build_load(psize, var_name.as_str())
.map(BasicValueEnum::into_int_value)
.unwrap()
}
}
impl<'ctx> ProxyValue<'ctx> for ListValue<'ctx> {
type Base = PointerValue<'ctx>;
type Underlying = StructValue<'ctx>;
type Type = ListType<'ctx>;
fn get_type(&self) -> Self::Type {
ListType::from_type(self.as_base_value().get_type(), self.llvm_usize)
}
fn as_base_value(&self) -> Self::Base {
self.value
}
fn as_underlying_value(
&self,
ctx: &mut CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> Self::Underlying {
ctx.builder
.build_load(self.as_base_value(), name.unwrap_or_default())
.map(BasicValueEnum::into_struct_value)
.unwrap()
}
}
impl<'ctx> From<ListValue<'ctx>> for PointerValue<'ctx> {
fn from(value: ListValue<'ctx>) -> Self {
value.as_base_value()
}
}
/// Proxy type for accessing the `data` array of an `list` instance in LLVM.
#[derive(Copy, Clone)]
pub struct ListDataProxy<'ctx, 'a>(&'a ListValue<'ctx>);
impl<'ctx> ArrayLikeValue<'ctx> for ListDataProxy<'ctx, '_> {
fn element_type<G: CodeGenerator + ?Sized>(
&self,
_: &CodeGenContext<'ctx, '_>,
_: &G,
) -> AnyTypeEnum<'ctx> {
self.0.value.get_type().get_element_type()
}
fn base_ptr<G: CodeGenerator + ?Sized>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
_: &G,
) -> PointerValue<'ctx> {
let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default();
ctx.builder
.build_load(self.0.pptr_to_data(ctx), var_name.as_str())
.map(BasicValueEnum::into_pointer_value)
.unwrap()
}
fn size<G: CodeGenerator + ?Sized>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
_: &G,
) -> IntValue<'ctx> {
self.0.load_size(ctx, None)
}
}
impl<'ctx> ArrayLikeIndexer<'ctx> for ListDataProxy<'ctx, '_> {
unsafe fn ptr_offset_unchecked<G: CodeGenerator + ?Sized>(
&self,
ctx: &mut CodeGenContext<'ctx, '_>,
generator: &mut G,
idx: &IntValue<'ctx>,
name: Option<&str>,
) -> PointerValue<'ctx> {
let var_name = name.map(|v| format!("{v}.addr")).unwrap_or_default();
unsafe {
ctx.builder
.build_in_bounds_gep(self.base_ptr(ctx, generator), &[*idx], var_name.as_str())
.unwrap()
}
}
fn ptr_offset<G: CodeGenerator + ?Sized>(
&self,
ctx: &mut CodeGenContext<'ctx, '_>,
generator: &mut G,
idx: &IntValue<'ctx>,
name: Option<&str>,
) -> PointerValue<'ctx> {
debug_assert_eq!(idx.get_type(), generator.get_size_type(ctx.ctx));
let size = self.size(ctx, generator);
let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, size, "").unwrap();
ctx.make_assert(
generator,
in_range,
"0:IndexError",
"list index out of range",
[None, None, None],
ctx.current_loc,
);
unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) }
}
}
impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ListDataProxy<'ctx, '_> {}
impl<'ctx> UntypedArrayLikeMutator<'ctx> for ListDataProxy<'ctx, '_> {}

View File

@ -1,18 +1,17 @@
use crate::{ use crate::{
codegen::{ codegen::{
classes::{
ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, ProxyType, ProxyValue,
UntypedArrayLikeAccessor,
},
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore}, concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name, gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name,
irrt::*, irrt::*,
llvm_intrinsics::{ llvm_intrinsics::{
call_expect, call_float_floor, call_float_pow, call_float_powi, call_int_smax, call_expect, call_float_floor, call_float_pow, call_float_powi, call_int_smax,
call_int_umin, call_memcpy_generic, call_int_umin,
}, },
need_sret, need_sret,
object::ndarray::{NDArrayOut, ScalarOrNDArray}, object::{
list::List,
ndarray::{NDArrayOut, ScalarOrNDArray},
},
stmt::{ stmt::{
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise, gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
gen_var, gen_var,
@ -38,16 +37,17 @@ use inkwell::{
use itertools::{chain, izip, Either, Itertools}; use itertools::{chain, izip, Either, Itertools};
use nac3parser::ast::{ use nac3parser::ast::{
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef, self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
Unaryop,
}; };
use std::cmp::min; use std::cmp::min;
use std::iter::{repeat, repeat_with}; use std::iter::{repeat, repeat_with};
use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip}; use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
use util::gen_for_model;
use super::{ use super::{
model::*, model::*,
object::{ object::{
any::AnyObject, any::AnyObject,
list::ListObject,
ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject}, ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject},
range::RangeObject, range::RangeObject,
}, },
@ -1081,33 +1081,6 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
Ok(ctx.build_call_or_invoke(fun_val, &param_vals, "call")) Ok(ctx.build_call_or_invoke(fun_val, &param_vals, "call"))
} }
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting
/// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
///
/// Setting `ty` to [`None`] implies that the list is empty **and** does not have a known element
/// type, and will therefore set the `list.data` type as `size_t*`. It is undefined behavior to
/// generate a sized list with an unknown element type.
pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
ty: Option<BasicTypeEnum<'ctx>>,
length: IntValue<'ctx>,
name: Option<&'ctx str>,
) -> ListValue<'ctx> {
let llvm_usize = generator.get_size_type(ctx.ctx);
let llvm_elem_ty = ty.unwrap_or(llvm_usize.into());
// List structure; type { ty*, size_t }
let arr_ty = ListType::new(generator, ctx.ctx, llvm_elem_ty);
let list = arr_ty.new_value(generator, ctx, name);
let length = ctx.builder.build_int_z_extend(length, llvm_usize, "").unwrap();
list.store_size(ctx, generator, length);
list.create_data(ctx, llvm_elem_ty, None);
list
}
/// Generates LLVM IR for a [list comprehension expression][expr]. /// Generates LLVM IR for a [list comprehension expression][expr].
pub fn gen_comprehension<'ctx, G: CodeGenerator>( pub fn gen_comprehension<'ctx, G: CodeGenerator>(
generator: &mut G, generator: &mut G,
@ -1148,8 +1121,10 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
let index = generator.gen_var_alloc(ctx, size_t.into(), Some("index.addr"))?; let index = generator.gen_var_alloc(ctx, size_t.into(), Some("index.addr"))?;
ctx.builder.build_store(index, zero_size_t).unwrap(); ctx.builder.build_store(index, zero_size_t).unwrap();
let elem_ty = ctx.get_llvm_type(generator, elt.custom.unwrap()); let elem_ty = elt.custom.unwrap();
let list; let elem_ty_llvm = ctx.get_llvm_type(generator, elem_ty);
let list: ListObject<'ctx>;
match &*ctx.unifier.get_ty(iter_ty) { match &*ctx.unifier.get_ty(iter_ty) {
TypeEnum::TObj { obj_id, .. } TypeEnum::TObj { obj_id, .. }
@ -1184,14 +1159,11 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
zero_size_t, zero_size_t,
"listcomp.alloc_size", "listcomp.alloc_size",
) )
.unwrap(); .unwrap()
list = allocate_list( .into_int_value();
generator,
ctx, let list_alloc_size = Int(SizeT).believe_value(list_alloc_size);
Some(elem_ty), list = ListObject::alloca(generator, ctx, elem_ty, list_alloc_size);
list_alloc_size.into_int_value(),
Some("listcomp.addr"),
);
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap(); let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap();
ctx.builder ctx.builder
@ -1230,14 +1202,12 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
TypeEnum::TObj { obj_id, .. } TypeEnum::TObj { obj_id, .. }
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
{ {
let length = ctx let source_list = AnyObject { ty: iter_ty, value: iter_val };
.build_gep_and_load( let source_list = ListObject::from_object(generator, ctx, source_list);
iter_val.into_pointer_value(),
&[zero_size_t, int32.const_int(1, false)], let len = source_list.instance.get(generator, ctx, |f| f.len);
Some("length"),
) list = ListObject::alloca(generator, ctx, elem_ty, len);
.into_int_value();
list = allocate_list(generator, ctx, Some(elem_ty), length, Some("listcomp"));
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?; let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
// counter = -1 // counter = -1
@ -1249,7 +1219,8 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
ctx.builder.build_load(counter, "i").map(BasicValueEnum::into_int_value).unwrap(); ctx.builder.build_load(counter, "i").map(BasicValueEnum::into_int_value).unwrap();
let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc").unwrap(); let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc").unwrap();
ctx.builder.build_store(counter, tmp).unwrap(); ctx.builder.build_store(counter, tmp).unwrap();
let cmp = ctx.builder.build_int_compare(IntPredicate::SLT, tmp, length, "cmp").unwrap(); let cmp =
ctx.builder.build_int_compare(IntPredicate::SLT, tmp, len.value, "cmp").unwrap();
ctx.builder.build_conditional_branch(cmp, body_bb, cont_bb).unwrap(); ctx.builder.build_conditional_branch(cmp, body_bb, cont_bb).unwrap();
ctx.builder.position_at_end(body_bb); ctx.builder.position_at_end(body_bb);
@ -1272,14 +1243,15 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
} }
// Emits the content of `cont_bb` // Emits the content of `cont_bb`
let emit_cont_bb = let emit_cont_bb = |ctx: &mut CodeGenContext<'ctx, '_>,
|ctx: &CodeGenContext<'ctx, '_>, generator: &dyn CodeGenerator, list: ListValue<'ctx>| { generator: &mut dyn CodeGenerator,
list: ListObject<'ctx>| {
ctx.builder.position_at_end(cont_bb); ctx.builder.position_at_end(cont_bb);
list.store_size(
ctx, let index = ctx.builder.build_load(index, "index").unwrap();
generator, let index = Int(SizeT).check_value(generator, ctx.ctx, index).unwrap();
ctx.builder.build_load(index, "index").map(BasicValueEnum::into_int_value).unwrap(),
); list.instance.set(ctx, |f| f.len, index);
}; };
for cond in ifs { for cond in ifs {
@ -1306,10 +1278,13 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
return Ok(None); return Ok(None);
}; };
let i = ctx.builder.build_load(index, "i").map(BasicValueEnum::into_int_value).unwrap(); let i = ctx.builder.build_load(index, "i").map(BasicValueEnum::into_int_value).unwrap();
let elem_ptr =
unsafe { list.data().ptr_offset_unchecked(ctx, generator, &i, Some("elem_ptr")) }; let elem_ptr = list.instance.get(generator, ctx, |f| f.items).offset(ctx, i);
let val = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
ctx.builder.build_store(elem_ptr, val).unwrap(); let elem = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
let elem = Any(elem_ty_llvm).check_value(generator, ctx.ctx, elem).unwrap();
elem_ptr.store(ctx, elem);
ctx.builder ctx.builder
.build_store( .build_store(
index, index,
@ -1320,7 +1295,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
emit_cont_bb(ctx, generator, list); emit_cont_bb(ctx, generator, list);
Ok(Some(list.as_base_value().into())) Ok(Some(list.instance.value.into()))
} }
/// Generates LLVM IR for a binary operator expression using the [`Type`] and /// Generates LLVM IR for a binary operator expression using the [`Type`] and
@ -1375,167 +1350,87 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
debug_assert_eq!(ty1.obj_id(&ctx.unifier), Some(PrimDef::List.id())); debug_assert_eq!(ty1.obj_id(&ctx.unifier), Some(PrimDef::List.id()));
debug_assert_eq!(ty2.obj_id(&ctx.unifier), Some(PrimDef::List.id())); debug_assert_eq!(ty2.obj_id(&ctx.unifier), Some(PrimDef::List.id()));
let elem_ty1 = let lhs = AnyObject { ty: ty1, value: left_val };
if let TypeEnum::TObj { params, .. } = &*ctx.unifier.get_ty_immutable(ty1) { let lhs = ListObject::from_object(generator, ctx, lhs);
ctx.unifier.get_representative(*params.iter().next().unwrap().1)
} else {
unreachable!()
};
let elem_ty2 =
if let TypeEnum::TObj { params, .. } = &*ctx.unifier.get_ty_immutable(ty2) {
ctx.unifier.get_representative(*params.iter().next().unwrap().1)
} else {
unreachable!()
};
debug_assert!(ctx.unifier.unioned(elem_ty1, elem_ty2));
let llvm_elem_ty = ctx.get_llvm_type(generator, elem_ty1); let rhs = AnyObject { ty: ty2, value: right_val };
let sizeof_elem = llvm_elem_ty.size_of().unwrap(); let rhs = ListObject::from_object(generator, ctx, rhs);
let lhs = ListValue::from_ptr_val(left_val.into_pointer_value(), llvm_usize, None); debug_assert!(ctx.unifier.unioned(lhs.item_type, rhs.item_type));
let rhs = ListValue::from_ptr_val(right_val.into_pointer_value(), llvm_usize, None); let common_item_type = lhs.item_type;
let size = ctx let lhs_len = lhs.instance.get(generator, ctx, |f| f.len);
.builder let rhs_len = rhs.instance.get(generator, ctx, |f| f.len);
.build_int_add(lhs.load_size(ctx, None), rhs.load_size(ctx, None), "")
.unwrap();
let new_list = allocate_list(generator, ctx, Some(llvm_elem_ty), size, None); let concat_len = lhs_len.add(ctx, rhs_len);
let concat = ListObject::alloca(generator, ctx, common_item_type, concat_len);
let lhs_size = ctx let lhs_items = lhs.instance.get(generator, ctx, |f| f.items);
.builder let rhs_items = rhs.instance.get(generator, ctx, |f| f.items);
.build_int_z_extend_or_bit_cast( let concat_items = concat.instance.get(generator, ctx, |f| f.items);
lhs.load_size(ctx, None),
sizeof_elem.get_type(),
"",
)
.unwrap();
let lhs_len = ctx.builder.build_int_mul(lhs_size, sizeof_elem, "").unwrap();
let rhs_size = ctx // Copy lhs
.builder concat_items.copy_from(generator, ctx, lhs_items, lhs_len.value);
.build_int_z_extend_or_bit_cast(
rhs.load_size(ctx, None),
sizeof_elem.get_type(),
"",
)
.unwrap();
let rhs_len = ctx.builder.build_int_mul(rhs_size, sizeof_elem, "").unwrap();
let list_ptr = new_list.data().base_ptr(ctx, generator); // Copy rhs
call_memcpy_generic( concat_items.offset(ctx, lhs_len.value).copy_from(
ctx,
list_ptr,
lhs.data().base_ptr(ctx, generator),
lhs_len,
ctx.ctx.bool_type().const_zero(),
);
let list_ptr = unsafe {
new_list.data().ptr_offset_unchecked(
ctx,
generator, generator,
&lhs.load_size(ctx, None),
None,
)
};
call_memcpy_generic(
ctx, ctx,
list_ptr, rhs_items,
rhs.data().base_ptr(ctx, generator), rhs_len.value,
rhs_len,
ctx.ctx.bool_type().const_zero(),
); );
Ok(Some(new_list.as_base_value().into())) Ok(Some(concat.instance.value.as_basic_value_enum().into()))
} }
Operator::Mult => { Operator::Mult => {
let (elem_ty, list_val, int_val) = let (list, int) = if ty1
if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) { .obj_id(&ctx.unifier)
let elem_ty = if let TypeEnum::TObj { params, .. } = .is_some_and(|id| id == PrimDef::List.id())
&*ctx.unifier.get_ty_immutable(ty1)
{ {
*params.iter().next().unwrap().1 // Handle `[1, 2, 3] * int`
} else { let list = AnyObject { ty: ty1, value: left_val };
unreachable!() let list = ListObject::from_object(generator, ctx, list);
};
(elem_ty, left_val, right_val) let int =
Int(SizeT).s_extend_or_bit_cast(generator, ctx, right_val.into_int_value());
(list, int)
} else if ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) { } else if ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) {
let elem_ty = if let TypeEnum::TObj { params, .. } = // Handle `int * [1, 2, 3]`
&*ctx.unifier.get_ty_immutable(ty2)
{ let list = AnyObject { ty: ty2, value: right_val };
*params.iter().next().unwrap().1 let list = ListObject::from_object(generator, ctx, list);
let int =
Int(SizeT).s_extend_or_bit_cast(generator, ctx, left_val.into_int_value());
(list, int)
} else { } else {
unreachable!() unreachable!()
}; };
(elem_ty, right_val, left_val) // [...] * (i where i <= 0) => []
} else { let int = call_int_smax(ctx, int.value, llvm_usize.const_zero(), None);
unreachable!() let int = Int(SizeT).check_value(generator, ctx.ctx, int).unwrap();
};
let list_val =
ListValue::from_ptr_val(list_val.into_pointer_value(), llvm_usize, None);
let int_val = ctx
.builder
.build_int_s_extend(int_val.into_int_value(), llvm_usize, "")
.unwrap();
// [...] * (i where i < 0) => []
let int_val = call_int_smax(ctx, int_val, llvm_usize.const_zero(), None);
let elem_llvm_ty = ctx.get_llvm_type(generator, elem_ty); let list_items = list.instance.get(generator, ctx, |f| f.items);
let sizeof_elem = elem_llvm_ty.size_of().unwrap(); let list_len = list.instance.get(generator, ctx, |f| f.len);
let new_list = allocate_list( let new_list_len = int.mul(ctx, list_len);
generator, let new_list = ListObject::alloca(generator, ctx, list.item_type, new_list_len);
ctx, let new_list_items = new_list.instance.get(generator, ctx, |f| f.items);
Some(elem_llvm_ty),
ctx.builder.build_int_mul(list_val.load_size(ctx, None), int_val, "").unwrap(),
None,
);
gen_for_callback_incrementing( let num_0 = Int(SizeT).const_int(generator, ctx.ctx, 0);
generator, let num_1 = Int(SizeT).const_int(generator, ctx.ctx, 1);
ctx, gen_for_model(generator, ctx, num_0, int, num_1, |generator, ctx, _hooks, i| {
None, let offset = list_len.mul(ctx, i);
llvm_usize.const_zero(), let ptr = new_list_items.offset(ctx, offset.value);
(int_val, false),
|generator, ctx, _, i| {
let offset = ctx
.builder
.build_int_mul(i, list_val.load_size(ctx, None), "")
.unwrap();
let ptr = unsafe {
new_list.data().ptr_offset_unchecked(ctx, generator, &offset, None)
};
let list_size = ctx
.builder
.build_int_z_extend_or_bit_cast(
list_val.load_size(ctx, None),
sizeof_elem.get_type(),
"",
)
.unwrap();
let memcpy_sz =
ctx.builder.build_int_mul(list_size, sizeof_elem, "").unwrap();
call_memcpy_generic(
ctx,
ptr,
list_val.data().base_ptr(ctx, generator),
memcpy_sz,
ctx.ctx.bool_type().const_zero(),
);
ptr.copy_from(generator, ctx, list_items, list_len.value);
Ok(()) Ok(())
}, })
llvm_usize.const_int(1, false), .unwrap();
)?;
Ok(Some(new_list.as_base_value().into())) Ok(Some(new_list.instance.value.into()))
} }
_ => todo!("Operator not supported"), _ => todo!("Operator not supported"),
@ -2074,8 +1969,6 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
.iter() .iter()
.any(|ty| ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())) .any(|ty| ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()))
{ {
let llvm_usize = generator.get_size_type(ctx.ctx);
let gen_list_cmpop = |generator: &mut G, let gen_list_cmpop = |generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>| ctx: &mut CodeGenContext<'ctx, '_>|
-> Result<IntValue<'ctx>, String> { -> Result<IntValue<'ctx>, String> {
@ -2098,145 +1991,109 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
return Ok(generator.bool_to_i8(ctx, gen_bool_const(ctx, false))); return Ok(generator.bool_to_i8(ctx, gen_bool_const(ctx, false)));
} }
let left_elem_ty = if let TypeEnum::TObj { params, .. } =
&*ctx.unifier.get_ty_immutable(left_ty)
{
*params.iter().next().unwrap().1
} else {
unreachable!()
};
let right_elem_ty = if let TypeEnum::TObj { params, .. } =
&*ctx.unifier.get_ty_immutable(right_ty)
{
*params.iter().next().unwrap().1
} else {
unreachable!()
};
if !ctx.unifier.unioned(left_elem_ty, right_elem_ty) {
return Ok(generator.bool_to_i8(ctx, gen_bool_const(ctx, false)));
}
if ![Cmpop::Eq, Cmpop::NotEq].contains(op) { if ![Cmpop::Eq, Cmpop::NotEq].contains(op) {
todo!("Only __eq__ and __ne__ is implemented for lists") todo!("Only __eq__ and __ne__ is implemented for lists")
} }
let left_val = let lhs = AnyObject { ty: left_ty, value: *lhs };
ListValue::from_ptr_val(lhs.into_pointer_value(), llvm_usize, None); let lhs = ListObject::from_object(generator, ctx, lhs);
let right_val =
ListValue::from_ptr_val(rhs.into_pointer_value(), llvm_usize, None);
Ok(gen_if_else_expr_callback( let rhs = AnyObject { ty: right_ty, value: *rhs };
let rhs = ListObject::from_object(generator, ctx, rhs);
let lhs_len = lhs.instance.get(generator, ctx, |f| f.len);
let rhs_len = rhs.instance.get(generator, ctx, |f| f.len);
let result = gen_if_else_expr_callback(
generator, generator,
ctx, ctx,
|_, ctx| { |_, ctx| {
Ok(ctx let same_len = lhs_len.compare(ctx, IntPredicate::EQ, rhs_len);
.builder Ok(same_len.value)
.build_int_compare(
IntPredicate::EQ,
left_val.load_size(ctx, None),
right_val.load_size(ctx, None),
"",
)
.unwrap())
}, },
|generator, ctx| { |generator, ctx| {
let acc_addr = generator // The list lengths match. Compare their contents.
.gen_var_alloc(ctx, ctx.ctx.bool_type().into(), None) let common_len = lhs_len;
.unwrap();
ctx.builder
.build_store(acc_addr, ctx.ctx.bool_type().const_all_ones())
.unwrap();
gen_for_callback_incrementing( let bool_false = Int(Bool).const_false(generator, ctx.ctx);
let bool_true = Int(Bool).const_true(generator, ctx.ctx);
let list_equal = Int(Bool).alloca(generator, ctx);
list_equal.store(ctx, bool_true);
let lhs_items = lhs.instance.get(generator, ctx, |f| f.items);
let rhs_items = rhs.instance.get(generator, ctx, |f| f.items);
let num_0 = Int(SizeT).const_int(generator, ctx.ctx, 0);
let num_1 = Int(SizeT).const_int(generator, ctx.ctx, 1);
gen_for_model(generator, ctx, num_0, common_len, num_1, |generator, ctx, hooks, i| {
let lhs_item = lhs_items.get_index(generator, ctx, i.value);
let rhs_item = rhs_items.get_index(generator, ctx, i.value);
let equal = gen_cmpop_expr_with_values(
generator, generator,
ctx, ctx,
None, (Some(lhs.item_type), lhs_item.value),
llvm_usize.const_zero(),
(left_val.load_size(ctx, None), false),
|generator, ctx, hooks, i| {
let left = unsafe {
left_val.data().get_unchecked(ctx, generator, &i, None)
};
let right = unsafe {
right_val.data().get_unchecked(ctx, generator, &i, None)
};
let res = gen_cmpop_expr_with_values(
generator,
ctx,
(Some(left_elem_ty), left),
&[Cmpop::Eq], &[Cmpop::Eq],
&[(Some(right_elem_ty), right)], &[(Some(rhs.item_type), rhs_item.value)],
)? )?
.unwrap() .unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.bool) .to_basic_value_enum(ctx, generator, ctx.primitives.bool)
.unwrap() .unwrap()
.into_int_value(); .into_int_value();
// equal is an i8, convert it into a i1 with this.
let equal = ctx
.builder
.build_int_compare(
IntPredicate::NE,
equal,
equal.get_type().const_zero(),
"",
)
.unwrap();
// if (!equal) { break; }
gen_if_callback( gen_if_callback(
generator, generator,
ctx, ctx,
|_, ctx| { |_generator, _ctx| {
Ok(ctx Ok(equal) },
.builder |_generator, _ctx| {
.build_int_compare( // Items match. Do nothing
IntPredicate::EQ,
res,
res.get_type().const_zero(),
"",
)
.unwrap())
},
|_, ctx| {
ctx.builder
.build_store(
acc_addr,
ctx.ctx.bool_type().const_zero(),
)
.unwrap();
ctx.builder
.build_unconditional_branch(hooks.exit_bb)
.unwrap();
Ok(()) Ok(())
}, },
|_, _| Ok(()), |_generator, ctx| {
) // Items don't match.
.unwrap(); list_equal.store(ctx, bool_false);
ctx.builder.build_unconditional_branch(hooks.exit_bb).unwrap();
Ok(()) Ok(())
}, },
llvm_usize.const_int(1, false),
)?; )?;
let acc = ctx Ok(())
.builder })?;
.build_load(acc_addr, "")
.map(BasicValueEnum::into_int_value) let bool_result = match op {
.unwrap(); Cmpop::Eq => {
let acc = if *op == Cmpop::NotEq { list_equal.load(generator, ctx)
gen_unaryop_expr_with_values( },
generator, Cmpop::NotEq => {
ctx, list_equal.load(generator, ctx).not(ctx)
Unaryop::Not, },
(&Some(ctx.primitives.bool), acc.into()), _ => unreachable!()
)?
.unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?
.into_int_value()
} else {
acc
}; };
Ok(Some(generator.bool_to_i8(ctx, acc))) Ok(Some(generator.bool_to_i8(ctx, bool_result.value)))
}, },
|generator, ctx| { |generator, ctx| {
// The list lengths don't match, return false / true.
Ok(Some(generator.bool_to_i8(ctx, gen_bool_const(ctx, false)))) Ok(Some(generator.bool_to_i8(ctx, gen_bool_const(ctx, false))))
}, },
)? )?.unwrap();
.map(BasicValueEnum::into_int_value)
.unwrap()) let result = result.into_int_value();
Ok(result)
}; };
gen_list_cmpop(generator, ctx)? gen_list_cmpop(generator, ctx)?
@ -2436,7 +2293,6 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
) -> Result<Option<ValueEnum<'ctx>>, String> { ) -> Result<Option<ValueEnum<'ctx>>, String> {
ctx.current_loc = expr.location; ctx.current_loc = expr.location;
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let usize = generator.get_size_type(ctx.ctx);
let zero = int32.const_int(0, false); let zero = int32.const_int(0, false);
let loc = ctx.debug_info.0.create_debug_location( let loc = ctx.debug_info.0.create_debug_location(
@ -2520,8 +2376,8 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
return Ok(None); return Ok(None);
} }
let ty = if elements.is_empty() { let item_ty = if elements.is_empty() {
let ty = if let TypeEnum::TObj { obj_id, params, .. } = let item_type = if let TypeEnum::TObj { obj_id, params, .. } =
&*ctx.unifier.get_ty(expr.custom.unwrap()) &*ctx.unifier.get_ty(expr.custom.unwrap())
{ {
assert_eq!(*obj_id, PrimDef::List.id()); assert_eq!(*obj_id, PrimDef::List.id());
@ -2531,27 +2387,37 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
unreachable!() unreachable!()
}; };
if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty_immutable(ty) { if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty_immutable(item_type) {
// `item_type` is not resolved.
// This could happen when the user wrote a `[]` and the typechecker has no hints about `item_type`.
None None
} else { } else {
Some(ctx.get_llvm_type(generator, ty)) Some(ctx.get_llvm_type(generator, item_type))
} }
} else { } else {
Some(elements[0].get_type()) Some(elements[0].get_type())
}; };
let length = generator.get_size_type(ctx.ctx).const_int(elements.len() as u64, false);
let arr_str_ptr = allocate_list(generator, ctx, ty, length, Some("list")); let list_len = Int(SizeT).const_int(generator, ctx.ctx, elements.len() as u64);
let arr_ptr = arr_str_ptr.data(); let list_item_ty = Any(item_ty.unwrap_or_else(|| {
for (i, v) in elements.iter().enumerate() { // The list is an empty list with an unresolved item_type.
let elem_ptr = arr_ptr.ptr_offset(
ctx, // TODO: LLVM 14 requires all pointer types to have a type.
generator, // We will use `size_t` as a placeholder for now.
&usize.const_int(i as u64, false), generator.get_size_type(ctx.ctx).as_basic_type_enum()
Some("elem_ptr"), }));
);
ctx.builder.build_store(elem_ptr, *v).unwrap(); // Allocate list_items and load in the items
let list_items = list_item_ty.array_alloca(generator, ctx, list_len.value);
for (i, item) in elements.iter().enumerate() {
let item = list_item_ty.believe_value(*item);
list_items.set_index_const(ctx, i as u64, item);
} }
arr_str_ptr.as_base_value().into()
let list = Struct(List { item: list_item_ty }).alloca(generator, ctx);
list.set(ctx, |f| f.items, list_items);
list.set(ctx, |f| f.len, list_len);
list.value.as_basic_value_enum().into()
} }
ExprKind::Tuple { elts, .. } => { ExprKind::Tuple { elts, .. } => {
let elements_val = elts let elements_val = elts
@ -2957,18 +2823,20 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
} }
ExprKind::Subscript { value, slice, .. } => { ExprKind::Subscript { value, slice, .. } => {
match &*ctx.unifier.get_ty(value.custom.unwrap()) { match &*ctx.unifier.get_ty(value.custom.unwrap()) {
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => { TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
let ty = params.iter().next().unwrap().1;
let v = if let Some(v) = generator.gen_expr(ctx, value)? { let v = if let Some(v) = generator.gen_expr(ctx, value)? {
v.to_basic_value_enum(ctx, generator, value.custom.unwrap())? v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_pointer_value()
} else { } else {
return Ok(None); return Ok(None);
}; };
let v = ListValue::from_ptr_val(v, usize, Some("arr"));
let ty = ctx.get_llvm_type(generator, *ty); let list = AnyObject { ty: value.custom.unwrap(), value: v };
let list = ListObject::from_object(generator, ctx, list);
let list_len = list.instance.get(generator, ctx, |f| f.len);
if let ExprKind::Slice { lower, upper, step } = &slice.node { if let ExprKind::Slice { lower, upper, step } = &slice.node {
// Handle `my_list[lower:upper:step]`
let one = int32.const_int(1, false); let one = int32.const_int(1, false);
let Some((start, end, step)) = handle_slice_indices( let Some((start, end, step)) = handle_slice_indices(
lower, lower,
@ -2976,7 +2844,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
step, step,
ctx, ctx,
generator, generator,
v.load_size(ctx, None), list_len.value,
)? )?
else { else {
return Ok(None); return Ok(None);
@ -2998,37 +2866,37 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
.unwrap(), .unwrap(),
step, step,
); );
let res_array_ret =
allocate_list(generator, ctx, Some(ty), length, Some("ret")); let sublist_length =
let Some(res_ind) = handle_slice_indices( Int(SizeT).s_extend_or_bit_cast(generator, ctx, length);
&None, let sublist =
&None, ListObject::alloca(generator, ctx, list.item_type, sublist_length);
&None, let Some(res_ind) =
ctx, handle_slice_indices(&None, &None, &None, ctx, generator, length)?
generator,
res_array_ret.load_size(ctx, None),
)?
else { else {
return Ok(None); return Ok(None);
}; };
let list_item_type_llvm = ctx.get_llvm_type(generator, list.item_type);
list_slice_assignment( list_slice_assignment(
generator, generator,
ctx, ctx,
ty, list_item_type_llvm,
res_array_ret, sublist.instance,
res_ind, res_ind,
v, list.instance,
(start, end, step), (start, end, step),
); );
res_array_ret.as_base_value().into() sublist.instance.value.as_basic_value_enum().into()
} else { } else {
let len = v.load_size(ctx, Some("len")); // Handle `my_list[i]`
let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? { let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? {
v.to_basic_value_enum(ctx, generator, slice.custom.unwrap())? v.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
.into_int_value() .into_int_value()
} else { } else {
return Ok(None); return Ok(None);
}; };
let raw_index = ctx let raw_index = ctx
.builder .builder
.build_int_s_extend(raw_index, generator.get_size_type(ctx.ctx), "sext") .build_int_s_extend(raw_index, generator.get_size_type(ctx.ctx), "sext")
@ -3043,8 +2911,10 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
"is_neg", "is_neg",
) )
.unwrap(); .unwrap();
let adjusted = let adjusted = ctx
ctx.builder.build_int_add(raw_index, len, "adjusted").unwrap(); .builder
.build_int_add(raw_index, list_len.value, "adjusted")
.unwrap();
let index = ctx let index = ctx
.builder .builder
.build_select(is_negative, adjusted, raw_index, "index") .build_select(is_negative, adjusted, raw_index, "index")
@ -3054,17 +2924,20 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
// bigger than the length (for unsigned cmp) // bigger than the length (for unsigned cmp)
let bound_check = ctx let bound_check = ctx
.builder .builder
.build_int_compare(IntPredicate::ULT, index, len, "inbound") .build_int_compare(IntPredicate::ULT, index, list_len.value, "inbound")
.unwrap(); .unwrap();
ctx.make_assert( ctx.make_assert(
generator, generator,
bound_check, bound_check,
"0:IndexError", "0:IndexError",
"index {0} out of bounds 0:{1}", "index {0} out of bounds 0:{1}",
[Some(raw_index), Some(len), None], [Some(raw_index), Some(list_len.value), None],
expr.location, expr.location,
); );
v.data().get(ctx, generator, &index, None).into()
let list_items = list.instance.get(generator, ctx, |f| f.items);
let item = list_items.get_index(generator, ctx, index);
item.value.into()
} }
} }
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => { TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {

View File

@ -1,7 +1,6 @@
use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type}; use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type};
use super::{ use super::{
classes::{ArrayLikeValue, ListValue},
model::*, model::*,
object::{ object::{
list::List, list::List,
@ -327,12 +326,11 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G, generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,
ty: BasicTypeEnum<'ctx>, ty: BasicTypeEnum<'ctx>,
dest_arr: ListValue<'ctx>, dest_arr: Instance<'ctx, Ptr<Struct<List<Any<'ctx>>>>>,
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
src_arr: ListValue<'ctx>, src_arr: Instance<'ctx, Ptr<Struct<List<Any<'ctx>>>>>,
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
) { ) {
let size_ty = generator.get_size_type(ctx.ctx);
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::default()); let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr); let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr);
@ -358,16 +356,14 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
let zero = int32.const_zero(); let zero = int32.const_zero();
let one = int32.const_int(1, false); let one = int32.const_int(1, false);
let dest_arr_ptr = dest_arr.data().base_ptr(ctx, generator);
let dest_arr_ptr = let dest_arr_ptr =
ctx.builder.build_pointer_cast(dest_arr_ptr, elem_ptr_type, "dest_arr_ptr_cast").unwrap(); dest_arr.get(generator, ctx, |f| f.items).pointer_cast(generator, ctx, Int(Byte)).value;
let dest_len = dest_arr.load_size(ctx, Some("dest.len"));
let dest_len = ctx.builder.build_int_truncate_or_bit_cast(dest_len, int32, "srclen32").unwrap();
let src_arr_ptr = src_arr.data().base_ptr(ctx, generator);
let src_arr_ptr = let src_arr_ptr =
ctx.builder.build_pointer_cast(src_arr_ptr, elem_ptr_type, "src_arr_ptr_cast").unwrap(); src_arr.get(generator, ctx, |f| f.items).pointer_cast(generator, ctx, Int(Byte)).value;
let src_len = src_arr.load_size(ctx, Some("src.len"));
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32").unwrap(); let dest_len = dest_arr.get(generator, ctx, |f| f.len).truncate(generator, ctx, Int32).value;
let src_len = src_arr.get(generator, ctx, |f| f.len).truncate(generator, ctx, Int32).value;
// index in bound and positive should be done // index in bound and positive should be done
// assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and // assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
@ -463,8 +459,10 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
let cont_bb = ctx.ctx.append_basic_block(current, "cont"); let cont_bb = ctx.ctx.append_basic_block(current, "cont");
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap(); ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap();
ctx.builder.position_at_end(update_bb); ctx.builder.position_at_end(update_bb);
let new_len = ctx.builder.build_int_z_extend_or_bit_cast(new_len, size_ty, "new_len").unwrap();
dest_arr.store_size(ctx, generator, new_len); let new_len = Int(SizeT).z_extend_or_bit_cast(generator, ctx, new_len);
dest_arr.set(ctx, |f| f.len, new_len);
ctx.builder.build_unconditional_branch(cont_bb).unwrap(); ctx.builder.build_unconditional_branch(cont_bb).unwrap();
ctx.builder.position_at_end(cont_bb); ctx.builder.position_at_end(cont_bb);
} }

View File

@ -1,5 +1,4 @@
use crate::{ use crate::{
codegen::classes::{ListType, ProxyType},
symbol_resolver::{StaticValue, SymbolResolver}, symbol_resolver::{StaticValue, SymbolResolver},
toplevel::{helper::PrimDef, TopLevelContext, TopLevelDef}, toplevel::{helper::PrimDef, TopLevelContext, TopLevelDef},
typecheck::{ typecheck::{
@ -26,7 +25,7 @@ use inkwell::{
use itertools::Itertools; use itertools::Itertools;
use model::*; use model::*;
use nac3parser::ast::{Location, Stmt, StrRef}; use nac3parser::ast::{Location, Stmt, StrRef};
use object::{ndarray::NDArray, range::Range}; use object::{list::List, ndarray::NDArray, range::Range};
use parking_lot::{Condvar, Mutex}; use parking_lot::{Condvar, Mutex};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::sync::{ use std::sync::{
@ -488,8 +487,8 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
type_cache, type_cache,
*params.iter().next().unwrap().1, *params.iter().next().unwrap().1,
); );
let item = Any(element_type);
ListType::new(generator, ctx, element_type).as_base_type().into() Ptr(Struct(List { item })).get_type(generator, ctx).as_basic_type_enum()
} }
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => { TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {

View File

@ -340,4 +340,10 @@ impl<'ctx, N: IntKind<'ctx>> Instance<'ctx, Int<N>> {
let value = ctx.builder.build_int_compare(op, self.value, other.value, "").unwrap(); let value = ctx.builder.build_int_compare(op, self.value, other.value, "").unwrap();
Int(Bool).believe_value(value) Int(Bool).believe_value(value)
} }
#[must_use]
pub fn not(&self, ctx: &CodeGenContext<'ctx, '_>) -> Self {
let value = ctx.builder.build_not(self.value, "").unwrap();
self.model.believe_value(value)
}
} }

View File

@ -175,8 +175,16 @@ impl<'ctx, Item: Model<'ctx>> Instance<'ctx, Ptr<Item>> {
source: Self, source: Self,
num_items: IntValue<'ctx>, num_items: IntValue<'ctx>,
) { ) {
let llvm_usize = generator.get_size_type(ctx.ctx);
// Force extend `num_items` and `itemsize` so their types would match.
let itemsize = self.model.sizeof(generator, ctx.ctx); let itemsize = self.model.sizeof(generator, ctx.ctx);
let totalsize = ctx.builder.build_int_mul(itemsize, num_items, "totalsize").unwrap(); // TODO: Int types may not match. let itemsize =
ctx.builder.build_int_z_extend_or_bit_cast(itemsize, llvm_usize, "").unwrap();
let num_items =
ctx.builder.build_int_z_extend_or_bit_cast(num_items, llvm_usize, "").unwrap();
let totalsize = ctx.builder.build_int_mul(itemsize, num_items, "totalsize").unwrap();
let is_volatile = ctx.ctx.bool_type().const_zero(); // is_volatile = false let is_volatile = ctx.ctx.bool_type().const_zero(); // is_volatile = false
call_memcpy_generic(ctx, self.value, source.value, totalsize, is_volatile); call_memcpy_generic(ctx, self.value, source.value, totalsize, is_volatile);

View File

@ -1,3 +1,5 @@
use inkwell::types::BasicType;
use crate::{ use crate::{
codegen::{model::*, CodeGenContext, CodeGenerator}, codegen::{model::*, CodeGenContext, CodeGenerator},
typecheck::typedef::{iter_type_vars, Type, TypeEnum}, typecheck::typedef::{iter_type_vars, Type, TypeEnum},
@ -25,7 +27,7 @@ impl<'ctx, Item: Model<'ctx>> StructKind<'ctx> for List<Item> {
fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> { fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> {
Self::Fields { Self::Fields {
items: traversal.add("data", Ptr(self.item)), items: traversal.add("items", Ptr(self.item)),
len: traversal.add_auto("len"), len: traversal.add_auto("len"),
} }
} }
@ -34,7 +36,8 @@ impl<'ctx, Item: Model<'ctx>> StructKind<'ctx> for List<Item> {
/// A NAC3 Python List object. /// A NAC3 Python List object.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct ListObject<'ctx> { pub struct ListObject<'ctx> {
/// Typechecker type of the list items /// Typechecker type of the list items. Could be [`TypeEnum::TVar`] if unresolved (like
/// in the case of empty lists and the typechecker does not have enough hints).
pub item_type: Type, pub item_type: Type,
pub instance: Instance<'ctx, Ptr<Struct<List<Any<'ctx>>>>>, pub instance: Instance<'ctx, Ptr<Struct<List<Any<'ctx>>>>>,
} }
@ -63,7 +66,15 @@ impl<'ctx> ListObject<'ctx> {
} }
}; };
let plist = Ptr(Struct(List { item: Any(ctx.get_llvm_type(generator, item_type)) })); // If `item_type` is unresolved, the list's ptr will default to `size_t*`
// as a placeholder because there are no opaque pointers in LLVM 14.
let item_type_llvm = if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty(item_type) {
generator.get_size_type(ctx.ctx).as_basic_type_enum()
} else {
ctx.get_llvm_type(generator, item_type)
};
let plist = Ptr(Struct(List { item: Any(item_type_llvm) }));
// Create object // Create object
let value = plist.check_value(generator, ctx.ctx, object.value).unwrap(); let value = plist.check_value(generator, ctx.ctx, object.value).unwrap();
@ -91,7 +102,7 @@ impl<'ctx> ListObject<'ctx> {
/// Get the value of this [`ListObject`] as a list with opaque items. /// Get the value of this [`ListObject`] as a list with opaque items.
/// ///
/// This function allocates on the stack to create the list, but the /// This function allocates on the stack to create the list, but the
/// reference to the `items` are preserved. /// reference to the `items` is preserved.
pub fn get_opaque_list_ptr<G: CodeGenerator + ?Sized>( pub fn get_opaque_list_ptr<G: CodeGenerator + ?Sized>(
&self, &self,
generator: &mut G, generator: &mut G,
@ -109,4 +120,31 @@ impl<'ctx> ListObject<'ctx> {
opaque_list opaque_list
} }
/// Allocate a list on the stack given its `item_type` and `len`.
///
/// The returned list's content will be:
/// - `items`: allocated with an array of length `len` with uninitialized values.
/// - `len`: set to `len`.
pub fn alloca<G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
item_type: Type,
len: Instance<'ctx, Int<SizeT>>,
) -> Self {
// If `item_type` is unresolved, the list's ptr will default to `size_t*`
// as a placeholder because there are no opaque pointers in LLVM 14.
let item_type_llvm = if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty(item_type) {
generator.get_size_type(ctx.ctx).as_basic_type_enum()
} else {
ctx.get_llvm_type(generator, item_type)
};
let items = Any(item_type_llvm).array_alloca(generator, ctx, len.value);
let instance = Struct(List { item: Any(item_type_llvm) }).alloca(generator, ctx);
instance.set(ctx, |f| f.items, items);
instance.set(ctx, |f| f.len, len);
ListObject { item_type, instance }
}
} }

View File

@ -3,6 +3,7 @@ use super::{
irrt::{handle_slice_indices, list_slice_assignment}, irrt::{handle_slice_indices, list_slice_assignment},
object::{ object::{
any::AnyObject, any::AnyObject,
list::ListObject,
ndarray::{ ndarray::{
indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray, indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray,
}, },
@ -11,11 +12,7 @@ use super::{
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
use crate::{ use crate::{
codegen::{ codegen::{classes::ArraySliceValue, expr::gen_binop_expr, gen_in_range_check, model::*},
classes::{ArrayLikeIndexer, ArraySliceValue, ListValue},
expr::gen_binop_expr,
gen_in_range_check,
},
toplevel::{DefinitionId, TopLevelDef}, toplevel::{DefinitionId, TopLevelDef},
typecheck::{ typecheck::{
magic_methods::Binop, magic_methods::Binop,
@ -296,63 +293,56 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
let key_ty = key.custom.unwrap(); let key_ty = key.custom.unwrap();
match &*ctx.unifier.get_ty(target_ty) { match &*ctx.unifier.get_ty(target_ty) {
TypeEnum::TObj { obj_id, params: list_params, .. } TypeEnum::TObj { obj_id, .. }
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() => if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
{ {
// Handle list item assignment // Handle list item assignment
let llvm_usize = generator.get_size_type(ctx.ctx);
let target_item_ty = iter_type_vars(list_params).next().unwrap().ty;
let target = generator let target = generator
.gen_expr(ctx, target)? .gen_expr(ctx, target)?
.unwrap() .unwrap()
.to_basic_value_enum(ctx, generator, target_ty)? .to_basic_value_enum(ctx, generator, target_ty)?;
.into_pointer_value();
let target = ListValue::from_ptr_val(target, llvm_usize, None); let target = AnyObject { ty: target_ty, value: target };
let target = ListObject::from_object(generator, ctx, target);
let target_len = target.instance.get(generator, ctx, |f| f.len);
let target_item_type_llvm = ctx.get_llvm_type(generator, target.item_type);
if let ExprKind::Slice { .. } = &key.node { if let ExprKind::Slice { .. } = &key.node {
// Handle assigning to a slice // Handle assigning to a slice
let ExprKind::Slice { lower, upper, step } = &key.node else { unreachable!() }; let ExprKind::Slice { lower, upper, step } = &key.node else { unreachable!() };
let Some((start, end, step)) = handle_slice_indices( let Some((start, end, step)) =
lower, handle_slice_indices(lower, upper, step, ctx, generator, target_len.value)?
upper,
step,
ctx,
generator,
target.load_size(ctx, None),
)?
else { else {
return Ok(()); return Ok(());
}; };
let value = let value = value.to_basic_value_enum(ctx, generator, value_ty)?;
value.to_basic_value_enum(ctx, generator, value_ty)?.into_pointer_value();
let value = ListValue::from_ptr_val(value, llvm_usize, None);
let target_item_ty = ctx.get_llvm_type(generator, target_item_ty); let value = AnyObject { ty: value_ty, value };
let Some(src_ind) = handle_slice_indices( let value = ListObject::from_object(generator, ctx, value);
&None,
&None, let value_len = value.instance.get(generator, ctx, |f| f.len);
&None,
ctx, let Some(src_ind) =
generator, handle_slice_indices(&None, &None, &None, ctx, generator, value_len.value)?
value.load_size(ctx, None),
)?
else { else {
return Ok(()); return Ok(());
}; };
list_slice_assignment( list_slice_assignment(
generator, generator,
ctx, ctx,
target_item_ty, target_item_type_llvm,
target, target.instance,
(start, end, step), (start, end, step),
value, value.instance,
src_ind, src_ind,
); );
} else { } else {
// Handle assigning to an index // Handle assigning to an index
let len = target.load_size(ctx, Some("len")); let value = value.to_basic_value_enum(ctx, generator, value_ty)?;
let value =
Any(target_item_type_llvm).check_value(generator, ctx.ctx, value).unwrap();
let index = generator let index = generator
.gen_expr(ctx, key)? .gen_expr(ctx, key)?
@ -374,7 +364,8 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
"is_neg", "is_neg",
) )
.unwrap(); .unwrap();
let adjusted = ctx.builder.build_int_add(index, len, "adjusted").unwrap(); let adjusted =
ctx.builder.build_int_add(index, target_len.value, "adjusted").unwrap();
let index = ctx let index = ctx
.builder .builder
.build_select(is_negative, adjusted, index, "index") .build_select(is_negative, adjusted, index, "index")
@ -385,22 +376,23 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
// bigger than the length (for unsigned cmp) // bigger than the length (for unsigned cmp)
let bound_check = ctx let bound_check = ctx
.builder .builder
.build_int_compare(IntPredicate::ULT, index, len, "inbound") .build_int_compare(IntPredicate::ULT, index, target_len.value, "inbound")
.unwrap(); .unwrap();
ctx.make_assert( ctx.make_assert(
generator, generator,
bound_check, bound_check,
"0:IndexError", "0:IndexError",
"index {0} out of bounds 0:{1}", "index {0} out of bounds 0:{1}",
[Some(index), Some(len), None], [Some(index), Some(target_len.value), None],
key.location, key.location,
); );
// Write value to index on list // Write value to index on list
let item_ptr = target
target.data().ptr_offset(ctx, generator, &index, Some("list_item_ptr")); .instance
let value = value.to_basic_value_enum(ctx, generator, value_ty)?; .get(generator, ctx, |f| f.items)
ctx.builder.build_store(item_ptr, value).unwrap(); .offset(ctx, index)
.store(ctx, value);
} }
} }
TypeEnum::TObj { obj_id, .. } TypeEnum::TObj { obj_id, .. }

View File

@ -1,9 +1,7 @@
use crate::{ use crate::{
codegen::{ codegen::{
classes::{ListType, ProxyType}, concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions,
concrete_type::ConcreteTypeStore, CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask,
CodeGenerator, DefaultCodeGenerator, WithCall, WorkerRegistry,
}, },
symbol_resolver::{SymbolResolver, ValueEnum}, symbol_resolver::{SymbolResolver, ValueEnum},
toplevel::{ toplevel::{
@ -436,15 +434,3 @@ fn test_simple_call() {
registry.add_task(task); registry.add_task(task);
registry.wait_tasks_complete(handles); registry.wait_tasks_complete(handles);
} }
#[test]
fn test_classes_list_type_new() {
let ctx = inkwell::context::Context::create();
let generator = DefaultCodeGenerator::new(String::new(), 64);
let llvm_i32 = ctx.i32_type();
let llvm_usize = generator.get_size_type(&ctx);
let llvm_list = ListType::new(&generator, &ctx, llvm_i32.into());
assert!(ListType::is_type(llvm_list.as_base_type(), llvm_usize).is_ok());
}