From 13beeaa2bff5239c6944400669e105394c759fc0 Mon Sep 17 00:00:00 2001 From: David Mak Date: Wed, 3 Jul 2024 17:13:59 +0800 Subject: [PATCH] core: Implement handling for zero-length lists --- nac3core/src/codegen/classes.rs | 17 +++++- nac3core/src/codegen/expr.rs | 76 ++++++++++++++---------- nac3core/src/typecheck/function_check.rs | 5 +- 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/nac3core/src/codegen/classes.rs b/nac3core/src/codegen/classes.rs index 24c53feb..cdb1841e 100644 --- a/nac3core/src/codegen/classes.rs +++ b/nac3core/src/codegen/classes.rs @@ -713,12 +713,25 @@ impl<'ctx> ListValue<'ctx> { /// If `size` is [None], the size stored in the field of this instance is used instead. pub fn create_data( &self, - ctx: &CodeGenContext<'ctx, '_>, + ctx: &mut CodeGenContext<'ctx, '_>, elem_ty: BasicTypeEnum<'ctx>, size: Option>, ) { 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` diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index be8e6b4f..e768d873 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -3,16 +3,18 @@ use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip}; use crate::{ codegen::{ classes::{ - ArrayLikeIndexer, ArrayLikeValue, ListValue, NDArrayValue, ProxyValue, RangeValue, - TypedArrayLikeAccessor, UntypedArrayLikeAccessor, + ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, NDArrayValue, ProxyType, + ProxyValue, RangeValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor, }, concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore}, gen_in_range_check, get_llvm_abi_type, get_llvm_type, irrt::*, - llvm_intrinsics::{call_expect, call_float_floor, call_float_pow, call_float_powi}, - numpy, + llvm_intrinsics::{ + 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}, - CodeGenContext, CodeGenTask, + CodeGenContext, CodeGenTask, CodeGenerator, }, symbol_resolver::{SymbolValue, ValueEnum}, toplevel::{ @@ -29,15 +31,13 @@ use inkwell::{ attributes::{Attribute, AttributeLoc}, types::{AnyType, BasicType, BasicTypeEnum}, values::{BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue}, - AddressSpace, IntPredicate, + AddressSpace, IntPredicate, OptimizationLevel, }; use itertools::{chain, izip, Either, Itertools}; use nac3parser::ast::{ self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef, }; -use super::{llvm_intrinsics::call_memcpy_generic, need_sret, CodeGenerator}; - pub fn get_subst_key( unifier: &mut Unifier, obj: Option, @@ -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 /// 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 -/// defined as `type { ty*, size_t }` in LLVM, where the first element stores the pointer to the -/// data, and the second element stores the size of the List. +/// Setting `ty` to [`None`] implies that the list does not have a known element type, which is only +/// valid for empty lists. 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: BasicTypeEnum<'ctx>, + ty: Option>, length: IntValue<'ctx>, - name: Option<&str>, + name: Option<&'ctx str>, ) -> 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 } - let arr_ty = - ctx.ctx.struct_type(&[ty.ptr_type(AddressSpace::default()).into(), size_t.into()], false); + let arr_ty = ListType::new(generator, ctx.ctx, llvm_elem_ty); + let list = arr_ty.new_value(generator, ctx, name); - let arr_str_ptr = ctx - .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(); + let length = ctx.builder.build_int_z_extend(length, llvm_usize, "").unwrap(); list.store_size(ctx, generator, length); - list.create_data(ctx, ty, None); + list.create_data(ctx, llvm_elem_ty, None); list } @@ -1042,7 +1038,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>( list = allocate_list( generator, ctx, - elem_ty, + Some(elem_ty), list_alloc_size.into_int_value(), Some("listcomp.addr"), ); @@ -1081,7 +1077,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>( Some("length"), ) .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); let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?; // counter = -1 @@ -1674,6 +1670,19 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>( _ => unreachable!(), }; 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 { unimplemented!() }; @@ -2127,18 +2136,20 @@ pub fn gen_expr<'ctx, G: CodeGenerator>( let ty = if let TypeEnum::TObj { obj_id, params, .. } = &*ctx.unifier.get_ty(expr.custom.unwrap()) { - if *obj_id != PrimDef::List.id() { - unreachable!() - } + assert_eq!(*obj_id, PrimDef::List.id()); *params.iter().next().unwrap().1 } else { 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 { - 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")); @@ -2599,7 +2610,8 @@ pub fn gen_expr<'ctx, G: CodeGenerator>( .unwrap(), 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( &None, &None, diff --git a/nac3core/src/typecheck/function_check.rs b/nac3core/src/typecheck/function_check.rs index bce8fbc5..d296fc6d 100644 --- a/nac3core/src/typecheck/function_check.rs +++ b/nac3core/src/typecheck/function_check.rs @@ -1,7 +1,7 @@ -use crate::typecheck::typedef::TypeEnum; +use crate::toplevel::helper::PrimDef; use super::type_inferencer::Inferencer; -use super::typedef::Type; +use super::typedef::{Type, TypeEnum}; use nac3parser::ast::{ self, Constant, Expr, ExprKind, Operator::{LShift, RShift}, @@ -69,6 +69,7 @@ impl<'a> Inferencer<'a> { // there are some cases where the custom field is None if let Some(ty) = &expr.custom { 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) { return Err(HashSet::from([format!(