core: Implement handling for zero-length lists

This commit is contained in:
David Mak 2024-07-03 17:13:59 +08:00
parent 2194dbddd5
commit 13beeaa2bf
3 changed files with 62 additions and 36 deletions

View File

@ -713,12 +713,25 @@ impl<'ctx> ListValue<'ctx> {
/// If `size` is [None], the size stored in the field of this instance is used instead. /// If `size` is [None], the size stored in the field of this instance is used instead.
pub fn create_data( pub fn create_data(
&self, &self,
ctx: &CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,
elem_ty: BasicTypeEnum<'ctx>, elem_ty: BasicTypeEnum<'ctx>,
size: Option<IntValue<'ctx>>, size: Option<IntValue<'ctx>>,
) { ) {
let size = size.unwrap_or_else(|| self.load_size(ctx, None)); let size = size.unwrap_or_else(|| self.load_size(ctx, None));
self.store_data(ctx, ctx.builder.build_array_alloca(elem_ty, size, "").unwrap());
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` /// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr`

View File

@ -3,16 +3,18 @@ use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
use crate::{ use crate::{
codegen::{ codegen::{
classes::{ classes::{
ArrayLikeIndexer, ArrayLikeValue, ListValue, NDArrayValue, ProxyValue, RangeValue, ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, NDArrayValue, ProxyType,
TypedArrayLikeAccessor, UntypedArrayLikeAccessor, ProxyValue, RangeValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
}, },
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore}, concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
gen_in_range_check, get_llvm_abi_type, get_llvm_type, gen_in_range_check, get_llvm_abi_type, get_llvm_type,
irrt::*, irrt::*,
llvm_intrinsics::{call_expect, call_float_floor, call_float_pow, call_float_powi}, llvm_intrinsics::{
numpy, call_expect, call_float_floor, call_float_pow, call_float_powi, call_memcpy_generic,
},
need_sret, numpy,
stmt::{gen_if_else_expr_callback, gen_raise, gen_var}, stmt::{gen_if_else_expr_callback, gen_raise, gen_var},
CodeGenContext, CodeGenTask, CodeGenContext, CodeGenTask, CodeGenerator,
}, },
symbol_resolver::{SymbolValue, ValueEnum}, symbol_resolver::{SymbolValue, ValueEnum},
toplevel::{ toplevel::{
@ -29,15 +31,13 @@ use inkwell::{
attributes::{Attribute, AttributeLoc}, attributes::{Attribute, AttributeLoc},
types::{AnyType, BasicType, BasicTypeEnum}, types::{AnyType, BasicType, BasicTypeEnum},
values::{BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue}, values::{BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue},
AddressSpace, IntPredicate, AddressSpace, IntPredicate, OptimizationLevel,
}; };
use itertools::{chain, izip, Either, Itertools}; use itertools::{chain, izip, Either, Itertools};
use nac3parser::ast::{ use nac3parser::ast::{
self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef, self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
}; };
use super::{llvm_intrinsics::call_memcpy_generic, need_sret, CodeGenerator};
pub fn get_subst_key( pub fn get_subst_key(
unifier: &mut Unifier, unifier: &mut Unifier,
obj: Option<Type>, obj: Option<Type>,
@ -946,30 +946,26 @@ pub fn destructure_range<'ctx>(
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting /// 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. /// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
/// ///
/// Returns an instance of [`PointerValue`] pointing to the List structure. The List structure is /// Setting `ty` to [`None`] implies that the list does not have a known element type, which is only
/// defined as `type { ty*, size_t }` in LLVM, where the first element stores the pointer to the /// valid for empty lists. It is undefined behavior to generate a sized list with an unknown element
/// data, and the second element stores the size of the List. /// type.
pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>( pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G, generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,
ty: BasicTypeEnum<'ctx>, ty: Option<BasicTypeEnum<'ctx>>,
length: IntValue<'ctx>, length: IntValue<'ctx>,
name: Option<&str>, name: Option<&'ctx str>,
) -> ListValue<'ctx> { ) -> ListValue<'ctx> {
let size_t = generator.get_size_type(ctx.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 } // List structure; type { ty*, size_t }
let arr_ty = let arr_ty = ListType::new(generator, ctx.ctx, llvm_elem_ty);
ctx.ctx.struct_type(&[ty.ptr_type(AddressSpace::default()).into(), size_t.into()], false); let list = arr_ty.new_value(generator, ctx, name);
let arr_str_ptr = ctx let length = ctx.builder.build_int_z_extend(length, llvm_usize, "").unwrap();
.builder
.build_alloca(arr_ty, format!("{}.addr", name.unwrap_or("list")).as_str())
.unwrap();
let list = ListValue::from_ptr_val(arr_str_ptr, size_t, Some("list"));
let length = ctx.builder.build_int_z_extend(length, size_t, "").unwrap();
list.store_size(ctx, generator, length); list.store_size(ctx, generator, length);
list.create_data(ctx, ty, None); list.create_data(ctx, llvm_elem_ty, None);
list list
} }
@ -1042,7 +1038,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
list = allocate_list( list = allocate_list(
generator, generator,
ctx, ctx,
elem_ty, Some(elem_ty),
list_alloc_size.into_int_value(), list_alloc_size.into_int_value(),
Some("listcomp.addr"), Some("listcomp.addr"),
); );
@ -1081,7 +1077,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
Some("length"), Some("length"),
) )
.into_int_value(); .into_int_value();
list = allocate_list(generator, ctx, elem_ty, length, Some("listcomp")); list = allocate_list(generator, ctx, Some(elem_ty), length, Some("listcomp"));
list_content = list.data().base_ptr(ctx, generator); list_content = list.data().base_ptr(ctx, generator);
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
@ -1674,6 +1670,19 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
_ => unreachable!(), _ => unreachable!(),
}; };
ctx.builder.build_float_compare(op, lhs, rhs, "cmp").unwrap() ctx.builder.build_float_compare(op, lhs, rhs, "cmp").unwrap()
} else if [left_ty, right_ty].iter().any(|ty| matches!(&*ctx.unifier.get_ty_immutable(*ty), TypeEnum::TVar { .. })) {
if ctx.registry.llvm_options.opt_level != OptimizationLevel::None {
ctx.make_assert(
generator,
ctx.ctx.bool_type().const_all_ones(),
"0:AssertionError",
"nac3core::codegen::expr::gen_cmpop_expr_with_values: Unexpected comparison between two typevar values",
[None, None, None],
ctx.current_loc,
);
}
ctx.ctx.bool_type().get_poison()
} else { } else {
unimplemented!() unimplemented!()
}; };
@ -2127,18 +2136,20 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
let ty = if let TypeEnum::TObj { obj_id, params, .. } = let ty = if let TypeEnum::TObj { obj_id, params, .. } =
&*ctx.unifier.get_ty(expr.custom.unwrap()) &*ctx.unifier.get_ty(expr.custom.unwrap())
{ {
if *obj_id != PrimDef::List.id() { assert_eq!(*obj_id, PrimDef::List.id());
unreachable!()
}
*params.iter().next().unwrap().1 *params.iter().next().unwrap().1
} else { } else {
unreachable!() unreachable!()
}; };
ctx.get_llvm_type(generator, ty) if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty_immutable(ty) {
None
} else {
Some(ctx.get_llvm_type(generator, ty))
}
} else { } else {
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 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 arr_str_ptr = allocate_list(generator, ctx, ty, length, Some("list"));
@ -2599,7 +2610,8 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
.unwrap(), .unwrap(),
step, step,
); );
let res_array_ret = allocate_list(generator, ctx, ty, length, Some("ret")); let res_array_ret =
allocate_list(generator, ctx, Some(ty), length, Some("ret"));
let Some(res_ind) = handle_slice_indices( let Some(res_ind) = handle_slice_indices(
&None, &None,
&None, &None,

View File

@ -1,7 +1,7 @@
use crate::typecheck::typedef::TypeEnum; use crate::toplevel::helper::PrimDef;
use super::type_inferencer::Inferencer; use super::type_inferencer::Inferencer;
use super::typedef::Type; use super::typedef::{Type, TypeEnum};
use nac3parser::ast::{ use nac3parser::ast::{
self, Constant, Expr, ExprKind, self, Constant, Expr, ExprKind,
Operator::{LShift, RShift}, Operator::{LShift, RShift},
@ -69,6 +69,7 @@ impl<'a> Inferencer<'a> {
// there are some cases where the custom field is None // there are some cases where the custom field is None
if let Some(ty) = &expr.custom { if let Some(ty) = &expr.custom {
if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. })
&& !ty.obj_id(self.unifier).is_some_and(|id| id == PrimDef::List.id())
&& !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) && !self.unifier.is_concrete(*ty, &self.function_data.bound_variables)
{ {
return Err(HashSet::from([format!( return Err(HashSet::from([format!(