forked from M-Labs/nac3
core: Implement handling for zero-length lists
This commit is contained in:
parent
2194dbddd5
commit
13beeaa2bf
|
@ -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`
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
Loading…
Reference in New Issue