Compare commits
8 Commits
d6565feed3
...
ba32fab374
Author | SHA1 | Date | |
---|---|---|---|
ba32fab374 | |||
c4052b6342 | |||
66c205275f | |||
c85e412206 | |||
075536d7bd | |||
13beeaa2bf | |||
2194dbddd5 | |||
94a1d547d6 |
78
Cargo.lock
generated
78
Cargo.lock
generated
@ -105,9 +105,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@ -117,9 +117,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.99"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
|
||||
checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -129,9 +129,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.7"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
||||
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -139,9 +139,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.7"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
||||
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -151,14 +151,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.5"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
||||
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -270,9 +270,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.12.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
@ -421,7 +421,7 @@ checksum = "4fa4d8d74483041a882adaa9a29f633253a66dde85055f0495c121620ac484b2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -501,9 +501,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
@ -513,9 +513,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets",
|
||||
@ -568,9 +568,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
@ -749,7 +749,7 @@ dependencies = [
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -796,9 +796,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.85"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -850,7 +850,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -863,7 +863,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1044,14 +1044,14 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.117"
|
||||
version = "1.0.120"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -1120,9 +1120,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.2"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
@ -1134,7 +1134,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1150,9 +1150,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.66"
|
||||
version = "2.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1218,7 +1218,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1471,20 +1471,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.34"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.34"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
@ -6,8 +6,8 @@ use nac3core::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
symbol_resolver::ValueEnum,
|
||||
toplevel::{helper::PrimDef, DefinitionId, GenCall},
|
||||
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, VarMap},
|
||||
toplevel::{helper::PrimDef, numpy::unpack_ndarray_var_tys, DefinitionId, GenCall},
|
||||
typecheck::typedef::{iter_type_vars, FunSignature, FuncArg, Type, TypeEnum, VarMap},
|
||||
};
|
||||
|
||||
use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef};
|
||||
@ -23,7 +23,6 @@ use pyo3::{
|
||||
|
||||
use crate::{symbol_resolver::InnerResolver, timeline::TimeFns};
|
||||
|
||||
use nac3core::toplevel::numpy::unpack_ndarray_var_tys;
|
||||
use std::{
|
||||
collections::hash_map::DefaultHasher,
|
||||
collections::HashMap,
|
||||
@ -394,9 +393,11 @@ fn gen_rpc_tag(
|
||||
gen_rpc_tag(ctx, *ty, buffer)?;
|
||||
}
|
||||
}
|
||||
TList { ty } => {
|
||||
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let ty = iter_type_vars(params).next().unwrap().ty;
|
||||
|
||||
buffer.push(b'l');
|
||||
gen_rpc_tag(ctx, *ty, buffer)?;
|
||||
gen_rpc_tag(ctx, ty, buffer)?;
|
||||
}
|
||||
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
let (ndarray_dtype, ndarray_ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, ty);
|
||||
@ -675,8 +676,10 @@ pub fn attributes_writeback(
|
||||
host_attributes.append(pydict)?;
|
||||
}
|
||||
}
|
||||
TypeEnum::TList { ty: elem_ty } => {
|
||||
if gen_rpc_tag(ctx, *elem_ty, &mut scratch_buffer).is_ok() {
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let elem_ty = iter_type_vars(params).next().unwrap().ty;
|
||||
|
||||
if gen_rpc_tag(ctx, elem_ty, &mut scratch_buffer).is_ok() {
|
||||
let pydict = PyDict::new(py);
|
||||
pydict.set_item("obj", val)?;
|
||||
host_attributes.append(pydict)?;
|
||||
|
@ -329,8 +329,19 @@ impl InnerResolver {
|
||||
Ok(Ok((primitives.exception, true)))
|
||||
} else if ty_id == self.primitive_ids.list {
|
||||
// do not handle type var param and concrete check here
|
||||
let list_tvar = if let TypeEnum::TObj { obj_id, params, .. } =
|
||||
&*unifier.get_ty_immutable(primitives.list)
|
||||
{
|
||||
assert_eq!(*obj_id, PrimDef::List.id());
|
||||
iter_type_vars(params).nth(0).unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let var = unifier.get_dummy_var().ty;
|
||||
let list = unifier.add_ty(TypeEnum::TList { ty: var });
|
||||
let list = unifier
|
||||
.subst(primitives.list, &into_var_map([TypeVar { id: list_tvar.id, ty: var }]))
|
||||
.unwrap();
|
||||
Ok(Ok((list, false)))
|
||||
} else if ty_id == self.primitive_ids.ndarray {
|
||||
// do not handle type var param and concrete check here
|
||||
@ -460,7 +471,7 @@ impl InnerResolver {
|
||||
};
|
||||
|
||||
match &*unifier.get_ty(origin_ty) {
|
||||
TypeEnum::TList { .. } => {
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
|
||||
if args.len() == 1 {
|
||||
let ty = match self.get_pyty_obj_type(
|
||||
py,
|
||||
@ -477,7 +488,21 @@ impl InnerResolver {
|
||||
"type list should take concrete parameters in typevar range".into(),
|
||||
));
|
||||
}
|
||||
Ok(Ok((unifier.add_ty(TypeEnum::TList { ty: ty.0 }), true)))
|
||||
let list_tvar = if let TypeEnum::TObj { obj_id, params, .. } =
|
||||
&*unifier.get_ty_immutable(primitives.list)
|
||||
{
|
||||
assert_eq!(*obj_id, PrimDef::List.id());
|
||||
iter_type_vars(params).nth(0).unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let list = unifier
|
||||
.subst(
|
||||
primitives.list,
|
||||
&into_var_map([TypeVar { id: list_tvar.id, ty: ty.0 }]),
|
||||
)
|
||||
.unwrap();
|
||||
Ok(Ok((list, true)))
|
||||
} else {
|
||||
return Ok(Err(format!(
|
||||
"type list needs exactly 1 type parameters, found {}",
|
||||
@ -693,11 +718,12 @@ impl InnerResolver {
|
||||
};
|
||||
match (&*unifier.get_ty(extracted_ty), inst_check) {
|
||||
// do the instantiation for these four types
|
||||
(TypeEnum::TList { ty }, false) => {
|
||||
(TypeEnum::TObj { obj_id, params, .. }, false) if *obj_id == PrimDef::List.id() => {
|
||||
let ty = iter_type_vars(params).nth(0).unwrap().ty;
|
||||
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
|
||||
if len == 0 {
|
||||
assert!(matches!(
|
||||
&*unifier.get_ty(*ty),
|
||||
&*unifier.get_ty(ty),
|
||||
TypeEnum::TVar { fields: None, range, .. }
|
||||
if range.is_empty()
|
||||
));
|
||||
@ -706,8 +732,25 @@ impl InnerResolver {
|
||||
let actual_ty =
|
||||
self.get_list_elem_type(py, obj, len, unifier, defs, primitives)?;
|
||||
match actual_ty {
|
||||
Ok(t) => match unifier.unify(*ty, t) {
|
||||
Ok(()) => Ok(Ok(unifier.add_ty(TypeEnum::TList { ty: *ty }))),
|
||||
Ok(t) => match unifier.unify(ty, t) {
|
||||
Ok(()) => {
|
||||
let list_tvar = if let TypeEnum::TObj { obj_id, params, .. } =
|
||||
&*unifier.get_ty_immutable(primitives.list)
|
||||
{
|
||||
assert_eq!(*obj_id, PrimDef::List.id());
|
||||
iter_type_vars(params).nth(0).unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let list = unifier
|
||||
.subst(
|
||||
primitives.list,
|
||||
&into_var_map([TypeVar { id: list_tvar.id, ty }]),
|
||||
)
|
||||
.unwrap();
|
||||
Ok(Ok(list))
|
||||
}
|
||||
|
||||
Err(e) => Ok(Err(format!(
|
||||
"type error ({}) for the list",
|
||||
e.to_display(unifier)
|
||||
@ -942,12 +985,11 @@ impl InnerResolver {
|
||||
}
|
||||
|
||||
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
|
||||
let elem_ty = if let TypeEnum::TList { ty } =
|
||||
ctx.unifier.get_ty_immutable(expected_ty).as_ref()
|
||||
{
|
||||
*ty
|
||||
} else {
|
||||
unreachable!("must be list")
|
||||
let elem_ty = match ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
iter_type_vars(params).nth(0).unwrap().ty
|
||||
}
|
||||
_ => unreachable!("must be list"),
|
||||
};
|
||||
let ty = ctx.get_llvm_type(generator, elem_ty);
|
||||
let size_t = generator.get_size_type(ctx.ctx);
|
||||
|
@ -10,7 +10,7 @@ constant-optimization = ["fold"]
|
||||
fold = []
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4"
|
||||
lazy_static = "1.5"
|
||||
parking_lot = "0.12"
|
||||
string-interner = "0.17"
|
||||
fxhash = "0.2"
|
||||
|
@ -725,7 +725,7 @@ pub fn call_numpy_min<'ctx, G: CodeGenerator + ?Sized>(
|
||||
ctx,
|
||||
llvm_usize.const_int(1, false),
|
||||
(n_sz, false),
|
||||
|generator, ctx, idx| {
|
||||
|generator, ctx, _, idx| {
|
||||
let elem = unsafe { n.data().get_unchecked(ctx, generator, &idx, None) };
|
||||
|
||||
let accumulator = ctx.builder.build_load(accumulator_addr, "").unwrap();
|
||||
@ -941,7 +941,7 @@ pub fn call_numpy_max<'ctx, G: CodeGenerator + ?Sized>(
|
||||
ctx,
|
||||
llvm_usize.const_int(1, false),
|
||||
(n_sz, false),
|
||||
|generator, ctx, idx| {
|
||||
|generator, ctx, _, idx| {
|
||||
let elem = unsafe { n.data().get_unchecked(ctx, generator, &idx, None) };
|
||||
|
||||
let accumulator = ctx.builder.build_load(accumulator_addr, "").unwrap();
|
||||
|
@ -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<IntValue<'ctx>>,
|
||||
) {
|
||||
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`
|
||||
@ -1706,7 +1719,7 @@ impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> ArrayLikeIndexer<'ctx, Index>
|
||||
ctx,
|
||||
llvm_usize.const_zero(),
|
||||
(len, false),
|
||||
|generator, ctx, i| {
|
||||
|generator, ctx, _, i| {
|
||||
let (dim_idx, dim_sz) = unsafe {
|
||||
(
|
||||
indices.get_unchecked(ctx, generator, &i, None).into_int_value(),
|
||||
|
@ -47,9 +47,6 @@ pub enum ConcreteTypeEnum {
|
||||
TTuple {
|
||||
ty: Vec<ConcreteType>,
|
||||
},
|
||||
TList {
|
||||
ty: ConcreteType,
|
||||
},
|
||||
TObj {
|
||||
obj_id: DefinitionId,
|
||||
fields: HashMap<StrRef, (ConcreteType, bool)>,
|
||||
@ -167,9 +164,6 @@ impl ConcreteTypeStore {
|
||||
.map(|t| self.from_unifier_type(unifier, primitives, *t, cache))
|
||||
.collect(),
|
||||
},
|
||||
TypeEnum::TList { ty } => ConcreteTypeEnum::TList {
|
||||
ty: self.from_unifier_type(unifier, primitives, *ty, cache),
|
||||
},
|
||||
TypeEnum::TObj { obj_id, fields, params } => ConcreteTypeEnum::TObj {
|
||||
obj_id: *obj_id,
|
||||
fields: fields
|
||||
@ -260,9 +254,6 @@ impl ConcreteTypeStore {
|
||||
.map(|cty| self.to_unifier_type(unifier, primitives, *cty, cache))
|
||||
.collect(),
|
||||
},
|
||||
ConcreteTypeEnum::TList { ty } => {
|
||||
TypeEnum::TList { ty: self.to_unifier_type(unifier, primitives, *ty, cache) }
|
||||
}
|
||||
ConcreteTypeEnum::TVirtual { ty } => {
|
||||
TypeEnum::TVirtual { ty: self.to_unifier_type(unifier, primitives, *ty, cache) }
|
||||
}
|
||||
|
@ -3,16 +3,22 @@ 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,
|
||||
stmt::{gen_if_else_expr_callback, gen_raise, gen_var},
|
||||
CodeGenContext, CodeGenTask,
|
||||
llvm_intrinsics::{
|
||||
call_expect, call_float_floor, call_float_pow, call_float_powi, call_int_smax,
|
||||
call_memcpy_generic,
|
||||
},
|
||||
need_sret, numpy,
|
||||
stmt::{
|
||||
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
|
||||
gen_var,
|
||||
},
|
||||
CodeGenContext, CodeGenTask, CodeGenerator,
|
||||
},
|
||||
symbol_resolver::{SymbolValue, ValueEnum},
|
||||
toplevel::{
|
||||
@ -29,15 +35,14 @@ 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,
|
||||
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
|
||||
Unaryop,
|
||||
};
|
||||
|
||||
use super::{llvm_intrinsics::call_memcpy_generic, need_sret, CodeGenerator};
|
||||
|
||||
pub fn get_subst_key(
|
||||
unifier: &mut Unifier,
|
||||
obj: Option<Type>,
|
||||
@ -946,30 +951,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<BasicTypeEnum<'ctx>>,
|
||||
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 +1043,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 +1082,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
|
||||
@ -1197,6 +1198,168 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
Some("f_pow_i"),
|
||||
);
|
||||
Ok(Some(res.into()))
|
||||
} else if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())
|
||||
|| ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())
|
||||
{
|
||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||
|
||||
if is_aug_assign {
|
||||
todo!("Augmented assignment operators not implemented for lists")
|
||||
}
|
||||
|
||||
match op {
|
||||
Operator::Add => {
|
||||
debug_assert_eq!(ty1.obj_id(&ctx.unifier), Some(PrimDef::List.id()));
|
||||
debug_assert_eq!(ty2.obj_id(&ctx.unifier), Some(PrimDef::List.id()));
|
||||
|
||||
let elem_ty1 =
|
||||
if let TypeEnum::TObj { params, .. } = &*ctx.unifier.get_ty_immutable(ty1) {
|
||||
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 lhs = ListValue::from_ptr_val(left_val.into_pointer_value(), llvm_usize, None);
|
||||
let rhs = ListValue::from_ptr_val(right_val.into_pointer_value(), llvm_usize, None);
|
||||
|
||||
let size = ctx
|
||||
.builder
|
||||
.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 lhs_len = ctx
|
||||
.builder
|
||||
.build_int_mul(lhs.load_size(ctx, None), llvm_elem_ty.size_of().unwrap(), "")
|
||||
.unwrap();
|
||||
let rhs_len = ctx
|
||||
.builder
|
||||
.build_int_mul(rhs.load_size(ctx, None), llvm_elem_ty.size_of().unwrap(), "")
|
||||
.unwrap();
|
||||
|
||||
let list_ptr = new_list.data().base_ptr(ctx, generator);
|
||||
call_memcpy_generic(
|
||||
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,
|
||||
&lhs.load_size(ctx, None),
|
||||
None,
|
||||
)
|
||||
};
|
||||
call_memcpy_generic(
|
||||
ctx,
|
||||
list_ptr,
|
||||
rhs.data().base_ptr(ctx, generator),
|
||||
rhs_len,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
);
|
||||
|
||||
Ok(Some(new_list.as_base_value().into()))
|
||||
}
|
||||
|
||||
Operator::Mult => {
|
||||
let (elem_ty, list_val, int_val) =
|
||||
if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) {
|
||||
let elem_ty = if let TypeEnum::TObj { params, .. } =
|
||||
&*ctx.unifier.get_ty_immutable(ty1)
|
||||
{
|
||||
*params.iter().next().unwrap().1
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
(elem_ty, left_val, right_val)
|
||||
} else if ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) {
|
||||
let elem_ty = if let TypeEnum::TObj { params, .. } =
|
||||
&*ctx.unifier.get_ty_immutable(ty2)
|
||||
{
|
||||
*params.iter().next().unwrap().1
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
(elem_ty, right_val, left_val)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
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 new_list = allocate_list(
|
||||
generator,
|
||||
ctx,
|
||||
Some(elem_llvm_ty),
|
||||
ctx.builder.build_int_mul(list_val.load_size(ctx, None), int_val, "").unwrap(),
|
||||
None,
|
||||
);
|
||||
|
||||
gen_for_callback_incrementing(
|
||||
generator,
|
||||
ctx,
|
||||
llvm_usize.const_zero(),
|
||||
(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 memcpy_sz = ctx
|
||||
.builder
|
||||
.build_int_mul(
|
||||
list_val.load_size(ctx, None),
|
||||
elem_llvm_ty.size_of().unwrap(),
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
call_memcpy_generic(
|
||||
ctx,
|
||||
ptr,
|
||||
list_val.data().base_ptr(ctx, generator),
|
||||
memcpy_sz,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
},
|
||||
llvm_usize.const_int(1, false),
|
||||
)?;
|
||||
|
||||
Ok(Some(new_list.as_base_value().into()))
|
||||
}
|
||||
|
||||
_ => todo!("Operator not supported"),
|
||||
}
|
||||
} else if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
||||
|| ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id())
|
||||
{
|
||||
@ -1674,6 +1837,188 @@ 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| 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,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>|
|
||||
-> Result<IntValue<'ctx>, String> {
|
||||
let is_list1 =
|
||||
left_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id());
|
||||
let is_list2 =
|
||||
right_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id());
|
||||
|
||||
let gen_bool_const = |ctx: &CodeGenContext<'ctx, '_>, val: bool| {
|
||||
let llvm_i1 = ctx.ctx.bool_type();
|
||||
|
||||
match (op, val) {
|
||||
(Cmpop::Eq, true) | (Cmpop::NotEq, false) => llvm_i1.const_all_ones(),
|
||||
(Cmpop::Eq, false) | (Cmpop::NotEq, true) => llvm_i1.const_zero(),
|
||||
(_, _) => unreachable!(),
|
||||
}
|
||||
};
|
||||
|
||||
if !(is_list1 && is_list2) {
|
||||
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) {
|
||||
todo!("Only __eq__ and __ne__ is implemented for lists")
|
||||
}
|
||||
|
||||
let left_val =
|
||||
ListValue::from_ptr_val(lhs.into_pointer_value(), llvm_usize, None);
|
||||
let right_val =
|
||||
ListValue::from_ptr_val(rhs.into_pointer_value(), llvm_usize, None);
|
||||
|
||||
Ok(gen_if_else_expr_callback(
|
||||
generator,
|
||||
ctx,
|
||||
|_, ctx| {
|
||||
Ok(ctx
|
||||
.builder
|
||||
.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
left_val.load_size(ctx, None),
|
||||
right_val.load_size(ctx, None),
|
||||
"",
|
||||
)
|
||||
.unwrap())
|
||||
},
|
||||
|generator, ctx| {
|
||||
let acc_addr = generator
|
||||
.gen_var_alloc(ctx, ctx.ctx.bool_type().into(), None)
|
||||
.unwrap();
|
||||
ctx.builder
|
||||
.build_store(acc_addr, ctx.ctx.bool_type().const_all_ones())
|
||||
.unwrap();
|
||||
|
||||
gen_for_callback_incrementing(
|
||||
generator,
|
||||
ctx,
|
||||
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],
|
||||
&[(Some(right_elem_ty), right)],
|
||||
)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
gen_if_callback(
|
||||
generator,
|
||||
ctx,
|
||||
|_, ctx| {
|
||||
Ok(ctx
|
||||
.builder
|
||||
.build_int_compare(
|
||||
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(()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
},
|
||||
llvm_usize.const_int(1, false),
|
||||
)?;
|
||||
|
||||
let acc = ctx
|
||||
.builder
|
||||
.build_load(acc_addr, "")
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap();
|
||||
let acc = if *op == Cmpop::NotEq {
|
||||
gen_unaryop_expr_with_values(
|
||||
generator,
|
||||
ctx,
|
||||
Unaryop::Not,
|
||||
(&Some(ctx.primitives.bool), acc.into()),
|
||||
)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?
|
||||
.into_int_value()
|
||||
} else {
|
||||
acc
|
||||
};
|
||||
|
||||
Ok(Some(generator.bool_to_i8(ctx, acc)))
|
||||
},
|
||||
|generator, ctx| {
|
||||
Ok(Some(generator.bool_to_i8(ctx, gen_bool_const(ctx, false))))
|
||||
},
|
||||
)?
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap())
|
||||
};
|
||||
|
||||
gen_list_cmpop(generator, ctx)?
|
||||
} 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!()
|
||||
};
|
||||
@ -2124,13 +2469,23 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
}
|
||||
|
||||
let ty = if elements.is_empty() {
|
||||
let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(expr.custom.unwrap()) else {
|
||||
let ty = if let TypeEnum::TObj { obj_id, params, .. } =
|
||||
&*ctx.unifier.get_ty(expr.custom.unwrap())
|
||||
{
|
||||
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"));
|
||||
@ -2550,7 +2905,9 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
}
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
match &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||
TypeEnum::TList { ty } => {
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let ty = params.iter().next().unwrap().1;
|
||||
|
||||
let v = if let Some(v) = generator.gen_expr(ctx, value)? {
|
||||
v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
||||
.into_pointer_value()
|
||||
@ -2589,7 +2946,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,
|
||||
|
@ -800,7 +800,7 @@ pub fn call_ndarray_calc_broadcast<'ctx, G: CodeGenerator + ?Sized>(
|
||||
ctx,
|
||||
llvm_usize.const_zero(),
|
||||
(min_ndims, false),
|
||||
|generator, ctx, idx| {
|
||||
|generator, ctx, _, idx| {
|
||||
let idx = ctx.builder.build_int_sub(min_ndims, idx, "").unwrap();
|
||||
let (lhs_dim_sz, rhs_dim_sz) = unsafe {
|
||||
(
|
||||
|
@ -456,6 +456,20 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
.into()
|
||||
}
|
||||
|
||||
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let element_type = get_llvm_type(
|
||||
ctx,
|
||||
module,
|
||||
generator,
|
||||
unifier,
|
||||
top_level,
|
||||
type_cache,
|
||||
*params.iter().next().unwrap().1,
|
||||
);
|
||||
|
||||
ListType::new(generator, ctx, element_type).as_base_type().into()
|
||||
}
|
||||
|
||||
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
let (dtype, _) = unpack_ndarray_var_tys(unifier, ty);
|
||||
let element_type = get_llvm_type(
|
||||
@ -516,12 +530,6 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
.collect_vec();
|
||||
ctx.struct_type(&fields, false).into()
|
||||
}
|
||||
TList { ty } => {
|
||||
let element_type =
|
||||
get_llvm_type(ctx, module, generator, unifier, top_level, type_cache, *ty);
|
||||
|
||||
ListType::new(generator, ctx, element_type).as_base_type().into()
|
||||
}
|
||||
TVirtual { .. } => unimplemented!(),
|
||||
_ => unreachable!("{}", ty_enum.get_type_name()),
|
||||
};
|
||||
|
@ -86,7 +86,7 @@ where
|
||||
ctx,
|
||||
llvm_usize.const_zero(),
|
||||
(shape_len, false),
|
||||
|generator, ctx, i| {
|
||||
|generator, ctx, _, i| {
|
||||
let shape_dim = shape_data_fn(generator, ctx, shape, i)?;
|
||||
debug_assert!(shape_dim.get_type().get_bit_width() <= llvm_usize.get_bit_width());
|
||||
|
||||
@ -131,7 +131,7 @@ where
|
||||
ctx,
|
||||
llvm_usize.const_zero(),
|
||||
(shape_len, false),
|
||||
|generator, ctx, i| {
|
||||
|generator, ctx, _, i| {
|
||||
let shape_dim = shape_data_fn(generator, ctx, shape, i)?;
|
||||
debug_assert!(shape_dim.get_type().get_bit_width() <= llvm_usize.get_bit_width());
|
||||
let shape_dim = ctx.builder.build_int_z_extend(shape_dim, llvm_usize, "").unwrap();
|
||||
@ -382,7 +382,7 @@ where
|
||||
ctx,
|
||||
llvm_usize.const_zero(),
|
||||
(ndarray_num_elems, false),
|
||||
|generator, ctx, i| {
|
||||
|generator, ctx, _, i| {
|
||||
let elem = unsafe { ndarray.data().ptr_offset_unchecked(ctx, generator, &i, None) };
|
||||
|
||||
let value = value_fn(generator, ctx, i)?;
|
||||
@ -1243,7 +1243,7 @@ pub fn ndarray_sliced_copy<'ctx, G: CodeGenerator + ?Sized>(
|
||||
ctx,
|
||||
llvm_usize.const_int(slices.len() as u64, false),
|
||||
(this.load_ndims(ctx), false),
|
||||
|generator, ctx, idx| {
|
||||
|generator, ctx, _, idx| {
|
||||
unsafe {
|
||||
let dim_sz = this.dim_sizes().get_typed_unchecked(ctx, generator, &idx, None);
|
||||
ndarray.dim_sizes().set_typed_unchecked(ctx, generator, &idx, dim_sz);
|
||||
@ -1647,7 +1647,7 @@ pub fn ndarray_matmul_2d<'ctx, G: CodeGenerator>(
|
||||
ctx,
|
||||
llvm_i32.const_zero(),
|
||||
(common_dim, false),
|
||||
|generator, ctx, i| {
|
||||
|generator, ctx, _, i| {
|
||||
let i = ctx.builder.build_int_truncate(i, llvm_i32, "").unwrap();
|
||||
|
||||
let ab_idx = generator.gen_array_var_alloc(
|
||||
@ -1804,10 +1804,15 @@ pub fn gen_ndarray_array<'ctx>(
|
||||
unpack_ndarray_var_tys(&mut context.unifier, obj_ty).0
|
||||
}
|
||||
|
||||
TypeEnum::TList { ty } => {
|
||||
let mut ty = *ty;
|
||||
while let TypeEnum::TList { ty: elem_ty } = &*context.unifier.get_ty_immutable(ty) {
|
||||
ty = *elem_ty;
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let mut ty = *params.iter().next().unwrap().1;
|
||||
while let TypeEnum::TObj { obj_id, params, .. } = &*context.unifier.get_ty_immutable(ty)
|
||||
{
|
||||
if *obj_id != PrimDef::List.id() {
|
||||
break;
|
||||
}
|
||||
|
||||
ty = *params.iter().next().unwrap().1;
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ pub fn gen_store_target<'ctx, G: CodeGenerator>(
|
||||
}
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
match ctx.unifier.get_ty_immutable(value.custom.unwrap()).as_ref() {
|
||||
TypeEnum::TList { .. } => {
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)?
|
||||
.unwrap()
|
||||
@ -243,7 +243,9 @@ pub fn gen_assign<'ctx, G: CodeGenerator>(
|
||||
.into_pointer_value();
|
||||
let value = ListValue::from_ptr_val(value, llvm_usize, None);
|
||||
let ty = match &*ctx.unifier.get_ty_immutable(target.custom.unwrap()) {
|
||||
TypeEnum::TList { ty } => *ty,
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
*params.iter().next().unwrap().1
|
||||
}
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
unpack_ndarray_var_tys(&mut ctx.unifier, target.custom.unwrap()).0
|
||||
}
|
||||
@ -462,6 +464,16 @@ pub fn gen_for<G: CodeGenerator>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
|
||||
pub struct BreakContinueHooks<'ctx> {
|
||||
/// The [exit block][`BasicBlock`] to branch to when `break`-ing out of a loop.
|
||||
pub exit_bb: BasicBlock<'ctx>,
|
||||
|
||||
/// The [latch basic block][`BasicBlock`] to branch to for `continue`-ing to the next iteration
|
||||
/// of the loop.
|
||||
pub latch_bb: BasicBlock<'ctx>,
|
||||
}
|
||||
|
||||
/// Generates a C-style `for` construct using lambdas, similar to the following C code:
|
||||
///
|
||||
/// ```c
|
||||
@ -489,7 +501,8 @@ where
|
||||
I: Clone,
|
||||
InitFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<I, String>,
|
||||
CondFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>, I) -> Result<IntValue<'ctx>, String>,
|
||||
BodyFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>, I) -> Result<(), String>,
|
||||
BodyFn:
|
||||
FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>, BreakContinueHooks, I) -> Result<(), String>,
|
||||
UpdateFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>, I) -> Result<(), String>,
|
||||
{
|
||||
let current_bb = ctx.builder.get_insert_block().unwrap();
|
||||
@ -520,7 +533,8 @@ where
|
||||
}
|
||||
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
body(generator, ctx, loop_var.clone())?;
|
||||
let hooks = BreakContinueHooks { exit_bb: cont_bb, latch_bb: update_bb };
|
||||
body(generator, ctx, hooks, loop_var.clone())?;
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(update_bb).unwrap();
|
||||
}
|
||||
@ -562,7 +576,12 @@ pub fn gen_for_callback_incrementing<'ctx, 'a, G, BodyFn>(
|
||||
) -> Result<(), String>
|
||||
where
|
||||
G: CodeGenerator + ?Sized,
|
||||
BodyFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>, IntValue<'ctx>) -> Result<(), String>,
|
||||
BodyFn: FnOnce(
|
||||
&mut G,
|
||||
&mut CodeGenContext<'ctx, 'a>,
|
||||
BreakContinueHooks,
|
||||
IntValue<'ctx>,
|
||||
) -> Result<(), String>,
|
||||
{
|
||||
let init_val_t = init_val.get_type();
|
||||
|
||||
@ -584,10 +603,10 @@ where
|
||||
|
||||
Ok(ctx.builder.build_int_compare(cmp_op, i, max_val, "").unwrap())
|
||||
},
|
||||
|generator, ctx, i_addr| {
|
||||
|generator, ctx, hooks, i_addr| {
|
||||
let i = ctx.builder.build_load(i_addr, "").map(BasicValueEnum::into_int_value).unwrap();
|
||||
|
||||
body(generator, ctx, i)
|
||||
body(generator, ctx, hooks, i)
|
||||
},
|
||||
|_, ctx, i_addr| {
|
||||
let i = ctx.builder.build_load(i_addr, "").map(BasicValueEnum::into_int_value).unwrap();
|
||||
@ -698,7 +717,7 @@ where
|
||||
|
||||
Ok(cond)
|
||||
},
|
||||
|generator, ctx, (i_addr, _)| {
|
||||
|generator, ctx, _, (i_addr, _)| {
|
||||
let i = ctx.builder.build_load(i_addr, "").map(BasicValueEnum::into_int_value).unwrap();
|
||||
|
||||
body_fn(generator, ctx, i)
|
||||
|
@ -382,13 +382,12 @@ pub trait SymbolResolver {
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static IDENTIFIER_ID: [StrRef; 12] = [
|
||||
static IDENTIFIER_ID: [StrRef; 11] = [
|
||||
"int32".into(),
|
||||
"int64".into(),
|
||||
"float".into(),
|
||||
"bool".into(),
|
||||
"virtual".into(),
|
||||
"list".into(),
|
||||
"tuple".into(),
|
||||
"str".into(),
|
||||
"Exception".into(),
|
||||
@ -413,13 +412,12 @@ pub fn parse_type_annotation<T>(
|
||||
let float_id = ids[2];
|
||||
let bool_id = ids[3];
|
||||
let virtual_id = ids[4];
|
||||
let list_id = ids[5];
|
||||
let tuple_id = ids[6];
|
||||
let str_id = ids[7];
|
||||
let exn_id = ids[8];
|
||||
let uint32_id = ids[9];
|
||||
let uint64_id = ids[10];
|
||||
let literal_id = ids[11];
|
||||
let tuple_id = ids[5];
|
||||
let str_id = ids[6];
|
||||
let exn_id = ids[7];
|
||||
let uint32_id = ids[8];
|
||||
let uint64_id = ids[9];
|
||||
let literal_id = ids[10];
|
||||
|
||||
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
|
||||
if *id == int32_id {
|
||||
@ -476,9 +474,6 @@ pub fn parse_type_annotation<T>(
|
||||
if *id == virtual_id {
|
||||
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
|
||||
Ok(unifier.add_ty(TypeEnum::TVirtual { ty }))
|
||||
} else if *id == list_id {
|
||||
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
|
||||
Ok(unifier.add_ty(TypeEnum::TList { ty }))
|
||||
} else if *id == tuple_id {
|
||||
if let Tuple { elts, .. } = &slice.node {
|
||||
let ty = elts
|
||||
|
@ -305,6 +305,8 @@ struct BuiltinBuilder<'a> {
|
||||
unwrap_ty: (Type, bool),
|
||||
option_tvar: TypeVar,
|
||||
|
||||
list_tvar: TypeVar,
|
||||
|
||||
ndarray_dtype_tvar: TypeVar,
|
||||
ndarray_ndims_tvar: TypeVar,
|
||||
ndarray_copy_ty: (Type, bool),
|
||||
@ -395,7 +397,17 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
unifier.get_fresh_var_with_range(&[num_ty.ty, ndarray_num_ty], Some("T".into()), None);
|
||||
let num_or_ndarray_var_map = into_var_map([num_ty, num_or_ndarray_ty]);
|
||||
|
||||
let list_int32 = unifier.add_ty(TypeEnum::TList { ty: int32 });
|
||||
let list_tvar = if let TypeEnum::TObj { obj_id, params, .. } =
|
||||
&*unifier.get_ty_immutable(primitives.list)
|
||||
{
|
||||
assert_eq!(*obj_id, PrimDef::List.id());
|
||||
iter_type_vars(params).nth(0).unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let list_int32 = unifier
|
||||
.subst(primitives.list, &into_var_map([TypeVar { id: list_tvar.id, ty: int32 }]))
|
||||
.unwrap();
|
||||
|
||||
let ndarray_factory_fn_shape_arg_tvar = unifier.get_fresh_var(Some("Shape".into()), None);
|
||||
|
||||
@ -407,6 +419,8 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
unwrap_ty,
|
||||
option_tvar,
|
||||
|
||||
list_tvar,
|
||||
|
||||
ndarray_dtype_tvar,
|
||||
ndarray_ndims_tvar,
|
||||
ndarray_copy_ty,
|
||||
@ -457,6 +471,8 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
| PrimDef::OptionUnwrap
|
||||
| PrimDef::FunSome => self.build_option_class_related(prim),
|
||||
|
||||
PrimDef::List => self.build_list_class_related(prim),
|
||||
|
||||
PrimDef::NDArray | PrimDef::NDArrayCopy | PrimDef::NDArrayFill => {
|
||||
self.build_ndarray_class_related(prim)
|
||||
}
|
||||
@ -735,6 +751,27 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_list_class_related(&self, prim: PrimDef) -> TopLevelDef {
|
||||
debug_assert_prim_is_allowed(prim, &[PrimDef::List]);
|
||||
|
||||
match prim {
|
||||
PrimDef::List => TopLevelDef::Class {
|
||||
name: prim.name().into(),
|
||||
object_id: prim.id(),
|
||||
type_vars: vec![self.list_tvar.ty],
|
||||
fields: Vec::default(),
|
||||
attributes: Vec::default(),
|
||||
methods: Vec::default(),
|
||||
ancestors: Vec::default(),
|
||||
constructor: None,
|
||||
resolver: None,
|
||||
loc: None,
|
||||
},
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the class `ndarray` and its associated methods.
|
||||
fn build_ndarray_class_related(&self, prim: PrimDef) -> TopLevelDef {
|
||||
debug_assert_prim_is_allowed(
|
||||
@ -1335,7 +1372,13 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
let PrimitiveStore { uint64, int32, .. } = *self.primitives;
|
||||
|
||||
let tvar = self.unifier.get_fresh_var(Some("L".into()), None);
|
||||
let list = self.unifier.add_ty(TypeEnum::TList { ty: tvar.ty });
|
||||
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));
|
||||
|
||||
@ -1367,7 +1410,7 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
Some(calculate_len_for_slice_range(generator, ctx, start, end, step).into())
|
||||
} else {
|
||||
match &*ctx.unifier.get_ty_immutable(arg_ty) {
|
||||
TypeEnum::TList { .. } => {
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let len = ctx
|
||||
|
@ -2,7 +2,7 @@ use std::convert::TryInto;
|
||||
|
||||
use crate::symbol_resolver::SymbolValue;
|
||||
use crate::toplevel::numpy::unpack_ndarray_var_tys;
|
||||
use crate::typecheck::typedef::{into_var_map, Mapping, TypeVarId, VarMap};
|
||||
use crate::typecheck::typedef::{into_var_map, iter_type_vars, Mapping, TypeVarId, VarMap};
|
||||
use nac3parser::ast::{Constant, Location};
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::EnumIter;
|
||||
@ -12,6 +12,7 @@ use super::*;
|
||||
/// All primitive types and functions in nac3core.
|
||||
#[derive(Clone, Copy, Debug, EnumIter, PartialEq, Eq)]
|
||||
pub enum PrimDef {
|
||||
// Classes
|
||||
Int32,
|
||||
Int64,
|
||||
Float,
|
||||
@ -23,10 +24,13 @@ pub enum PrimDef {
|
||||
UInt32,
|
||||
UInt64,
|
||||
Option,
|
||||
List,
|
||||
NDArray,
|
||||
|
||||
// Member Functions
|
||||
OptionIsSome,
|
||||
OptionIsNone,
|
||||
OptionUnwrap,
|
||||
NDArray,
|
||||
NDArrayCopy,
|
||||
NDArrayFill,
|
||||
FunInt32,
|
||||
@ -99,6 +103,8 @@ pub enum PrimDef {
|
||||
FunNpLdExp,
|
||||
FunNpHypot,
|
||||
FunNpNextAfter,
|
||||
|
||||
// Top-Level Functions
|
||||
FunSome,
|
||||
}
|
||||
|
||||
@ -177,6 +183,7 @@ impl PrimDef {
|
||||
PrimDef::OptionIsSome => fun("Option.is_some", Some("is_some")),
|
||||
PrimDef::OptionIsNone => fun("Option.is_none", Some("is_none")),
|
||||
PrimDef::OptionUnwrap => fun("Option.unwrap", Some("unwrap")),
|
||||
PrimDef::List => class("list"),
|
||||
PrimDef::NDArray => class("ndarray"),
|
||||
PrimDef::NDArrayCopy => fun("ndarray.copy", Some("copy")),
|
||||
PrimDef::NDArrayFill => fun("ndarray.fill", Some("fill")),
|
||||
@ -410,6 +417,13 @@ impl TopLevelComposer {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let list_elem_tvar = unifier.get_fresh_var(Some("list_elem".into()), None);
|
||||
let list = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: PrimDef::List.id(),
|
||||
fields: Mapping::new(),
|
||||
params: into_var_map([list_elem_tvar]),
|
||||
});
|
||||
|
||||
let ndarray_dtype_tvar = unifier.get_fresh_var(Some("ndarray_dtype".into()), None);
|
||||
let ndarray_ndims_tvar =
|
||||
unifier.get_fresh_const_generic_var(size_t_ty, Some("ndarray_ndims".into()), None);
|
||||
@ -451,6 +465,7 @@ impl TopLevelComposer {
|
||||
str,
|
||||
exception,
|
||||
option,
|
||||
list,
|
||||
ndarray,
|
||||
size_t,
|
||||
};
|
||||
@ -888,7 +903,9 @@ pub fn arraylike_flatten_element_type(unifier: &mut Unifier, ty: Type) -> Type {
|
||||
unpack_ndarray_var_tys(unifier, ty).0
|
||||
}
|
||||
|
||||
TypeEnum::TList { ty } => arraylike_flatten_element_type(unifier, *ty),
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
arraylike_flatten_element_type(unifier, iter_type_vars(params).next().unwrap().ty)
|
||||
}
|
||||
_ => ty,
|
||||
}
|
||||
}
|
||||
@ -909,7 +926,9 @@ pub fn arraylike_get_ndims(unifier: &mut Unifier, ty: Type) -> u64 {
|
||||
u64::try_from(values[0].clone()).unwrap()
|
||||
}
|
||||
|
||||
TypeEnum::TList { ty } => arraylike_get_ndims(unifier, *ty) + 1,
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
arraylike_get_ndims(unifier, iter_type_vars(params).next().unwrap().ty) + 1
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ expression: res_vec
|
||||
[
|
||||
"Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
|
||||
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [TypeVarId(240)]\n}\n",
|
||||
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [TypeVarId(245)]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
|
||||
|
@ -7,7 +7,7 @@ expression: res_vec
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[t:T], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"A.foo\",\nsig: \"fn[[c:C], none]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"B[typevar229]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"typevar229\"]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"B[typevar234]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"typevar234\"]\n}\n",
|
||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"C\",\nancestors: [\"C\", \"B[bool]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\", \"e\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: []\n}\n",
|
||||
|
@ -5,8 +5,8 @@ expression: res_vec
|
||||
[
|
||||
"Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"A\",\nancestors: [\"A[T, V]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v:V], none]\"), (\"fun\", \"fn[[a:T], V]\")],\ntype_vars: [\"T\", \"V\"]\n}\n",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [TypeVarId(242)]\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(247)]\n}\n",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [TypeVarId(247)]\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(252)]\n}\n",
|
||||
"Function {\nname: \"gfun\",\nsig: \"fn[[a:A[list[float], int32]], none]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
|
@ -3,7 +3,7 @@ source: nac3core/src/toplevel/test.rs
|
||||
expression: res_vec
|
||||
---
|
||||
[
|
||||
"Class {\nname: \"A\",\nancestors: [\"A[typevar228, typevar229]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[float, bool], b:B], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\")],\ntype_vars: [\"typevar228\", \"typevar229\"]\n}\n",
|
||||
"Class {\nname: \"A\",\nancestors: [\"A[typevar233, typevar234]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[float, bool], b:B], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\")],\ntype_vars: [\"typevar233\", \"typevar234\"]\n}\n",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[float, bool], b:B], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[float, bool]], A[bool, int32]]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"B\", \"A[int64, bool]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[list[B], int32]], tuple[A[virtual[A[B, int32]], bool], B]]\")],\ntype_vars: []\n}\n",
|
||||
|
@ -6,12 +6,12 @@ expression: res_vec
|
||||
"Class {\nname: \"A\",\nancestors: [\"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [TypeVarId(248)]\n}\n",
|
||||
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [TypeVarId(253)]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(256)]\n}\n",
|
||||
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(261)]\n}\n",
|
||||
]
|
||||
|
@ -1,3 +1,6 @@
|
||||
use super::*;
|
||||
use crate::toplevel::helper::PrimDef;
|
||||
use crate::typecheck::typedef::into_var_map;
|
||||
use crate::{
|
||||
codegen::CodeGenContext,
|
||||
symbol_resolver::{SymbolResolver, ValueEnum},
|
||||
@ -14,8 +17,6 @@ use parking_lot::Mutex;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use test_case::test_case;
|
||||
|
||||
use super::*;
|
||||
|
||||
struct ResolverInternal {
|
||||
id_to_type: Mutex<HashMap<StrRef, Type>>,
|
||||
id_to_def: Mutex<HashMap<StrRef, DefinitionId>>,
|
||||
@ -775,8 +776,15 @@ fn make_internal_resolver_with_tvar(
|
||||
unifier: &mut Unifier,
|
||||
print: bool,
|
||||
) -> Arc<ResolverInternal> {
|
||||
let list_elem_tvar = unifier.get_fresh_var(Some("list_elem".into()), None);
|
||||
let list = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: PrimDef::List.id(),
|
||||
fields: HashMap::new(),
|
||||
params: into_var_map([list_elem_tvar]),
|
||||
});
|
||||
|
||||
let res: Arc<ResolverInternal> = ResolverInternal {
|
||||
id_to_def: Mutex::default(),
|
||||
id_to_def: Mutex::new(HashMap::from([("list".into(), PrimDef::List.id())])),
|
||||
id_to_type: tvars
|
||||
.into_iter()
|
||||
.map(|(name, range)| {
|
||||
@ -790,7 +798,7 @@ fn make_internal_resolver_with_tvar(
|
||||
})
|
||||
.collect::<HashMap<_, _>>()
|
||||
.into(),
|
||||
class_names: Mutex::default(),
|
||||
class_names: Mutex::new(HashMap::from([("list".into(), list)])),
|
||||
}
|
||||
.into();
|
||||
if print {
|
||||
|
@ -18,7 +18,6 @@ pub enum TypeAnnotation {
|
||||
TypeVar(Type),
|
||||
/// A `Literal` allowing a subset of literals.
|
||||
Literal(Vec<Constant>),
|
||||
List(Box<TypeAnnotation>),
|
||||
Tuple(Vec<TypeAnnotation>),
|
||||
}
|
||||
|
||||
@ -51,7 +50,6 @@ impl TypeAnnotation {
|
||||
format!("Literal({})", values.iter().map(|v| format!("{v:?}")).join(", "))
|
||||
}
|
||||
Virtual(ty) => format!("virtual[{}]", ty.stringify(unifier)),
|
||||
List(ty) => format!("list[{}]", ty.stringify(unifier)),
|
||||
Tuple(types) => {
|
||||
format!(
|
||||
"tuple[{}]",
|
||||
@ -145,9 +143,7 @@ pub fn parse_ast_to_type_annotation_kinds<T, S: std::hash::BuildHasher + Clone>(
|
||||
slice: &ast::Expr<T>,
|
||||
unifier: &mut Unifier,
|
||||
mut locked: HashMap<DefinitionId, Vec<Type>, S>| {
|
||||
if ["virtual".into(), "Generic".into(), "list".into(), "tuple".into(), "Option".into()]
|
||||
.contains(id)
|
||||
{
|
||||
if ["virtual".into(), "Generic".into(), "tuple".into(), "Option".into()].contains(id) {
|
||||
return Err(HashSet::from([format!(
|
||||
"keywords cannot be class name (at {})",
|
||||
expr.location
|
||||
@ -236,23 +232,6 @@ pub fn parse_ast_to_type_annotation_kinds<T, S: std::hash::BuildHasher + Clone>(
|
||||
Ok(TypeAnnotation::Virtual(def.into()))
|
||||
}
|
||||
|
||||
// list
|
||||
ast::ExprKind::Subscript { value, slice, .. }
|
||||
if {
|
||||
matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"list".into())
|
||||
} =>
|
||||
{
|
||||
let def_ann = parse_ast_to_type_annotation_kinds(
|
||||
resolver,
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
slice.as_ref(),
|
||||
locked,
|
||||
)?;
|
||||
Ok(TypeAnnotation::List(def_ann.into()))
|
||||
}
|
||||
|
||||
// option
|
||||
ast::ExprKind::Subscript { value, slice, .. }
|
||||
if {
|
||||
@ -516,15 +495,6 @@ pub fn get_type_from_type_annotation_kinds(
|
||||
)?;
|
||||
Ok(unifier.add_ty(TypeEnum::TVirtual { ty }))
|
||||
}
|
||||
TypeAnnotation::List(ty) => {
|
||||
let ty = get_type_from_type_annotation_kinds(
|
||||
top_level_defs,
|
||||
unifier,
|
||||
ty.as_ref(),
|
||||
subst_list,
|
||||
)?;
|
||||
Ok(unifier.add_ty(TypeEnum::TList { ty }))
|
||||
}
|
||||
TypeAnnotation::Tuple(tys) => {
|
||||
let tys = tys
|
||||
.iter()
|
||||
@ -565,7 +535,7 @@ pub fn get_type_var_contained_in_type_annotation(ann: &TypeAnnotation) -> Vec<Ty
|
||||
let mut result: Vec<TypeAnnotation> = Vec::new();
|
||||
match ann {
|
||||
TypeAnnotation::TypeVar(..) => result.push(ann.clone()),
|
||||
TypeAnnotation::Virtual(ann) | TypeAnnotation::List(ann) => {
|
||||
TypeAnnotation::Virtual(ann) => {
|
||||
result.extend(get_type_var_contained_in_type_annotation(ann.as_ref()));
|
||||
}
|
||||
TypeAnnotation::CustomClass { params, .. } => {
|
||||
@ -606,8 +576,7 @@ pub fn check_overload_type_annotation_compatible(
|
||||
|
||||
a == b
|
||||
}
|
||||
(TypeAnnotation::Virtual(a), TypeAnnotation::Virtual(b))
|
||||
| (TypeAnnotation::List(a), TypeAnnotation::List(b)) => {
|
||||
(TypeAnnotation::Virtual(a), TypeAnnotation::Virtual(b)) => {
|
||||
check_overload_type_annotation_compatible(a.as_ref(), b.as_ref(), unifier)
|
||||
}
|
||||
|
||||
|
@ -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!(
|
||||
|
@ -429,12 +429,27 @@ pub fn typeof_binop(
|
||||
lhs: Type,
|
||||
rhs: Type,
|
||||
) -> Result<Option<Type>, String> {
|
||||
let is_left_list = lhs.obj_id(unifier).is_some_and(|id| id == PrimDef::List.id());
|
||||
let is_right_list = rhs.obj_id(unifier).is_some_and(|id| id == PrimDef::List.id());
|
||||
let is_left_ndarray = lhs.obj_id(unifier).is_some_and(|id| id == PrimDef::NDArray.id());
|
||||
let is_right_ndarray = rhs.obj_id(unifier).is_some_and(|id| id == PrimDef::NDArray.id());
|
||||
|
||||
Ok(Some(match op {
|
||||
Operator::Add | Operator::Sub | Operator::Mult | Operator::Mod | Operator::FloorDiv => {
|
||||
if is_left_ndarray || is_right_ndarray {
|
||||
if is_left_list || is_right_list {
|
||||
if ![Operator::Add, Operator::Mult].contains(&op) {
|
||||
return Err(format!(
|
||||
"Binary operator {} not supported for list",
|
||||
binop_name(op)
|
||||
));
|
||||
}
|
||||
|
||||
if is_left_list {
|
||||
lhs
|
||||
} else {
|
||||
rhs
|
||||
}
|
||||
} else if is_left_ndarray || is_right_ndarray {
|
||||
typeof_ndarray_broadcast(unifier, primitives, lhs, rhs)?
|
||||
} else if unifier.unioned(lhs, rhs) {
|
||||
lhs
|
||||
@ -604,6 +619,7 @@ pub fn set_primitives_magic_methods(store: &PrimitiveStore, unifier: &mut Unifie
|
||||
bool: bool_t,
|
||||
uint32: uint32_t,
|
||||
uint64: uint64_t,
|
||||
list: list_t,
|
||||
ndarray: ndarray_t,
|
||||
..
|
||||
} = *store;
|
||||
@ -648,6 +664,11 @@ pub fn set_primitives_magic_methods(store: &PrimitiveStore, unifier: &mut Unifie
|
||||
impl_sign(unifier, store, bool_t, Some(int32_t));
|
||||
impl_eq(unifier, store, bool_t, &[bool_t, ndarray_bool_t], None);
|
||||
|
||||
/* list ======== */
|
||||
impl_binop(unifier, store, list_t, &[list_t], Some(list_t), &[Operator::Add]);
|
||||
impl_binop(unifier, store, list_t, &[int32_t, int64_t], Some(list_t), &[Operator::Mult]);
|
||||
impl_cmpop(unifier, store, list_t, &[list_t], &[Cmpop::Eq, Cmpop::NotEq], Some(bool_t));
|
||||
|
||||
/* ndarray ===== */
|
||||
let ndarray_usized_ndims_tvar =
|
||||
unifier.get_fresh_const_generic_var(size_t, Some("ndarray_ndims".into()), None);
|
||||
|
@ -4,15 +4,20 @@ use std::iter::once;
|
||||
use std::ops::Not;
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
use super::typedef::{Call, FunSignature, FuncArg, RecordField, Type, TypeEnum, Unifier, VarMap};
|
||||
use super::{magic_methods::*, type_error::TypeError, typedef::CallId};
|
||||
use crate::toplevel::TopLevelDef;
|
||||
use super::{
|
||||
magic_methods::*,
|
||||
type_error::TypeError,
|
||||
typedef::{
|
||||
into_var_map, iter_type_vars, Call, CallId, FunSignature, FuncArg, RecordField, Type,
|
||||
TypeEnum, TypeVar, Unifier, VarMap,
|
||||
},
|
||||
};
|
||||
use crate::{
|
||||
symbol_resolver::{SymbolResolver, SymbolValue},
|
||||
toplevel::{
|
||||
helper::{arraylike_flatten_element_type, arraylike_get_ndims, PrimDef},
|
||||
numpy::{make_ndarray_ty, unpack_ndarray_var_tys},
|
||||
TopLevelContext,
|
||||
TopLevelContext, TopLevelDef,
|
||||
},
|
||||
};
|
||||
use itertools::{izip, Itertools};
|
||||
@ -50,6 +55,7 @@ pub struct PrimitiveStore {
|
||||
pub str: Type,
|
||||
pub exception: Type,
|
||||
pub option: Type,
|
||||
pub list: Type,
|
||||
pub ndarray: Type,
|
||||
pub size_t: u32,
|
||||
}
|
||||
@ -242,8 +248,17 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
||||
self.unify(self.primitives.int32, target.custom.unwrap(), &target.location)?;
|
||||
} else {
|
||||
let list_like_ty = match &*self.unifier.get_ty(iter.custom.unwrap()) {
|
||||
TypeEnum::TList { .. } => {
|
||||
self.unifier.add_ty(TypeEnum::TList { ty: target.custom.unwrap() })
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let list_tvar = iter_type_vars(params).nth(0).unwrap();
|
||||
self.unifier
|
||||
.subst(
|
||||
self.primitives.list,
|
||||
&into_var_map([TypeVar {
|
||||
id: list_tvar.id,
|
||||
ty: target.custom.unwrap(),
|
||||
}]),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
todo!()
|
||||
@ -764,6 +779,16 @@ impl<'a> Inferencer<'a> {
|
||||
generators[0].target.location,
|
||||
);
|
||||
}
|
||||
|
||||
let list_tvar = if let TypeEnum::TObj { obj_id, params, .. } =
|
||||
&*self.unifier.get_ty_immutable(self.primitives.list)
|
||||
{
|
||||
assert_eq!(*obj_id, PrimDef::List.id());
|
||||
iter_type_vars(params).nth(0).unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let variable_mapping = self.variable_mapping.clone();
|
||||
let defined_identifiers = self.defined_identifiers.clone();
|
||||
let mut new_context = Inferencer {
|
||||
@ -792,7 +817,13 @@ impl<'a> Inferencer<'a> {
|
||||
&target.location,
|
||||
)?;
|
||||
} else {
|
||||
let list = new_context.unifier.add_ty(TypeEnum::TList { ty: target.custom.unwrap() });
|
||||
let list = new_context
|
||||
.unifier
|
||||
.subst(
|
||||
self.primitives.list,
|
||||
&into_var_map([TypeVar { id: list_tvar.id, ty: target.custom.unwrap() }]),
|
||||
)
|
||||
.unwrap();
|
||||
new_context.unify(iter.custom.unwrap(), list, &iter.location)?;
|
||||
}
|
||||
let ifs: Vec<_> = generator
|
||||
@ -809,9 +840,16 @@ impl<'a> Inferencer<'a> {
|
||||
new_context.unify(v.custom.unwrap(), new_context.primitives.bool, &v.location)?;
|
||||
}
|
||||
|
||||
let custom = new_context
|
||||
.unifier
|
||||
.subst(
|
||||
self.primitives.list,
|
||||
&into_var_map([TypeVar { id: list_tvar.id, ty: elt.custom.unwrap() }]),
|
||||
)
|
||||
.unwrap();
|
||||
Ok(Located {
|
||||
location,
|
||||
custom: Some(new_context.unifier.add_ty(TypeEnum::TList { ty: elt.custom.unwrap() })),
|
||||
custom: Some(custom),
|
||||
node: ExprKind::ListComp {
|
||||
elt: Box::new(elt),
|
||||
generators: vec![Comprehension {
|
||||
@ -893,11 +931,13 @@ impl<'a> Inferencer<'a> {
|
||||
// Here, we also take the opportunity to deduce `ndims` statically.
|
||||
let shape_ty_enum = &*self.unifier.get_ty(shape_ty);
|
||||
let ndims = match shape_ty_enum {
|
||||
TypeEnum::TList { ty } => {
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
// Handle 1. A list of int32s
|
||||
|
||||
let ty = iter_type_vars(params).nth(0).unwrap().ty;
|
||||
|
||||
// Typecheck
|
||||
self.unifier.unify(*ty, self.primitives.int32).map_err(|err| {
|
||||
self.unifier.unify(ty, self.primitives.int32).map_err(|err| {
|
||||
HashSet::from([err
|
||||
.at(Some(shape.location))
|
||||
.to_display(self.unifier)
|
||||
@ -1563,7 +1603,19 @@ impl<'a> Inferencer<'a> {
|
||||
for t in elts {
|
||||
self.unify(ty, t.custom.unwrap(), &t.location)?;
|
||||
}
|
||||
Ok(self.unifier.add_ty(TypeEnum::TList { ty }))
|
||||
let list_tvar = if let TypeEnum::TObj { obj_id, params, .. } =
|
||||
&*self.unifier.get_ty_immutable(self.primitives.list)
|
||||
{
|
||||
assert_eq!(*obj_id, PrimDef::List.id());
|
||||
iter_type_vars(params).nth(0).unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let list = self
|
||||
.unifier
|
||||
.subst(self.primitives.list, &into_var_map([TypeVar { id: list_tvar.id, ty }]))
|
||||
.unwrap();
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
@ -1885,7 +1937,16 @@ impl<'a> Inferencer<'a> {
|
||||
self.constrain(v.custom.unwrap(), self.primitives.int32, &v.location)?;
|
||||
}
|
||||
let list_like_ty = match &*self.unifier.get_ty(value.custom.unwrap()) {
|
||||
TypeEnum::TList { .. } => self.unifier.add_ty(TypeEnum::TList { ty }),
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let list_tvar = iter_type_vars(params).nth(0).unwrap();
|
||||
self.unifier
|
||||
.subst(
|
||||
self.primitives.list,
|
||||
&into_var_map([TypeVar { id: list_tvar.id, ty }]),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
let (_, ndims) =
|
||||
unpack_ndarray_var_tys(self.unifier, value.custom.unwrap());
|
||||
@ -1960,13 +2021,20 @@ impl<'a> Inferencer<'a> {
|
||||
|
||||
// the index is not a constant, so value can only be a list-like structure
|
||||
match &*self.unifier.get_ty(value.custom.unwrap()) {
|
||||
TypeEnum::TList { .. } => {
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
self.constrain(
|
||||
slice.custom.unwrap(),
|
||||
self.primitives.int32,
|
||||
&slice.location,
|
||||
)?;
|
||||
let list = self.unifier.add_ty(TypeEnum::TList { ty });
|
||||
let list_tvar = iter_type_vars(params).nth(0).unwrap();
|
||||
let list = self
|
||||
.unifier
|
||||
.subst(
|
||||
self.primitives.list,
|
||||
&into_var_map([TypeVar { id: list_tvar.id, ty }]),
|
||||
)
|
||||
.unwrap();
|
||||
self.constrain(value.custom.unwrap(), list, &value.location)?;
|
||||
Ok(ty)
|
||||
}
|
||||
|
@ -139,6 +139,12 @@ impl TestEnvironment {
|
||||
fields: HashMap::new(),
|
||||
params: VarMap::new(),
|
||||
});
|
||||
let list_elem_tvar = unifier.get_fresh_var(Some("list_elem".into()), None);
|
||||
let list = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: PrimDef::List.id(),
|
||||
fields: HashMap::new(),
|
||||
params: into_var_map([list_elem_tvar]),
|
||||
});
|
||||
let ndarray_dtype_tvar = unifier.get_fresh_var(Some("ndarray_dtype".into()), None);
|
||||
let ndarray_ndims_tvar =
|
||||
unifier.get_fresh_const_generic_var(uint64, Some("ndarray_ndims".into()), None);
|
||||
@ -159,6 +165,7 @@ impl TestEnvironment {
|
||||
uint32,
|
||||
uint64,
|
||||
option,
|
||||
list,
|
||||
ndarray,
|
||||
size_t: 64,
|
||||
};
|
||||
@ -273,15 +280,35 @@ impl TestEnvironment {
|
||||
fields: HashMap::new(),
|
||||
params: VarMap::new(),
|
||||
});
|
||||
let list_elem_tvar = unifier.get_fresh_var(Some("list_elem".into()), None);
|
||||
let list = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: PrimDef::List.id(),
|
||||
fields: HashMap::new(),
|
||||
params: into_var_map([list_elem_tvar]),
|
||||
});
|
||||
let ndarray = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: PrimDef::NDArray.id(),
|
||||
fields: HashMap::new(),
|
||||
params: VarMap::new(),
|
||||
});
|
||||
identifier_mapping.insert("None".into(), none);
|
||||
for (i, name) in ["int32", "int64", "float", "bool", "none", "range", "str", "Exception"]
|
||||
.iter()
|
||||
.enumerate()
|
||||
for (i, name) in [
|
||||
"int32",
|
||||
"int64",
|
||||
"float",
|
||||
"bool",
|
||||
"none",
|
||||
"range",
|
||||
"str",
|
||||
"Exception",
|
||||
"uint32",
|
||||
"uint64",
|
||||
"Option",
|
||||
"list",
|
||||
"ndarray",
|
||||
]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
top_level_defs.push(
|
||||
RwLock::new(TopLevelDef::Class {
|
||||
@ -299,7 +326,7 @@ impl TestEnvironment {
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let defs = 7;
|
||||
let defs = 12;
|
||||
|
||||
let primitives = PrimitiveStore {
|
||||
int32,
|
||||
@ -313,6 +340,7 @@ impl TestEnvironment {
|
||||
uint32,
|
||||
uint64,
|
||||
option,
|
||||
list,
|
||||
ndarray,
|
||||
size_t: 64,
|
||||
};
|
||||
@ -424,6 +452,11 @@ impl TestEnvironment {
|
||||
"range".into(),
|
||||
"str".into(),
|
||||
"exception".into(),
|
||||
"uint32".into(),
|
||||
"uint64".into(),
|
||||
"option".into(),
|
||||
"list".into(),
|
||||
"ndarray".into(),
|
||||
"Foo".into(),
|
||||
"Bar".into(),
|
||||
"Bar2".into(),
|
||||
|
@ -13,7 +13,7 @@ use nac3parser::ast::{Location, StrRef};
|
||||
use super::type_error::{TypeError, TypeErrorKind};
|
||||
use super::unification_table::{UnificationKey, UnificationTable};
|
||||
use crate::symbol_resolver::SymbolValue;
|
||||
use crate::toplevel::{DefinitionId, TopLevelContext, TopLevelDef};
|
||||
use crate::toplevel::{helper::PrimDef, DefinitionId, TopLevelContext, TopLevelDef};
|
||||
use crate::typecheck::type_inferencer::PrimitiveStore;
|
||||
|
||||
#[cfg(test)]
|
||||
@ -207,12 +207,6 @@ pub enum TypeEnum {
|
||||
ty: Vec<Type>,
|
||||
},
|
||||
|
||||
/// A list type.
|
||||
TList {
|
||||
/// The type of elements present in this list.
|
||||
ty: Type,
|
||||
},
|
||||
|
||||
/// An object type.
|
||||
TObj {
|
||||
/// The [`DefinitionId`] of this object type.
|
||||
@ -246,7 +240,6 @@ impl TypeEnum {
|
||||
TypeEnum::TVar { .. } => "TVar",
|
||||
TypeEnum::TLiteral { .. } => "TConstant",
|
||||
TypeEnum::TTuple { .. } => "TTuple",
|
||||
TypeEnum::TList { .. } => "TList",
|
||||
TypeEnum::TObj { .. } => "TObj",
|
||||
TypeEnum::TVirtual { .. } => "TVirtual",
|
||||
TypeEnum::TCall { .. } => "TCall",
|
||||
@ -482,9 +475,27 @@ impl Unifier {
|
||||
)
|
||||
}
|
||||
}
|
||||
TypeEnum::TList { ty } => self
|
||||
.get_instantiations(*ty)
|
||||
.map(|ty| ty.iter().map(|&ty| self.add_ty(TypeEnum::TList { ty })).collect_vec()),
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let tv = iter_type_vars(params).nth(0).unwrap();
|
||||
|
||||
let tv_id = if let TypeEnum::TVar { id, .. } =
|
||||
self.unification_table.probe_value(tv.ty).as_ref()
|
||||
{
|
||||
*id
|
||||
} else {
|
||||
tv.id
|
||||
};
|
||||
|
||||
self.get_instantiations(tv.ty).map(|ty_insts| {
|
||||
ty_insts
|
||||
.iter()
|
||||
.map(|&ty_inst| {
|
||||
self.subst(ty, &into_var_map([TypeVar { id: tv_id, ty: ty_inst }]))
|
||||
.unwrap_or(ty)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
TypeEnum::TVirtual { ty } => self.get_instantiations(*ty).map(|ty| {
|
||||
ty.iter().map(|&ty| self.add_ty(TypeEnum::TVirtual { ty })).collect_vec()
|
||||
}),
|
||||
@ -541,9 +552,7 @@ impl Unifier {
|
||||
|
||||
TVar { .. } => allowed_typevars.iter().any(|b| self.unification_table.unioned(a, *b)),
|
||||
TCall { .. } => false,
|
||||
TList { ty }
|
||||
| TVirtual { ty } => self.is_concrete(*ty, allowed_typevars),
|
||||
|
||||
TVirtual { ty } => self.is_concrete(*ty, allowed_typevars),
|
||||
TTuple { ty } => ty.iter().all(|ty| self.is_concrete(*ty, allowed_typevars)),
|
||||
TObj { params: vars, .. } => {
|
||||
vars.values().all(|ty| self.is_concrete(*ty, allowed_typevars))
|
||||
@ -885,22 +894,6 @@ impl Unifier {
|
||||
self.unify_impl(x, b, false)?;
|
||||
self.set_a_to_b(a, x);
|
||||
}
|
||||
(TVar { fields: Some(fields), range, is_const_generic: false, .. }, TList { ty }) => {
|
||||
for (k, v) in fields {
|
||||
match *k {
|
||||
RecordKey::Int(_) => {
|
||||
self.unify_impl(v.ty, *ty, false).map_err(|e| e.at(v.loc))?;
|
||||
}
|
||||
RecordKey::Str(_) => {
|
||||
return Err(TypeError::new(TypeErrorKind::NoSuchField(*k, b), v.loc))
|
||||
}
|
||||
}
|
||||
}
|
||||
let x = self.check_var_compatibility(b, range)?.unwrap_or(b);
|
||||
self.unify_impl(x, b, false)?;
|
||||
self.set_a_to_b(a, x);
|
||||
}
|
||||
|
||||
(
|
||||
TVar { id: id1, range: ty1, is_const_generic: true, .. },
|
||||
TVar { id: id2, range: ty2, .. },
|
||||
@ -979,13 +972,7 @@ impl Unifier {
|
||||
}
|
||||
self.set_a_to_b(a, b);
|
||||
}
|
||||
(TList { ty: ty1 }, TList { ty: ty2 }) => {
|
||||
if self.unify_impl(*ty1, *ty2, false).is_err() {
|
||||
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
|
||||
}
|
||||
self.set_a_to_b(a, b);
|
||||
}
|
||||
(TVar { fields: Some(map), range, .. }, TObj { fields, .. }) => {
|
||||
(TVar { fields: Some(map), range, .. }, TObj { obj_id, fields, params }) => {
|
||||
for (k, field) in map {
|
||||
match *k {
|
||||
RecordKey::Str(s) => {
|
||||
@ -1004,10 +991,18 @@ impl Unifier {
|
||||
self.unify_impl(field.ty, ty, false).map_err(|v| v.at(field.loc))?;
|
||||
}
|
||||
RecordKey::Int(_) => {
|
||||
return Err(TypeError::new(
|
||||
TypeErrorKind::NoSuchField(*k, b),
|
||||
field.loc,
|
||||
))
|
||||
// Allow expressions such as list[0]
|
||||
if *obj_id == PrimDef::List.id() {
|
||||
let ty = iter_type_vars(params).nth(0).unwrap().ty;
|
||||
|
||||
self.unify_impl(field.ty, ty, false)
|
||||
.map_err(|e| e.at(field.loc))?;
|
||||
} else {
|
||||
return Err(TypeError::new(
|
||||
TypeErrorKind::NoSuchField(*k, b),
|
||||
field.loc,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1222,9 +1217,6 @@ impl Unifier {
|
||||
ty.iter().map(|v| self.internal_stringify(*v, obj_to_name, var_to_name, notes));
|
||||
format!("tuple[{}]", fields.join(", "))
|
||||
}
|
||||
TypeEnum::TList { ty } => {
|
||||
format!("list[{}]", self.internal_stringify(*ty, obj_to_name, var_to_name, notes))
|
||||
}
|
||||
TypeEnum::TVirtual { ty } => {
|
||||
format!(
|
||||
"virtual[{}]",
|
||||
@ -1357,9 +1349,6 @@ impl Unifier {
|
||||
None
|
||||
}
|
||||
}
|
||||
TypeEnum::TList { ty } => {
|
||||
self.subst_impl(*ty, mapping, cache).map(|t| self.add_ty(TypeEnum::TList { ty: t }))
|
||||
}
|
||||
TypeEnum::TVirtual { ty } => self
|
||||
.subst_impl(*ty, mapping, cache)
|
||||
.map(|t| self.add_ty(TypeEnum::TVirtual { ty: t })),
|
||||
@ -1370,6 +1359,7 @@ impl Unifier {
|
||||
// This is also used to prevent infinite substitution...
|
||||
let need_subst = params.values().any(|v| {
|
||||
let ty = self.unification_table.probe_value(*v);
|
||||
// TODO(Derppening): #444
|
||||
if let TypeEnum::TVar { id, .. } = ty.as_ref() {
|
||||
mapping.contains_key(id)
|
||||
} else {
|
||||
@ -1526,8 +1516,22 @@ impl Unifier {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
(TList { ty: ty1 }, TList { ty: ty2 }) => {
|
||||
Ok(self.get_intersection(*ty1, *ty2)?.map(|ty| self.add_ty(TList { ty })))
|
||||
// TODO(Derppening): #444
|
||||
(
|
||||
TObj { obj_id: id1, fields, params: params1 },
|
||||
TObj { obj_id: id2, params: params2, .. },
|
||||
) if *id1 == PrimDef::List.id() && *id2 == PrimDef::List.id() => {
|
||||
let tv_id = iter_type_vars(params1).nth(0).unwrap().id;
|
||||
let ty1 = iter_type_vars(params1).nth(0).unwrap().ty;
|
||||
let ty2 = iter_type_vars(params2).nth(0).unwrap().ty;
|
||||
|
||||
Ok(self.get_intersection(ty1, ty2)?.map(|ty| {
|
||||
self.add_ty(TObj {
|
||||
obj_id: *id1,
|
||||
fields: fields.clone(),
|
||||
params: into_var_map([TypeVar { id: tv_id, ty }]),
|
||||
})
|
||||
}))
|
||||
}
|
||||
(TVirtual { ty: ty1 }, TVirtual { ty: ty2 }) => {
|
||||
Ok(self.get_intersection(*ty1, *ty2)?.map(|ty| self.add_ty(TVirtual { ty })))
|
||||
|
@ -32,10 +32,7 @@ impl Unifier {
|
||||
ty1.len() == ty2.len()
|
||||
&& ty1.iter().zip(ty2.iter()).all(|(t1, t2)| self.eq(*t1, *t2))
|
||||
}
|
||||
(TypeEnum::TList { ty: ty1 }, TypeEnum::TList { ty: ty2 })
|
||||
| (TypeEnum::TVirtual { ty: ty1 }, TypeEnum::TVirtual { ty: ty2 }) => {
|
||||
self.eq(*ty1, *ty2)
|
||||
}
|
||||
(TypeEnum::TVirtual { ty: ty1 }, TypeEnum::TVirtual { ty: ty2 }) => self.eq(*ty1, *ty2),
|
||||
(
|
||||
TypeEnum::TObj { obj_id: id1, params: params1, .. },
|
||||
TypeEnum::TObj { obj_id: id2, params: params2, .. },
|
||||
@ -119,6 +116,15 @@ impl TestEnvironment {
|
||||
params: into_var_map([tvar]),
|
||||
}),
|
||||
);
|
||||
let tvar = unifier.get_dummy_var();
|
||||
type_mapping.insert(
|
||||
"list".into(),
|
||||
unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: PrimDef::List.id(),
|
||||
fields: HashMap::new(),
|
||||
params: into_var_map([tvar]),
|
||||
}),
|
||||
);
|
||||
|
||||
TestEnvironment { unifier, type_mapping }
|
||||
}
|
||||
@ -133,6 +139,36 @@ impl TestEnvironment {
|
||||
// for testing only, so we can just panic when the input is malformed
|
||||
let end = typ.find(|c| ['[', ',', ']', '='].contains(&c)).unwrap_or(typ.len());
|
||||
match &typ[..end] {
|
||||
"list" => {
|
||||
let mut s = &typ[end..];
|
||||
assert_eq!(&s[0..1], "[");
|
||||
let mut ty = Vec::new();
|
||||
while &s[0..1] != "]" {
|
||||
let result = self.internal_parse(&s[1..], mapping);
|
||||
ty.push(result.0);
|
||||
s = result.1;
|
||||
}
|
||||
|
||||
assert_eq!(ty.len(), 1);
|
||||
|
||||
let list_elem_tvar = if let TypeEnum::TObj { params, .. } =
|
||||
&*self.unifier.get_ty_immutable(self.type_mapping["list"])
|
||||
{
|
||||
iter_type_vars(params).next().unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
(
|
||||
self.unifier
|
||||
.subst(
|
||||
self.type_mapping["list"],
|
||||
&into_var_map([TypeVar { id: list_elem_tvar.id, ty: ty[0] }]),
|
||||
)
|
||||
.unwrap(),
|
||||
&s[1..],
|
||||
)
|
||||
}
|
||||
"tuple" => {
|
||||
let mut s = &typ[end..];
|
||||
assert_eq!(&s[0..1], "[");
|
||||
@ -144,12 +180,6 @@ impl TestEnvironment {
|
||||
}
|
||||
(self.unifier.add_ty(TypeEnum::TTuple { ty }), &s[1..])
|
||||
}
|
||||
"list" => {
|
||||
assert_eq!(&typ[end..=end], "[");
|
||||
let (ty, s) = self.internal_parse(&typ[end + 1..], mapping);
|
||||
assert_eq!(&s[0..1], "]");
|
||||
(self.unifier.add_ty(TypeEnum::TList { ty }), &s[1..])
|
||||
}
|
||||
"Record" => {
|
||||
let mut s = &typ[end..];
|
||||
assert_eq!(&s[0..1], "[");
|
||||
@ -274,7 +304,7 @@ fn test_unify(
|
||||
("v1", "tuple[int]"),
|
||||
("v2", "list[int]"),
|
||||
],
|
||||
(("v1", "v2"), "Incompatible types: list[0] and tuple[0]")
|
||||
(("v1", "v2"), "Incompatible types: 11[0] and tuple[0]")
|
||||
; "type mismatch"
|
||||
)]
|
||||
#[test_case(2,
|
||||
@ -298,7 +328,7 @@ fn test_unify(
|
||||
("v1", "Record[a=float,b=int]"),
|
||||
("v2", "Foo[v3]"),
|
||||
],
|
||||
(("v1", "v2"), "`3[typevar4]::b` field/method does not exist")
|
||||
(("v1", "v2"), "`3[typevar5]::b` field/method does not exist")
|
||||
; "record obj merge"
|
||||
)]
|
||||
/// Test cases for invalid unifications.
|
||||
@ -388,6 +418,14 @@ fn test_typevar_range() {
|
||||
let int_list = env.parse("list[int]", &HashMap::new());
|
||||
let float_list = env.parse("list[float]", &HashMap::new());
|
||||
|
||||
let list_elem_tvar = if let TypeEnum::TObj { params, .. } =
|
||||
&*env.unifier.get_ty_immutable(env.type_mapping["list"])
|
||||
{
|
||||
iter_type_vars(params).next().unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
// unification between v and int
|
||||
// where v in (int, bool)
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).ty;
|
||||
@ -398,7 +436,7 @@ fn test_typevar_range() {
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).ty;
|
||||
assert_eq!(
|
||||
env.unify(int_list, v),
|
||||
Err("Expected any one of these types: 0, 2, but got list[0]".to_string())
|
||||
Err("Expected any one of these types: 0, 2, but got 11[0]".to_string())
|
||||
);
|
||||
|
||||
// unification between v and float
|
||||
@ -410,7 +448,11 @@ fn test_typevar_range() {
|
||||
);
|
||||
|
||||
let v1 = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).ty;
|
||||
let v1_list = env.unifier.add_ty(TypeEnum::TList { ty: v1 });
|
||||
let v1_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: v1 }]),
|
||||
});
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, v1_list], None, None).ty;
|
||||
// unification between v and int
|
||||
// where v in (int, list[v1]), v1 in (int, bool)
|
||||
@ -424,9 +466,10 @@ fn test_typevar_range() {
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, v1_list], None, None).ty;
|
||||
// unification between v and list[float]
|
||||
// where v in (int, list[v1]), v1 in (int, bool)
|
||||
println!("float_list: {}, v: {}", env.unifier.stringify(float_list), env.unifier.stringify(v));
|
||||
assert_eq!(
|
||||
env.unify(float_list, v),
|
||||
Err("Expected any one of these types: 0, list[typevar5], but got list[1]\n\nNotes:\n typevar5 ∈ {0, 2}".to_string())
|
||||
Err("Expected any one of these types: 0, 11[typevar6], but got 11[1]\n\nNotes:\n typevar6 ∈ {0, 2}".to_string())
|
||||
);
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).ty;
|
||||
@ -441,34 +484,66 @@ fn test_typevar_range() {
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).ty;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float], None, None).ty;
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TList { ty: a });
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: a }]),
|
||||
});
|
||||
let a_list = env.unifier.get_fresh_var_with_range(&[a_list], None, None).ty;
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TList { ty: b });
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: b }]),
|
||||
});
|
||||
let b_list = env.unifier.get_fresh_var_with_range(&[b_list], None, None).ty;
|
||||
env.unifier.unify(a_list, b_list).unwrap();
|
||||
let float_list = env.unifier.add_ty(TypeEnum::TList { ty: float });
|
||||
let float_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: float }]),
|
||||
});
|
||||
env.unifier.unify(a_list, float_list).unwrap();
|
||||
// previous unifications should not affect a and b
|
||||
env.unifier.unify(a, int).unwrap();
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).ty;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float], None, None).ty;
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TList { ty: a });
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TList { ty: b });
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: a }]),
|
||||
});
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: b }]),
|
||||
});
|
||||
env.unifier.unify(a_list, b_list).unwrap();
|
||||
let int_list = env.unifier.add_ty(TypeEnum::TList { ty: int });
|
||||
let int_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: int }]),
|
||||
});
|
||||
assert_eq!(
|
||||
env.unify(a_list, int_list),
|
||||
Err("Incompatible types: list[typevar22] and list[0]\
|
||||
\n\nNotes:\n typevar22 ∈ {1}"
|
||||
Err("Incompatible types: 11[typevar23] and 11[0]\
|
||||
\n\nNotes:\n typevar23 ∈ {1}"
|
||||
.into())
|
||||
);
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).ty;
|
||||
let b = env.unifier.get_dummy_var().ty;
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TList { ty: a });
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: a }]),
|
||||
});
|
||||
let a_list = env.unifier.get_fresh_var_with_range(&[a_list], None, None).ty;
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TList { ty: b });
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: b }]),
|
||||
});
|
||||
env.unifier.unify(a_list, b_list).unwrap();
|
||||
assert_eq!(
|
||||
env.unify(b, boolean),
|
||||
@ -482,16 +557,25 @@ fn test_rigid_var() {
|
||||
let a = env.unifier.get_fresh_rigid_var(None, None).ty;
|
||||
let b = env.unifier.get_fresh_rigid_var(None, None).ty;
|
||||
let x = env.unifier.get_dummy_var().ty;
|
||||
let list_a = env.unifier.add_ty(TypeEnum::TList { ty: a });
|
||||
let list_x = env.unifier.add_ty(TypeEnum::TList { ty: x });
|
||||
let list_elem_tvar = env.unifier.get_fresh_var(Some("list_elem".into()), None);
|
||||
let list_a = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: a }]),
|
||||
});
|
||||
let list_x = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: env.type_mapping["list"].obj_id(&env.unifier).unwrap(),
|
||||
fields: Mapping::default(),
|
||||
params: into_var_map([TypeVar { id: list_elem_tvar.id, ty: x }]),
|
||||
});
|
||||
let int = env.parse("int", &HashMap::new());
|
||||
let list_int = env.parse("list[int]", &HashMap::new());
|
||||
|
||||
assert_eq!(env.unify(a, b), Err("Incompatible types: typevar3 and typevar2".to_string()));
|
||||
assert_eq!(env.unify(a, b), Err("Incompatible types: typevar4 and typevar3".to_string()));
|
||||
env.unifier.unify(list_a, list_x).unwrap();
|
||||
assert_eq!(
|
||||
env.unify(list_x, list_int),
|
||||
Err("Incompatible types: list[typevar2] and list[0]".to_string())
|
||||
Err("Incompatible types: 11[typevar3] and 11[0]".to_string())
|
||||
);
|
||||
|
||||
env.unifier.replace_rigid_var(a, int);
|
||||
@ -506,10 +590,21 @@ fn test_instantiation() {
|
||||
let float = env.parse("float", &HashMap::new());
|
||||
let list_int = env.parse("list[int]", &HashMap::new());
|
||||
|
||||
let obj_map: HashMap<_, _> = [(0usize, "int"), (1, "float"), (2, "bool")].into();
|
||||
let list_elem_tvar = if let TypeEnum::TObj { params, .. } =
|
||||
&*env.unifier.get_ty_immutable(env.type_mapping["list"])
|
||||
{
|
||||
iter_type_vars(params).next().unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let obj_map: HashMap<_, _> = [(0usize, "int"), (1, "float"), (2, "bool"), (11, "list")].into();
|
||||
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).ty;
|
||||
let list_v = env.unifier.add_ty(TypeEnum::TList { ty: v });
|
||||
let list_v = env
|
||||
.unifier
|
||||
.subst(env.type_mapping["list"], &into_var_map([TypeVar { id: list_elem_tvar.id, ty: v }]))
|
||||
.unwrap();
|
||||
let v1 = env.unifier.get_fresh_var_with_range(&[list_v, int], None, None).ty;
|
||||
let v2 = env.unifier.get_fresh_var_with_range(&[list_int, float], None, None).ty;
|
||||
let t = env.unifier.get_dummy_var().ty;
|
||||
@ -536,7 +631,7 @@ fn test_instantiation() {
|
||||
tuple[int, list[bool], list[int]]
|
||||
tuple[int, list[int], float]
|
||||
tuple[int, list[int], list[int]]
|
||||
v5"
|
||||
v6"
|
||||
}
|
||||
.split('\n')
|
||||
.collect_vec();
|
||||
|
@ -1,3 +1,7 @@
|
||||
@extern
|
||||
def output_bool(x: bool):
|
||||
...
|
||||
|
||||
@extern
|
||||
def output_int32_list(x: list[int32]):
|
||||
...
|
||||
@ -30,6 +34,32 @@ def run() -> int32:
|
||||
|
||||
get_list_slice()
|
||||
list_slice_assignment()
|
||||
|
||||
output_int32_list([1, 2, 3] + [4, 5, 6])
|
||||
output_int32_list([1, 2, 3] * 3)
|
||||
output_bool([] == [])
|
||||
output_bool([0] == [])
|
||||
output_bool([0] == [0])
|
||||
output_bool([0, 1] == [0])
|
||||
output_bool([0, 1] == [0, 1])
|
||||
output_bool([] != [])
|
||||
output_bool([0] != [])
|
||||
output_bool([0] != [0])
|
||||
output_bool([0] != [0, 1])
|
||||
output_bool([0, 1] != [0, 1])
|
||||
output_bool([] == [] == [])
|
||||
output_bool([0] == [0] == [0])
|
||||
output_bool([0, 1] == [0] == [0, 1])
|
||||
output_bool([0, 1] == [0, 1] == [0])
|
||||
output_bool([0] == [0, 1] == [0, 1])
|
||||
output_bool([0, 1] == [0, 1] == [0, 1])
|
||||
output_bool([] != [] != [])
|
||||
output_bool([0] != [0] != [0])
|
||||
output_bool([0, 1] != [0] != [0, 1])
|
||||
output_bool([0, 1] != [0, 1] != [0])
|
||||
output_bool([0] != [0, 1] != [0, 1])
|
||||
output_bool([0, 1] != [0, 1] != [0, 1])
|
||||
|
||||
return 0
|
||||
|
||||
def get_list_slice():
|
||||
|
Loading…
Reference in New Issue
Block a user