Compare commits

...

15 Commits

Author SHA1 Message Date
wylited 35b6459c58 nac3core: replace paramter with parameter 2022-04-13 15:42:26 +08:00
wylited e94b25f544 spelling (#264)
Co-authored-by: wylited <ds@m-labs.hk>
Co-committed-by: wylited <ds@m-labs.hk>
2022-04-13 11:32:31 +08:00
Sebastien Bourdeauducq 6972689469 nac3artiq: cleanup demo 2022-04-12 10:34:14 +08:00
Sebastien Bourdeauducq 3fb22c9182 nac3artiq: treat host numpy.float64 as float. Closes #90 2022-04-12 10:33:28 +08:00
Sebastien Bourdeauducq 1e7abf0268 fix tests 2022-04-12 10:06:41 +08:00
Sebastien Bourdeauducq f5a6d29106 update insta snapshots 2022-04-12 09:56:49 +08:00
Sebastien Bourdeauducq ca07cb66cd format typevars consistently 2022-04-12 09:28:17 +08:00
Sebastien Bourdeauducq 93e9a6a38a update dependencies 2022-04-12 09:13:04 +08:00
ychenfo 722e3df086 nac3core, artiq: optimize kernel invariant for tuple index 2022-04-11 14:58:40 +08:00
ychenfo ad9ad22cb8 nac3core: optimize unwrap KernelInvariant 2022-04-11 14:58:35 +08:00
ychenfo f66f66b3a4 nac3core, artiq: remove unnecessary ptr casts 2022-04-10 01:28:46 +08:00
ychenfo 6c485bc9dc nac3artiq: skip attribute writeback for option
option types do not have any fields to be written back to the host so it is ok to skip. If we do not skip, there will be error when getting the value of it since it can be `none`, whose type is not concrete
2022-04-10 01:28:30 +08:00
ychenfo 089bba96a3 nac3artiq: get_obj_value take an additional argument for expected type 2022-04-10 01:28:30 +08:00
ychenfo 0e0871bc38 nac3core, artiq: to_basic_value_enum takes an argument indicating the expected type 2022-04-10 01:28:22 +08:00
ychenfo 26187bff0b nac3core: add missing bound check and negative index handling for list subscription assignment 2022-04-09 04:56:31 +08:00
20 changed files with 414 additions and 244 deletions

16
Cargo.lock generated
View File

@ -422,9 +422,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.121"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
[[package]]
name = "libloading"
@ -740,9 +740,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [
"unicode-xid",
]
@ -797,9 +797,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
@ -1035,9 +1035,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.90"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
dependencies = [
"proc-macro2",
"quote",

View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1649024309,
"narHash": "sha256-AWbvj/NHZXVwAnHaVOFlxg7tcNerEKrKBmgGfztSHWM=",
"lastModified": 1649619156,
"narHash": "sha256-p0q4zpuKMwrzGF+5ZU7Thnpac5TinhDI9jr2mBxhV4w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "af0a9bc0e5341855518e9c1734d7ef913e5138b9",
"rev": "e7d63bd0d50df412f5a1d8acfa3caae75522e347",
"type": "github"
},
"original": {

View File

@ -1,10 +1,4 @@
from min_artiq import *
from numpy import int32, int64
@extern
def output_int(x: int32):
...
@nac3

View File

@ -68,7 +68,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let result = gen_call(self, ctx, obj, fun, params)?;
if let Some(end) = self.end.clone() {
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self)?;
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self, end.custom.unwrap())?;
let now = self.timeline.emit_now_mu(ctx);
let smax = ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
@ -88,7 +88,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
ctx.builder.build_store(end_store, max);
}
if let Some(start) = self.start.clone() {
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self)?;
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self, start.custom.unwrap())?;
self.timeline.emit_at_mu(ctx, start_val);
}
Ok(result)
@ -120,7 +120,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let old_start = self.start.take();
let old_end = self.end.take();
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self)?
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
} else {
self.timeline.emit_now_mu(ctx)
};
@ -174,8 +174,10 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val =
self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(ctx, self)?;
let end_val = self
.gen_expr(ctx, &end_expr)?
.unwrap()
.to_basic_value_enum(ctx, self, end_expr.custom.unwrap())?;
// inside a sequential block
if old_start.is_none() {
@ -186,7 +188,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let outer_end_val = self
.gen_expr(ctx, old_end)?
.unwrap()
.to_basic_value_enum(ctx, self)?;
.to_basic_value_enum(ctx, self, old_end.custom.unwrap())?;
let smax =
ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
@ -371,7 +373,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
.0
.args
.iter()
.map(|arg| mapping.remove(&arg.name).unwrap().to_basic_value_enum(ctx, generator))
.map(|arg| mapping.remove(&arg.name).unwrap().to_basic_value_enum(ctx, generator, arg.ty))
.collect::<Result<Vec<_>, _>>()?;
if let Some(obj) = obj {
if let ValueEnum::Static(obj) = obj.1 {
@ -511,11 +513,13 @@ pub fn attributes_writeback<'ctx, 'a>(
}
let ty = ty.unwrap();
match &*ctx.unifier.get_ty(ty) {
TypeEnum::TObj { fields, .. } => {
TypeEnum::TObj { fields, obj_id, .. }
if *obj_id != ctx.primitives.option.get_obj_id(&ctx.unifier) =>
{
// we only care about primitive attributes
// for non-primitive attributes, they should be in another global
let mut attributes = Vec::new();
let obj = inner_resolver.get_obj_value(py, val, ctx, generator)?.unwrap();
let obj = inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap();
for (name, (field_ty, is_mutable)) in fields.iter() {
if !is_mutable {
continue
@ -540,7 +544,7 @@ pub fn attributes_writeback<'ctx, 'a>(
let pydict = PyDict::new(py);
pydict.set_item("obj", val)?;
host_attributes.append(pydict)?;
values.push((ty, inner_resolver.get_obj_value(py, val, ctx, generator)?.unwrap()));
values.push((ty, inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap()));
}
},
_ => {}

View File

@ -65,6 +65,7 @@ pub struct PrimitivePythonId {
uint32: u64,
uint64: u64,
float: u64,
float64: u64,
bool: u64,
list: u64,
tuple: u64,
@ -328,8 +329,9 @@ impl Nac3 {
ret: primitive.none,
vars: HashMap::new(),
},
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator).unwrap();
Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
time_fns.emit_at_mu(ctx, arg);
Ok(None)
}))),
@ -345,8 +347,9 @@ impl Nac3 {
ret: primitive.none,
vars: HashMap::new(),
},
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator).unwrap();
Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
time_fns.emit_delay_mu(ctx, arg);
Ok(None)
}))),
@ -395,6 +398,7 @@ impl Nac3 {
uint64: get_attr_id(numpy_mod, "uint64"),
bool: get_attr_id(builtins_mod, "bool"),
float: get_attr_id(builtins_mod, "float"),
float64: get_attr_id(numpy_mod, "float64"),
list: get_attr_id(builtins_mod, "list"),
tuple: get_attr_id(builtins_mod, "tuple"),
exception: get_attr_id(builtins_mod, "Exception"),

View File

@ -131,6 +131,7 @@ impl StaticValue for PythonValue {
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> Result<BasicValueEnum<'ctx>, String> {
if let Some(val) = self.resolver.id_to_primitive.read().get(&self.id) {
return Ok(match val {
@ -150,7 +151,7 @@ impl StaticValue for PythonValue {
Python::with_gil(|py| -> PyResult<BasicValueEnum<'ctx>> {
self.resolver
.get_obj_value(py, self.value.as_ref(py), ctx, generator)
.get_obj_value(py, self.value.as_ref(py), ctx, generator, expected_ty)
.map(Option::unwrap)
}).map_err(|e| e.to_string())
}
@ -169,6 +170,16 @@ impl StaticValue for PythonValue {
let helper = &self.resolver.helper;
let ty = helper.type_fn.call1(py, (&self.value,))?;
let ty_id: u64 = helper.id_fn.call1(py, (ty,))?.extract(py)?;
// for optimizing unwrap KernelInvariant
if ty_id == self.resolver.primitive_ids.option && name == "_nac3_option".into() {
let obj = self.value.getattr(py, &name.to_string())?;
let id = self.resolver.helper.id_fn.call1(py, (&obj,))?.extract(py)?;
if self.id == self.resolver.primitive_ids.none {
return Ok(None)
} else {
return Ok(Some((id, obj)))
}
}
let def_id = { *self.resolver.pyid_to_def.read().get(&ty_id).unwrap() };
let mut mutable = true;
let defs = ctx.top_level.definitions.read();
@ -201,6 +212,28 @@ impl StaticValue for PythonValue {
}))
})
}
fn get_tuple_element<'ctx>(&self, index: u32) -> Option<ValueEnum<'ctx>> {
Python::with_gil(|py| -> PyResult<Option<(u64, PyObject)>> {
let helper = &self.resolver.helper;
let ty = helper.type_fn.call1(py, (&self.value,))?;
let ty_id: u64 = helper.id_fn.call1(py, (ty,))?.extract(py)?;
assert_eq!(ty_id, self.resolver.primitive_ids.tuple);
let tup: &PyTuple = self.value.extract(py)?;
let elem = tup.get_item(index as usize);
let id = self.resolver.helper.id_fn.call1(py, (elem,))?.extract(py)?;
Ok(Some((id, elem.into())))
})
.unwrap()
.map(|(id, obj)| {
ValueEnum::Static(Arc::new(PythonValue {
id,
value: obj,
store_obj: self.store_obj.clone(),
resolver: self.resolver.clone(),
}))
})
}
}
impl InnerResolver {
@ -269,6 +302,8 @@ impl InnerResolver {
Ok(Ok((primitives.bool, true)))
} else if ty_id == self.primitive_ids.float {
Ok(Ok((primitives.float, true)))
} else if ty_id == self.primitive_ids.float64 {
Ok(Ok((primitives.float, true)))
} else if ty_id == self.primitive_ids.exception {
Ok(Ok((primitives.exception, true)))
} else if ty_id == self.primitive_ids.list {
@ -544,7 +579,7 @@ impl InnerResolver {
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
if len == 0 {
assert!(matches!(
&*unifier.get_ty(extracted_ty),
&*unifier.get_ty(*ty),
TypeEnum::TVar { fields: None, range, .. }
if range.is_empty()
));
@ -692,6 +727,7 @@ impl InnerResolver {
obj: &PyAny,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> PyResult<Option<BasicValueEnum<'ctx>>> {
let ty_id: u64 =
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
@ -721,7 +757,7 @@ impl InnerResolver {
format!("{} is not in the range of bool", obj)))?;
self.id_to_primitive.write().insert(id, PrimitiveValue::Bool(val));
Ok(Some(ctx.ctx.bool_type().const_int(val as u64, false).into()))
} else if ty_id == self.primitive_ids.float {
} else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 {
let val: f64 = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of float64", obj)))?;
self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val));
@ -734,20 +770,14 @@ impl InnerResolver {
}
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
let ty = if len == 0 {
ctx.primitives.int32
let elem_ty =
if let TypeEnum::TList { ty } = ctx.unifier.get_ty_immutable(expected_ty).as_ref()
{
*ty
} else {
self.get_list_elem_type(
py,
obj,
len,
&mut ctx.unifier,
&ctx.top_level.definitions.read(),
&ctx.primitives,
)?
.unwrap()
unreachable!("must be list")
};
let ty = ctx.get_llvm_type(generator, ty);
let ty = ctx.get_llvm_type(generator, elem_ty);
let size_t = generator.get_size_type(ctx.ctx);
let arr_ty = ctx
.ctx
@ -766,8 +796,13 @@ impl InnerResolver {
let arr: Result<Option<Vec<_>>, _> = (0..len)
.map(|i| {
obj.get_item(i).and_then(|elem| self.get_obj_value(py, elem, ctx, generator).map_err(
|e| super::CompileError::new_err(format!("Error getting element {}: {}", i, e))))
obj
.get_item(i)
.and_then(|elem| self.get_obj_value(py, elem, ctx, generator, elem_ty)
.map_err(
|e| super::CompileError::new_err(
format!("Error getting element {}: {}", i, e))
))
})
.collect();
let arr = arr?.unwrap();
@ -808,21 +843,48 @@ impl InnerResolver {
Ok(Some(global.as_pointer_value().into()))
} else if ty_id == self.primitive_ids.tuple {
if let TypeEnum::TTuple { ty } = ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
let tup_tys = ty.iter();
let elements: &PyTuple = obj.cast_as()?;
assert_eq!(elements.len(), tup_tys.len());
let val: Result<Option<Vec<_>>, _> =
elements.iter().enumerate().map(|(i, elem)| self.get_obj_value(py, elem, ctx, generator).map_err(|e|
super::CompileError::new_err(format!("Error getting element {}: {}", i, e)))).collect();
elements
.iter()
.enumerate()
.zip(tup_tys)
.map(|((i, elem), ty)| self
.get_obj_value(py, elem, ctx, generator, *ty).map_err(|e|
super::CompileError::new_err(
format!("Error getting element {}: {}", i, e)
)
)
).collect();
let val = val?.unwrap();
let val = ctx.ctx.const_struct(&val, false);
Ok(Some(val.into()))
} else {
unreachable!("must expect tuple type")
}
} else if ty_id == self.primitive_ids.option {
let option_val_ty = match ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == ctx.primitives.option.get_obj_id(&ctx.unifier) =>
{
*params.iter().next().unwrap().1
}
_ => unreachable!("must be option type")
};
if id == self.primitive_ids.none {
// for option type, just a null ptr, whose type needs to be casted in codegen
// according to the type info attached in the ast
Ok(Some(ctx.ctx.i8_type().ptr_type(AddressSpace::Generic).const_null().into()))
// for option type, just a null ptr
Ok(Some(
ctx.get_llvm_type(generator, option_val_ty)
.ptr_type(AddressSpace::Generic)
.const_null()
.into(),
))
} else {
match self
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator)
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator, option_val_ty)
.map_err(|e| {
super::CompileError::new_err(format!(
"Error getting value of Option object: {}",
@ -881,23 +943,8 @@ impl InnerResolver {
let values: Result<Option<Vec<_>>, _> = fields
.iter()
.map(|(name, ty, _)| {
let v = self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator)
.map_err(|e| super::CompileError::new_err(format!("Error getting field {}: {}", name, e)));
match (v, ctx.unifier.get_ty_immutable(*ty).as_ref()) {
(Ok(Some(v)), TypeEnum::TObj { obj_id, params, .. })
if *obj_id == ctx.primitives.option.get_obj_id(&ctx.unifier) =>
{
let actual_ptr_ty = ctx
.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::Generic);
Ok(Some(ctx.builder.build_bitcast(
v,
actual_ptr_ty,
"option_none_ptr_cast",
)))
}
(v, _) => v,
}
self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator, *ty)
.map_err(|e| super::CompileError::new_err(format!("Error getting field {}: {}", name, e)))
})
.collect();
let values = values?;
@ -940,7 +987,7 @@ impl InnerResolver {
} else if ty_id == self.primitive_ids.bool {
let val: bool = obj.extract()?;
Ok(SymbolValue::Bool(val))
} else if ty_id == self.primitive_ids.float {
} else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 {
let val: f64 = obj.extract()?;
Ok(SymbolValue::Double(val))
} else if ty_id == self.primitive_ids.tuple {

View File

@ -150,11 +150,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
};
let actual_ptr_type =
self.get_llvm_type(generator, ty).ptr_type(AddressSpace::Generic);
self.builder.build_bitcast(
self.ctx.i8_type().ptr_type(AddressSpace::Generic).const_null(),
actual_ptr_type,
"default_opt_none",
)
actual_ptr_type.const_null().into()
}
}
}
@ -643,14 +639,14 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
}
// reorder the parameters
let mut real_params =
fun.0.args.iter().map(|arg| mapping.remove(&arg.name).unwrap()).collect_vec();
fun.0.args.iter().map(|arg| (mapping.remove(&arg.name).unwrap(), arg.ty)).collect_vec();
if let Some(obj) = &obj {
real_params.insert(0, obj.1.clone());
real_params.insert(0, (obj.1.clone(), obj.0));
}
let static_params = real_params
.iter()
.enumerate()
.filter_map(|(i, v)| {
.filter_map(|(i, (v, _))| {
if let ValueEnum::Static(s) = v {
Some((i, s.clone()))
} else {
@ -682,7 +678,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
};
param_vals = real_params
.into_iter()
.map(|p| p.to_basic_value_enum(ctx, generator))
.map(|(p, t)| p.to_basic_value_enum(ctx, generator, t))
.collect::<Result<Vec<_>, String>>()?;
instance_to_symbol.get(&key).cloned().ok_or_else(|| "".into())
}
@ -795,7 +791,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
let Comprehension { target, iter, ifs, .. } = &generators[0];
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator)?;
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator, iter.custom.unwrap())?;
let int32 = ctx.ctx.i32_type();
let size_t = generator.get_size_type(ctx.ctx);
let zero_size_t = size_t.const_zero();
@ -900,7 +896,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
let result = generator
.gen_expr(ctx, cond)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, cond.custom.unwrap())?
.into_int_value();
let succ = ctx.ctx.append_basic_block(current, "then");
ctx.builder.build_conditional_branch(result, succ, test_bb);
@ -909,7 +905,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
let elem = generator.gen_expr(ctx, elt)?.unwrap();
let i = ctx.builder.build_load(index, "i").into_int_value();
let elem_ptr = unsafe { ctx.builder.build_gep(list_content, &[i], "elem_ptr") };
let val = elem.to_basic_value_enum(ctx, generator)?;
let val = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
ctx.builder.build_store(elem_ptr, val);
ctx.builder
.build_store(index, ctx.builder.build_int_add(i, size_t.const_int(1, false), "inc"));
@ -934,8 +930,8 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
) -> Result<ValueEnum<'ctx>, String> {
let ty1 = ctx.unifier.get_representative(left.custom.unwrap());
let ty2 = ctx.unifier.get_representative(right.custom.unwrap());
let left = generator.gen_expr(ctx, left)?.unwrap().to_basic_value_enum(ctx, generator)?;
let right = generator.gen_expr(ctx, right)?.unwrap().to_basic_value_enum(ctx, generator)?;
let left = generator.gen_expr(ctx, left)?.unwrap().to_basic_value_enum(ctx, generator, left.custom.unwrap())?;
let right = generator.gen_expr(ctx, right)?.unwrap().to_basic_value_enum(ctx, generator, right.custom.unwrap())?;
// we can directly compare the types, because we've got their representatives
// which would be unchanged until further unification, which we would never do
@ -999,25 +995,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),
None => {
let resolver = ctx.resolver.clone();
let val = resolver.get_symbol_value(*id, ctx).unwrap();
// if is option, need to cast pointer to handle None
match &*ctx.unifier.get_ty(expr.custom.unwrap()) {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == ctx.primitives.option.get_obj_id(&ctx.unifier) =>
{
if let BasicValueEnum::PointerValue(ptr) = val.to_basic_value_enum(ctx, generator)? {
let actual_ptr_ty = ctx.get_llvm_type(
generator,
*params.iter().next().unwrap().1,
)
.ptr_type(AddressSpace::Generic);
ctx.builder.build_bitcast(ptr, actual_ptr_ty, "option_ptr_cast").into()
} else {
unreachable!("option obj must be ptr")
}
}
_ => val,
}
resolver.get_symbol_value(*id, ctx).unwrap()
}
},
ExprKind::List { elts, .. } => {
@ -1028,7 +1006,10 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.map(|x| {
generator
.gen_expr(ctx, x)
.map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator))
.map_or_else(
Err,
|v| v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap())
)
})
.collect::<Result<Vec<_>, _>>()?;
let ty = if elements.is_empty() {
@ -1061,7 +1042,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.map(|x| {
generator
.gen_expr(ctx, x)
.map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator))
.map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap()))
})
.collect::<Result<Vec<_>, _>>()?;
let element_ty = element_val.iter().map(BasicValueEnum::get_type).collect_vec();
@ -1083,7 +1064,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
// note that we would handle class methods directly in calls
match generator.gen_expr(ctx, value)?.unwrap() {
ValueEnum::Static(v) => v.get_field(*attr, ctx).map_or_else(|| {
let v = v.to_basic_value_enum(ctx, generator)?;
let v = v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?;
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
Ok(ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(),
@ -1104,7 +1085,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let left = generator
.gen_expr(ctx, &values[0])?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, values[0].custom.unwrap())?
.into_int_value();
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let a_bb = ctx.ctx.append_basic_block(current, "a");
@ -1120,7 +1101,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let b = generator
.gen_expr(ctx, &values[1])?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, values[1].custom.unwrap())?
.into_int_value();
ctx.builder.build_unconditional_branch(cont_bb);
(a, b)
@ -1130,7 +1111,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let a = generator
.gen_expr(ctx, &values[1])?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, values[1].custom.unwrap())?
.into_int_value();
ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(b_bb);
@ -1148,7 +1129,9 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
ExprKind::UnaryOp { op, operand } => {
let ty = ctx.unifier.get_representative(operand.custom.unwrap());
let val =
generator.gen_expr(ctx, operand)?.unwrap().to_basic_value_enum(ctx, generator)?;
generator.gen_expr(ctx, operand)?
.unwrap()
.to_basic_value_enum(ctx, generator, operand.custom.unwrap())?;
if ty == ctx.primitives.bool {
let val = val.into_int_value();
match op {
@ -1208,11 +1191,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
generator
.gen_expr(ctx, lhs)?
.unwrap()
.to_basic_value_enum(ctx, generator)?,
.to_basic_value_enum(ctx, generator, lhs.custom.unwrap())?,
generator
.gen_expr(ctx, rhs)?
.unwrap()
.to_basic_value_enum(ctx, generator)?,
.to_basic_value_enum(ctx, generator, rhs.custom.unwrap())?,
) {
(lhs, rhs)
} else {
@ -1236,11 +1219,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
generator
.gen_expr(ctx, lhs)?
.unwrap()
.to_basic_value_enum(ctx, generator)?,
.to_basic_value_enum(ctx, generator, lhs.custom.unwrap())?,
generator
.gen_expr(ctx, rhs)?
.unwrap()
.to_basic_value_enum(ctx, generator)?,
.to_basic_value_enum(ctx, generator, rhs.custom.unwrap())?,
) {
(lhs, rhs)
} else {
@ -1268,7 +1251,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let test = generator
.gen_expr(ctx, test)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, test.custom.unwrap())?
.into_int_value();
let body_ty = body.custom.unwrap();
let is_none = ctx.unifier.get_representative(body_ty) == ctx.primitives.none;
@ -1288,7 +1271,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
match result {
None => None,
Some(v) => {
let a = a.unwrap().to_basic_value_enum(ctx, generator)?;
let a = a.unwrap().to_basic_value_enum(ctx, generator, body.custom.unwrap())?;
Some(ctx.builder.build_store(v, a))
}
};
@ -1298,7 +1281,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
match result {
None => None,
Some(v) => {
let b = b.unwrap().to_basic_value_enum(ctx, generator)?;
let b = b.unwrap().to_basic_value_enum(ctx, generator, orelse.custom.unwrap())?;
Some(ctx.builder.build_store(v, b))
}
};
@ -1371,11 +1354,51 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
}
};
// directly generate code for option.unwrap
// since it needs location information from ast
// since it needs to return static value to optimize for kernel invariant
if attr == &"unwrap".into()
&& id == ctx.primitives.option.get_obj_id(&ctx.unifier)
{
if let BasicValueEnum::PointerValue(ptr) = val.to_basic_value_enum(ctx, generator)? {
match val {
ValueEnum::Static(v) => match v.get_field("_nac3_option".into(), ctx) {
// if is none, raise exception directly
None => {
let err_msg = ctx.gen_string(generator, "");
let current_fun = ctx
.builder
.get_insert_block()
.unwrap()
.get_parent()
.unwrap();
let unreachable_block = ctx.ctx.append_basic_block(
current_fun,
"unwrap_none_unreachable"
);
let exn_block = ctx.ctx.append_basic_block(
current_fun,
"unwrap_none_exception"
);
ctx.builder.build_unconditional_branch(exn_block);
ctx.builder.position_at_end(exn_block);
ctx.raise_exn(
generator,
"0:UnwrapNoneError",
err_msg,
[None, None, None],
ctx.current_loc
);
ctx.builder.position_at_end(unreachable_block);
let ptr = ctx
.get_llvm_type(generator, value.custom.unwrap())
.into_pointer_type()
.const_null();
return Ok(Some(ctx.builder.build_load(
ptr,
"unwrap_none_unreachable_load"
).into()));
}
Some(v) => return Ok(Some(v)),
}
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
let not_null = ctx.builder.build_is_not_null(ptr, "unwrap_not_null");
ctx.make_assert(
generator,
@ -1385,9 +1408,12 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
[None, None, None],
expr.location,
);
return Ok(Some(ctx.builder.build_load(ptr, "unwrap_some").into()))
} else {
unreachable!("option must be ptr")
return Ok(Some(ctx.builder.build_load(
ptr,
"unwrap_some_load"
).into()))
}
_ => unreachable!("option must be static or ptr")
}
}
return Ok(generator
@ -1407,7 +1433,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let v = generator
.gen_expr(ctx, value)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_pointer_value();
let ty = ctx.get_llvm_type(generator, *ty);
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero]).into_pointer_value();
@ -1454,7 +1480,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let raw_index = generator
.gen_expr(ctx, slice)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
.into_int_value();
let raw_index = ctx.builder.build_int_s_extend(
raw_index,
@ -1489,26 +1515,39 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
[Some(raw_index), Some(len), None],
expr.location,
);
ctx.build_gep_and_load(arr_ptr, &[index])
ctx.build_gep_and_load(arr_ptr, &[index]).into()
}
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
let v = generator
.gen_expr(ctx, value)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.into_struct_value();
let index: u32 =
if let ExprKind::Constant { value: ast::Constant::Int(v), .. } = &slice.node {
(*v).try_into().unwrap()
} else {
unreachable!("tuple subscript must be const int after type check");
};
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap()
let v = generator
.gen_expr(ctx, value)?
.unwrap();
match v {
ValueEnum::Dynamic(v) => {
let v = v.into_struct_value();
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap().into()
}
ValueEnum::Static(v) => {
match v.get_tuple_element(index) {
Some(v) => v,
None => {
let tup = v
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_struct_value();
ctx.builder.build_extract_value(tup, index, "tup_elem").unwrap().into()
}
}
}
}
} else {
unreachable!("should not be other subscriptable types after type check");
}
}
.into(),
},
ExprKind::ListComp { .. } => gen_comprehension(generator, ctx, expr)?.into(),
_ => unimplemented!(),
}))

View File

@ -36,7 +36,7 @@ pub trait CodeGenerator {
}
/// Generate object constructor and returns the constructed object.
/// - signature: Function signature of the contructor.
/// - signature: Function signature of the constructor.
/// - def: Class definition for the constructor class.
/// - params: Function parameters.
fn gen_constructor<'ctx, 'a>(

View File

@ -183,7 +183,7 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
let step = generator
.gen_expr(ctx, step)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, ctx.primitives.int32)?
.into_int_value();
// assert step != 0, throw exception if not
let not_zero = ctx.builder.build_int_compare(
@ -261,7 +261,7 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
ctx.module.add_function(SYMBOL, fn_t, None)
});
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator)?;
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator, i.custom.unwrap())?;
Ok(ctx
.builder
.build_call(func, &[i.into(), length.into()], "bounded_ind")

View File

@ -54,7 +54,11 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
}
ExprKind::Attribute { value, attr, .. } => {
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
let val = generator.gen_expr(ctx, value)?.unwrap().to_basic_value_enum(ctx, generator)?;
let val = generator.gen_expr(ctx, value)?.unwrap().to_basic_value_enum(
ctx,
generator,
value.custom.unwrap(),
)?;
let ptr = if let BasicValueEnum::PointerValue(v) = val {
v
} else {
@ -72,17 +76,58 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
}
}
ExprKind::Subscript { value, slice, .. } => {
assert!(matches!(
ctx.unifier.get_ty_immutable(value.custom.unwrap()).as_ref(),
TypeEnum::TList { .. },
));
let i32_type = ctx.ctx.i32_type();
let zero = i32_type.const_zero();
let v = generator
.gen_expr(ctx, value)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_pointer_value();
let index = generator
let len = ctx
.build_gep_and_load(v, &[zero, i32_type.const_int(1, false)])
.into_int_value();
let raw_index = generator
.gen_expr(ctx, slice)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
.into_int_value();
let raw_index = ctx.builder.build_int_s_extend(
raw_index,
generator.get_size_type(ctx.ctx),
"sext",
);
// handle negative index
let is_negative = ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
raw_index,
generator.get_size_type(ctx.ctx).const_zero(),
"is_neg",
);
let adjusted = ctx.builder.build_int_add(raw_index, len, "adjusted");
let index = ctx
.builder
.build_select(is_negative, adjusted, raw_index, "index")
.into_int_value();
// unsigned less than is enough, because negative index after adjustment is
// bigger than the length (for unsigned cmp)
let bound_check = ctx.builder.build_int_compare(
inkwell::IntPredicate::ULT,
index,
len,
"inbound",
);
ctx.make_assert(
generator,
bound_check,
"0:IndexError",
"index {0} out of bounds 0:{1}",
[Some(raw_index), Some(len), None],
slice.location,
);
unsafe {
let arr_ptr = ctx
.build_gep_and_load(v, &[i32_type.const_zero(), i32_type.const_zero()])
@ -102,7 +147,9 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
) -> Result<(), String> {
match &target.node {
ExprKind::Tuple { elts, .. } => {
if let BasicValueEnum::StructValue(v) = value.to_basic_value_enum(ctx, generator)? {
if let BasicValueEnum::StructValue(v) =
value.to_basic_value_enum(ctx, generator, target.custom.unwrap())?
{
for (i, elt) in elts.iter().enumerate() {
let v = ctx
.builder
@ -121,11 +168,13 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
let ls = generator
.gen_expr(ctx, ls)?
.unwrap()
.to_basic_value_enum(ctx, generator)?
.to_basic_value_enum(ctx, generator, ls.custom.unwrap())?
.into_pointer_value();
let (start, end, step) =
handle_slice_indices(lower, upper, step, ctx, generator, ls)?;
let value = value.to_basic_value_enum(ctx, generator)?.into_pointer_value();
let value = value
.to_basic_value_enum(ctx, generator, target.custom.unwrap())?
.into_pointer_value();
let ty =
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(target.custom.unwrap()) {
ctx.get_llvm_type(generator, *ty)
@ -133,15 +182,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
unreachable!()
};
let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value)?;
list_slice_assignment(
generator,
ctx,
ty,
ls,
(start, end, step),
value,
src_ind,
)
list_slice_assignment(generator, ctx, ty, ls, (start, end, step), value, src_ind)
} else {
unreachable!()
}
@ -155,7 +196,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
*static_value = Some(s.clone());
}
}
let val = value.to_basic_value_enum(ctx, generator)?;
let val = value.to_basic_value_enum(ctx, generator, target.custom.unwrap())?;
ctx.builder.build_store(ptr, val);
}
};
@ -185,7 +226,11 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
// store loop bb information and restore it later
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator)?;
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(
ctx,
generator,
iter.custom.unwrap(),
)?;
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
// setup
let iter_val = iter_val.into_pointer_value();
@ -298,7 +343,11 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
ctx.builder.build_unconditional_branch(test_bb);
ctx.builder.position_at_end(test_bb);
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
ctx,
generator,
test.custom.unwrap(),
)?;
if let BasicValueEnum::IntValue(test) = test {
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
} else {
@ -359,7 +408,11 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
};
ctx.builder.build_unconditional_branch(test_bb);
ctx.builder.position_at_end(test_bb);
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
ctx,
generator,
test.custom.unwrap(),
)?;
if let BasicValueEnum::IntValue(test) = test {
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
} else {
@ -456,7 +509,7 @@ pub fn exn_constructor<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let (zelf_ty, zelf) = obj.unwrap();
let zelf = zelf.to_basic_value_enum(ctx, generator)?.into_pointer_value();
let zelf = zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value();
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let zelf_id = {
@ -479,14 +532,14 @@ pub fn exn_constructor<'ctx, 'a>(
let ptr =
ctx.builder.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg");
let msg = if !args.is_empty() {
args.remove(0).1.to_basic_value_enum(ctx, generator)?
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?
} else {
empty_string
};
ctx.builder.build_store(ptr, msg);
for i in [6, 7, 8].iter() {
let value = if !args.is_empty() {
args.remove(0).1.to_basic_value_enum(ctx, generator)?
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?
} else {
ctx.ctx.i64_type().const_zero().into()
};
@ -908,7 +961,11 @@ pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
) -> Result<(), String> {
let value = value
.as_ref()
.map(|v| generator.gen_expr(ctx, v).and_then(|v| v.unwrap().to_basic_value_enum(ctx, generator)))
.map(|v_expr| {
generator.gen_expr(ctx, v_expr).and_then(|v| {
v.unwrap().to_basic_value_enum(ctx, generator, v_expr.custom.unwrap())
})
})
.transpose()?;
if let Some(return_target) = ctx.return_target {
if let Some(value) = value {
@ -969,20 +1026,28 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
StmtKind::Try { .. } => gen_try(generator, ctx, stmt)?,
StmtKind::Raise { exc, .. } => {
if let Some(exc) = exc {
let exc =
generator.gen_expr(ctx, exc)?.unwrap().to_basic_value_enum(ctx, generator)?;
let exc = generator.gen_expr(ctx, exc)?.unwrap().to_basic_value_enum(
ctx,
generator,
exc.custom.unwrap(),
)?;
gen_raise(generator, ctx, Some(&exc), stmt.location);
} else {
gen_raise(generator, ctx, None, stmt.location);
}
}
StmtKind::Assert { test, msg, .. } => {
let test =
generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
ctx,
generator,
test.custom.unwrap(),
)?;
let err_msg = match msg {
Some(msg) => {
generator.gen_expr(ctx, msg)?.unwrap().to_basic_value_enum(ctx, generator)?
}
Some(msg) => generator.gen_expr(ctx, msg)?.unwrap().to_basic_value_enum(
ctx,
generator,
msg.custom.unwrap(),
)?,
None => ctx.gen_string(generator, ""),
};
ctx.make_assert_impl(

View File

@ -71,6 +71,7 @@ pub trait StaticValue {
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> Result<BasicValueEnum<'ctx>, String>;
fn get_field<'ctx, 'a>(
@ -78,6 +79,8 @@ pub trait StaticValue {
name: StrRef,
ctx: &mut CodeGenContext<'ctx, 'a>,
) -> Option<ValueEnum<'ctx>>;
fn get_tuple_element<'ctx>(&self, index: u32) -> Option<ValueEnum<'ctx>>;
}
#[derive(Clone)]
@ -121,9 +124,10 @@ impl<'ctx> ValueEnum<'ctx> {
self,
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> Result<BasicValueEnum<'ctx>, String> {
match self {
ValueEnum::Static(v) => v.to_basic_value_enum(ctx, generator),
ValueEnum::Static(v) => v.to_basic_value_enum(ctx, generator, expected_ty),
ValueEnum::Dynamic(v) => Ok(v),
}
}
@ -363,7 +367,7 @@ impl dyn SymbolResolver + Send + Sync {
unreachable!("expected class definition")
}
},
&mut |id| format!("var{}", id),
&mut |id| format!("typevar{}", id),
&mut None,
)
}

View File

@ -224,7 +224,12 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(ctx, generator)?;
let expect_ty = obj.clone().unwrap().0;
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
ctx,
generator,
expect_ty,
)?;
if let BasicValueEnum::PointerValue(ptr) = obj_val {
Ok(Some(ctx.builder.build_is_not_null(ptr, "is_some").into()))
} else {
@ -244,7 +249,12 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(ctx, generator)?;
let expect_ty = obj.clone().unwrap().0;
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
ctx,
generator,
expect_ty,
)?;
if let BasicValueEnum::PointerValue(ptr) = obj_val {
Ok(Some(ctx.builder.build_is_null(ptr, "is_none").into()))
} else {
@ -290,7 +300,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(if ctx.unifier.unioned(arg_ty, boolean) {
Some(
ctx.builder
@ -355,7 +365,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(
if ctx.unifier.unioned(arg_ty, boolean)
|| ctx.unifier.unioned(arg_ty, uint32)
@ -422,7 +432,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let res = if ctx.unifier.unioned(arg_ty, boolean) {
ctx.builder
.build_int_z_extend(arg.into_int_value(), ctx.ctx.i64_type(), "zext")
@ -474,7 +484,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let res = if ctx.unifier.unioned(arg_ty, int32)
|| ctx.unifier.unioned(arg_ty, uint32)
|| ctx.unifier.unioned(arg_ty, boolean)
@ -521,7 +531,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let boolean = ctx.primitives.bool;
let float = ctx.primitives.float;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(
if ctx.unifier.unioned(arg_ty, boolean)
|| ctx.unifier.unioned(arg_ty, int32)
@ -557,7 +567,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let round_intrinsic =
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -597,7 +607,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let round_intrinsic =
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -655,19 +665,20 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let mut step = None;
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let ty_i32 = ctx.primitives.int32;
for (i, arg) in args.iter().enumerate() {
if arg.0 == Some("start".into()) {
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if arg.0 == Some("stop".into()) {
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if arg.0 == Some("step".into()) {
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 0 {
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 1 {
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
} else if i == 2 {
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
}
}
let step = match step {
@ -734,8 +745,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
Ok(Some(args[0].1.clone().to_basic_value_enum(ctx, generator)?))
|ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
Ok(Some(args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?))
},
)))),
loc: None,
@ -759,7 +771,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let float = ctx.primitives.float;
let boolean = ctx.primitives.bool;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(if ctx.unifier.unioned(arg_ty, boolean) {
Some(arg)
} else if ctx.unifier.unioned(arg_ty, int32) {
@ -817,7 +829,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let floor_intrinsic =
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -857,7 +869,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let floor_intrinsic =
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -897,7 +909,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let ceil_intrinsic =
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -937,7 +949,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _, args, generator| {
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
let ceil_intrinsic =
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
let float = ctx.ctx.f64_type();
@ -989,7 +1001,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|ctx, _, fun, args, generator| {
let range_ty = ctx.primitives.range;
let arg_ty = fun.0.args[0].ty;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
let arg = arg.into_pointer_value();
let (start, end, step) = destructure_range(ctx, arg);
@ -1043,8 +1055,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let m_ty = fun.0.args[0].ty;
let n_ty = fun.0.args[1].ty;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator)?;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) {
("llvm.umin.i1", llvm_i1)
@ -1105,8 +1117,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let m_ty = fun.0.args[0].ty;
let n_ty = fun.0.args[1].ty;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator)?;
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?;
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) {
("llvm.umax.i1", llvm_i1)
@ -1163,7 +1175,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let llvm_i64 = ctx.ctx.i64_type().as_basic_type_enum();
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
let n_ty = fun.0.args[0].ty;
let n_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let n_val = args[0].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
let mut is_float = false;
let (fun_name, arg_ty) =
@ -1220,8 +1232,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _fun, args, generator| {
let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|ctx, _, fun, args, generator| {
let arg_ty = fun.0.args[0].ty;
let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
let alloca = ctx.builder.build_alloca(arg_val.get_type(), "alloca_some");
ctx.builder.build_store(alloca, arg_val);
Ok(Some(alloca.into()))

View File

@ -434,11 +434,11 @@ impl TopLevelComposer {
// check if all are unique type vars
let all_unique_type_var = {
let mut occured_type_var_id: HashSet<u32> = HashSet::new();
let mut occurred_type_var_id: HashSet<u32> = HashSet::new();
type_vars.iter().all(|x| {
let ty = unifier.get_ty(*x);
if let TypeEnum::TVar { id, .. } = ty.as_ref() {
occured_type_var_id.insert(*id)
occurred_type_var_id.insert(*id)
} else {
false
}
@ -536,7 +536,7 @@ impl TopLevelComposer {
}
has_base = true;
// the function parse_ast_to make sure that no type var occured in
// the function parse_ast_to make sure that no type var occurred in
// bast_ty if it is a CustomClassKind
let base_ty = parse_ast_to_type_annotation_kinds(
class_resolver,
@ -696,7 +696,7 @@ impl TopLevelComposer {
return Err(errors.into_iter().sorted().join("\n----------\n"));
}
// handle the inheritanced methods and fields
// handle the inherited methods and fields
// Note: we cannot defer error handling til the end of the loop, because there is loop
// carried dependency, ignoring the error (temporarily) will cause all assumptions to break
// and produce weird error messages
@ -825,9 +825,9 @@ impl TopLevelComposer {
let mut function_var_map: HashMap<u32, Type> = HashMap::new();
let arg_types = {
// make sure no duplicate parameter
let mut defined_paramter_name: HashSet<_> = HashSet::new();
let mut defined_parameter_name: HashSet<_> = HashSet::new();
for x in args.args.iter() {
if !defined_paramter_name.insert(x.node.arg)
if !defined_parameter_name.insert(x.node.arg)
|| keyword_list.contains(&x.node.arg)
{
return Err(format!(
@ -1074,10 +1074,10 @@ impl TopLevelComposer {
let arg_types: Vec<FuncArg> = {
// check method parameters cannot have same name
let mut defined_paramter_name: HashSet<_> = HashSet::new();
let mut defined_parameter_name: HashSet<_> = HashSet::new();
let zelf: StrRef = "self".into();
for x in args.args.iter() {
if !defined_paramter_name.insert(x.node.arg)
if !defined_parameter_name.insert(x.node.arg)
|| (keyword_list.contains(&x.node.arg) && x.node.arg != zelf)
{
return Err(format!(
@ -1088,13 +1088,13 @@ impl TopLevelComposer {
}
}
if name == &"__init__".into() && !defined_paramter_name.contains(&zelf) {
if name == &"__init__".into() && !defined_parameter_name.contains(&zelf) {
return Err(format!(
"__init__ method must have a `self` parameter (at {})",
b.location
));
}
if !defined_paramter_name.contains(&zelf) {
if !defined_parameter_name.contains(&zelf) {
return Err(format!(
"class method must have a `self` parameter (at {})",
b.location
@ -1227,7 +1227,7 @@ impl TopLevelComposer {
dummy_return_type
} else {
// if do not have return annotation, return none
// for uniform handling, still use type annoatation
// for uniform handling, still use type annotation
let dummy_return_type = unifier.get_dummy_var().0;
type_var_to_concrete_def.insert(
dummy_return_type,
@ -1468,7 +1468,7 @@ impl TopLevelComposer {
Ok(())
}
/// step 5, analyze and call type inferecer to fill the `instance_to_stmt` of topleveldef::function
/// step 5, analyze and call type inferencer to fill the `instance_to_stmt` of topleveldef::function
fn analyze_function_instance(&mut self) -> Result<(), String> {
// first get the class contructor type correct for the following type check in function body
// also do class field instantiation check
@ -1906,7 +1906,7 @@ impl TopLevelComposer {
unreachable!("must be class id here")
}
},
&mut |id| format!("tvar{}", id),
&mut |id| format!("typevar{}", id),
&mut None,
);
return Err(format!(

View File

@ -149,7 +149,7 @@ impl TopLevelComposer {
}
/// already include the definition_id of itself inside the ancestors vector
/// when first regitering, the type_vars, fields, methods, ancestors are invalid
/// when first registering, the type_vars, fields, methods, ancestors are invalid
pub fn make_top_level_class_def(
index: usize,
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,

View File

@ -9,7 +9,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[var7]\", \"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: [\"var7\"]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B[typevar7]\", \"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: [\"typevar7\"]\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",

View File

@ -5,7 +5,7 @@ expression: res_vec
---
[
"Class {\nname: \"A\",\nancestors: [\"A[var6, var7]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"var6\", \"var7\"]\n}\n",
"Class {\nname: \"A\",\nancestors: [\"A[typevar6, typevar7]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"typevar6\", \"typevar7\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[bool, float], b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[bool, float]], 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[bool, float]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\")],\ntype_vars: []\n}\n",

View File

@ -763,7 +763,7 @@ fn make_internal_resolver_with_tvar(
(name, {
let (ty, id) = unifier.get_fresh_var_with_range(range.as_slice(), None, None);
if print {
println!("{}: {:?}, tvar{}", name, ty, id);
println!("{}: {:?}, typevar{}", name, ty, id);
}
ty
})
@ -791,7 +791,7 @@ impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
self.unifier.internal_stringify(
ty,
&mut |id| format!("class{}", id.to_string()),
&mut |id| format!("tvar{}", id.to_string()),
&mut |id| format!("typevar{}", id.to_string()),
&mut None,
)
} else {

View File

@ -356,7 +356,7 @@ pub fn get_type_from_type_annotation_kinds(
unifier.internal_stringify(
p,
&mut |id| format!("class{}", id),
&mut |id| format!("tvar{}", id),
&mut |id| format!("typevar{}", id),
&mut None
),
*id
@ -436,7 +436,7 @@ pub fn get_type_from_type_annotation_kinds(
/// the type of `self` should be similar to `A[T, V]`, where `T`, `V`
/// considered to be type variables associated with the class \
/// \
/// But note that here we do not make a duplication of `T`, `V`, we direclty
/// But note that here we do not make a duplication of `T`, `V`, we directly
/// use them as they are in the TopLevelDef::Class since those in the
/// TopLevelDef::Class.type_vars will be substitute later when seeing applications/instantiations
/// the Type of their fields and methods will also be subst when application/instantiation

View File

@ -16,7 +16,7 @@ use crate::toplevel::{DefinitionId, TopLevelContext, TopLevelDef};
#[cfg(test)]
mod test;
/// Handle for a type, implementated as a key in the unification table.
/// Handle for a type, implemented as a key in the unification table.
pub type Type = UnificationKey;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
@ -830,7 +830,7 @@ impl Unifier {
},
)
},
&mut |id| format!("var{}", id),
&mut |id| format!("typevar{}", id),
notes,
)
}

View File

@ -302,14 +302,14 @@ fn test_unify(
("v1", "Record[a=float,b=int]"),
("v2", "Foo[v3]"),
],
(("v1", "v2"), "`3[var4]::b` field/method does not exist")
(("v1", "v2"), "`3[typevar4]::b` field/method does not exist")
; "record obj merge"
)]
/// Test cases for invalid unifications.
fn test_invalid_unification(
variable_count: u32,
unify_pairs: &[(&'static str, &'static str)],
errornous_pair: ((&'static str, &'static str), &'static str),
erroneous_pair: ((&'static str, &'static str), &'static str),
) {
let mut env = TestEnvironment::new();
let mut mapping = HashMap::new();
@ -326,11 +326,11 @@ fn test_invalid_unification(
pairs.push((t1, t2));
}
let (t1, t2) =
(env.parse(errornous_pair.0 .0, &mapping), env.parse(errornous_pair.0 .1, &mapping));
(env.parse(erroneous_pair.0 .0, &mapping), env.parse(erroneous_pair.0 .1, &mapping));
for (a, b) in pairs {
env.unifier.unify(a, b).unwrap();
}
assert_eq!(env.unify(t1, t2), Err(errornous_pair.1.to_string()));
assert_eq!(env.unify(t1, t2), Err(erroneous_pair.1.to_string()));
}
#[test]
@ -445,7 +445,7 @@ fn test_typevar_range() {
// where v in (int, list[v1]), v1 in (int, bool)
assert_eq!(
env.unify(float_list, v),
Err("Expected any one of these types: 0, list[var5], but got list[1]\n\nNotes:\n var5 ∈ {0, 2}".to_string())
Err("Expected any one of these types: 0, list[typevar5], but got list[1]\n\nNotes:\n typevar5 ∈ {0, 2}".to_string())
);
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
@ -504,9 +504,9 @@ fn test_rigid_var() {
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: var3 and var2".to_string()));
assert_eq!(env.unify(a, b), Err("Incompatible types: typevar3 and typevar2".to_string()));
env.unifier.unify(list_a, list_x).unwrap();
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: 0 and var2".to_string()));
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: 0 and typevar2".to_string()));
env.unifier.replace_rigid_var(a, int);
env.unifier.unify(list_x, list_int).unwrap();