core: Implement len()
on tuples. #489
|
@ -1,17 +1,20 @@
|
||||||
use inkwell::types::BasicTypeEnum;
|
use inkwell::types::BasicTypeEnum;
|
||||||
use inkwell::values::{BasicValue, BasicValueEnum, PointerValue};
|
use inkwell::values::{BasicValue, BasicValueEnum, IntValue, PointerValue};
|
||||||
use inkwell::{FloatPredicate, IntPredicate, OptimizationLevel};
|
use inkwell::{FloatPredicate, IntPredicate, OptimizationLevel};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::codegen::classes::{
|
use crate::codegen::classes::{
|
||||||
NDArrayValue, ProxyValue, UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
ArrayLikeValue, NDArrayValue, ProxyValue, RangeValue, TypedArrayLikeAccessor,
|
||||||
|
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
||||||
};
|
};
|
||||||
|
use crate::codegen::expr::destructure_range;
|
||||||
|
use crate::codegen::irrt::calculate_len_for_slice_range;
|
||||||
use crate::codegen::numpy::ndarray_elementwise_unaryop_impl;
|
use crate::codegen::numpy::ndarray_elementwise_unaryop_impl;
|
||||||
use crate::codegen::stmt::gen_for_callback_incrementing;
|
use crate::codegen::stmt::gen_for_callback_incrementing;
|
||||||
use crate::codegen::{extern_fns, irrt, llvm_intrinsics, numpy, CodeGenContext, CodeGenerator};
|
use crate::codegen::{extern_fns, irrt, llvm_intrinsics, numpy, CodeGenContext, CodeGenerator};
|
||||||
use crate::toplevel::helper::PrimDef;
|
use crate::toplevel::helper::PrimDef;
|
||||||
use crate::toplevel::numpy::unpack_ndarray_var_tys;
|
use crate::toplevel::numpy::unpack_ndarray_var_tys;
|
||||||
use crate::typecheck::typedef::Type;
|
use crate::typecheck::typedef::{Type, TypeEnum};
|
||||||
|
|
||||||
/// Shorthand for [`unreachable!()`] when a type of argument is not supported.
|
/// Shorthand for [`unreachable!()`] when a type of argument is not supported.
|
||||||
///
|
///
|
||||||
|
@ -23,6 +26,67 @@ fn unsupported_type(ctx: &CodeGenContext<'_, '_>, fn_name: &str, tys: &[Type]) -
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Invokes the `len` builtin function.
|
||||||
|
pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
n: (Type, BasicValueEnum<'ctx>),
|
||||||
|
) -> Result<IntValue<'ctx>, String> {
|
||||||
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
let range_ty = ctx.primitives.range;
|
||||||
|
let (arg_ty, arg) = n;
|
||||||
|
|
||||||
|
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
|
||||||
|
let arg = RangeValue::from_ptr_val(arg.into_pointer_value(), Some("range"));
|
||||||
|
let (start, end, step) = destructure_range(ctx, arg);
|
||||||
|
calculate_len_for_slice_range(generator, ctx, start, end, step)
|
||||||
|
} else {
|
||||||
|
match &*ctx.unifier.get_ty_immutable(arg_ty) {
|
||||||
|
TypeEnum::TTuple { ty, .. } => llvm_i32.const_int(ty.len() as u64, false),
|
||||||
|
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
|
||||||
|
let zero = llvm_i32.const_zero();
|
||||||
|
let len = ctx
|
||||||
|
.build_gep_and_load(
|
||||||
|
arg.into_pointer_value(),
|
||||||
|
&[zero, llvm_i32.const_int(1, false)],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.into_int_value();
|
||||||
|
ctx.builder.build_int_truncate_or_bit_cast(len, llvm_i32, "len").unwrap()
|
||||||
|
}
|
||||||
|
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||||
|
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||||
|
|
||||||
|
let arg = NDArrayValue::from_ptr_val(arg.into_pointer_value(), llvm_usize, None);
|
||||||
|
|
||||||
|
let ndims = arg.dim_sizes().size(ctx, generator);
|
||||||
|
ctx.make_assert(
|
||||||
|
generator,
|
||||||
|
ctx.builder
|
||||||
|
.build_int_compare(IntPredicate::NE, ndims, llvm_usize.const_zero(), "")
|
||||||
|
.unwrap(),
|
||||||
|
"0:TypeError",
|
||||||
|
"len() of unsized object",
|
||||||
|
[None, None, None],
|
||||||
|
ctx.current_loc,
|
||||||
|
);
|
||||||
|
|
||||||
|
let len = unsafe {
|
||||||
|
arg.dim_sizes().get_typed_unchecked(
|
||||||
|
ctx,
|
||||||
|
generator,
|
||||||
|
&llvm_usize.const_zero(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.builder.build_int_truncate_or_bit_cast(len, llvm_i32, "len").unwrap()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Invokes the `int32` builtin function.
|
/// Invokes the `int32` builtin function.
|
||||||
pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
|
|
|
@ -14,9 +14,7 @@ use strum::IntoEnumIterator;
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
builtin_fns,
|
builtin_fns,
|
||||||
classes::{ArrayLikeValue, NDArrayValue, ProxyValue, RangeValue, TypedArrayLikeAccessor},
|
classes::{ProxyValue, RangeValue},
|
||||||
expr::destructure_range,
|
|
||||||
irrt::*,
|
|
||||||
numpy::*,
|
numpy::*,
|
||||||
stmt::exn_constructor,
|
stmt::exn_constructor,
|
||||||
},
|
},
|
||||||
|
@ -1466,36 +1464,21 @@ impl<'a> BuiltinBuilder<'a> {
|
||||||
fn build_len_function(&mut self) -> TopLevelDef {
|
fn build_len_function(&mut self) -> TopLevelDef {
|
||||||
let prim = PrimDef::FunLen;
|
let prim = PrimDef::FunLen;
|
||||||
|
|
||||||
let PrimitiveStore { uint64, int32, .. } = *self.primitives;
|
// Type handled in [`Inferencer::try_fold_special_call`]
|
||||||
|
let arg_tvar = self.unifier.get_dummy_var();
|
||||||
|
|
||||||
let tvar = self.unifier.get_fresh_var(Some("L".into()), None);
|
|
||||||
let list = self
|
|
||||||
.unifier
|
|
||||||
.subst(
|
|
||||||
self.primitives.list,
|
|
||||||
&into_var_map([TypeVar { id: self.list_tvar.id, ty: tvar.ty }]),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let ndims = self.unifier.get_fresh_const_generic_var(uint64, Some("N".into()), None);
|
|
||||||
let ndarray = make_ndarray_ty(self.unifier, self.primitives, Some(tvar.ty), Some(ndims.ty));
|
|
||||||
|
|
||||||
let arg_ty = self.unifier.get_fresh_var_with_range(
|
|
||||||
&[list, ndarray, self.primitives.range],
|
|
||||||
Some("I".into()),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
TopLevelDef::Function {
|
TopLevelDef::Function {
|
||||||
name: prim.name().into(),
|
name: prim.name().into(),
|
||||||
simple_name: prim.simple_name().into(),
|
simple_name: prim.simple_name().into(),
|
||||||
signature: self.unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
signature: self.unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||||
args: vec![FuncArg {
|
args: vec![FuncArg {
|
||||||
name: "ls".into(),
|
name: "obj".into(),
|
||||||
ty: arg_ty.ty,
|
ty: arg_tvar.ty,
|
||||||
default_value: None,
|
default_value: None,
|
||||||
is_vararg: false,
|
is_vararg: false,
|
||||||
}],
|
}],
|
||||||
ret: int32,
|
ret: self.primitives.int32,
|
||||||
vars: into_var_map([tvar, arg_ty]),
|
vars: into_var_map([arg_tvar]),
|
||||||
})),
|
})),
|
||||||
var_id: Vec::default(),
|
var_id: Vec::default(),
|
||||||
instance_to_symbol: HashMap::default(),
|
instance_to_symbol: HashMap::default(),
|
||||||
|
@ -1503,86 +1486,10 @@ impl<'a> BuiltinBuilder<'a> {
|
||||||
resolver: None,
|
resolver: None,
|
||||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||||
move |ctx, _, fun, args, generator| {
|
move |ctx, _, fun, args, generator| {
|
||||||
let range_ty = ctx.primitives.range;
|
|
||||||
let arg_ty = fun.0.args[0].ty;
|
let arg_ty = fun.0.args[0].ty;
|
||||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||||
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
|
|
||||||
let arg = RangeValue::from_ptr_val(arg.into_pointer_value(), Some("range"));
|
|
||||||
let (start, end, step) = destructure_range(ctx, arg);
|
|
||||||
Some(calculate_len_for_slice_range(generator, ctx, start, end, step).into())
|
|
||||||
} else {
|
|
||||||
match &*ctx.unifier.get_ty_immutable(arg_ty) {
|
|
||||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
|
|
||||||
let int32 = ctx.ctx.i32_type();
|
|
||||||
let zero = int32.const_zero();
|
|
||||||
let len = ctx
|
|
||||||
.build_gep_and_load(
|
|
||||||
arg.into_pointer_value(),
|
|
||||||
&[zero, int32.const_int(1, false)],
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.into_int_value();
|
|
||||||
if len.get_type().get_bit_width() == 32 {
|
|
||||||
Some(len.into())
|
|
||||||
} else {
|
|
||||||
Some(
|
|
||||||
ctx.builder
|
|
||||||
.build_int_truncate(len, int32, "len2i32")
|
|
||||||
.map(Into::into)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
|
||||||
|
|
||||||
let arg = NDArrayValue::from_ptr_val(
|
builtin_fns::call_len(generator, ctx, (arg_ty, arg)).map(|ret| Some(ret.into()))
|
||||||
arg.into_pointer_value(),
|
|
||||||
llvm_usize,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
let ndims = arg.dim_sizes().size(ctx, generator);
|
|
||||||
ctx.make_assert(
|
|
||||||
generator,
|
|
||||||
ctx.builder
|
|
||||||
.build_int_compare(
|
|
||||||
IntPredicate::NE,
|
|
||||||
ndims,
|
|
||||||
llvm_usize.const_zero(),
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
"0:TypeError",
|
|
||||||
&format!("{name}() of unsized object", name = prim.name()),
|
|
||||||
[None, None, None],
|
|
||||||
ctx.current_loc,
|
|
||||||
);
|
|
||||||
|
|
||||||
let len = unsafe {
|
|
||||||
arg.dim_sizes().get_typed_unchecked(
|
|
||||||
ctx,
|
|
||||||
generator,
|
|
||||||
&llvm_usize.const_zero(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if len.get_type().get_bit_width() == 32 {
|
|
||||||
Some(len.into())
|
|
||||||
} else {
|
|
||||||
Some(
|
|
||||||
ctx.builder
|
|
||||||
.build_int_truncate(len, llvm_i32, "len")
|
|
||||||
.map(Into::into)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
)))),
|
)))),
|
||||||
loc: None,
|
loc: None,
|
||||||
|
|
|
@ -1069,6 +1069,58 @@ impl<'a> Inferencer<'a> {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if id == &"len".into() && args.len() == 1 {
|
||||||
|
let obj = self.fold_expr(args.remove(0))?;
|
||||||
|
let obj_ty = obj.custom.unwrap();
|
||||||
|
|
||||||
|
match &*self.unifier.get_ty(obj_ty) {
|
||||||
|
TypeEnum::TObj { obj_id, .. }
|
||||||
|
if *obj_id == self.primitives.range.obj_id(self.unifier).unwrap() => {}
|
||||||
|
TypeEnum::TObj { obj_id, .. }
|
||||||
|
if *obj_id == self.primitives.list.obj_id(self.unifier).unwrap() => {}
|
||||||
|
TypeEnum::TObj { obj_id, .. }
|
||||||
|
if *obj_id == self.primitives.ndarray.obj_id(self.unifier).unwrap() => {}
|
||||||
|
TypeEnum::TTuple { .. } => {}
|
||||||
|
_ => {
|
||||||
|
return report_error(
|
||||||
|
format!(
|
||||||
|
"len() only accepts range, list, ndarray, or tuple. Got {}",
|
||||||
|
self.unifier.stringify(obj_ty)
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
obj.location,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret_ty = self.primitives.int32;
|
||||||
|
|
||||||
|
let func_ty = self.unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||||
|
args: vec![FuncArg {
|
||||||
|
name: "obj".into(),
|
||||||
|
ty: obj_ty,
|
||||||
|
default_value: None,
|
||||||
|
is_vararg: false,
|
||||||
|
}],
|
||||||
|
ret: ret_ty,
|
||||||
|
vars: VarMap::new(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return Ok(Some(Located {
|
||||||
|
location,
|
||||||
|
custom: Some(ret_ty),
|
||||||
|
node: ExprKind::Call {
|
||||||
|
func: Box::new(Located {
|
||||||
|
custom: Some(func_ty),
|
||||||
|
location: func.location,
|
||||||
|
node: ExprKind::Name { id: *id, ctx: *ctx },
|
||||||
|
}),
|
||||||
|
args: vec![obj],
|
||||||
|
keywords: vec![],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
if ["int32", "float", "bool", "round", "round64", "np_isnan", "np_isinf"]
|
if ["int32", "float", "bool", "round", "round64", "np_isnan", "np_isinf"]
|
||||||
.iter()
|
.iter()
|
||||||
.any(|fun_id| id == &(*fun_id).into())
|
.any(|fun_id| id == &(*fun_id).into())
|
||||||
|
|
|
@ -26,4 +26,11 @@ def run() -> int32:
|
||||||
output_int32(tl[0][1])
|
output_int32(tl[0][1])
|
||||||
output_int32(tl[1])
|
output_int32(tl[1])
|
||||||
|
|
||||||
|
output_int32(len(()))
|
||||||
|
output_int32(len((1,)))
|
||||||
|
output_int32(len((1, 2)))
|
||||||
|
output_int32(len((1, 2, 3)))
|
||||||
|
output_int32(len((1, 2, 3, 4)))
|
||||||
|
output_int32(len((1, 2, 3, 4, 5)))
|
||||||
|
|
||||||
return 0
|
return 0
|
Loading…
Reference in New Issue