Better error messages #195
|
@ -1,4 +1,13 @@
|
|||
from min_artiq import *
|
||||
from numpy import int32, int64
|
||||
|
||||
@extern
|
||||
def output_int(x: int32):
|
||||
...
|
||||
|
||||
|
||||
class InexistingException(Exception):
|
||||
pass
|
||||
|
||||
@nac3
|
||||
class Demo:
|
||||
|
@ -11,6 +20,16 @@ class Demo:
|
|||
self.led0 = TTLOut(self.core, 18)
|
||||
self.led1 = TTLOut(self.core, 19)
|
||||
|
||||
@kernel
|
||||
def test(self):
|
||||
a = (1, True)
|
||||
a[0]()
|
||||
|
||||
@kernel
|
||||
def test2(self):
|
||||
a = (1, True)
|
||||
output_int(int32(a))
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
|
|
|
@ -64,10 +64,10 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
let result = gen_call(self, ctx, obj, fun, params);
|
||||
) -> 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);
|
||||
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();
|
||||
|
@ -83,21 +83,21 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
let end_store = self.gen_store_target(ctx, &end);
|
||||
let end_store = self.gen_store_target(ctx, &end)?;
|
||||
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);
|
||||
self.timeline.emit_at_mu(ctx, start_val);
|
||||
}
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn gen_with<'ctx, 'a>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::With { items, body, .. } = &stmt.node {
|
||||
if items.len() == 1 && items[0].optional_vars.is_none() {
|
||||
let item = &items[0];
|
||||
|
@ -119,7 +119,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)
|
||||
} else {
|
||||
self.timeline.emit_now_mu(ctx)
|
||||
};
|
||||
|
@ -130,18 +130,21 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
// the LLVM Context.
|
||||
// The name is guaranteed to be unique as users cannot use this as variable
|
||||
// name.
|
||||
self.start = old_start.clone().or_else(|| {
|
||||
let start = format!("with-{}-start", self.name_counter).into();
|
||||
let start_expr = Located {
|
||||
// location does not matter at this point
|
||||
location: stmt.location,
|
||||
node: ExprKind::Name { id: start, ctx: name_ctx.clone() },
|
||||
custom: Some(ctx.primitives.int64),
|
||||
};
|
||||
let start = self.gen_store_target(ctx, &start_expr);
|
||||
ctx.builder.build_store(start, now);
|
||||
Some(start_expr)
|
||||
});
|
||||
self.start = old_start.clone().map_or_else(
|
||||
|| {
|
||||
let start = format!("with-{}-start", self.name_counter).into();
|
||||
let start_expr = Located {
|
||||
// location does not matter at this point
|
||||
location: stmt.location,
|
||||
node: ExprKind::Name { id: start, ctx: name_ctx.clone() },
|
||||
custom: Some(ctx.primitives.int64),
|
||||
};
|
||||
let start = self.gen_store_target(ctx, &start_expr)?;
|
||||
ctx.builder.build_store(start, now);
|
||||
Ok(Some(start_expr)) as Result<_, String>
|
||||
},
|
||||
|v| Ok(Some(v)),
|
||||
)?;
|
||||
let end = format!("with-{}-end", self.name_counter).into();
|
||||
let end_expr = Located {
|
||||
// location does not matter at this point
|
||||
|
@ -149,11 +152,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
node: ExprKind::Name { id: end, ctx: name_ctx.clone() },
|
||||
custom: Some(ctx.primitives.int64),
|
||||
};
|
||||
let end = self.gen_store_target(ctx, &end_expr);
|
||||
let end = self.gen_store_target(ctx, &end_expr)?;
|
||||
ctx.builder.build_store(end, now);
|
||||
self.end = Some(end_expr);
|
||||
self.name_counter += 1;
|
||||
gen_block(self, ctx, body.iter());
|
||||
gen_block(self, ctx, body.iter())?;
|
||||
let current = ctx.builder.get_insert_block().unwrap();
|
||||
// if the current block is terminated, move before the terminator
|
||||
// we want to set the timeline before reaching the terminator
|
||||
|
@ -171,7 +174,7 @@ 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);
|
||||
self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(ctx, self);
|
||||
|
||||
// inside a sequential block
|
||||
if old_start.is_none() {
|
||||
|
@ -179,8 +182,10 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
}
|
||||
// inside a parallel block, should update the outer max now_mu
|
||||
if let Some(old_end) = &old_end {
|
||||
let outer_end_val =
|
||||
self.gen_expr(ctx, old_end).unwrap().to_basic_value_enum(ctx, self);
|
||||
let outer_end_val = self
|
||||
.gen_expr(ctx, old_end)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, self);
|
||||
let smax =
|
||||
ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
|
||||
let i64 = ctx.ctx.i64_type();
|
||||
|
@ -196,7 +201,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
let outer_end = self.gen_store_target(ctx, old_end);
|
||||
let outer_end = self.gen_store_target(ctx, old_end)?;
|
||||
ctx.builder.build_store(outer_end, max);
|
||||
}
|
||||
self.start = old_start;
|
||||
|
@ -204,29 +209,33 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
if reset_position {
|
||||
ctx.builder.position_at_end(current);
|
||||
}
|
||||
return;
|
||||
return Ok(());
|
||||
} else if id == &"sequential".into() {
|
||||
let start = self.start.take();
|
||||
for stmt in body.iter() {
|
||||
self.gen_stmt(ctx, stmt);
|
||||
self.gen_stmt(ctx, stmt)?;
|
||||
if ctx.is_terminated() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.start = start;
|
||||
return
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
// not parallel/sequential
|
||||
gen_with(self, ctx, stmt);
|
||||
gen_with(self, ctx, stmt)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_rpc_tag<'ctx, 'a>(ctx: &mut CodeGenContext<'ctx, 'a>, ty: Type, buffer: &mut Vec<u8>) {
|
||||
fn gen_rpc_tag<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ty: Type,
|
||||
buffer: &mut Vec<u8>,
|
||||
) -> Result<(), String> {
|
||||
use nac3core::typecheck::typedef::TypeEnum::*;
|
||||
|
||||
let int32 = ctx.primitives.int32;
|
||||
|
@ -249,24 +258,25 @@ fn gen_rpc_tag<'ctx, 'a>(ctx: &mut CodeGenContext<'ctx, 'a>, ty: Type, buffer: &
|
|||
} else if ctx.unifier.unioned(ty, none) {
|
||||
buffer.push(b'n');
|
||||
} else {
|
||||
let ty = ctx.unifier.get_ty(ty);
|
||||
match &*ty {
|
||||
let ty_enum = ctx.unifier.get_ty(ty);
|
||||
match &*ty_enum {
|
||||
TTuple { ty } => {
|
||||
buffer.push(b't');
|
||||
buffer.push(ty.len() as u8);
|
||||
for ty in ty {
|
||||
gen_rpc_tag(ctx, *ty, buffer);
|
||||
gen_rpc_tag(ctx, *ty, buffer)?;
|
||||
}
|
||||
}
|
||||
TList { ty } => {
|
||||
buffer.push(b'l');
|
||||
gen_rpc_tag(ctx, *ty, buffer);
|
||||
gen_rpc_tag(ctx, *ty, buffer)?;
|
||||
}
|
||||
// we should return an error, this will be fixed after improving error message
|
||||
// as this requires returning an error during codegen
|
||||
_ => unimplemented!(),
|
||||
_ => return Err(format!("Unsupported type: {:?}", ctx.unifier.stringify(ty))),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rpc_codegen_callback_fn<'ctx, 'a>(
|
||||
|
@ -275,24 +285,24 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
fun: (&FunSignature, DefinitionId),
|
||||
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::Generic);
|
||||
let size_type = generator.get_size_type(ctx.ctx);
|
||||
let int8 = ctx.ctx.i8_type();
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
|
||||
|
||||
let service_id = int32.const_int(fun.1.0 as u64, false);
|
||||
let service_id = int32.const_int(fun.1 .0 as u64, false);
|
||||
// -- setup rpc tags
|
||||
let mut tag = Vec::new();
|
||||
if obj.is_some() {
|
||||
tag.push(b'O');
|
||||
}
|
||||
for arg in fun.0.args.iter() {
|
||||
gen_rpc_tag(ctx, arg.ty, &mut tag);
|
||||
gen_rpc_tag(ctx, arg.ty, &mut tag)?;
|
||||
}
|
||||
tag.push(b':');
|
||||
gen_rpc_tag(ctx, fun.0.ret, &mut tag);
|
||||
gen_rpc_tag(ctx, fun.0.ret, &mut tag)?;
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
tag.hash(&mut hasher);
|
||||
|
@ -432,7 +442,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
|
||||
if ctx.unifier.unioned(fun.0.ret, ctx.primitives.none) {
|
||||
ctx.build_call_or_invoke(rpc_recv, &[ptr_type.const_null().into()], "rpc_recv");
|
||||
return None
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let prehead_bb = ctx.builder.get_insert_block().unwrap();
|
||||
|
@ -474,7 +484,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
|
||||
ctx.builder.position_at_end(tail_bb);
|
||||
|
||||
if need_load {
|
||||
Ok(if need_load {
|
||||
let result = ctx.builder.build_load(slot, "rpc.result");
|
||||
ctx.builder.build_call(
|
||||
stackrestore,
|
||||
|
@ -484,7 +494,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
Some(result)
|
||||
} else {
|
||||
Some(slot.into())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rpc_codegen_callback() -> Arc<GenCall> {
|
||||
|
|
|
@ -10,7 +10,7 @@ use inkwell::{
|
|||
targets::*,
|
||||
OptimizationLevel,
|
||||
};
|
||||
use nac3core::typecheck::typedef::{Unifier, TypeEnum};
|
||||
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
|
||||
use nac3parser::{
|
||||
ast::{self, ExprKind, Stmt, StmtKind, StrRef},
|
||||
parser::{self, parse_program},
|
||||
|
@ -21,8 +21,8 @@ use pyo3::{exceptions, types::PyBytes, types::PyDict, types::PySet};
|
|||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
use nac3core::{
|
||||
codegen::{concrete_type::ConcreteTypeStore, CodeGenTask, WithCall, WorkerRegistry},
|
||||
codegen::irrt::load_irrt,
|
||||
codegen::{concrete_type::ConcreteTypeStore, CodeGenTask, WithCall, WorkerRegistry},
|
||||
symbol_resolver::SymbolResolver,
|
||||
toplevel::{
|
||||
composer::{ComposerConfig, TopLevelComposer},
|
||||
|
@ -96,10 +96,7 @@ impl Nac3 {
|
|||
) -> PyResult<()> {
|
||||
let (module_name, source_file) = Python::with_gil(|py| -> PyResult<(String, String)> {
|
||||
let module: &PyAny = module.extract(py)?;
|
||||
Ok((
|
||||
module.getattr("__name__")?.extract()?,
|
||||
module.getattr("__file__")?.extract()?,
|
||||
))
|
||||
Ok((module.getattr("__name__")?.extract()?, module.getattr("__file__")?.extract()?))
|
||||
})?;
|
||||
|
||||
let source = fs::read_to_string(&source_file).map_err(|e| {
|
||||
|
@ -111,10 +108,7 @@ impl Nac3 {
|
|||
for mut stmt in parser_result.into_iter() {
|
||||
let include = match stmt.node {
|
||||
ast::StmtKind::ClassDef {
|
||||
ref decorator_list,
|
||||
ref mut body,
|
||||
ref mut bases,
|
||||
..
|
||||
ref decorator_list, ref mut body, ref mut bases, ..
|
||||
} => {
|
||||
let nac3_class = decorator_list.iter().any(|decorator| {
|
||||
if let ast::ExprKind::Name { id, .. } = decorator.node {
|
||||
|
@ -146,10 +140,7 @@ impl Nac3 {
|
|||
.unwrap()
|
||||
});
|
||||
body.retain(|stmt| {
|
||||
if let ast::StmtKind::FunctionDef {
|
||||
ref decorator_list, ..
|
||||
} = stmt.node
|
||||
{
|
||||
if let ast::StmtKind::FunctionDef { ref decorator_list, .. } = stmt.node {
|
||||
decorator_list.iter().any(|decorator| {
|
||||
if let ast::ExprKind::Name { id, .. } = decorator.node {
|
||||
id.to_string() == "kernel"
|
||||
|
@ -165,22 +156,21 @@ impl Nac3 {
|
|||
});
|
||||
true
|
||||
}
|
||||
ast::StmtKind::FunctionDef {
|
||||
ref decorator_list, ..
|
||||
} => decorator_list.iter().any(|decorator| {
|
||||
if let ast::ExprKind::Name { id, .. } = decorator.node {
|
||||
let id = id.to_string();
|
||||
id == "extern" || id == "portable" || id == "kernel" || id == "rpc"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
ast::StmtKind::FunctionDef { ref decorator_list, .. } => {
|
||||
decorator_list.iter().any(|decorator| {
|
||||
if let ast::ExprKind::Name { id, .. } = decorator.node {
|
||||
let id = id.to_string();
|
||||
id == "extern" || id == "portable" || id == "kernel" || id == "rpc"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if include {
|
||||
self.top_levels
|
||||
.push((stmt, module_name.clone(), module.clone()));
|
||||
self.top_levels.push((stmt, module_name.clone(), module.clone()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -197,58 +187,68 @@ impl Nac3 {
|
|||
let base_ty =
|
||||
match resolver.get_symbol_type(unifier, top_level_defs, primitives, "base".into()) {
|
||||
Ok(ty) => ty,
|
||||
Err(e) => return Some(format!("type error inside object launching kernel: {}", e))
|
||||
Err(e) => return Some(format!("type error inside object launching kernel: {}", e)),
|
||||
};
|
||||
|
||||
let fun_ty = if method_name.is_empty() {
|
||||
base_ty
|
||||
} else if let TypeEnum::TObj { fields, .. } = &*unifier.get_ty(base_ty) {
|
||||
match fields.borrow().get(&(*method_name).into()) {
|
||||
match fields.get(&(*method_name).into()) {
|
||||
Some(t) => t.0,
|
||||
None => return Some(
|
||||
format!("object launching kernel does not have method `{}`", method_name)
|
||||
)
|
||||
None => {
|
||||
return Some(format!(
|
||||
"object launching kernel does not have method `{}`",
|
||||
method_name
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Some("cannot launch kernel by calling a non-callable".into())
|
||||
return Some("cannot launch kernel by calling a non-callable".into());
|
||||
};
|
||||
|
||||
if let TypeEnum::TFunc(sig) = &*unifier.get_ty(fun_ty) {
|
||||
let FunSignature { args, .. } = &*sig.borrow();
|
||||
if let TypeEnum::TFunc(FunSignature { args, .. }) = &*unifier.get_ty(fun_ty) {
|
||||
if arg_names.len() > args.len() {
|
||||
return Some(format!(
|
||||
"launching kernel function with too many arguments (expect {}, found {})",
|
||||
args.len(),
|
||||
arg_names.len(),
|
||||
))
|
||||
));
|
||||
}
|
||||
for (i, FuncArg { ty, default_value, name }) in args.iter().enumerate() {
|
||||
let in_name = match arg_names.get(i) {
|
||||
Some(n) => n,
|
||||
None if default_value.is_none() => return Some(format!(
|
||||
"argument `{}` not provided when launching kernel function", name
|
||||
)),
|
||||
None if default_value.is_none() => {
|
||||
return Some(format!(
|
||||
"argument `{}` not provided when launching kernel function",
|
||||
name
|
||||
))
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
let in_ty = match resolver.get_symbol_type(
|
||||
unifier,
|
||||
top_level_defs,
|
||||
primitives,
|
||||
in_name.clone().into()
|
||||
in_name.clone().into(),
|
||||
) {
|
||||
Ok(t) => t,
|
||||
Err(e) => return Some(format!(
|
||||
"type error ({}) at parameter #{} when calling kernel function", e, i
|
||||
))
|
||||
Err(e) => {
|
||||
return Some(format!(
|
||||
"type error ({}) at parameter #{} when calling kernel function",
|
||||
e, i
|
||||
))
|
||||
}
|
||||
};
|
||||
if let Err(e) = unifier.unify(in_ty, *ty) {
|
||||
return Some(format!(
|
||||
"type error ({}) at parameter #{} when calling kernel function", e, i
|
||||
"type error ({}) at parameter #{} when calling kernel function",
|
||||
e.to_display(unifier).to_string(),
|
||||
i
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Some("cannot launch kernel by calling a non-callable".into())
|
||||
return Some("cannot launch kernel by calling a non-callable".into());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -275,13 +275,9 @@ impl Nac3 {
|
|||
let builtins = vec![
|
||||
(
|
||||
"now_mu".into(),
|
||||
FunSignature {
|
||||
args: vec![],
|
||||
ret: primitive.int64,
|
||||
vars: HashMap::new(),
|
||||
},
|
||||
FunSignature { args: vec![], ret: primitive.int64, vars: HashMap::new() },
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, _, _| {
|
||||
Some(time_fns.emit_now_mu(ctx))
|
||||
Ok(Some(time_fns.emit_now_mu(ctx)))
|
||||
}))),
|
||||
),
|
||||
(
|
||||
|
@ -298,7 +294,7 @@ impl Nac3 {
|
|||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
time_fns.emit_at_mu(ctx, arg);
|
||||
None
|
||||
Ok(None)
|
||||
}))),
|
||||
),
|
||||
(
|
||||
|
@ -315,16 +311,13 @@ impl Nac3 {
|
|||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
time_fns.emit_delay_mu(ctx, arg);
|
||||
None
|
||||
Ok(None)
|
||||
}))),
|
||||
),
|
||||
];
|
||||
let (_, builtins_def, builtins_ty) = TopLevelComposer::new(
|
||||
builtins.clone(),
|
||||
ComposerConfig {
|
||||
kernel_ann: Some("Kernel"),
|
||||
kernel_invariant_ann: "KernelInvariant",
|
||||
},
|
||||
ComposerConfig { kernel_ann: Some("Kernel"), kernel_invariant_ann: "KernelInvariant" },
|
||||
);
|
||||
|
||||
let builtins_mod = PyModule::import(py, "builtins").unwrap();
|
||||
|
@ -356,46 +349,22 @@ impl Nac3 {
|
|||
.extract()
|
||||
.unwrap(),
|
||||
),
|
||||
none: id_fn
|
||||
.call1((builtins_mod.getattr("None").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
none: id_fn.call1((builtins_mod.getattr("None").unwrap(),)).unwrap().extract().unwrap(),
|
||||
typevar: id_fn
|
||||
.call1((typing_mod.getattr("TypeVar").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
int: id_fn
|
||||
.call1((builtins_mod.getattr("int").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
int32: id_fn
|
||||
.call1((numpy_mod.getattr("int32").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
int64: id_fn
|
||||
.call1((numpy_mod.getattr("int64").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
bool: id_fn
|
||||
.call1((builtins_mod.getattr("bool").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
int: id_fn.call1((builtins_mod.getattr("int").unwrap(),)).unwrap().extract().unwrap(),
|
||||
int32: id_fn.call1((numpy_mod.getattr("int32").unwrap(),)).unwrap().extract().unwrap(),
|
||||
int64: id_fn.call1((numpy_mod.getattr("int64").unwrap(),)).unwrap().extract().unwrap(),
|
||||
bool: id_fn.call1((builtins_mod.getattr("bool").unwrap(),)).unwrap().extract().unwrap(),
|
||||
float: id_fn
|
||||
.call1((builtins_mod.getattr("float").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
list: id_fn
|
||||
.call1((builtins_mod.getattr("list").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
list: id_fn.call1((builtins_mod.getattr("list").unwrap(),)).unwrap().extract().unwrap(),
|
||||
tuple: id_fn
|
||||
.call1((builtins_mod.getattr("tuple").unwrap(),))
|
||||
.unwrap()
|
||||
|
@ -409,11 +378,7 @@ impl Nac3 {
|
|||
};
|
||||
|
||||
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
|
||||
fs::write(
|
||||
working_directory.path().join("kernel.ld"),
|
||||
include_bytes!("kernel.ld"),
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(working_directory.path().join("kernel.ld"), include_bytes!("kernel.ld")).unwrap();
|
||||
|
||||
Ok(Nac3 {
|
||||
isa,
|
||||
|
@ -426,7 +391,7 @@ impl Nac3 {
|
|||
top_levels: Default::default(),
|
||||
pyid_to_def: Default::default(),
|
||||
working_directory,
|
||||
string_store: Default::default()
|
||||
string_store: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -466,20 +431,17 @@ impl Nac3 {
|
|||
embedding_map: &PyAny,
|
||||
py: Python,
|
||||
) -> PyResult<()> {
|
||||
let (mut composer, _, _) = TopLevelComposer::new(self.builtins.clone(), ComposerConfig {
|
||||
kernel_ann: Some("Kernel"),
|
||||
kernel_invariant_ann: "KernelInvariant"
|
||||
});
|
||||
let (mut composer, _, _) = TopLevelComposer::new(
|
||||
self.builtins.clone(),
|
||||
ComposerConfig { kernel_ann: Some("Kernel"), kernel_invariant_ann: "KernelInvariant" },
|
||||
);
|
||||
|
||||
let builtins = PyModule::import(py, "builtins")?;
|
||||
let typings = PyModule::import(py, "typing")?;
|
||||
let id_fn = builtins.getattr("id")?;
|
||||
let store_obj = embedding_map.getattr("store_object").unwrap().to_object(py);
|
||||
let store_str = embedding_map.getattr("store_str").unwrap().to_object(py);
|
||||
let store_fun = embedding_map
|
||||
.getattr("store_function")
|
||||
.unwrap()
|
||||
.to_object(py);
|
||||
let store_fun = embedding_map.getattr("store_function").unwrap().to_object(py);
|
||||
let helper = PythonHelper {
|
||||
id_fn: builtins.getattr("id").unwrap().to_object(py),
|
||||
len_fn: builtins.getattr("len").unwrap().to_object(py),
|
||||
|
@ -487,7 +449,7 @@ impl Nac3 {
|
|||
origin_ty_fn: typings.getattr("get_origin").unwrap().to_object(py),
|
||||
args_ty_fn: typings.getattr("get_args").unwrap().to_object(py),
|
||||
store_obj,
|
||||
store_str
|
||||
store_str,
|
||||
};
|
||||
let mut module_to_resolver_cache: HashMap<u64, _> = HashMap::new();
|
||||
|
||||
|
@ -498,10 +460,8 @@ impl Nac3 {
|
|||
let py_module: &PyAny = module.extract(py)?;
|
||||
let module_id: u64 = id_fn.call1((py_module,))?.extract()?;
|
||||
let helper = helper.clone();
|
||||
let (name_to_pyid, resolver) = module_to_resolver_cache
|
||||
.get(&module_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
let (name_to_pyid, resolver) =
|
||||
module_to_resolver_cache.get(&module_id).cloned().unwrap_or_else(|| {
|
||||
let mut name_to_pyid: HashMap<StrRef, u64> = HashMap::new();
|
||||
let members: &PyDict =
|
||||
py_module.getattr("__dict__").unwrap().cast_as().unwrap();
|
||||
|
@ -536,7 +496,10 @@ impl Nac3 {
|
|||
let (name, def_id, ty) = composer
|
||||
.register_top_level(stmt.clone(), Some(resolver.clone()), path.clone())
|
||||
.map_err(|e| {
|
||||
exceptions::PyRuntimeError::new_err(format!("nac3 compilation failure: {}", e))
|
||||
exceptions::PyRuntimeError::new_err(format!(
|
||||
"nac3 compilation failure\n----------\n{}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
match &stmt.node {
|
||||
|
@ -584,16 +547,10 @@ impl Nac3 {
|
|||
let synthesized = if method_name.is_empty() {
|
||||
format!("def __modinit__():\n base({})", arg_names.join(", "))
|
||||
} else {
|
||||
format!(
|
||||
"def __modinit__():\n base.{}({})",
|
||||
method_name,
|
||||
arg_names.join(", ")
|
||||
)
|
||||
format!("def __modinit__():\n base.{}({})", method_name, arg_names.join(", "))
|
||||
};
|
||||
let mut synthesized = parse_program(
|
||||
&synthesized,
|
||||
"__nac3_synthesized_modinit__".to_string().into(),
|
||||
).unwrap();
|
||||
let mut synthesized =
|
||||
parse_program(&synthesized, "__nac3_synthesized_modinit__".to_string().into()).unwrap();
|
||||
let resolver = Arc::new(Resolver(Arc::new(InnerResolver {
|
||||
id_to_type: self.builtins_ty.clone().into(),
|
||||
id_to_def: self.builtins_def.clone().into(),
|
||||
|
@ -611,34 +568,24 @@ impl Nac3 {
|
|||
string_store: self.string_store.clone(),
|
||||
}))) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||
let (_, def_id, _) = composer
|
||||
.register_top_level(
|
||||
synthesized.pop().unwrap(),
|
||||
Some(resolver.clone()),
|
||||
"".into(),
|
||||
)
|
||||
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into())
|
||||
.unwrap();
|
||||
|
||||
let signature = FunSignature {
|
||||
args: vec![],
|
||||
ret: self.primitive.none,
|
||||
vars: HashMap::new(),
|
||||
};
|
||||
let signature =
|
||||
FunSignature { args: vec![], ret: self.primitive.none, vars: HashMap::new() };
|
||||
let mut store = ConcreteTypeStore::new();
|
||||
let mut cache = HashMap::new();
|
||||
let signature = store.from_signature(
|
||||
&mut composer.unifier,
|
||||
&self.primitive,
|
||||
&signature,
|
||||
&mut cache,
|
||||
);
|
||||
let signature =
|
||||
store.from_signature(&mut composer.unifier, &self.primitive, &signature, &mut cache);
|
||||
let signature = store.add_cty(signature);
|
||||
|
||||
if let Err(e) = composer.start_analysis(true) {
|
||||
// report error of __modinit__ separately
|
||||
if !e.contains("__nac3_synthesized_modinit__") {
|
||||
return Err(exceptions::PyRuntimeError::new_err(
|
||||
format!("nac3 compilation failure: {}", e)
|
||||
));
|
||||
return Err(exceptions::PyRuntimeError::new_err(format!(
|
||||
"nac3 compilation failure: \n----------\n{}",
|
||||
e
|
||||
)));
|
||||
} else {
|
||||
let msg = Self::report_modinit(
|
||||
&arg_names,
|
||||
|
@ -646,7 +593,7 @@ impl Nac3 {
|
|||
resolver.clone(),
|
||||
&composer.extract_def_list(),
|
||||
&mut composer.unifier,
|
||||
&self.primitive
|
||||
&self.primitive,
|
||||
);
|
||||
return Err(exceptions::PyRuntimeError::new_err(msg.unwrap()));
|
||||
}
|
||||
|
@ -659,9 +606,7 @@ impl Nac3 {
|
|||
for (class_data, id) in rpc_ids.iter() {
|
||||
let mut def = defs[id.0].write();
|
||||
match &mut *def {
|
||||
TopLevelDef::Function {
|
||||
codegen_callback, ..
|
||||
} => {
|
||||
TopLevelDef::Function { codegen_callback, .. } => {
|
||||
*codegen_callback = Some(rpc_codegen.clone());
|
||||
}
|
||||
TopLevelDef::Class { methods, .. } => {
|
||||
|
@ -670,9 +615,8 @@ impl Nac3 {
|
|||
if name != method_name {
|
||||
continue;
|
||||
}
|
||||
if let TopLevelDef::Function {
|
||||
codegen_callback, ..
|
||||
} = &mut *defs[id.0].write()
|
||||
if let TopLevelDef::Function { codegen_callback, .. } =
|
||||
&mut *defs[id.0].write()
|
||||
{
|
||||
*codegen_callback = Some(rpc_codegen.clone());
|
||||
store_fun
|
||||
|
@ -694,11 +638,8 @@ impl Nac3 {
|
|||
let instance = {
|
||||
let defs = top_level.definitions.read();
|
||||
let mut definition = defs[def_id.0].write();
|
||||
if let TopLevelDef::Function {
|
||||
instance_to_stmt,
|
||||
instance_to_symbol,
|
||||
..
|
||||
} = &mut *definition
|
||||
if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } =
|
||||
&mut *definition
|
||||
{
|
||||
instance_to_symbol.insert("".to_string(), "__modinit__".into());
|
||||
instance_to_stmt[""].clone()
|
||||
|
@ -734,13 +675,7 @@ impl Nac3 {
|
|||
let thread_names: Vec<String> = (0..4).map(|_| "main".to_string()).collect();
|
||||
let threads: Vec<_> = thread_names
|
||||
.iter()
|
||||
.map(|s| {
|
||||
Box::new(ArtiqCodeGenerator::new(
|
||||
s.to_string(),
|
||||
size_t,
|
||||
self.time_fns,
|
||||
))
|
||||
})
|
||||
.map(|s| Box::new(ArtiqCodeGenerator::new(s.to_string(), size_t, self.time_fns)))
|
||||
.collect();
|
||||
|
||||
py.allow_threads(|| {
|
||||
|
@ -785,14 +720,10 @@ impl Nac3 {
|
|||
TargetMachine::get_default_triple(),
|
||||
TargetMachine::get_host_cpu_features().to_string(),
|
||||
),
|
||||
Isa::RiscV32G => (
|
||||
TargetTriple::create("riscv32-unknown-linux"),
|
||||
"+a,+m,+f,+d".to_string(),
|
||||
),
|
||||
Isa::RiscV32IMA => (
|
||||
TargetTriple::create("riscv32-unknown-linux"),
|
||||
"+a,+m".to_string(),
|
||||
),
|
||||
Isa::RiscV32G => {
|
||||
(TargetTriple::create("riscv32-unknown-linux"), "+a,+m,+f,+d".to_string())
|
||||
}
|
||||
Isa::RiscV32IMA => (TargetTriple::create("riscv32-unknown-linux"), "+a,+m".to_string()),
|
||||
Isa::CortexA9 => (
|
||||
TargetTriple::create("armv7-unknown-linux-gnueabihf"),
|
||||
"+dsp,+fp16,+neon,+vfp3".to_string(),
|
||||
|
@ -820,28 +751,18 @@ impl Nac3 {
|
|||
"-x".to_string(),
|
||||
"-o".to_string(),
|
||||
filename.to_string(),
|
||||
working_directory
|
||||
.join("module.o")
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
working_directory.join("module.o").to_string_lossy().to_string(),
|
||||
];
|
||||
if isa != Isa::Host {
|
||||
linker_args.push(
|
||||
"-T".to_string()
|
||||
+ self
|
||||
.working_directory
|
||||
.path()
|
||||
.join("kernel.ld")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
+ self.working_directory.path().join("kernel.ld").to_str().unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Ok(linker_status) = Command::new("ld.lld").args(linker_args).status() {
|
||||
if !linker_status.success() {
|
||||
return Err(exceptions::PyRuntimeError::new_err(
|
||||
"failed to start linker",
|
||||
));
|
||||
return Err(exceptions::PyRuntimeError::new_err("failed to start linker"));
|
||||
}
|
||||
} else {
|
||||
return Err(exceptions::PyRuntimeError::new_err(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use inkwell::{types::BasicType, values::BasicValueEnum, AddressSpace};
|
||||
use nac3core::{
|
||||
codegen::{CodeGenContext, CodeGenerator},
|
||||
location::Location,
|
||||
symbol_resolver::{StaticValue, SymbolResolver, SymbolValue, ValueEnum},
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
typecheck::{
|
||||
|
@ -16,7 +15,6 @@ use pyo3::{
|
|||
PyAny, PyObject, PyResult, Python,
|
||||
};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
@ -85,17 +83,21 @@ impl StaticValue for PythonValue {
|
|||
Python::with_gil(|py| -> PyResult<BasicValueEnum<'ctx>> {
|
||||
let id: u32 = self.store_obj.call1(py, (self.value.clone(),))?.extract(py)?;
|
||||
let struct_type = ctx.ctx.struct_type(&[ctx.ctx.i32_type().into()], false);
|
||||
let global =
|
||||
ctx.module
|
||||
.add_global(struct_type, None, format!("{}_const", self.id).as_str());
|
||||
let global = ctx.module.add_global(
|
||||
struct_type,
|
||||
None,
|
||||
format!("{}_const", self.id).as_str(),
|
||||
);
|
||||
global.set_constant(true);
|
||||
global.set_initializer(&ctx.ctx.const_struct(
|
||||
&[ctx.ctx.i32_type().const_int(id as u64, false).into()],
|
||||
false,
|
||||
));
|
||||
let global2 =
|
||||
ctx.module
|
||||
.add_global(struct_type.ptr_type(AddressSpace::Generic), None, format!("{}_const2", self.id).as_str());
|
||||
let global2 = ctx.module.add_global(
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
None,
|
||||
format!("{}_const2", self.id).as_str(),
|
||||
);
|
||||
global2.set_initializer(&global.as_pointer_value());
|
||||
Ok(global2.as_pointer_value().into())
|
||||
})
|
||||
|
@ -162,10 +164,7 @@ impl StaticValue for PythonValue {
|
|||
let id = self.resolver.helper.id_fn.call1(py, (&obj,))?.extract(py)?;
|
||||
Some((id, obj))
|
||||
};
|
||||
self.resolver
|
||||
.field_to_val
|
||||
.write()
|
||||
.insert((self.id, name), result.clone());
|
||||
self.resolver.field_to_val.write().insert((self.id, name), result.clone());
|
||||
Ok(result)
|
||||
})
|
||||
.unwrap()
|
||||
|
@ -193,24 +192,27 @@ impl InnerResolver {
|
|||
) -> PyResult<Result<Type, String>> {
|
||||
let mut ty = match self.get_obj_type(py, list.get_item(0)?, unifier, defs, primitives)? {
|
||||
Ok(t) => t,
|
||||
Err(e) => return Ok(Err(format!(
|
||||
"type error ({}) at element #0 of the list", e
|
||||
))),
|
||||
Err(e) => return Ok(Err(format!("type error ({}) at element #0 of the list", e))),
|
||||
};
|
||||
for i in 1..len {
|
||||
let b = match list
|
||||
.get_item(i)
|
||||
.map(|elem| self.get_obj_type(py, elem, unifier, defs, primitives))?? {
|
||||
Ok(t) => t,
|
||||
Err(e) => return Ok(Err(format!(
|
||||
"type error ({}) at element #{} of the list", e, i
|
||||
))),
|
||||
};
|
||||
.map(|elem| self.get_obj_type(py, elem, unifier, defs, primitives))??
|
||||
{
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Ok(Err(format!("type error ({}) at element #{} of the list", e, i)))
|
||||
}
|
||||
};
|
||||
ty = match unifier.unify(ty, b) {
|
||||
Ok(_) => ty,
|
||||
Err(e) => return Ok(Err(format!(
|
||||
"inhomogeneous type ({}) at element #{} of the list", e, i
|
||||
)))
|
||||
Err(e) => {
|
||||
return Ok(Err(format!(
|
||||
"inhomogeneous type ({}) at element #{} of the list",
|
||||
e.to_display(unifier).to_string(),
|
||||
i
|
||||
)))
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(Ok(ty))
|
||||
|
@ -229,11 +231,8 @@ impl InnerResolver {
|
|||
primitives: &PrimitiveStore,
|
||||
) -> PyResult<Result<(Type, bool), String>> {
|
||||
let ty_id: u64 = self.helper.id_fn.call1(py, (pyty,))?.extract(py)?;
|
||||
let ty_ty_id: u64 = self
|
||||
.helper
|
||||
.id_fn
|
||||
.call1(py, (self.helper.type_fn.call1(py, (pyty,))?,))?
|
||||
.extract(py)?;
|
||||
let ty_ty_id: u64 =
|
||||
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (pyty,))?,))?.extract(py)?;
|
||||
|
||||
if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
|
||||
Ok(Ok((primitives.int32, true)))
|
||||
|
@ -245,9 +244,9 @@ impl InnerResolver {
|
|||
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 {
|
||||
} else if ty_id == self.primitive_ids.list {
|
||||
// do not handle type var param and concrete check here
|
||||
let var = unifier.get_fresh_var().0;
|
||||
let var = unifier.get_dummy_var().0;
|
||||
let list = unifier.add_ty(TypeEnum::TList { ty: var });
|
||||
Ok(Ok((list, false)))
|
||||
} else if ty_id == self.primitive_ids.tuple {
|
||||
|
@ -255,38 +254,29 @@ impl InnerResolver {
|
|||
Ok(Ok((unifier.add_ty(TypeEnum::TTuple { ty: vec![] }), false)))
|
||||
} else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).cloned() {
|
||||
let def = defs[def_id.0].read();
|
||||
if let TopLevelDef::Class {
|
||||
object_id,
|
||||
type_vars,
|
||||
fields,
|
||||
methods,
|
||||
..
|
||||
} = &*def
|
||||
{
|
||||
if let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def {
|
||||
// do not handle type var param and concrete check here, and no subst
|
||||
Ok(Ok({
|
||||
let ty = TypeEnum::TObj {
|
||||
obj_id: *object_id,
|
||||
params: RefCell::new({
|
||||
type_vars
|
||||
.iter()
|
||||
.map(|x| {
|
||||
if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*x) {
|
||||
(*id, *x)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
fields: RefCell::new({
|
||||
params: type_vars
|
||||
.iter()
|
||||
.map(|x| {
|
||||
if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*x) {
|
||||
(*id, *x)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
fields: {
|
||||
let mut res = methods
|
||||
.iter()
|
||||
.map(|(iden, ty, _)| (*iden, (*ty, false)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
res.extend(fields.clone().into_iter().map(|x| (x.0, (x.1, x.2))));
|
||||
res
|
||||
}),
|
||||
},
|
||||
};
|
||||
// here also false, later instantiation use python object to check compatible
|
||||
(unifier.add_ty(ty), false)
|
||||
|
@ -296,6 +286,7 @@ impl InnerResolver {
|
|||
unreachable!("function type is not supported, should not be queried")
|
||||
}
|
||||
} else if ty_ty_id == self.primitive_ids.typevar {
|
||||
let name: &str = pyty.getattr("__name__").unwrap().extract().unwrap();
|
||||
let constraint_types = {
|
||||
let constraints = pyty.getattr("__constraints__").unwrap();
|
||||
let mut result: Vec<Type> = vec![];
|
||||
|
@ -323,7 +314,8 @@ impl InnerResolver {
|
|||
}
|
||||
result
|
||||
};
|
||||
let res = unifier.get_fresh_var_with_range(&constraint_types).0;
|
||||
let res =
|
||||
unifier.get_fresh_var_with_range(&constraint_types, Some(name.into()), None).0;
|
||||
Ok(Ok((res, true)))
|
||||
} else if ty_ty_id == self.primitive_ids.generic_alias.0
|
||||
|| ty_ty_id == self.primitive_ids.generic_alias.1
|
||||
|
@ -355,7 +347,7 @@ impl InnerResolver {
|
|||
};
|
||||
if !unifier.is_concrete(ty.0, &[]) && !ty.1 {
|
||||
return Ok(Err(
|
||||
"type list should take concrete parameters in typevar range".into()
|
||||
"type list should take concrete parameters in typevar range".into(),
|
||||
));
|
||||
}
|
||||
Ok(Ok((unifier.add_ty(TypeEnum::TList { ty: ty.0 }), true)))
|
||||
|
@ -389,7 +381,6 @@ impl InnerResolver {
|
|||
}
|
||||
TypeEnum::TObj { params, obj_id, .. } => {
|
||||
let subst = {
|
||||
let params = &*params.borrow();
|
||||
if params.len() != args.len() {
|
||||
return Ok(Err(format!(
|
||||
"for class #{}, expect {} type parameters, got {}.",
|
||||
|
@ -421,10 +412,7 @@ impl InnerResolver {
|
|||
.map(|((id, _), ty)| (*id, *ty))
|
||||
.collect::<HashMap<_, _>>()
|
||||
};
|
||||
Ok(Ok((
|
||||
unifier.subst(origin_ty, &subst).unwrap_or(origin_ty),
|
||||
true,
|
||||
)))
|
||||
Ok(Ok((unifier.subst(origin_ty, &subst).unwrap_or(origin_ty), true)))
|
||||
}
|
||||
TypeEnum::TVirtual { .. } => {
|
||||
if args.len() == 1 {
|
||||
|
@ -456,15 +444,19 @@ impl InnerResolver {
|
|||
} else if ty_id == self.primitive_ids.virtual_id {
|
||||
Ok(Ok((
|
||||
{
|
||||
let ty = TypeEnum::TVirtual {
|
||||
ty: unifier.get_fresh_var().0,
|
||||
};
|
||||
let ty = TypeEnum::TVirtual { ty: unifier.get_dummy_var().0 };
|
||||
unifier.add_ty(ty)
|
||||
},
|
||||
false,
|
||||
)))
|
||||
} else {
|
||||
Ok(Err("unknown type".into()))
|
||||
let str_fn =
|
||||
pyo3::types::PyModule::import(py, "builtins").unwrap().getattr("repr").unwrap();
|
||||
let str_repr: String = str_fn.call1((pyty,)).unwrap().extract().unwrap();
|
||||
Ok(Err(format!(
|
||||
"{} is not supported in nac3 (did you forgot to put @nac3 annotation?)",
|
||||
str_repr
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,13 +477,8 @@ impl InnerResolver {
|
|||
self.primitive_ids.generic_alias.0,
|
||||
self.primitive_ids.generic_alias.1,
|
||||
]
|
||||
.contains(
|
||||
&self
|
||||
.helper
|
||||
.id_fn
|
||||
.call1(py, (ty.clone(),))?
|
||||
.extract::<u64>(py)?,
|
||||
) {
|
||||
.contains(&self.helper.id_fn.call1(py, (ty.clone(),))?.extract::<u64>(py)?)
|
||||
{
|
||||
obj
|
||||
} else {
|
||||
ty.as_ref(py)
|
||||
|
@ -511,8 +498,8 @@ impl InnerResolver {
|
|||
if len == 0 {
|
||||
assert!(matches!(
|
||||
&*unifier.get_ty(extracted_ty),
|
||||
TypeEnum::TVar { meta: nac3core::typecheck::typedef::TypeVarMeta::Generic, range, .. }
|
||||
if range.borrow().is_empty()
|
||||
TypeEnum::TVar { fields: None, range, .. }
|
||||
if range.is_empty()
|
||||
));
|
||||
Ok(Ok(extracted_ty))
|
||||
} else {
|
||||
|
@ -520,9 +507,12 @@ impl InnerResolver {
|
|||
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 }))),
|
||||
Err(e) => Ok(Err(format!("type error ({}) for the list", e))),
|
||||
}
|
||||
Ok(_) => Ok(Ok(unifier.add_ty(TypeEnum::TList { ty: *ty }))),
|
||||
Err(e) => Ok(Err(format!(
|
||||
"type error ({}) for the list",
|
||||
e.to_display(unifier).to_string()
|
||||
))),
|
||||
},
|
||||
Err(e) => Ok(Err(e)),
|
||||
}
|
||||
}
|
||||
|
@ -538,36 +528,40 @@ impl InnerResolver {
|
|||
}
|
||||
(TypeEnum::TObj { params, fields, .. }, false) => {
|
||||
let var_map = params
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|(id_var, ty)| {
|
||||
if let TypeEnum::TVar { id, range, .. } = &*unifier.get_ty(*ty) {
|
||||
if let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) {
|
||||
assert_eq!(*id, *id_var);
|
||||
(*id, unifier.get_fresh_var_with_range(&range.borrow()).0)
|
||||
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
// loop through non-function fields of the class to get the instantiated value
|
||||
for field in fields.borrow().iter() {
|
||||
for field in fields.iter() {
|
||||
let name: String = (*field.0).into();
|
||||
if let TypeEnum::TFunc(..) = &*unifier.get_ty(field.1 .0) {
|
||||
continue;
|
||||
} else {
|
||||
let field_data = obj.getattr(&name)?;
|
||||
let ty = match self
|
||||
.get_obj_type(py, field_data, unifier, defs, primitives)? {
|
||||
let ty =
|
||||
match self.get_obj_type(py, field_data, unifier, defs, primitives)? {
|
||||
Ok(t) => t,
|
||||
Err(e) => return Ok(Err(format!(
|
||||
"error when getting type of field `{}` ({})", name, e
|
||||
))),
|
||||
Err(e) => {
|
||||
return Ok(Err(format!(
|
||||
"error when getting type of field `{}` ({})",
|
||||
name, e
|
||||
)))
|
||||
}
|
||||
};
|
||||
let field_ty = unifier.subst(field.1 .0, &var_map).unwrap_or(field.1 .0);
|
||||
if let Err(e) = unifier.unify(ty, field_ty) {
|
||||
// field type mismatch
|
||||
return Ok(Err(format!(
|
||||
"error when getting type of field `{}` ({})", name, e
|
||||
"error when getting type of field `{}` ({})",
|
||||
name,
|
||||
e.to_display(unifier).to_string()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -578,11 +572,7 @@ impl InnerResolver {
|
|||
return Ok(Err("object is not of concrete type".into()));
|
||||
}
|
||||
}
|
||||
return Ok(Ok(
|
||||
unifier
|
||||
.subst(extracted_ty, &var_map)
|
||||
.unwrap_or(extracted_ty),
|
||||
));
|
||||
return Ok(Ok(unifier.subst(extracted_ty, &var_map).unwrap_or(extracted_ty)));
|
||||
}
|
||||
_ => Ok(Ok(extracted_ty)),
|
||||
};
|
||||
|
@ -595,37 +585,24 @@ impl InnerResolver {
|
|||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> PyResult<Option<BasicValueEnum<'ctx>>> {
|
||||
let ty_id: u64 = self
|
||||
.helper
|
||||
.id_fn
|
||||
.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?
|
||||
.extract(py)?;
|
||||
let ty_id: u64 =
|
||||
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
|
||||
let id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
|
||||
if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
|
||||
let val: i32 = obj.extract()?;
|
||||
self.id_to_primitive
|
||||
.write()
|
||||
.insert(id, PrimitiveValue::I32(val));
|
||||
self.id_to_primitive.write().insert(id, PrimitiveValue::I32(val));
|
||||
Ok(Some(ctx.ctx.i32_type().const_int(val as u64, false).into()))
|
||||
} else if ty_id == self.primitive_ids.int64 {
|
||||
let val: i64 = obj.extract()?;
|
||||
self.id_to_primitive
|
||||
.write()
|
||||
.insert(id, PrimitiveValue::I64(val));
|
||||
self.id_to_primitive.write().insert(id, PrimitiveValue::I64(val));
|
||||
Ok(Some(ctx.ctx.i64_type().const_int(val as u64, false).into()))
|
||||
} else if ty_id == self.primitive_ids.bool {
|
||||
let val: bool = obj.extract()?;
|
||||
self.id_to_primitive
|
||||
.write()
|
||||
.insert(id, PrimitiveValue::Bool(val));
|
||||
Ok(Some(
|
||||
ctx.ctx.bool_type().const_int(val as u64, false).into(),
|
||||
))
|
||||
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 {
|
||||
let val: f64 = obj.extract()?;
|
||||
self.id_to_primitive
|
||||
.write()
|
||||
.insert(id, PrimitiveValue::F64(val));
|
||||
self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val));
|
||||
Ok(Some(ctx.ctx.f64_type().const_float(val).into()))
|
||||
} else if ty_id == self.primitive_ids.list {
|
||||
let id_str = id.to_string();
|
||||
|
@ -650,16 +627,14 @@ impl InnerResolver {
|
|||
};
|
||||
let ty = ctx.get_llvm_type(generator, ty);
|
||||
let size_t = generator.get_size_type(ctx.ctx);
|
||||
let arr_ty = ctx.ctx.struct_type(
|
||||
&[ty.ptr_type(AddressSpace::Generic).into(), size_t.into()],
|
||||
false,
|
||||
);
|
||||
let arr_ty = ctx
|
||||
.ctx
|
||||
.struct_type(&[ty.ptr_type(AddressSpace::Generic).into(), size_t.into()], false);
|
||||
|
||||
{
|
||||
if self.global_value_ids.read().contains(&id) {
|
||||
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
|
||||
ctx.module
|
||||
.add_global(arr_ty, Some(AddressSpace::Generic), &id_str)
|
||||
ctx.module.add_global(arr_ty, Some(AddressSpace::Generic), &id_str)
|
||||
});
|
||||
return Ok(Some(global.as_pointer_value().into()));
|
||||
} else {
|
||||
|
@ -669,8 +644,7 @@ 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))
|
||||
obj.get_item(i).and_then(|elem| self.get_obj_value(py, elem, ctx, generator))
|
||||
})
|
||||
.collect();
|
||||
let arr = arr?.unwrap();
|
||||
|
@ -681,34 +655,19 @@ impl InnerResolver {
|
|||
&(id_str.clone() + "_"),
|
||||
);
|
||||
let arr: BasicValueEnum = if ty.is_int_type() {
|
||||
let arr: Vec<_> = arr
|
||||
.into_iter()
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.collect();
|
||||
let arr: Vec<_> = arr.into_iter().map(BasicValueEnum::into_int_value).collect();
|
||||
ty.into_int_type().const_array(&arr)
|
||||
} else if ty.is_float_type() {
|
||||
let arr: Vec<_> = arr
|
||||
.into_iter()
|
||||
.map(BasicValueEnum::into_float_value)
|
||||
.collect();
|
||||
let arr: Vec<_> = arr.into_iter().map(BasicValueEnum::into_float_value).collect();
|
||||
ty.into_float_type().const_array(&arr)
|
||||
} else if ty.is_array_type() {
|
||||
let arr: Vec<_> = arr
|
||||
.into_iter()
|
||||
.map(BasicValueEnum::into_array_value)
|
||||
.collect();
|
||||
let arr: Vec<_> = arr.into_iter().map(BasicValueEnum::into_array_value).collect();
|
||||
ty.into_array_type().const_array(&arr)
|
||||
} else if ty.is_struct_type() {
|
||||
let arr: Vec<_> = arr
|
||||
.into_iter()
|
||||
.map(BasicValueEnum::into_struct_value)
|
||||
.collect();
|
||||
let arr: Vec<_> = arr.into_iter().map(BasicValueEnum::into_struct_value).collect();
|
||||
ty.into_struct_type().const_array(&arr)
|
||||
} else if ty.is_pointer_type() {
|
||||
let arr: Vec<_> = arr
|
||||
.into_iter()
|
||||
.map(BasicValueEnum::into_pointer_value)
|
||||
.collect();
|
||||
let arr: Vec<_> = arr.into_iter().map(BasicValueEnum::into_pointer_value).collect();
|
||||
ty.into_pointer_type().const_array(&arr)
|
||||
} else {
|
||||
unreachable!()
|
||||
|
@ -717,16 +676,11 @@ impl InnerResolver {
|
|||
arr_global.set_initializer(&arr);
|
||||
|
||||
let val = arr_ty.const_named_struct(&[
|
||||
arr_global
|
||||
.as_pointer_value()
|
||||
.const_cast(ty.ptr_type(AddressSpace::Generic))
|
||||
.into(),
|
||||
arr_global.as_pointer_value().const_cast(ty.ptr_type(AddressSpace::Generic)).into(),
|
||||
size_t.const_int(len as u64, false).into(),
|
||||
]);
|
||||
|
||||
let global = ctx
|
||||
.module
|
||||
.add_global(arr_ty, Some(AddressSpace::Generic), &id_str);
|
||||
let global = ctx.module.add_global(arr_ty, Some(AddressSpace::Generic), &id_str);
|
||||
global.set_initializer(&val);
|
||||
|
||||
Ok(Some(global.as_pointer_value().into()))
|
||||
|
@ -757,8 +711,7 @@ impl InnerResolver {
|
|||
{
|
||||
if self.global_value_ids.read().contains(&id) {
|
||||
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
|
||||
ctx.module
|
||||
.add_global(ty, Some(AddressSpace::Generic), &id_str)
|
||||
ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str)
|
||||
});
|
||||
return Ok(Some(global.as_pointer_value().into()));
|
||||
} else {
|
||||
|
@ -766,15 +719,11 @@ impl InnerResolver {
|
|||
}
|
||||
}
|
||||
|
||||
let val: Result<Option<Vec<_>>, _> = elements
|
||||
.iter()
|
||||
.map(|elem| self.get_obj_value(py, elem, ctx, generator))
|
||||
.collect();
|
||||
let val: Result<Option<Vec<_>>, _> =
|
||||
elements.iter().map(|elem| self.get_obj_value(py, elem, ctx, generator)).collect();
|
||||
let val = val?.unwrap();
|
||||
let val = ctx.ctx.const_struct(&val, false);
|
||||
let global = ctx
|
||||
.module
|
||||
.add_global(ty, Some(AddressSpace::Generic), &id_str);
|
||||
let global = ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str);
|
||||
global.set_initializer(&val);
|
||||
Ok(Some(global.as_pointer_value().into()))
|
||||
} else {
|
||||
|
@ -796,8 +745,7 @@ impl InnerResolver {
|
|||
{
|
||||
if self.global_value_ids.read().contains(&id) {
|
||||
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
|
||||
ctx.module
|
||||
.add_global(ty, Some(AddressSpace::Generic), &id_str)
|
||||
ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str)
|
||||
});
|
||||
return Ok(Some(global.as_pointer_value().into()));
|
||||
} else {
|
||||
|
@ -805,10 +753,8 @@ impl InnerResolver {
|
|||
}
|
||||
}
|
||||
// should be classes
|
||||
let definition = top_level_defs
|
||||
.get(self.pyid_to_def.read().get(&ty_id).unwrap().0)
|
||||
.unwrap()
|
||||
.read();
|
||||
let definition =
|
||||
top_level_defs.get(self.pyid_to_def.read().get(&ty_id).unwrap().0).unwrap().read();
|
||||
if let TopLevelDef::Class { fields, .. } = &*definition {
|
||||
let values: Result<Option<Vec<_>>, _> = fields
|
||||
.iter()
|
||||
|
@ -819,9 +765,7 @@ impl InnerResolver {
|
|||
let values = values?;
|
||||
if let Some(values) = values {
|
||||
let val = ty.const_named_struct(&values);
|
||||
let global = ctx
|
||||
.module
|
||||
.add_global(ty, Some(AddressSpace::Generic), &id_str);
|
||||
let global = ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str);
|
||||
global.set_initializer(&val);
|
||||
Ok(Some(global.as_pointer_value().into()))
|
||||
} else {
|
||||
|
@ -838,39 +782,32 @@ impl InnerResolver {
|
|||
py: Python,
|
||||
obj: &PyAny,
|
||||
) -> PyResult<Result<SymbolValue, String>> {
|
||||
let ty_id: u64 = self
|
||||
.helper
|
||||
.id_fn
|
||||
.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?
|
||||
.extract(py)?;
|
||||
Ok(
|
||||
if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
|
||||
let val: i32 = obj.extract()?;
|
||||
Ok(SymbolValue::I32(val))
|
||||
} else if ty_id == self.primitive_ids.int64 {
|
||||
let val: i64 = obj.extract()?;
|
||||
Ok(SymbolValue::I64(val))
|
||||
} 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 {
|
||||
let val: f64 = obj.extract()?;
|
||||
Ok(SymbolValue::Double(val))
|
||||
} else if ty_id == self.primitive_ids.tuple {
|
||||
let elements: &PyTuple = obj.cast_as()?;
|
||||
let elements: Result<Result<Vec<_>, String>, _> = elements
|
||||
.iter()
|
||||
.map(|elem| self.get_default_param_obj_value(py, elem))
|
||||
.collect();
|
||||
let elements = match elements? {
|
||||
Ok(el) => el,
|
||||
Err(err) => return Ok(Err(err)),
|
||||
};
|
||||
Ok(SymbolValue::Tuple(elements))
|
||||
} else {
|
||||
Err("only primitives values and tuple can be default parameter value".into())
|
||||
},
|
||||
)
|
||||
let ty_id: u64 =
|
||||
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
|
||||
Ok(if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
|
||||
let val: i32 = obj.extract()?;
|
||||
Ok(SymbolValue::I32(val))
|
||||
} else if ty_id == self.primitive_ids.int64 {
|
||||
let val: i64 = obj.extract()?;
|
||||
Ok(SymbolValue::I64(val))
|
||||
} 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 {
|
||||
let val: f64 = obj.extract()?;
|
||||
Ok(SymbolValue::Double(val))
|
||||
} else if ty_id == self.primitive_ids.tuple {
|
||||
let elements: &PyTuple = obj.cast_as()?;
|
||||
let elements: Result<Result<Vec<_>, String>, _> =
|
||||
elements.iter().map(|elem| self.get_default_param_obj_value(py, elem)).collect();
|
||||
let elements = match elements? {
|
||||
Ok(el) => el,
|
||||
Err(err) => return Ok(Err(err)),
|
||||
};
|
||||
Ok(SymbolValue::Tuple(elements))
|
||||
} else {
|
||||
Err("only primitives values and tuple can be default parameter value".into())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -885,12 +822,8 @@ impl SymbolResolver for Resolver {
|
|||
for (key, val) in members.iter() {
|
||||
let key: &str = key.extract()?;
|
||||
if key == id.to_string() {
|
||||
sym_value = Some(
|
||||
self.0
|
||||
.get_default_param_obj_value(py, val)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
);
|
||||
sym_value =
|
||||
Some(self.0.get_default_param_obj_value(py, val).unwrap().unwrap());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -989,22 +922,20 @@ impl SymbolResolver for Resolver {
|
|||
})
|
||||
}
|
||||
|
||||
fn get_symbol_location(&self, _: StrRef) -> Option<Location> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
{
|
||||
let id_to_def = self.0.id_to_def.read();
|
||||
id_to_def.get(&id).cloned()
|
||||
id_to_def.get(&id).cloned().ok_or_else(|| "".to_string())
|
||||
}
|
||||
.or_else(|| {
|
||||
let py_id = self.0.name_to_pyid.get(&id);
|
||||
let result = py_id.and_then(|id| self.0.pyid_to_def.read().get(id).copied());
|
||||
if let Some(result) = &result {
|
||||
self.0.id_to_def.write().insert(id, *result);
|
||||
}
|
||||
result
|
||||
.or_else(|_| {
|
||||
let py_id =
|
||||
self.0.name_to_pyid.get(&id).ok_or(format!("Undefined identifier `{}`", id))?;
|
||||
let result = self.0.pyid_to_def.read().get(py_id).copied().ok_or(format!(
|
||||
"`{}` is not registered in nac3, did you forgot to add @nac3?",
|
||||
id
|
||||
))?;
|
||||
self.0.id_to_def.write().insert(id, result);
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1014,8 +945,9 @@ impl SymbolResolver for Resolver {
|
|||
*id
|
||||
} else {
|
||||
let id = Python::with_gil(|py| -> PyResult<i32> {
|
||||
self.0.helper.store_str.call1(py, (s, ))?.extract(py)
|
||||
}).unwrap();
|
||||
self.0.helper.store_str.call1(py, (s,))?.extract(py)
|
||||
})
|
||||
.unwrap();
|
||||
string_store.insert(s.into(), id);
|
||||
id
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use nac3core::codegen::CodeGenContext;
|
||||
use inkwell::{values::BasicValueEnum, AddressSpace, AtomicOrdering};
|
||||
use nac3core::codegen::CodeGenContext;
|
||||
|
||||
pub trait TimeFns {
|
||||
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx>;
|
||||
|
@ -19,41 +19,23 @@ impl TimeFns for NowPinningTimeFns64 {
|
|||
.module
|
||||
.get_global("now")
|
||||
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
||||
let now_hiptr = ctx.builder.build_bitcast(
|
||||
now,
|
||||
i32_type.ptr_type(AddressSpace::Generic),
|
||||
"now_hiptr"
|
||||
);
|
||||
let now_hiptr =
|
||||
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::Generic), "now_hiptr");
|
||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
||||
let now_loptr = unsafe {
|
||||
ctx.builder.build_gep(
|
||||
now_hiptr,
|
||||
&[i32_type.const_int(2, false)],
|
||||
"now_gep",
|
||||
)
|
||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_gep")
|
||||
};
|
||||
if let (
|
||||
BasicValueEnum::IntValue(now_hi),
|
||||
BasicValueEnum::IntValue(now_lo)
|
||||
) = (
|
||||
if let (BasicValueEnum::IntValue(now_hi), BasicValueEnum::IntValue(now_lo)) = (
|
||||
ctx.builder.build_load(now_hiptr, "now_hi"),
|
||||
ctx.builder.build_load(now_loptr, "now_lo")
|
||||
ctx.builder.build_load(now_loptr, "now_lo"),
|
||||
) {
|
||||
let zext_hi = ctx.builder.build_int_z_extend(
|
||||
now_hi,
|
||||
i64_type,
|
||||
"now_zext_hi"
|
||||
);
|
||||
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "now_zext_hi");
|
||||
let shifted_hi = ctx.builder.build_left_shift(
|
||||
zext_hi,
|
||||
i64_type.const_int(32, false),
|
||||
"now_shifted_zext_hi"
|
||||
);
|
||||
let zext_lo = ctx.builder.build_int_z_extend(
|
||||
now_lo,
|
||||
i64_type,
|
||||
"now_zext_lo"
|
||||
"now_shifted_zext_hi",
|
||||
);
|
||||
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "now_zext_lo");
|
||||
ctx.builder.build_or(shifted_hi, zext_lo, "now_or").into()
|
||||
} else {
|
||||
unreachable!();
|
||||
|
@ -69,8 +51,7 @@ impl TimeFns for NowPinningTimeFns64 {
|
|||
let i64_32 = i64_type.const_int(32, false);
|
||||
if let BasicValueEnum::IntValue(time) = t {
|
||||
let time_hi = ctx.builder.build_int_truncate(
|
||||
ctx.builder
|
||||
.build_right_shift(time, i64_32, false, "now_lshr"),
|
||||
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
|
||||
i32_type,
|
||||
"now_trunc",
|
||||
);
|
||||
|
@ -86,11 +67,7 @@ impl TimeFns for NowPinningTimeFns64 {
|
|||
);
|
||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
||||
let now_loptr = unsafe {
|
||||
ctx.builder.build_gep(
|
||||
now_hiptr,
|
||||
&[i32_type.const_int(2, false)],
|
||||
"now_gep",
|
||||
)
|
||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_gep")
|
||||
};
|
||||
ctx.builder
|
||||
.build_store(now_hiptr, time_hi)
|
||||
|
@ -108,66 +85,54 @@ impl TimeFns for NowPinningTimeFns64 {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_delay_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, dt: BasicValueEnum<'ctx>) {
|
||||
fn emit_delay_mu<'ctx, 'a>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
dt: BasicValueEnum<'ctx>,
|
||||
) {
|
||||
let i64_type = ctx.ctx.i64_type();
|
||||
let i32_type = ctx.ctx.i32_type();
|
||||
let now = ctx
|
||||
.module
|
||||
.get_global("now")
|
||||
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
||||
let now_hiptr = ctx.builder.build_bitcast(
|
||||
now,
|
||||
i32_type.ptr_type(AddressSpace::Generic),
|
||||
"now_hiptr"
|
||||
);
|
||||
let now_hiptr =
|
||||
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::Generic), "now_hiptr");
|
||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
||||
let now_loptr = unsafe {
|
||||
ctx.builder.build_gep(
|
||||
now_hiptr,
|
||||
&[i32_type.const_int(2, false)],
|
||||
"now_loptr",
|
||||
)
|
||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_loptr")
|
||||
};
|
||||
if let (
|
||||
BasicValueEnum::IntValue(now_hi),
|
||||
BasicValueEnum::IntValue(now_lo),
|
||||
BasicValueEnum::IntValue(dt)
|
||||
BasicValueEnum::IntValue(dt),
|
||||
) = (
|
||||
ctx.builder.build_load(now_hiptr, "now_hi"),
|
||||
ctx.builder.build_load(now_loptr, "now_lo"),
|
||||
dt
|
||||
dt,
|
||||
) {
|
||||
let zext_hi = ctx.builder.build_int_z_extend(
|
||||
now_hi,
|
||||
i64_type,
|
||||
"now_zext_hi"
|
||||
);
|
||||
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "now_zext_hi");
|
||||
let shifted_hi = ctx.builder.build_left_shift(
|
||||
zext_hi,
|
||||
i64_type.const_int(32, false),
|
||||
"now_shifted_zext_hi"
|
||||
);
|
||||
let zext_lo = ctx.builder.build_int_z_extend(
|
||||
now_lo,
|
||||
i64_type,
|
||||
"now_zext_lo"
|
||||
"now_shifted_zext_hi",
|
||||
);
|
||||
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "now_zext_lo");
|
||||
let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now_or");
|
||||
|
||||
|
||||
let time = ctx.builder.build_int_add(now_val, dt, "now_add");
|
||||
let time_hi = ctx.builder.build_int_truncate(
|
||||
ctx.builder
|
||||
.build_right_shift(
|
||||
time,
|
||||
i64_type.const_int(32, false),
|
||||
false,
|
||||
"now_lshr"
|
||||
),
|
||||
ctx.builder.build_right_shift(
|
||||
time,
|
||||
i64_type.const_int(32, false),
|
||||
false,
|
||||
"now_lshr",
|
||||
),
|
||||
i32_type,
|
||||
"now_trunc",
|
||||
);
|
||||
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
|
||||
|
||||
|
||||
ctx.builder
|
||||
.build_store(now_hiptr, time_hi)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
|
@ -200,9 +165,7 @@ impl TimeFns for NowPinningTimeFns {
|
|||
if let BasicValueEnum::IntValue(now_raw) = now_raw {
|
||||
let i64_32 = i64_type.const_int(32, false);
|
||||
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now_shl");
|
||||
let now_hi = ctx
|
||||
.builder
|
||||
.build_right_shift(now_raw, i64_32, false, "now_lshr");
|
||||
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now_lshr");
|
||||
ctx.builder.build_or(now_lo, now_hi, "now_or").into()
|
||||
} else {
|
||||
unreachable!();
|
||||
|
@ -215,8 +178,7 @@ impl TimeFns for NowPinningTimeFns {
|
|||
let i64_32 = i64_type.const_int(32, false);
|
||||
if let BasicValueEnum::IntValue(time) = t {
|
||||
let time_hi = ctx.builder.build_int_truncate(
|
||||
ctx.builder
|
||||
.build_right_shift(time, i64_32, false, "now_lshr"),
|
||||
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
|
||||
i32_type,
|
||||
"now_trunc",
|
||||
);
|
||||
|
@ -232,11 +194,7 @@ impl TimeFns for NowPinningTimeFns {
|
|||
);
|
||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
||||
let now_loptr = unsafe {
|
||||
ctx.builder.build_gep(
|
||||
now_hiptr,
|
||||
&[i32_type.const_int(1, false)],
|
||||
"now_gep",
|
||||
)
|
||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now_gep")
|
||||
};
|
||||
ctx.builder
|
||||
.build_store(now_hiptr, time_hi)
|
||||
|
@ -254,7 +212,11 @@ impl TimeFns for NowPinningTimeFns {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_delay_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, dt: BasicValueEnum<'ctx>) {
|
||||
fn emit_delay_mu<'ctx, 'a>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
dt: BasicValueEnum<'ctx>,
|
||||
) {
|
||||
let i32_type = ctx.ctx.i32_type();
|
||||
let i64_type = ctx.ctx.i64_type();
|
||||
let i64_32 = i64_type.const_int(32, false);
|
||||
|
@ -263,18 +225,13 @@ impl TimeFns for NowPinningTimeFns {
|
|||
.get_global("now")
|
||||
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
||||
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now");
|
||||
if let (BasicValueEnum::IntValue(now_raw), BasicValueEnum::IntValue(dt)) =
|
||||
(now_raw, dt)
|
||||
{
|
||||
if let (BasicValueEnum::IntValue(now_raw), BasicValueEnum::IntValue(dt)) = (now_raw, dt) {
|
||||
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now_shl");
|
||||
let now_hi = ctx
|
||||
.builder
|
||||
.build_right_shift(now_raw, i64_32, false, "now_lshr");
|
||||
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now_lshr");
|
||||
let now_val = ctx.builder.build_or(now_lo, now_hi, "now_or");
|
||||
let time = ctx.builder.build_int_add(now_val, dt, "now_add");
|
||||
let time_hi = ctx.builder.build_int_truncate(
|
||||
ctx.builder
|
||||
.build_right_shift(time, i64_32, false, "now_lshr"),
|
||||
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
|
||||
i32_type,
|
||||
"now_trunc",
|
||||
);
|
||||
|
@ -286,11 +243,7 @@ impl TimeFns for NowPinningTimeFns {
|
|||
);
|
||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
||||
let now_loptr = unsafe {
|
||||
ctx.builder.build_gep(
|
||||
now_hiptr,
|
||||
&[i32_type.const_int(1, false)],
|
||||
"now_gep",
|
||||
)
|
||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now_gep")
|
||||
};
|
||||
ctx.builder
|
||||
.build_store(now_hiptr, time_hi)
|
||||
|
@ -315,33 +268,36 @@ pub struct ExternTimeFns {}
|
|||
|
||||
impl TimeFns for ExternTimeFns {
|
||||
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx> {
|
||||
let now_mu = ctx
|
||||
.module
|
||||
.get_function("now_mu")
|
||||
.unwrap_or_else(|| ctx.module.add_function("now_mu", ctx.ctx.i64_type().fn_type(&[], false), None));
|
||||
ctx.builder
|
||||
.build_call(now_mu, &[], "now_mu")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap()
|
||||
let now_mu = ctx.module.get_function("now_mu").unwrap_or_else(|| {
|
||||
ctx.module.add_function("now_mu", ctx.ctx.i64_type().fn_type(&[], false), None)
|
||||
});
|
||||
ctx.builder.build_call(now_mu, &[], "now_mu").try_as_basic_value().left().unwrap()
|
||||
}
|
||||
|
||||
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
|
||||
let at_mu = ctx
|
||||
.module
|
||||
.get_function("at_mu")
|
||||
.unwrap_or_else(|| ctx.module.add_function("at_mu", ctx.ctx.void_type().fn_type(&[ctx.ctx.i64_type().into()], false), None));
|
||||
ctx.builder
|
||||
.build_call(at_mu, &[t.into()], "at_mu");
|
||||
let at_mu = ctx.module.get_function("at_mu").unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
"at_mu",
|
||||
ctx.ctx.void_type().fn_type(&[ctx.ctx.i64_type().into()], false),
|
||||
None,
|
||||
)
|
||||
});
|
||||
ctx.builder.build_call(at_mu, &[t.into()], "at_mu");
|
||||
}
|
||||
|
||||
fn emit_delay_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, dt: BasicValueEnum<'ctx>) {
|
||||
let delay_mu = ctx
|
||||
.module
|
||||
.get_function("delay_mu")
|
||||
.unwrap_or_else(|| ctx.module.add_function("delay_mu", ctx.ctx.void_type().fn_type(&[ctx.ctx.i64_type().into()], false), None));
|
||||
ctx.builder
|
||||
.build_call(delay_mu, &[dt.into()], "delay_mu");
|
||||
fn emit_delay_mu<'ctx, 'a>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
dt: BasicValueEnum<'ctx>,
|
||||
) {
|
||||
let delay_mu = ctx.module.get_function("delay_mu").unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
"delay_mu",
|
||||
ctx.ctx.void_type().fn_type(&[ctx.ctx.i64_type().into()], false),
|
||||
None,
|
||||
)
|
||||
});
|
||||
ctx.builder.build_call(delay_mu, &[dt.into()], "delay_mu");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ pub enum Primitive {
|
|||
None,
|
||||
Range,
|
||||
Str,
|
||||
Exception
|
||||
Exception,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -157,21 +157,25 @@ impl ConcreteTypeStore {
|
|||
TypeEnum::TObj { obj_id, fields, params } => ConcreteTypeEnum::TObj {
|
||||
obj_id: *obj_id,
|
||||
fields: fields
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter_map(|(name, ty)| {
|
||||
// here we should not have type vars, but some partial instantiated
|
||||
// class methods can still have uninstantiated type vars, so
|
||||
// filter out all the methods, as this will not affect codegen
|
||||
if let TypeEnum::TFunc( .. ) = &*unifier.get_ty(ty.0) {
|
||||
if let TypeEnum::TFunc(..) = &*unifier.get_ty(ty.0) {
|
||||
None
|
||||
} else {
|
||||
Some((*name, (self.from_unifier_type(unifier, primitives, ty.0, cache), ty.1)))
|
||||
Some((
|
||||
*name,
|
||||
(
|
||||
self.from_unifier_type(unifier, primitives, ty.0, cache),
|
||||
ty.1,
|
||||
),
|
||||
))
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
params: params
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|(id, ty)| {
|
||||
(*id, self.from_unifier_type(unifier, primitives, *ty, cache))
|
||||
|
@ -182,7 +186,6 @@ impl ConcreteTypeStore {
|
|||
ty: self.from_unifier_type(unifier, primitives, *ty, cache),
|
||||
},
|
||||
TypeEnum::TFunc(signature) => {
|
||||
let signature = signature.borrow();
|
||||
self.from_signature(unifier, primitives, &*signature, cache)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -210,7 +213,7 @@ impl ConcreteTypeStore {
|
|||
return if let Some(ty) = ty {
|
||||
*ty
|
||||
} else {
|
||||
*ty = Some(unifier.get_fresh_var().0);
|
||||
*ty = Some(unifier.get_dummy_var().0);
|
||||
ty.unwrap()
|
||||
};
|
||||
}
|
||||
|
@ -249,34 +252,27 @@ impl ConcreteTypeStore {
|
|||
.map(|(name, cty)| {
|
||||
(*name, (self.to_unifier_type(unifier, primitives, cty.0, cache), cty.1))
|
||||
})
|
||||
.collect::<HashMap<_, _>>()
|
||||
.into(),
|
||||
.collect::<HashMap<_, _>>(),
|
||||
params: params
|
||||
.iter()
|
||||
.map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache)))
|
||||
.collect::<HashMap<_, _>>()
|
||||
.into(),
|
||||
.collect::<HashMap<_, _>>(),
|
||||
},
|
||||
ConcreteTypeEnum::TFunc { args, ret, vars } => TypeEnum::TFunc(
|
||||
FunSignature {
|
||||
args: args
|
||||
.iter()
|
||||
.map(|arg| FuncArg {
|
||||
name: arg.name,
|
||||
ty: self.to_unifier_type(unifier, primitives, arg.ty, cache),
|
||||
default_value: arg.default_value.clone(),
|
||||
})
|
||||
.collect(),
|
||||
ret: self.to_unifier_type(unifier, primitives, *ret, cache),
|
||||
vars: vars
|
||||
.iter()
|
||||
.map(|(id, cty)| {
|
||||
(*id, self.to_unifier_type(unifier, primitives, *cty, cache))
|
||||
})
|
||||
.collect::<HashMap<_, _>>(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
ConcreteTypeEnum::TFunc { args, ret, vars } => TypeEnum::TFunc(FunSignature {
|
||||
args: args
|
||||
.iter()
|
||||
.map(|arg| FuncArg {
|
||||
name: arg.name,
|
||||
ty: self.to_unifier_type(unifier, primitives, arg.ty, cache),
|
||||
default_value: arg.default_value.clone(),
|
||||
})
|
||||
.collect(),
|
||||
ret: self.to_unifier_type(unifier, primitives, *ret, cache),
|
||||
vars: vars
|
||||
.iter()
|
||||
.map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache)))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
}),
|
||||
};
|
||||
let result = unifier.add_ty(result);
|
||||
if let Some(ty) = cache.get(&cty).unwrap() {
|
||||
|
|
|
@ -3,9 +3,9 @@ use std::{collections::HashMap, convert::TryInto, iter::once};
|
|||
use crate::{
|
||||
codegen::{
|
||||
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
||||
stmt::gen_raise,
|
||||
get_llvm_type,
|
||||
irrt::*,
|
||||
stmt::gen_raise,
|
||||
CodeGenContext, CodeGenTask,
|
||||
},
|
||||
symbol_resolver::{SymbolValue, ValueEnum},
|
||||
|
@ -13,12 +13,14 @@ use crate::{
|
|||
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
|
||||
};
|
||||
use inkwell::{
|
||||
AddressSpace,
|
||||
types::{BasicType, BasicTypeEnum},
|
||||
values::{BasicValueEnum, FunctionValue, IntValue, PointerValue}
|
||||
values::{BasicValueEnum, FunctionValue, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use itertools::{chain, izip, zip, Itertools};
|
||||
use nac3parser::ast::{self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef};
|
||||
use nac3parser::ast::{
|
||||
self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
|
||||
};
|
||||
|
||||
use super::CodeGenerator;
|
||||
|
||||
|
@ -31,7 +33,7 @@ pub fn get_subst_key(
|
|||
let mut vars = obj
|
||||
.map(|ty| {
|
||||
if let TypeEnum::TObj { params, .. } = &*unifier.get_ty(ty) {
|
||||
params.borrow().clone()
|
||||
params.clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -40,7 +42,14 @@ pub fn get_subst_key(
|
|||
vars.extend(fun_vars.iter());
|
||||
let sorted = vars.keys().filter(|id| filter.map(|v| v.contains(id)).unwrap_or(true)).sorted();
|
||||
sorted
|
||||
.map(|id| unifier.stringify(vars[id], &mut |id| id.to_string(), &mut |id| id.to_string()))
|
||||
.map(|id| {
|
||||
unifier.internal_stringify(
|
||||
vars[id],
|
||||
&mut |id| id.to_string(),
|
||||
&mut |id| id.to_string(),
|
||||
&mut None,
|
||||
)
|
||||
})
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
|
@ -77,14 +86,19 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
index
|
||||
}
|
||||
|
||||
pub fn gen_symbol_val(&mut self, generator: &mut dyn CodeGenerator, val: &SymbolValue) -> BasicValueEnum<'ctx> {
|
||||
pub fn gen_symbol_val(
|
||||
&mut self,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
val: &SymbolValue,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
match val {
|
||||
SymbolValue::I32(v) => self.ctx.i32_type().const_int(*v as u64, true).into(),
|
||||
SymbolValue::I64(v) => self.ctx.i64_type().const_int(*v as u64, true).into(),
|
||||
SymbolValue::Bool(v) => self.ctx.bool_type().const_int(*v as u64, true).into(),
|
||||
SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(),
|
||||
SymbolValue::Str(v) => {
|
||||
let str_ptr = self.builder.build_global_string_ptr(v, "const").as_pointer_value().into();
|
||||
let str_ptr =
|
||||
self.builder.build_global_string_ptr(v, "const").as_pointer_value().into();
|
||||
let size = generator.get_size_type(self.ctx).const_int(v.len() as u64, false);
|
||||
let ty = self.get_llvm_type(generator, self.primitives.str).into_struct_type();
|
||||
ty.const_named_struct(&[str_ptr, size.into()]).into()
|
||||
|
@ -125,7 +139,12 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn gen_const(&mut self, generator: &mut dyn CodeGenerator, value: &Constant, ty: Type) -> BasicValueEnum<'ctx> {
|
||||
pub fn gen_const(
|
||||
&mut self,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
value: &Constant,
|
||||
ty: Type,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
match value {
|
||||
Constant::Bool(v) => {
|
||||
assert!(self.unifier.unioned(ty, self.primitives.bool));
|
||||
|
@ -163,10 +182,12 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
if let Some(v) = self.const_strings.get(v) {
|
||||
*v
|
||||
} else {
|
||||
let str_ptr = self.builder.build_global_string_ptr(v, "const").as_pointer_value().into();
|
||||
let str_ptr =
|
||||
self.builder.build_global_string_ptr(v, "const").as_pointer_value().into();
|
||||
let size = generator.get_size_type(self.ctx).const_int(v.len() as u64, false);
|
||||
let ty = self.get_llvm_type(generator, self.primitives.str);
|
||||
let val = ty.into_struct_type().const_named_struct(&[str_ptr, size.into()]).into();
|
||||
let val =
|
||||
ty.into_struct_type().const_named_struct(&[str_ptr, size.into()]).into();
|
||||
self.const_strings.insert(v.to_string(), val);
|
||||
val
|
||||
}
|
||||
|
@ -262,12 +283,16 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
&self,
|
||||
fun: FunctionValue<'ctx>,
|
||||
params: &[BasicValueEnum<'ctx>],
|
||||
call_name: &str
|
||||
call_name: &str,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
if let Some(target) = self.unwind_target {
|
||||
let current = self.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let then_block = self.ctx.append_basic_block(current, &format!("after.{}", call_name));
|
||||
let result = self.builder.build_invoke(fun, params, then_block, target, call_name).try_as_basic_value().left();
|
||||
let result = self
|
||||
.builder
|
||||
.build_invoke(fun, params, then_block, target, call_name)
|
||||
.try_as_basic_value()
|
||||
.left();
|
||||
self.builder.position_at_end(then_block);
|
||||
result
|
||||
} else {
|
||||
|
@ -279,7 +304,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
pub fn gen_string<G: CodeGenerator, S: Into<String>>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
s: S
|
||||
s: S,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str)
|
||||
}
|
||||
|
@ -290,7 +315,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
name: &str,
|
||||
msg: BasicValueEnum<'ctx>,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
loc: Location
|
||||
loc: Location,
|
||||
) {
|
||||
let ty = self.get_llvm_type(generator, self.primitives.exception).into_pointer_type();
|
||||
let zelf_ty: BasicTypeEnum = ty.get_element_type().into_struct_type().into();
|
||||
|
@ -302,13 +327,21 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
let id = self.resolver.get_string_id(name);
|
||||
self.builder.build_store(id_ptr, int32.const_int(id as u64, false));
|
||||
let ptr = self.builder.build_in_bounds_gep(
|
||||
zelf, &[zero, int32.const_int(5, false)], "exn.msg");
|
||||
zelf,
|
||||
&[zero, int32.const_int(5, false)],
|
||||
"exn.msg",
|
||||
);
|
||||
self.builder.build_store(ptr, msg);
|
||||
let i64_zero = self.ctx.i64_type().const_zero();
|
||||
for (i, attr_ind) in [6, 7, 8].iter().enumerate() {
|
||||
let ptr = self.builder.build_in_bounds_gep(
|
||||
zelf, &[zero, int32.const_int(*attr_ind, false)], "exn.param");
|
||||
let val = params[i].map_or(i64_zero, |v| self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext"));
|
||||
zelf,
|
||||
&[zero, int32.const_int(*attr_ind, false)],
|
||||
"exn.param",
|
||||
);
|
||||
let val = params[i].map_or(i64_zero, |v| {
|
||||
self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext")
|
||||
});
|
||||
self.builder.build_store(ptr, val);
|
||||
}
|
||||
}
|
||||
|
@ -322,19 +355,28 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
err_name: &str,
|
||||
err_msg: &str,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
loc: Location
|
||||
loc: Location,
|
||||
) {
|
||||
let i1 = self.ctx.bool_type();
|
||||
let i1_true = i1.const_all_ones();
|
||||
let expect_fun = self.module.get_function("llvm.expect.i1").unwrap_or_else(|| {
|
||||
self.module.add_function("llvm.expect", i1.fn_type(&[i1.into(), i1.into()], false), None)
|
||||
self.module.add_function(
|
||||
"llvm.expect",
|
||||
i1.fn_type(&[i1.into(), i1.into()], false),
|
||||
None,
|
||||
)
|
||||
});
|
||||
// we assume that the condition is most probably true, so the normal path is the most
|
||||
// probable path
|
||||
// even if this assumption is violated, it does not matter as exception unwinding is
|
||||
// slow anyway...
|
||||
let cond = self.builder.build_call(expect_fun, &[cond.into(), i1_true.into()], "expect")
|
||||
.try_as_basic_value().left().unwrap().into_int_value();
|
||||
let cond = self
|
||||
.builder
|
||||
.build_call(expect_fun, &[cond.into(), i1_true.into()], "expect")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
let current_fun = self.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let then_block = self.ctx.append_basic_block(current_fun, "succ");
|
||||
let exn_block = self.ctx.append_basic_block(current_fun, "fail");
|
||||
|
@ -352,7 +394,7 @@ pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
|
|||
signature: &FunSignature,
|
||||
def: &TopLevelDef,
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
match def {
|
||||
TopLevelDef::Class { methods, .. } => {
|
||||
// TODO: what about other fields that require alloca?
|
||||
|
@ -374,9 +416,9 @@ pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
|
|||
Some((signature.ret, zelf.into())),
|
||||
(&sign, fun_id),
|
||||
params,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
zelf
|
||||
Ok(zelf)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -387,7 +429,7 @@ pub fn gen_func_instance<'ctx, 'a>(
|
|||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, &mut TopLevelDef, String),
|
||||
id: usize,
|
||||
) -> String {
|
||||
) -> Result<String, String> {
|
||||
if let (
|
||||
sign,
|
||||
TopLevelDef::Function {
|
||||
|
@ -396,56 +438,57 @@ pub fn gen_func_instance<'ctx, 'a>(
|
|||
key,
|
||||
) = fun
|
||||
{
|
||||
instance_to_symbol.get(&key).cloned().unwrap_or_else(|| {
|
||||
let symbol = format!("{}.{}", name, instance_to_symbol.len());
|
||||
instance_to_symbol.insert(key.clone(), symbol.clone());
|
||||
let key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), sign, Some(var_id));
|
||||
let instance = instance_to_stmt.get(&key).unwrap();
|
||||
if let Some(sym) = instance_to_symbol.get(&key) {
|
||||
return Ok(sym.clone());
|
||||
}
|
||||
let symbol = format!("{}.{}", name, instance_to_symbol.len());
|
||||
instance_to_symbol.insert(key, symbol.clone());
|
||||
let key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), sign, Some(var_id));
|
||||
let instance = instance_to_stmt.get(&key).unwrap();
|
||||
|
||||
let mut store = ConcreteTypeStore::new();
|
||||
let mut cache = HashMap::new();
|
||||
let mut store = ConcreteTypeStore::new();
|
||||
let mut cache = HashMap::new();
|
||||
|
||||
let subst = sign
|
||||
.vars
|
||||
.iter()
|
||||
.map(|(id, ty)| {
|
||||
(
|
||||
*instance.subst.get(id).unwrap(),
|
||||
store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, *ty, &mut cache),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let subst = sign
|
||||
.vars
|
||||
.iter()
|
||||
.map(|(id, ty)| {
|
||||
(
|
||||
*instance.subst.get(id).unwrap(),
|
||||
store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, *ty, &mut cache),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut signature =
|
||||
store.from_signature(&mut ctx.unifier, &ctx.primitives, sign, &mut cache);
|
||||
let mut signature =
|
||||
store.from_signature(&mut ctx.unifier, &ctx.primitives, sign, &mut cache);
|
||||
|
||||
if let Some(obj) = &obj {
|
||||
let zelf =
|
||||
store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, obj.0, &mut cache);
|
||||
if let ConcreteTypeEnum::TFunc { args, .. } = &mut signature {
|
||||
args.insert(
|
||||
0,
|
||||
ConcreteFuncArg { name: "self".into(), ty: zelf, default_value: None },
|
||||
)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
if let Some(obj) = &obj {
|
||||
let zelf =
|
||||
store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, obj.0, &mut cache);
|
||||
if let ConcreteTypeEnum::TFunc { args, .. } = &mut signature {
|
||||
args.insert(
|
||||
0,
|
||||
ConcreteFuncArg { name: "self".into(), ty: zelf, default_value: None },
|
||||
)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
let signature = store.add_cty(signature);
|
||||
}
|
||||
let signature = store.add_cty(signature);
|
||||
|
||||
ctx.registry.add_task(CodeGenTask {
|
||||
symbol_name: symbol.clone(),
|
||||
body: instance.body.clone(),
|
||||
resolver: resolver.as_ref().unwrap().clone(),
|
||||
calls: instance.calls.clone(),
|
||||
subst,
|
||||
signature,
|
||||
store,
|
||||
unifier_index: instance.unifier_id,
|
||||
id,
|
||||
});
|
||||
symbol
|
||||
})
|
||||
ctx.registry.add_task(CodeGenTask {
|
||||
symbol_name: symbol.clone(),
|
||||
body: instance.body.clone(),
|
||||
resolver: resolver.as_ref().unwrap().clone(),
|
||||
calls: instance.calls.clone(),
|
||||
subst,
|
||||
signature,
|
||||
store,
|
||||
unifier_index: instance.unifier_id,
|
||||
id,
|
||||
});
|
||||
Ok(symbol)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -457,9 +500,8 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let definition = ctx.top_level.definitions.read().get(fun.1 .0).cloned().unwrap();
|
||||
|
||||
let id;
|
||||
let key;
|
||||
let param_vals;
|
||||
|
@ -484,7 +526,10 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
// default value handling
|
||||
for k in keys.into_iter() {
|
||||
mapping.insert(k.name, ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into());
|
||||
mapping.insert(
|
||||
k.name,
|
||||
ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into(),
|
||||
);
|
||||
}
|
||||
// reorder the parameters
|
||||
let mut real_params =
|
||||
|
@ -492,7 +537,6 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
if let Some(obj) = &obj {
|
||||
real_params.insert(0, obj.1.clone());
|
||||
}
|
||||
|
||||
let static_params = real_params
|
||||
.iter()
|
||||
.enumerate()
|
||||
|
@ -530,16 +574,16 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
.into_iter()
|
||||
.map(|p| p.to_basic_value_enum(ctx, generator))
|
||||
.collect_vec();
|
||||
instance_to_symbol.get(&key).cloned()
|
||||
instance_to_symbol.get(&key).cloned().ok_or_else(|| "".into())
|
||||
}
|
||||
TopLevelDef::Class { .. } => {
|
||||
return Some(generator.gen_constructor(ctx, fun.0, &*def, params))
|
||||
return Ok(Some(generator.gen_constructor(ctx, fun.0, &*def, params)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
.or_else(|_: String| {
|
||||
generator.gen_func_instance(ctx, obj.clone(), (fun.0, &mut *definition.write(), key), id)
|
||||
});
|
||||
})?;
|
||||
let fun_val = ctx.module.get_function(&symbol).unwrap_or_else(|| {
|
||||
let mut args = fun.0.args.clone();
|
||||
if let Some(obj) = &obj {
|
||||
|
@ -554,8 +598,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
};
|
||||
ctx.module.add_function(&symbol, fun_ty, None)
|
||||
});
|
||||
|
||||
ctx.build_call_or_invoke(fun_val, ¶m_vals, "call")
|
||||
Ok(ctx.build_call_or_invoke(fun_val, ¶m_vals, "call"))
|
||||
}
|
||||
|
||||
pub fn destructure_range<'ctx, 'a>(
|
||||
|
@ -607,7 +650,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
expr: &Expr<Option<Type>>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
if let ExprKind::ListComp { elt, generators } = &expr.node {
|
||||
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let test_bb = ctx.ctx.append_basic_block(current, "test");
|
||||
|
@ -615,13 +658,13 @@ 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);
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let size_t = generator.get_size_type(ctx.ctx);
|
||||
let zero_size_t = size_t.const_zero();
|
||||
let zero_32 = int32.const_zero();
|
||||
|
||||
let index = generator.gen_var_alloc(ctx, size_t.into());
|
||||
let index = generator.gen_var_alloc(ctx, size_t.into())?;
|
||||
ctx.builder.build_store(index, zero_size_t);
|
||||
|
||||
let elem_ty = ctx.get_llvm_type(generator, elt.custom.unwrap());
|
||||
|
@ -664,7 +707,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
list_content =
|
||||
ctx.build_gep_and_load(list, &[zero_size_t, zero_32]).into_pointer_value();
|
||||
|
||||
let i = generator.gen_store_target(ctx, target);
|
||||
let i = generator.gen_store_target(ctx, target)?;
|
||||
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
ctx.builder.position_at_end(test_bb);
|
||||
|
@ -699,7 +742,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
list = allocate_list(generator, ctx, elem_ty, length);
|
||||
list_content =
|
||||
ctx.build_gep_and_load(list, &[zero_size_t, zero_32]).into_pointer_value();
|
||||
let counter = generator.gen_var_alloc(ctx, size_t.into());
|
||||
let counter = generator.gen_var_alloc(ctx, size_t.into())?;
|
||||
// counter = -1
|
||||
ctx.builder.build_store(counter, size_t.const_int(u64::max_value(), true));
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
|
@ -714,11 +757,11 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
.build_gep_and_load(iter_val.into_pointer_value(), &[zero_size_t, zero_32])
|
||||
.into_pointer_value();
|
||||
let val = ctx.build_gep_and_load(arr_ptr, &[tmp]);
|
||||
generator.gen_assign(ctx, target, val.into());
|
||||
generator.gen_assign(ctx, target, val.into())?;
|
||||
}
|
||||
for cond in ifs.iter() {
|
||||
let result = generator
|
||||
.gen_expr(ctx, cond)
|
||||
.gen_expr(ctx, cond)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
|
@ -726,7 +769,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx.builder.build_conditional_branch(result, succ, test_bb);
|
||||
ctx.builder.position_at_end(succ);
|
||||
}
|
||||
let elem = generator.gen_expr(ctx, elt).unwrap();
|
||||
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);
|
||||
|
@ -739,7 +782,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx.builder.build_gep(list, &[zero_size_t, int32.const_int(1, false)], "length")
|
||||
};
|
||||
ctx.builder.build_store(len_ptr, ctx.builder.build_load(index, "index"));
|
||||
list.into()
|
||||
Ok(list.into())
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -751,16 +794,16 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
left: &Expr<Option<Type>>,
|
||||
op: &Operator,
|
||||
right: &Expr<Option<Type>>,
|
||||
) -> ValueEnum<'ctx> {
|
||||
) -> 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);
|
||||
let right = generator.gen_expr(ctx, right)?.unwrap().to_basic_value_enum(ctx, generator);
|
||||
|
||||
// we can directly compare the types, because we've got their representatives
|
||||
// which would be unchanged until further unification, which we would never do
|
||||
// when doing code generation for function instances
|
||||
if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
|
||||
Ok(if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
|
||||
ctx.gen_int_ops(op, left, right)
|
||||
} else if ty1 == ty2 && ctx.primitives.float == ty1 {
|
||||
ctx.gen_float_ops(op, left, right)
|
||||
|
@ -783,17 +826,17 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
.into()
|
||||
.into())
|
||||
}
|
||||
|
||||
pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
expr: &Expr<Option<Type>>,
|
||||
) -> Option<ValueEnum<'ctx>> {
|
||||
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_int(0, false);
|
||||
Some(match &expr.node {
|
||||
Ok(Some(match &expr.node {
|
||||
ExprKind::Constant { value, .. } => {
|
||||
let ty = expr.custom.unwrap();
|
||||
ctx.gen_const(generator, value, ty).into()
|
||||
|
@ -823,8 +866,12 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
// we should use memcpy for that instead of generating thousands of stores
|
||||
let elements = elts
|
||||
.iter()
|
||||
.map(|x| generator.gen_expr(ctx, x).unwrap().to_basic_value_enum(ctx, generator))
|
||||
.collect_vec();
|
||||
.map(|x| {
|
||||
generator
|
||||
.gen_expr(ctx, x)
|
||||
.map(|v| v.unwrap().to_basic_value_enum(ctx, generator))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let ty = if elements.is_empty() {
|
||||
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(expr.custom.unwrap()) {
|
||||
ctx.get_llvm_type(generator, *ty)
|
||||
|
@ -852,8 +899,12 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
ExprKind::Tuple { elts, .. } => {
|
||||
let element_val = elts
|
||||
.iter()
|
||||
.map(|x| generator.gen_expr(ctx, x).unwrap().to_basic_value_enum(ctx, generator))
|
||||
.collect_vec();
|
||||
.map(|x| {
|
||||
generator
|
||||
.gen_expr(ctx, x)
|
||||
.map(|v| v.unwrap().to_basic_value_enum(ctx, generator))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let element_ty = element_val.iter().map(BasicValueEnum::get_type).collect_vec();
|
||||
let tuple_ty = ctx.ctx.struct_type(&element_ty, false);
|
||||
let tuple_ptr = ctx.builder.build_alloca(tuple_ty, "tuple");
|
||||
|
@ -871,7 +922,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
ExprKind::Attribute { value, attr, .. } => {
|
||||
// note that we would handle class methods directly in calls
|
||||
match generator.gen_expr(ctx, value).unwrap() {
|
||||
match generator.gen_expr(ctx, value)?.unwrap() {
|
||||
ValueEnum::Static(v) => v.get_field(*attr, ctx).unwrap_or_else(|| {
|
||||
let v = v.to_basic_value_enum(ctx, generator);
|
||||
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
|
||||
|
@ -892,7 +943,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
ExprKind::BoolOp { op, values } => {
|
||||
// requires conditional branches for short-circuiting...
|
||||
let left = generator
|
||||
.gen_expr(ctx, &values[0])
|
||||
.gen_expr(ctx, &values[0])?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
|
@ -908,7 +959,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
ctx.builder.position_at_end(b_bb);
|
||||
let b = generator
|
||||
.gen_expr(ctx, &values[1])
|
||||
.gen_expr(ctx, &values[1])?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
|
@ -918,7 +969,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
Boolop::And => {
|
||||
ctx.builder.position_at_end(a_bb);
|
||||
let a = generator
|
||||
.gen_expr(ctx, &values[1])
|
||||
.gen_expr(ctx, &values[1])?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
|
@ -934,10 +985,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
phi.add_incoming(&[(&a, a_bb), (&b, b_bb)]);
|
||||
phi.as_basic_value().into()
|
||||
}
|
||||
ExprKind::BinOp { op, left, right } => gen_binop_expr(generator, ctx, left, op, right),
|
||||
ExprKind::BinOp { op, left, right } => gen_binop_expr(generator, ctx, left, op, right)?,
|
||||
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);
|
||||
let val =
|
||||
generator.gen_expr(ctx, operand)?.unwrap().to_basic_value_enum(ctx, generator);
|
||||
if ty == ctx.primitives.bool {
|
||||
let val = val.into_int_value();
|
||||
match op {
|
||||
|
@ -984,7 +1036,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
ExprKind::Compare { left, ops, comparators } => {
|
||||
izip!(chain(once(left.as_ref()), comparators.iter()), comparators.iter(), ops.iter(),)
|
||||
.fold(None, |prev, (lhs, rhs, op)| {
|
||||
.fold(Ok(None), |prev: Result<Option<_>, String>, (lhs, rhs, op)| {
|
||||
let ty = ctx.unifier.get_representative(lhs.custom.unwrap());
|
||||
let current =
|
||||
if [ctx.primitives.int32, ctx.primitives.int64, ctx.primitives.bool]
|
||||
|
@ -995,11 +1047,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
BasicValueEnum::IntValue(rhs),
|
||||
) = (
|
||||
generator
|
||||
.gen_expr(ctx, lhs)
|
||||
.gen_expr(ctx, lhs)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator),
|
||||
generator
|
||||
.gen_expr(ctx, rhs)
|
||||
.gen_expr(ctx, rhs)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator),
|
||||
) {
|
||||
|
@ -1023,11 +1075,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
BasicValueEnum::FloatValue(rhs),
|
||||
) = (
|
||||
generator
|
||||
.gen_expr(ctx, lhs)
|
||||
.gen_expr(ctx, lhs)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator),
|
||||
generator
|
||||
.gen_expr(ctx, rhs)
|
||||
.gen_expr(ctx, rhs)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator),
|
||||
) {
|
||||
|
@ -1048,14 +1100,14 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
prev.map(|v| ctx.builder.build_and(v, current, "cmp")).or(Some(current))
|
||||
})
|
||||
Ok(prev?.map(|v| ctx.builder.build_and(v, current, "cmp")).or(Some(current)))
|
||||
})?
|
||||
.unwrap()
|
||||
.into() // as there should be at least 1 element, it should never be none
|
||||
}
|
||||
ExprKind::IfExp { test, body, orelse } => {
|
||||
let test = generator
|
||||
.gen_expr(ctx, test)
|
||||
.gen_expr(ctx, test)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
|
@ -1065,10 +1117,10 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
||||
ctx.builder.build_conditional_branch(test, then_bb, else_bb);
|
||||
ctx.builder.position_at_end(then_bb);
|
||||
let a = generator.gen_expr(ctx, body).unwrap().to_basic_value_enum(ctx, generator);
|
||||
let a = generator.gen_expr(ctx, body)?.unwrap().to_basic_value_enum(ctx, generator);
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
ctx.builder.position_at_end(else_bb);
|
||||
let b = generator.gen_expr(ctx, orelse).unwrap().to_basic_value_enum(ctx, generator);
|
||||
let b = generator.gen_expr(ctx, orelse)?.unwrap().to_basic_value_enum(ctx, generator);
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
ctx.builder.position_at_end(cont_bb);
|
||||
let phi = ctx.builder.build_phi(a.get_type(), "ifexpr");
|
||||
|
@ -1076,14 +1128,17 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
phi.as_basic_value().into()
|
||||
}
|
||||
ExprKind::Call { func, args, keywords } => {
|
||||
let mut params =
|
||||
args.iter().map(|arg| (None, generator.gen_expr(ctx, arg).unwrap())).collect_vec();
|
||||
let mut params = args
|
||||
.iter()
|
||||
.map(|arg| Ok((None, generator.gen_expr(ctx, arg)?.unwrap())) as Result<_, String>)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let kw_iter = keywords.iter().map(|kw| {
|
||||
(
|
||||
Ok((
|
||||
Some(*kw.node.arg.as_ref().unwrap()),
|
||||
generator.gen_expr(ctx, &kw.node.value).unwrap(),
|
||||
)
|
||||
generator.gen_expr(ctx, &kw.node.value)?.unwrap(),
|
||||
)) as Result<_, String>
|
||||
});
|
||||
let kw_iter = kw_iter.collect::<Result<Vec<_>, _>>()?;
|
||||
params.extend(kw_iter);
|
||||
let call = ctx.calls.get(&expr.location.into());
|
||||
let signature = match call {
|
||||
|
@ -1091,22 +1146,26 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
None => {
|
||||
let ty = func.custom.unwrap();
|
||||
if let TypeEnum::TFunc(sign) = &*ctx.unifier.get_ty(ty) {
|
||||
sign.borrow().clone()
|
||||
sign.clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
};
|
||||
match &func.as_ref().node {
|
||||
let func = func.as_ref();
|
||||
match &func.node {
|
||||
ExprKind::Name { id, .. } => {
|
||||
// TODO: handle primitive casts and function pointers
|
||||
let fun = ctx.resolver.get_identifier_def(*id).expect("Unknown identifier");
|
||||
return generator
|
||||
.gen_call(ctx, None, (&signature, fun), params)
|
||||
.map(|v| v.into());
|
||||
let fun = ctx
|
||||
.resolver
|
||||
.get_identifier_def(*id)
|
||||
.map_err(|e| format!("{} (at {})", e, func.location))?;
|
||||
return Ok(generator
|
||||
.gen_call(ctx, None, (&signature, fun), params)?
|
||||
.map(|v| v.into()));
|
||||
}
|
||||
ExprKind::Attribute { value, attr, .. } => {
|
||||
let val = generator.gen_expr(ctx, value).unwrap();
|
||||
let val = generator.gen_expr(ctx, value)?.unwrap();
|
||||
let id = if let TypeEnum::TObj { obj_id, .. } =
|
||||
&*ctx.unifier.get_ty(value.custom.unwrap())
|
||||
{
|
||||
|
@ -1129,14 +1188,14 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
unreachable!()
|
||||
}
|
||||
};
|
||||
return generator
|
||||
return Ok(generator
|
||||
.gen_call(
|
||||
ctx,
|
||||
Some((value.custom.unwrap(), val)),
|
||||
(&signature, fun_id),
|
||||
params,
|
||||
)
|
||||
.map(|v| v.into());
|
||||
)?
|
||||
.map(|v| v.into()));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
@ -1144,7 +1203,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
ExprKind::Subscript { value, slice, .. } => {
|
||||
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)
|
||||
.gen_expr(ctx, value)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_pointer_value();
|
||||
|
@ -1153,7 +1212,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
||||
let one = int32.const_int(1, false);
|
||||
let (start, end, step) =
|
||||
handle_slice_indices(lower, upper, step, ctx, generator, v);
|
||||
handle_slice_indices(lower, upper, step, ctx, generator, v)?;
|
||||
let length = calculate_len_for_slice_range(
|
||||
ctx,
|
||||
start,
|
||||
|
@ -1174,7 +1233,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
);
|
||||
let res_array_ret = allocate_list(generator, ctx, ty, length);
|
||||
let res_ind =
|
||||
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret);
|
||||
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)?;
|
||||
list_slice_assignment(
|
||||
ctx,
|
||||
generator.get_size_type(ctx.ctx),
|
||||
|
@ -1186,29 +1245,52 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
);
|
||||
res_array_ret.into()
|
||||
} else {
|
||||
let len = ctx.build_gep_and_load(v, &[zero, int32.const_int(1, false)])
|
||||
let len = ctx
|
||||
.build_gep_and_load(v, &[zero, int32.const_int(1, false)])
|
||||
.into_int_value();
|
||||
let raw_index = generator
|
||||
.gen_expr(ctx, slice)
|
||||
.gen_expr(ctx, slice)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
let raw_index = ctx.builder.build_int_s_extend(raw_index, generator.get_size_type(ctx.ctx), "sext");
|
||||
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 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();
|
||||
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], expr.location);
|
||||
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],
|
||||
expr.location,
|
||||
);
|
||||
ctx.build_gep_and_load(arr_ptr, &[index])
|
||||
}
|
||||
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)
|
||||
.gen_expr(ctx, value)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_struct_value();
|
||||
|
@ -1224,7 +1306,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
.into(),
|
||||
ExprKind::ListComp { .. } => gen_comprehension(generator, ctx, expr).into(),
|
||||
ExprKind::ListComp { .. } => gen_comprehension(generator, ctx, expr)?.into(),
|
||||
_ => unimplemented!(),
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ pub trait CodeGenerator {
|
|||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
) -> Option<BasicValueEnum<'ctx>>
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
@ -45,7 +45,7 @@ pub trait CodeGenerator {
|
|||
signature: &FunSignature,
|
||||
def: &TopLevelDef,
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
) -> BasicValueEnum<'ctx>
|
||||
) -> Result<BasicValueEnum<'ctx>, String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
@ -65,7 +65,7 @@ pub trait CodeGenerator {
|
|||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, &mut TopLevelDef, String),
|
||||
id: usize,
|
||||
) -> String {
|
||||
) -> Result<String, String> {
|
||||
gen_func_instance(ctx, obj, fun, id)
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ pub trait CodeGenerator {
|
|||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
expr: &Expr<Option<Type>>,
|
||||
) -> Option<ValueEnum<'ctx>>
|
||||
) -> Result<Option<ValueEnum<'ctx>>, String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
@ -87,7 +87,7 @@ pub trait CodeGenerator {
|
|||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
) -> Result<PointerValue<'ctx>, String> {
|
||||
gen_var(ctx, ty)
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ pub trait CodeGenerator {
|
|||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
pattern: &Expr<Option<Type>>,
|
||||
) -> PointerValue<'ctx>
|
||||
) -> Result<PointerValue<'ctx>, String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
@ -109,7 +109,8 @@ pub trait CodeGenerator {
|
|||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
target: &Expr<Option<Type>>,
|
||||
value: ValueEnum<'ctx>,
|
||||
) where
|
||||
) -> Result<(), String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_assign(self, ctx, target, value)
|
||||
|
@ -117,45 +118,65 @@ pub trait CodeGenerator {
|
|||
|
||||
/// Generate code for a while expression.
|
||||
/// Return true if the while loop must early return
|
||||
fn gen_while<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
fn gen_while<'ctx, 'a>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_while(self, ctx, stmt);
|
||||
gen_while(self, ctx, stmt)
|
||||
}
|
||||
|
||||
/// Generate code for a while expression.
|
||||
/// Return true if the while loop must early return
|
||||
fn gen_for<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
fn gen_for<'ctx, 'a>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_for(self, ctx, stmt);
|
||||
gen_for(self, ctx, stmt)
|
||||
}
|
||||
|
||||
/// Generate code for an if expression.
|
||||
/// Return true if the statement must early return
|
||||
fn gen_if<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
fn gen_if<'ctx, 'a>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_if(self, ctx, stmt);
|
||||
gen_if(self, ctx, stmt)
|
||||
}
|
||||
|
||||
fn gen_with<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
fn gen_with<'ctx, 'a>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_with(self, ctx, stmt);
|
||||
gen_with(self, ctx, stmt)
|
||||
}
|
||||
|
||||
/// Generate code for a statement
|
||||
/// Return true if the statement must early return
|
||||
fn gen_stmt<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
fn gen_stmt<'ctx, 'a>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_stmt(self, ctx, stmt);
|
||||
gen_stmt(self, ctx, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ pub fn calculate_len_for_slice_range<'ctx, 'a>(
|
|||
/// case Some(e):
|
||||
/// handle_in_bound(e) + 1
|
||||
/// ,step
|
||||
/// )
|
||||
/// )
|
||||
/// ```
|
||||
pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
||||
start: &Option<Box<Expr<Option<Type>>>>,
|
||||
|
@ -125,31 +125,31 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
|||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut G,
|
||||
list: PointerValue<'ctx>,
|
||||
) -> (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>) {
|
||||
) -> Result<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), String> {
|
||||
// TODO: throw exception when step is 0
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let one = int32.const_int(1, false);
|
||||
let length = ctx.build_gep_and_load(list, &[zero, one]).into_int_value();
|
||||
let length = ctx.builder.build_int_truncate_or_bit_cast(length, int32, "leni32");
|
||||
match (start, end, step) {
|
||||
Ok(match (start, end, step) {
|
||||
(s, e, None) => (
|
||||
s.as_ref().map_or_else(
|
||||
|| int32.const_zero(),
|
||||
|| Ok(int32.const_zero()),
|
||||
|s| handle_slice_index_bound(s, ctx, generator, length),
|
||||
),
|
||||
)?,
|
||||
{
|
||||
let e = e.as_ref().map_or_else(
|
||||
|| length,
|
||||
|| Ok(length),
|
||||
|e| handle_slice_index_bound(e, ctx, generator, length),
|
||||
);
|
||||
)?;
|
||||
ctx.builder.build_int_sub(e, one, "final_end")
|
||||
},
|
||||
one,
|
||||
),
|
||||
(s, e, Some(step)) => {
|
||||
let step = generator
|
||||
.gen_expr(ctx, step)
|
||||
.gen_expr(ctx, step)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
|
@ -158,7 +158,7 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
|||
(
|
||||
match s {
|
||||
Some(s) => {
|
||||
let s = handle_slice_index_bound(s, ctx, generator, length);
|
||||
let s = handle_slice_index_bound(s, ctx, generator, length)?;
|
||||
ctx.builder
|
||||
.build_select(
|
||||
ctx.builder.build_and(
|
||||
|
@ -181,7 +181,7 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
|||
},
|
||||
match e {
|
||||
Some(e) => {
|
||||
let e = handle_slice_index_bound(e, ctx, generator, length);
|
||||
let e = handle_slice_index_bound(e, ctx, generator, length)?;
|
||||
ctx.builder
|
||||
.build_select(
|
||||
neg,
|
||||
|
@ -196,7 +196,7 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
|||
step,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// this function allows index out of range, since python
|
||||
|
@ -206,7 +206,7 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
|
|||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut G,
|
||||
length: IntValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
) -> Result<IntValue<'ctx>, String> {
|
||||
const SYMBOL: &str = "__nac3_slice_index_bound";
|
||||
let func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
|
||||
let i32_t = ctx.ctx.i32_type();
|
||||
|
@ -214,13 +214,14 @@ 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);
|
||||
ctx.builder
|
||||
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator);
|
||||
Ok(ctx
|
||||
.builder
|
||||
.build_call(func, &[i.into(), length.into()], "bounded_ind")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap()
|
||||
.into_int_value()
|
||||
.into_int_value())
|
||||
}
|
||||
|
||||
/// This function handles 'end' **inclusively**.
|
||||
|
|
|
@ -20,7 +20,7 @@ use inkwell::{
|
|||
use itertools::Itertools;
|
||||
use nac3parser::ast::{Stmt, StrRef};
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
|
@ -30,8 +30,8 @@ use std::thread;
|
|||
pub mod concrete_type;
|
||||
pub mod expr;
|
||||
mod generator;
|
||||
pub mod stmt;
|
||||
pub mod irrt;
|
||||
pub mod stmt;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
@ -206,16 +206,26 @@ impl WorkerRegistry {
|
|||
let passes = PassManager::create(&module);
|
||||
pass_builder.populate_function_pass_manager(&passes);
|
||||
|
||||
let mut errors = HashSet::new();
|
||||
while let Some(task) = self.receiver.recv().unwrap() {
|
||||
let tmp_module = context.create_module("tmp");
|
||||
let result = gen_func(&context, generator, self, builder, tmp_module, task);
|
||||
builder = result.0;
|
||||
passes.run_on(&result.2);
|
||||
module.link_in_module(result.1).unwrap();
|
||||
// module = result.1;
|
||||
match gen_func(&context, generator, self, builder, tmp_module, task) {
|
||||
Ok(result) => {
|
||||
builder = result.0;
|
||||
passes.run_on(&result.2);
|
||||
module.link_in_module(result.1).unwrap();
|
||||
}
|
||||
Err((old_builder, e)) => {
|
||||
builder = old_builder;
|
||||
errors.insert(e);
|
||||
}
|
||||
}
|
||||
*self.task_count.lock() -= 1;
|
||||
self.wait_condvar.notify_all();
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
panic!("Codegen error: {}", errors.into_iter().sorted().join("\n----------\n"));
|
||||
}
|
||||
|
||||
let result = module.verify();
|
||||
if let Err(err) = result {
|
||||
|
@ -264,13 +274,22 @@ fn get_llvm_type<'ctx>(
|
|||
// a struct with fields in the order of declaration
|
||||
let top_level_defs = top_level.definitions.read();
|
||||
let definition = top_level_defs.get(obj_id.0).unwrap();
|
||||
let ty = if let TopLevelDef::Class { name, fields: fields_list, .. } = &*definition.read()
|
||||
let ty = if let TopLevelDef::Class { name, fields: fields_list, .. } =
|
||||
&*definition.read()
|
||||
{
|
||||
let struct_type = ctx.opaque_struct_type(&name.to_string());
|
||||
let fields = fields.borrow();
|
||||
let fields = fields_list
|
||||
.iter()
|
||||
.map(|f| get_llvm_type(ctx, generator, unifier, top_level, type_cache, fields[&f.0].0))
|
||||
.map(|f| {
|
||||
get_llvm_type(
|
||||
ctx,
|
||||
generator,
|
||||
unifier,
|
||||
top_level,
|
||||
type_cache,
|
||||
fields[&f.0].0,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
struct_type.set_body(&fields, false);
|
||||
struct_type.ptr_type(AddressSpace::Generic).into()
|
||||
|
@ -289,9 +308,12 @@ fn get_llvm_type<'ctx>(
|
|||
}
|
||||
TList { ty } => {
|
||||
// a struct with an integer and a pointer to an array
|
||||
let element_type = get_llvm_type(ctx, generator, unifier, top_level, type_cache, *ty);
|
||||
let fields =
|
||||
[element_type.ptr_type(AddressSpace::Generic).into(), generator.get_size_type(ctx).into()];
|
||||
let element_type =
|
||||
get_llvm_type(ctx, generator, unifier, top_level, type_cache, *ty);
|
||||
let fields = [
|
||||
element_type.ptr_type(AddressSpace::Generic).into(),
|
||||
generator.get_size_type(ctx).into(),
|
||||
];
|
||||
ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
TVirtual { .. } => unimplemented!(),
|
||||
|
@ -309,7 +331,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
builder: Builder<'ctx>,
|
||||
module: Module<'ctx>,
|
||||
task: CodeGenTask,
|
||||
) -> (Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>) {
|
||||
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
|
||||
let top_level_ctx = registry.top_level_ctx.clone();
|
||||
let static_value_store = registry.static_value_store.clone();
|
||||
let (mut unifier, primitives) = {
|
||||
|
@ -322,14 +344,17 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
// this should be unification between variables and concrete types
|
||||
// and should not cause any problem...
|
||||
let b = task.store.to_unifier_type(&mut unifier, &primitives, *b, &mut cache);
|
||||
unifier.unify(*a, b).or_else(|err| {
|
||||
if matches!(&*unifier.get_ty(*a), TypeEnum::TRigidVar { .. }) {
|
||||
unifier.replace_rigid_var(*a, b);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}).unwrap()
|
||||
unifier
|
||||
.unify(*a, b)
|
||||
.or_else(|err| {
|
||||
if matches!(&*unifier.get_ty(*a), TypeEnum::TRigidVar { .. }) {
|
||||
unifier.replace_rigid_var(*a, b);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// rebuild primitive store with unique representatives
|
||||
|
@ -358,10 +383,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
str_type.set_body(&fields, false);
|
||||
str_type.into()
|
||||
}),
|
||||
(
|
||||
primitives.range,
|
||||
context.i32_type().array_type(3).ptr_type(AddressSpace::Generic).into(),
|
||||
),
|
||||
(primitives.range, context.i32_type().array_type(3).ptr_type(AddressSpace::Generic).into()),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -371,17 +393,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
let int32 = context.i32_type().into();
|
||||
let int64 = context.i64_type().into();
|
||||
let str_ty = *type_cache.get(&primitives.str).unwrap();
|
||||
let fields = [
|
||||
int32,
|
||||
str_ty,
|
||||
int32,
|
||||
int32,
|
||||
str_ty,
|
||||
str_ty,
|
||||
int64,
|
||||
int64,
|
||||
int64
|
||||
];
|
||||
let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64];
|
||||
exception.set_body(&fields, false);
|
||||
exception.ptr_type(AddressSpace::Generic).into()
|
||||
});
|
||||
|
@ -405,15 +417,30 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
let params = args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, arg.ty).into()
|
||||
get_llvm_type(
|
||||
context,
|
||||
generator,
|
||||
&mut unifier,
|
||||
top_level_ctx.as_ref(),
|
||||
&mut type_cache,
|
||||
arg.ty,
|
||||
)
|
||||
.into()
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let fn_type = if unifier.unioned(ret, primitives.none) {
|
||||
context.void_type().fn_type(¶ms, false)
|
||||
} else {
|
||||
get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, ret)
|
||||
.fn_type(¶ms, false)
|
||||
get_llvm_type(
|
||||
context,
|
||||
generator,
|
||||
&mut unifier,
|
||||
top_level_ctx.as_ref(),
|
||||
&mut type_cache,
|
||||
ret,
|
||||
)
|
||||
.fn_type(¶ms, false)
|
||||
};
|
||||
|
||||
let symbol = &task.symbol_name;
|
||||
|
@ -436,7 +463,14 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
for (n, arg) in args.iter().enumerate() {
|
||||
let param = fn_val.get_nth_param(n as u32).unwrap();
|
||||
let alloca = builder.build_alloca(
|
||||
get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, arg.ty),
|
||||
get_llvm_type(
|
||||
context,
|
||||
generator,
|
||||
&mut unifier,
|
||||
top_level_ctx.as_ref(),
|
||||
&mut type_cache,
|
||||
arg.ty,
|
||||
),
|
||||
&arg.name.to_string(),
|
||||
);
|
||||
builder.build_store(alloca, param);
|
||||
|
@ -478,8 +512,12 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
static_value_store,
|
||||
};
|
||||
|
||||
let mut err = None;
|
||||
for stmt in task.body.iter() {
|
||||
generator.gen_stmt(&mut code_gen_context, stmt);
|
||||
if let Err(e) = generator.gen_stmt(&mut code_gen_context, stmt) {
|
||||
err = Some(e);
|
||||
break;
|
||||
}
|
||||
if code_gen_context.is_terminated() {
|
||||
break;
|
||||
}
|
||||
|
@ -490,6 +528,9 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
}
|
||||
|
||||
let CodeGenContext { builder, module, .. } = code_gen_context;
|
||||
if let Some(e) = err {
|
||||
return Err((builder, e));
|
||||
}
|
||||
|
||||
(builder, module, fn_val)
|
||||
Ok((builder, module, fn_val))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use super::{
|
|||
use crate::{
|
||||
codegen::expr::gen_binop_expr,
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
typecheck::typedef::{Type, TypeEnum, FunSignature}
|
||||
typecheck::typedef::{FunSignature, Type, TypeEnum},
|
||||
};
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
|
@ -16,39 +16,45 @@ use inkwell::{
|
|||
values::{BasicValue, BasicValueEnum, FunctionValue, PointerValue},
|
||||
IntPredicate::EQ,
|
||||
};
|
||||
use nac3parser::ast::{ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef, Constant};
|
||||
use nac3parser::ast::{
|
||||
Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef,
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub fn gen_var<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
) -> Result<PointerValue<'ctx>, String> {
|
||||
// put the alloca in init block
|
||||
let current = ctx.builder.get_insert_block().unwrap();
|
||||
// position before the last branching instruction...
|
||||
ctx.builder.position_before(&ctx.init_bb.get_last_instruction().unwrap());
|
||||
let ptr = ctx.builder.build_alloca(ty, "tmp");
|
||||
ctx.builder.position_at_end(current);
|
||||
ptr
|
||||
Ok(ptr)
|
||||
}
|
||||
|
||||
pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
pattern: &Expr<Option<Type>>,
|
||||
) -> PointerValue<'ctx> {
|
||||
) -> Result<PointerValue<'ctx>, String> {
|
||||
// very similar to gen_expr, but we don't do an extra load at the end
|
||||
// and we flatten nested tuples
|
||||
match &pattern.node {
|
||||
ExprKind::Name { id, .. } => ctx.var_assignment.get(id).map(|v| v.0).unwrap_or_else(|| {
|
||||
let ptr_ty = ctx.get_llvm_type(generator, pattern.custom.unwrap());
|
||||
let ptr = generator.gen_var_alloc(ctx, ptr_ty);
|
||||
ctx.var_assignment.insert(*id, (ptr, None, 0));
|
||||
ptr
|
||||
}),
|
||||
Ok(match &pattern.node {
|
||||
ExprKind::Name { id, .. } => {
|
||||
ctx.var_assignment.get(id).map(|v| Ok(v.0) as Result<_, String>).unwrap_or_else(
|
||||
|| {
|
||||
let ptr_ty = ctx.get_llvm_type(generator, pattern.custom.unwrap());
|
||||
let ptr = generator.gen_var_alloc(ctx, ptr_ty)?;
|
||||
ctx.var_assignment.insert(*id, (ptr, None, 0));
|
||||
Ok(ptr)
|
||||
},
|
||||
)?
|
||||
}
|
||||
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);
|
||||
let ptr = if let BasicValueEnum::PointerValue(v) = val {
|
||||
v
|
||||
} else {
|
||||
|
@ -68,12 +74,12 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
|||
ExprKind::Subscript { value, slice, .. } => {
|
||||
let i32_type = ctx.ctx.i32_type();
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)
|
||||
.gen_expr(ctx, value)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_pointer_value();
|
||||
let index = generator
|
||||
.gen_expr(ctx, slice)
|
||||
.gen_expr(ctx, slice)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
|
@ -85,7 +91,7 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
||||
|
@ -93,7 +99,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
target: &Expr<Option<Type>>,
|
||||
value: ValueEnum<'ctx>,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
match &target.node {
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
if let BasicValueEnum::StructValue(v) = value.to_basic_value_enum(ctx, generator) {
|
||||
|
@ -102,7 +108,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
.builder
|
||||
.build_extract_value(v, u32::try_from(i).unwrap(), "struct_elem")
|
||||
.unwrap();
|
||||
generator.gen_assign(ctx, elt, v.into());
|
||||
generator.gen_assign(ctx, elt, v.into())?;
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
|
@ -113,21 +119,20 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
{
|
||||
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
||||
let ls = generator
|
||||
.gen_expr(ctx, ls)
|
||||
.gen_expr(ctx, ls)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_pointer_value();
|
||||
let (start, end, step) =
|
||||
handle_slice_indices(lower, upper, step, ctx, generator, ls);
|
||||
handle_slice_indices(lower, upper, step, ctx, generator, ls)?;
|
||||
let value = value.to_basic_value_enum(ctx, generator).into_pointer_value();
|
||||
let ty = if let TypeEnum::TList { ty } =
|
||||
&*ctx.unifier.get_ty(target.custom.unwrap())
|
||||
{
|
||||
ctx.get_llvm_type(generator, *ty)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value);
|
||||
let ty =
|
||||
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(target.custom.unwrap()) {
|
||||
ctx.get_llvm_type(generator, *ty)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value)?;
|
||||
list_slice_assignment(
|
||||
ctx,
|
||||
generator.get_size_type(ctx.ctx),
|
||||
|
@ -142,7 +147,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
let ptr = generator.gen_store_target(ctx, target);
|
||||
let ptr = generator.gen_store_target(ctx, target)?;
|
||||
if let ExprKind::Name { id, .. } = &target.node {
|
||||
let (_, static_value, counter) = ctx.var_assignment.get_mut(id).unwrap();
|
||||
*counter += 1;
|
||||
|
@ -153,14 +158,15 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
let val = value.to_basic_value_enum(ctx, generator);
|
||||
ctx.builder.build_store(ptr, val);
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::For { iter, target, body, orelse, .. } = &stmt.node {
|
||||
// var_assignment static values may be changed in another branch
|
||||
// if so, remove the static value as it may not be correct in this branch
|
||||
|
@ -179,11 +185,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);
|
||||
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
|
||||
// setup
|
||||
let iter_val = iter_val.into_pointer_value();
|
||||
let i = generator.gen_store_target(ctx, target);
|
||||
let i = generator.gen_store_target(ctx, target)?;
|
||||
let (start, end, step) = destructure_range(ctx, iter_val);
|
||||
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
|
@ -214,7 +220,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
);
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
} else {
|
||||
let counter = generator.gen_var_alloc(ctx, size_t.into());
|
||||
let counter = generator.gen_var_alloc(ctx, size_t.into())?;
|
||||
// counter = -1
|
||||
ctx.builder.build_store(counter, size_t.const_int(u64::max_value(), true));
|
||||
let len = ctx
|
||||
|
@ -235,10 +241,10 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
.build_gep_and_load(iter_val.into_pointer_value(), &[zero, zero])
|
||||
.into_pointer_value();
|
||||
let val = ctx.build_gep_and_load(arr_ptr, &[tmp]);
|
||||
generator.gen_assign(ctx, target, val.into());
|
||||
generator.gen_assign(ctx, target, val.into())?;
|
||||
}
|
||||
|
||||
gen_block(generator, ctx, body.iter());
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
|
@ -250,7 +256,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
gen_block(generator, ctx, orelse.iter());
|
||||
gen_block(generator, ctx, orelse.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
|
@ -266,13 +272,14 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::While { test, body, orelse, .. } = &stmt.node {
|
||||
// var_assignment static values may be changed in another branch
|
||||
// if so, remove the static value as it may not be correct in this branch
|
||||
|
@ -289,14 +296,14 @@ 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);
|
||||
if let BasicValueEnum::IntValue(test) = test {
|
||||
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
gen_block(generator, ctx, body.iter());
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
|
@ -308,7 +315,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
gen_block(generator, ctx, orelse.iter());
|
||||
gen_block(generator, ctx, orelse.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
|
@ -324,13 +331,14 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::If { test, body, orelse, .. } = &stmt.node {
|
||||
// var_assignment static values may be changed in another branch
|
||||
// if so, remove the static value as it may not be correct in this branch
|
||||
|
@ -349,14 +357,14 @@ 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);
|
||||
if let BasicValueEnum::IntValue(test) = test {
|
||||
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
gen_block(generator, ctx, body.iter());
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
|
@ -372,7 +380,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
gen_block(generator, ctx, orelse.iter());
|
||||
gen_block(generator, ctx, orelse.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
if cont_bb.is_none() {
|
||||
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
||||
|
@ -392,6 +400,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn final_proxy<'ctx, 'a>(
|
||||
|
@ -417,10 +426,10 @@ pub fn get_builtins<'ctx, 'a, G: CodeGenerator>(
|
|||
) -> FunctionValue<'ctx> {
|
||||
ctx.module.get_function(symbol).unwrap_or_else(|| {
|
||||
let ty = match symbol {
|
||||
"__artiq_raise" => ctx.ctx.void_type().fn_type(
|
||||
&[ctx.get_llvm_type(generator, ctx.primitives.exception).into()],
|
||||
false,
|
||||
),
|
||||
"__artiq_raise" => ctx
|
||||
.ctx
|
||||
.void_type()
|
||||
.fn_type(&[ctx.get_llvm_type(generator, ctx.primitives.exception).into()], false),
|
||||
"__artiq_resume" => ctx.ctx.void_type().fn_type(&[], false),
|
||||
"__artiq_end_catch" => ctx.ctx.void_type().fn_type(&[], false),
|
||||
_ => unimplemented!(),
|
||||
|
@ -441,8 +450,8 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
_fun: (&FunSignature, DefinitionId),
|
||||
mut args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
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 int32 = ctx.ctx.i32_type();
|
||||
|
@ -456,19 +465,16 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
};
|
||||
let defs = ctx.top_level.definitions.read();
|
||||
let def = defs[zelf_id].read();
|
||||
let zelf_name = if let TopLevelDef::Class { name, .. } = &*def {
|
||||
*name
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let zelf_name =
|
||||
if let TopLevelDef::Class { name, .. } = &*def { *name } else { unreachable!() };
|
||||
let exception_name = format!("0:{}", zelf_name);
|
||||
unsafe {
|
||||
let id_ptr = ctx.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id");
|
||||
let id = ctx.resolver.get_string_id(&exception_name);
|
||||
ctx.builder.build_store(id_ptr, int32.const_int(id as u64, false));
|
||||
let empty_string = ctx.gen_const(generator, &Constant::Str("".into()), ctx.primitives.str);
|
||||
let ptr = ctx.builder.build_in_bounds_gep(
|
||||
zelf, &[zero, int32.const_int(5, false)], "exn.msg");
|
||||
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)
|
||||
} else {
|
||||
|
@ -482,23 +488,32 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
ctx.ctx.i64_type().const_zero().into()
|
||||
};
|
||||
let ptr = ctx.builder.build_in_bounds_gep(
|
||||
zelf, &[zero, int32.const_int(*i, false)], "exn.param");
|
||||
zelf,
|
||||
&[zero, int32.const_int(*i, false)],
|
||||
"exn.param",
|
||||
);
|
||||
ctx.builder.build_store(ptr, value);
|
||||
}
|
||||
// set file, func to empty string
|
||||
for i in [1, 4].iter() {
|
||||
let ptr = ctx.builder.build_in_bounds_gep(
|
||||
zelf, &[zero, int32.const_int(*i, false)], "exn.str");
|
||||
zelf,
|
||||
&[zero, int32.const_int(*i, false)],
|
||||
"exn.str",
|
||||
);
|
||||
ctx.builder.build_store(ptr, empty_string);
|
||||
}
|
||||
// set ints to zero
|
||||
for i in [2, 3].iter() {
|
||||
let ptr = ctx.builder.build_in_bounds_gep(
|
||||
zelf, &[zero, int32.const_int(*i, false)], "exn.ints");
|
||||
zelf,
|
||||
&[zero, int32.const_int(*i, false)],
|
||||
"exn.ints",
|
||||
);
|
||||
ctx.builder.build_store(ptr, zero);
|
||||
}
|
||||
}
|
||||
Some(zelf.into())
|
||||
Ok(Some(zelf.into()))
|
||||
}
|
||||
|
||||
pub fn gen_raise<'ctx, 'a, G: CodeGenerator>(
|
||||
|
@ -512,17 +527,33 @@ pub fn gen_raise<'ctx, 'a, G: CodeGenerator>(
|
|||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let exception = exception.into_pointer_value();
|
||||
let file_ptr = ctx.builder.build_in_bounds_gep(exception, &[zero, int32.const_int(1, false)], "file_ptr");
|
||||
let file_ptr = ctx.builder.build_in_bounds_gep(
|
||||
exception,
|
||||
&[zero, int32.const_int(1, false)],
|
||||
"file_ptr",
|
||||
);
|
||||
let filename = ctx.gen_string(generator, loc.file.0);
|
||||
ctx.builder.build_store(file_ptr, filename);
|
||||
let row_ptr = ctx.builder.build_in_bounds_gep(exception, &[zero, int32.const_int(2, false)], "row_ptr");
|
||||
let row_ptr = ctx.builder.build_in_bounds_gep(
|
||||
exception,
|
||||
&[zero, int32.const_int(2, false)],
|
||||
"row_ptr",
|
||||
);
|
||||
ctx.builder.build_store(row_ptr, int32.const_int(loc.row as u64, false));
|
||||
let col_ptr = ctx.builder.build_in_bounds_gep(exception, &[zero, int32.const_int(3, false)], "col_ptr");
|
||||
let col_ptr = ctx.builder.build_in_bounds_gep(
|
||||
exception,
|
||||
&[zero, int32.const_int(3, false)],
|
||||
"col_ptr",
|
||||
);
|
||||
ctx.builder.build_store(col_ptr, int32.const_int(loc.column as u64, false));
|
||||
|
||||
let current_fun = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
|
||||
let name_ptr = ctx.builder.build_in_bounds_gep(exception, &[zero, int32.const_int(4, false)], "name_ptr");
|
||||
let name_ptr = ctx.builder.build_in_bounds_gep(
|
||||
exception,
|
||||
&[zero, int32.const_int(4, false)],
|
||||
"name_ptr",
|
||||
);
|
||||
ctx.builder.build_store(name_ptr, fun_name);
|
||||
}
|
||||
|
||||
|
@ -540,7 +571,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
target: &Stmt<Option<Type>>,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::Try { body, handlers, orelse, finalbody, .. } = &target.node {
|
||||
// if we need to generate anything related to exception, we must have personality defined
|
||||
let personality_symbol = ctx.top_level.personality_symbol.as_ref().unwrap();
|
||||
|
@ -564,7 +595,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
let mut old_return = None;
|
||||
let mut old_outer_final = None;
|
||||
let has_cleanup = if !finalbody.is_empty() {
|
||||
let final_state = generator.gen_var_alloc(ctx, ptr_type.into());
|
||||
let final_state = generator.gen_var_alloc(ctx, ptr_type.into())?;
|
||||
old_outer_final = ctx.outer_final.replace((final_state, Vec::new(), Vec::new()));
|
||||
if let Some((continue_target, break_target)) = ctx.loop_target {
|
||||
let break_proxy = ctx.ctx.append_basic_block(current_fun, "try.break");
|
||||
|
@ -596,7 +627,11 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
for handler_node in handlers.iter() {
|
||||
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler_node.node;
|
||||
// none or Exception
|
||||
if type_.is_none() || ctx.unifier.unioned(type_.as_ref().unwrap().custom.unwrap(), ctx.primitives.exception) {
|
||||
if type_.is_none()
|
||||
|| ctx
|
||||
.unifier
|
||||
.unioned(type_.as_ref().unwrap().custom.unwrap(), ctx.primitives.exception)
|
||||
{
|
||||
clauses.push(None);
|
||||
found_catch_all = true;
|
||||
break;
|
||||
|
@ -622,9 +657,9 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
let old_clauses = ctx.outer_catch_clauses.replace((all_clauses, dispatcher, exn));
|
||||
let old_unwind = ctx.unwind_target.replace(landingpad);
|
||||
gen_block(generator, ctx, body.iter());
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
if ctx.builder.get_insert_block().unwrap().get_terminator().is_none() {
|
||||
gen_block(generator, ctx, orelse.iter());
|
||||
gen_block(generator, ctx, orelse.iter())?;
|
||||
}
|
||||
let body = ctx.builder.get_insert_block().unwrap();
|
||||
// reset old_clauses and old_unwind
|
||||
|
@ -723,11 +758,11 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx.builder.position_at_end(handler_bb);
|
||||
if let Some(name) = name {
|
||||
let exn_ty = ctx.get_llvm_type(generator, type_.as_ref().unwrap().custom.unwrap());
|
||||
let exn_store = generator.gen_var_alloc(ctx, exn_ty);
|
||||
let exn_store = generator.gen_var_alloc(ctx, exn_ty)?;
|
||||
ctx.var_assignment.insert(*name, (exn_store, None, 0));
|
||||
ctx.builder.build_store(exn_store, exn.as_basic_value());
|
||||
}
|
||||
gen_block(generator, ctx, body.iter());
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
let current = ctx.builder.get_insert_block().unwrap();
|
||||
// only need to call end catch if not terminated
|
||||
// otherwise, we already handled in return/break/continue/raise
|
||||
|
@ -813,7 +848,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
// exception path
|
||||
let cleanup = cleanup.unwrap();
|
||||
ctx.builder.position_at_end(cleanup);
|
||||
gen_block(generator, ctx, finalbody.iter());
|
||||
gen_block(generator, ctx, finalbody.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
ctx.builder.build_unreachable();
|
||||
|
@ -825,7 +860,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
final_targets.push(tail);
|
||||
let finalizer = ctx.ctx.append_basic_block(current_fun, "try.finally");
|
||||
ctx.builder.position_at_end(finalizer);
|
||||
gen_block(generator, ctx, finalbody.iter());
|
||||
gen_block(generator, ctx, finalbody.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
let dest = ctx.builder.build_load(final_state, "final_dest");
|
||||
ctx.builder.build_indirect_branch(dest, &final_targets);
|
||||
|
@ -847,6 +882,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
ctx.builder.position_at_end(tail);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -855,20 +891,21 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
|
||||
_: &mut G,
|
||||
_: &mut CodeGenContext<'ctx, 'a>,
|
||||
_: &Stmt<Option<Type>>,
|
||||
) -> bool {
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
// TODO: Implement with statement after finishing exceptions
|
||||
unimplemented!()
|
||||
Err(format!("With statement with custom types is not yet supported (at {})", stmt.location))
|
||||
}
|
||||
|
||||
pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
value: &Option<Box<Expr<Option<Type>>>>,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
let value = value
|
||||
.as_ref()
|
||||
.map(|v| generator.gen_expr(ctx, v).unwrap().to_basic_value_enum(ctx, generator));
|
||||
.map(|v| generator.gen_expr(ctx, v).map(|v| v.unwrap().to_basic_value_enum(ctx, generator)))
|
||||
.transpose()?;
|
||||
if let Some(return_target) = ctx.return_target {
|
||||
if let Some(value) = value {
|
||||
ctx.builder.build_store(ctx.return_buffer.unwrap(), value);
|
||||
|
@ -878,31 +915,32 @@ pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
|
|||
let value = value.as_ref().map(|v| v as &dyn BasicValue);
|
||||
ctx.builder.build_return(value);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
match &stmt.node {
|
||||
StmtKind::Pass { .. } => {}
|
||||
StmtKind::Expr { value, .. } => {
|
||||
generator.gen_expr(ctx, value);
|
||||
generator.gen_expr(ctx, value)?;
|
||||
}
|
||||
StmtKind::Return { value, .. } => {
|
||||
gen_return(generator, ctx, value);
|
||||
gen_return(generator, ctx, value)?;
|
||||
}
|
||||
StmtKind::AnnAssign { target, value, .. } => {
|
||||
if let Some(value) = value {
|
||||
let value = generator.gen_expr(ctx, value).unwrap();
|
||||
generator.gen_assign(ctx, target, value);
|
||||
let value = generator.gen_expr(ctx, value)?.unwrap();
|
||||
generator.gen_assign(ctx, target, value)?;
|
||||
}
|
||||
}
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
let value = generator.gen_expr(ctx, value).unwrap();
|
||||
let value = generator.gen_expr(ctx, value)?.unwrap();
|
||||
for target in targets.iter() {
|
||||
generator.gen_assign(ctx, target, value.clone());
|
||||
generator.gen_assign(ctx, target, value.clone())?;
|
||||
}
|
||||
}
|
||||
StmtKind::Continue { .. } => {
|
||||
|
@ -911,32 +949,39 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|||
StmtKind::Break { .. } => {
|
||||
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().1);
|
||||
}
|
||||
StmtKind::If { .. } => generator.gen_if(ctx, stmt),
|
||||
StmtKind::While { .. } => generator.gen_while(ctx, stmt),
|
||||
StmtKind::For { .. } => generator.gen_for(ctx, stmt),
|
||||
StmtKind::With { .. } => generator.gen_with(ctx, stmt),
|
||||
StmtKind::If { .. } => generator.gen_if(ctx, stmt)?,
|
||||
StmtKind::While { .. } => generator.gen_while(ctx, stmt)?,
|
||||
StmtKind::For { .. } => generator.gen_for(ctx, stmt)?,
|
||||
StmtKind::With { .. } => generator.gen_with(ctx, stmt)?,
|
||||
StmtKind::AugAssign { target, op, value, .. } => {
|
||||
let value = gen_binop_expr(generator, ctx, target, op, value);
|
||||
generator.gen_assign(ctx, target, value);
|
||||
let value = gen_binop_expr(generator, ctx, target, op, value)?;
|
||||
generator.gen_assign(ctx, target, value)?;
|
||||
}
|
||||
StmtKind::Try { .. } => gen_try(generator, ctx, stmt),
|
||||
StmtKind::Try { .. } => gen_try(generator, ctx, stmt)?,
|
||||
StmtKind::Raise { exc, .. } => {
|
||||
let exc = exc.as_ref().map(|exc| generator.gen_expr(ctx, exc).unwrap().to_basic_value_enum(ctx, generator));
|
||||
gen_raise(generator, ctx, exc.as_ref(), stmt.location)
|
||||
if let Some(exc) = exc {
|
||||
let exc =
|
||||
generator.gen_expr(ctx, exc)?.unwrap().to_basic_value_enum(ctx, generator);
|
||||
gen_raise(generator, ctx, Some(&exc), stmt.location);
|
||||
} else {
|
||||
gen_raise(generator, ctx, None, stmt.location);
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn gen_block<'ctx, 'a, 'b, G: CodeGenerator, I: Iterator<Item = &'b Stmt<Option<Type>>>>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmts: I,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
for stmt in stmts {
|
||||
generator.gen_stmt(ctx, stmt);
|
||||
generator.gen_stmt(ctx, stmt)?;
|
||||
if ctx.is_terminated() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
|||
concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenTask, DefaultCodeGenerator,
|
||||
WithCall, WorkerRegistry,
|
||||
},
|
||||
location::Location,
|
||||
symbol_resolver::{SymbolResolver, ValueEnum},
|
||||
toplevel::{
|
||||
composer::TopLevelComposer, DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
|
||||
|
@ -19,7 +18,6 @@ use nac3parser::{
|
|||
parser::parse_program,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -36,7 +34,10 @@ impl Resolver {
|
|||
}
|
||||
|
||||
impl SymbolResolver for Resolver {
|
||||
fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
fn get_default_param_value(
|
||||
&self,
|
||||
_: &nac3parser::ast::Expr,
|
||||
) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -58,12 +59,12 @@ impl SymbolResolver for Resolver {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_symbol_location(&self, _: StrRef) -> Option<Location> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
self.id_to_def.read().get(&id).cloned()
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
self.id_to_def
|
||||
.read()
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("cannot find symbol `{}`", id))
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
|
@ -124,7 +125,7 @@ fn test_primitives() {
|
|||
virtual_checks: &mut virtual_checks,
|
||||
calls: &mut calls,
|
||||
defined_identifiers: identifiers.clone(),
|
||||
in_handler: false
|
||||
in_handler: false,
|
||||
};
|
||||
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
|
||||
inferencer.variable_mapping.insert("b".into(), inferencer.primitives.int32);
|
||||
|
@ -216,7 +217,7 @@ fn test_simple_call() {
|
|||
ret: primitives.int32,
|
||||
vars: HashMap::new(),
|
||||
};
|
||||
let fun_ty = unifier.add_ty(TypeEnum::TFunc(RefCell::new(signature.clone())));
|
||||
let fun_ty = unifier.add_ty(TypeEnum::TFunc(signature.clone()));
|
||||
let mut store = ConcreteTypeStore::new();
|
||||
let mut cache = HashMap::new();
|
||||
let signature = store.from_signature(&mut unifier, &primitives, &signature, &mut cache);
|
||||
|
@ -232,6 +233,7 @@ fn test_simple_call() {
|
|||
instance_to_symbol: HashMap::new(),
|
||||
resolver: None,
|
||||
codegen_callback: None,
|
||||
loc: None,
|
||||
})));
|
||||
|
||||
let resolver = Resolver {
|
||||
|
@ -268,7 +270,7 @@ fn test_simple_call() {
|
|||
virtual_checks: &mut virtual_checks,
|
||||
calls: &mut calls,
|
||||
defined_identifiers: identifiers.clone(),
|
||||
in_handler: false
|
||||
in_handler: false,
|
||||
};
|
||||
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
|
||||
inferencer.variable_mapping.insert("foo".into(), fun_ty);
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
pub mod codegen;
|
||||
pub mod location;
|
||||
pub mod symbol_resolver;
|
||||
pub mod toplevel;
|
||||
pub mod typecheck;
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
use nac3parser::ast;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct FileID(u32);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Location {
|
||||
CodeRange(FileID, ast::Location),
|
||||
Builtin,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FileRegistry {
|
||||
files: Vec<String>,
|
||||
}
|
||||
|
||||
impl FileRegistry {
|
||||
pub fn add_file(&mut self, path: &str) -> FileID {
|
||||
let index = self.files.len() as u32;
|
||||
self.files.push(path.to_owned());
|
||||
FileID(index)
|
||||
}
|
||||
|
||||
pub fn query_file(&self, id: FileID) -> &str {
|
||||
&self.files[id.0 as usize]
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
use std::{collections::HashMap, fmt::Display};
|
||||
|
||||
use crate::typecheck::typedef::TypeEnum;
|
||||
use crate::{
|
||||
codegen::CodeGenContext,
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
|
@ -13,10 +14,9 @@ use crate::{
|
|||
typedef::{Type, Unifier},
|
||||
},
|
||||
};
|
||||
use crate::{location::Location, typecheck::typedef::TypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue};
|
||||
use itertools::{chain, izip};
|
||||
use nac3parser::ast::{Expr, StrRef};
|
||||
use nac3parser::ast::{Expr, Location, StrRef};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
|
@ -29,6 +29,27 @@ pub enum SymbolValue {
|
|||
Tuple(Vec<SymbolValue>),
|
||||
}
|
||||
|
||||
impl Display for SymbolValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SymbolValue::I32(i) => write!(f, "{}", i),
|
||||
SymbolValue::I64(i) => write!(f, "int64({})", i),
|
||||
SymbolValue::Str(s) => write!(f, "\"{}\"", s),
|
||||
SymbolValue::Double(d) => write!(f, "{}", d),
|
||||
SymbolValue::Bool(b) => {
|
||||
if *b {
|
||||
write!(f, "True")
|
||||
} else {
|
||||
write!(f, "False")
|
||||
}
|
||||
}
|
||||
SymbolValue::Tuple(t) => {
|
||||
write!(f, "({})", t.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StaticValue {
|
||||
fn get_unique_identifier(&self) -> u64;
|
||||
|
||||
|
@ -105,7 +126,7 @@ pub trait SymbolResolver {
|
|||
) -> Result<Type, String>;
|
||||
|
||||
// get the top-level definition of identifiers
|
||||
fn get_identifier_def(&self, str: StrRef) -> Option<DefinitionId>;
|
||||
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, String>;
|
||||
|
||||
fn get_symbol_value<'ctx, 'a>(
|
||||
&self,
|
||||
|
@ -113,7 +134,6 @@ pub trait SymbolResolver {
|
|||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
) -> Option<ValueEnum<'ctx>>;
|
||||
|
||||
fn get_symbol_location(&self, str: StrRef) -> Option<Location>;
|
||||
fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option<SymbolValue>;
|
||||
fn get_string_id(&self, s: &str) -> i32;
|
||||
// handle function call etc.
|
||||
|
@ -155,7 +175,7 @@ pub fn parse_type_annotation<T>(
|
|||
let str_id = ids[8];
|
||||
let exn_id = ids[9];
|
||||
|
||||
let name_handling = |id: &StrRef, unifier: &mut Unifier| {
|
||||
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
|
||||
if *id == int32_id {
|
||||
Ok(primitives.int32)
|
||||
} else if *id == int64_id {
|
||||
|
@ -172,37 +192,39 @@ pub fn parse_type_annotation<T>(
|
|||
Ok(primitives.exception)
|
||||
} else {
|
||||
let obj_id = resolver.get_identifier_def(*id);
|
||||
if let Some(obj_id) = obj_id {
|
||||
let def = top_level_defs[obj_id.0].read();
|
||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||
if !type_vars.is_empty() {
|
||||
return Err(format!(
|
||||
"Unexpected number of type parameters: expected {} but got 0",
|
||||
type_vars.len()
|
||||
));
|
||||
}
|
||||
let fields = RefCell::new(
|
||||
chain(
|
||||
match obj_id {
|
||||
Ok(obj_id) => {
|
||||
let def = top_level_defs[obj_id.0].read();
|
||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||
if !type_vars.is_empty() {
|
||||
return Err(format!(
|
||||
"Unexpected number of type parameters: expected {} but got 0",
|
||||
type_vars.len()
|
||||
));
|
||||
}
|
||||
let fields = chain(
|
||||
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
|
||||
methods.iter().map(|(k, v, _)| (*k, (*v, false))),
|
||||
)
|
||||
.collect(),
|
||||
);
|
||||
Ok(unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id,
|
||||
fields,
|
||||
params: Default::default(),
|
||||
}))
|
||||
} else {
|
||||
Err("Cannot use function name as type".into())
|
||||
.collect();
|
||||
Ok(unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id,
|
||||
fields,
|
||||
params: Default::default(),
|
||||
}))
|
||||
} else {
|
||||
Err(format!("Cannot use function name as type at {}", loc))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// it could be a type variable
|
||||
let ty = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id)?;
|
||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
||||
Ok(ty)
|
||||
} else {
|
||||
Err(format!("Unknown type annotation {}", id))
|
||||
Err(e) => {
|
||||
let ty = resolver
|
||||
.get_symbol_type(unifier, top_level_defs, primitives, *id)
|
||||
.map_err(|_| format!("Unknown type annotation at {}: {}", loc, e))?;
|
||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
||||
Ok(ty)
|
||||
} else {
|
||||
Err(format!("Unknown type annotation {} at {}", id, loc))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -238,9 +260,7 @@ pub fn parse_type_annotation<T>(
|
|||
vec![parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?]
|
||||
};
|
||||
|
||||
let obj_id = resolver
|
||||
.get_identifier_def(*id)
|
||||
.ok_or_else(|| format!("Unknown type annotation {}", id))?;
|
||||
let obj_id = resolver.get_identifier_def(*id)?;
|
||||
let def = top_level_defs[obj_id.0].read();
|
||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||
if types.len() != type_vars.len() {
|
||||
|
@ -270,11 +290,7 @@ pub fn parse_type_annotation<T>(
|
|||
let ty = unifier.subst(*ty, &subst).unwrap_or(*ty);
|
||||
(*attr, (ty, false))
|
||||
}));
|
||||
Ok(unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id,
|
||||
fields: fields.into(),
|
||||
params: subst.into(),
|
||||
}))
|
||||
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }))
|
||||
} else {
|
||||
Err("Cannot use function name as type".into())
|
||||
}
|
||||
|
@ -282,7 +298,7 @@ pub fn parse_type_annotation<T>(
|
|||
};
|
||||
|
||||
match &expr.node {
|
||||
Name { id, .. } => name_handling(id, unifier),
|
||||
Name { id, .. } => name_handling(id, expr.location, unifier),
|
||||
Subscript { value, slice, .. } => {
|
||||
if let Name { id, .. } = &value.node {
|
||||
subscript_name_handle(id, slice, unifier)
|
||||
|
@ -311,7 +327,7 @@ impl dyn SymbolResolver + Send + Sync {
|
|||
unifier: &mut Unifier,
|
||||
ty: Type,
|
||||
) -> String {
|
||||
unifier.stringify(
|
||||
unifier.internal_stringify(
|
||||
ty,
|
||||
&mut |id| {
|
||||
if let TopLevelDef::Class { name, .. } = &*top_level_defs[id].read() {
|
||||
|
@ -321,6 +337,7 @@ impl dyn SymbolResolver + Send + Sync {
|
|||
}
|
||||
},
|
||||
&mut |id| format!("var{}", id),
|
||||
&mut None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,32 +1,22 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use nac3parser::ast::{Constant, Location};
|
||||
use crate::symbol_resolver::SymbolValue;
|
||||
use nac3parser::ast::{Constant, Location};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl TopLevelDef {
|
||||
pub fn to_string(
|
||||
&self,
|
||||
unifier: &mut Unifier,
|
||||
) -> String
|
||||
{
|
||||
pub fn to_string(&self, unifier: &mut Unifier) -> String {
|
||||
match self {
|
||||
TopLevelDef::Class {
|
||||
name, ancestors, fields, methods, type_vars, ..
|
||||
} => {
|
||||
TopLevelDef::Class { name, ancestors, fields, methods, type_vars, .. } => {
|
||||
let fields_str = fields
|
||||
.iter()
|
||||
.map(|(n, ty, _)| {
|
||||
(n.to_string(), unifier.default_stringify(*ty))
|
||||
})
|
||||
.map(|(n, ty, _)| (n.to_string(), unifier.stringify(*ty)))
|
||||
.collect_vec();
|
||||
|
||||
let methods_str = methods
|
||||
.iter()
|
||||
.map(|(n, ty, id)| {
|
||||
(n.to_string(), unifier.default_stringify(*ty), *id)
|
||||
})
|
||||
.map(|(n, ty, id)| (n.to_string(), unifier.stringify(*ty), *id))
|
||||
.collect_vec();
|
||||
format!(
|
||||
"Class {{\nname: {:?},\nancestors: {:?},\nfields: {:?},\nmethods: {:?},\ntype_vars: {:?}\n}}",
|
||||
|
@ -34,13 +24,13 @@ impl TopLevelDef {
|
|||
ancestors.iter().map(|ancestor| ancestor.stringify(unifier)).collect_vec(),
|
||||
fields_str.iter().map(|(a, _)| a).collect_vec(),
|
||||
methods_str.iter().map(|(a, b, _)| (a, b)).collect_vec(),
|
||||
type_vars.iter().map(|id| unifier.default_stringify(*id)).collect_vec(),
|
||||
type_vars.iter().map(|id| unifier.stringify(*id)).collect_vec(),
|
||||
)
|
||||
}
|
||||
TopLevelDef::Function { name, signature, var_id, .. } => format!(
|
||||
"Function {{\nname: {:?},\nsig: {:?},\nvar_id: {:?}\n}}",
|
||||
name,
|
||||
unifier.default_stringify(*signature),
|
||||
unifier.stringify(*signature),
|
||||
{
|
||||
// preserve the order for debug output and test
|
||||
let mut r = var_id.clone();
|
||||
|
@ -57,38 +47,38 @@ impl TopLevelComposer {
|
|||
let mut unifier = Unifier::new();
|
||||
let int32 = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(0),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let int64 = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(1),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let float = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(2),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let bool = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(3),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let none = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(4),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let range = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(5),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let str = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(6),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let exception = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(7),
|
||||
|
@ -102,8 +92,10 @@ impl TopLevelComposer {
|
|||
("__param0__".into(), (int64, true)),
|
||||
("__param1__".into(), (int64, true)),
|
||||
("__param2__".into(), (int64, true)),
|
||||
].into_iter().collect::<HashMap<_, _>>().into(),
|
||||
params: HashMap::new().into(),
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<HashMap<_, _>>(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
|
||||
crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier);
|
||||
|
@ -117,6 +109,7 @@ impl TopLevelComposer {
|
|||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||
name: StrRef,
|
||||
constructor: Option<Type>,
|
||||
loc: Option<Location>,
|
||||
) -> TopLevelDef {
|
||||
TopLevelDef::Class {
|
||||
name,
|
||||
|
@ -127,6 +120,7 @@ impl TopLevelComposer {
|
|||
ancestors: Default::default(),
|
||||
constructor,
|
||||
resolver,
|
||||
loc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,6 +130,7 @@ impl TopLevelComposer {
|
|||
simple_name: StrRef,
|
||||
ty: Type,
|
||||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||
loc: Option<Location>,
|
||||
) -> TopLevelDef {
|
||||
TopLevelDef::Function {
|
||||
name,
|
||||
|
@ -146,6 +141,7 @@ impl TopLevelComposer {
|
|||
instance_to_stmt: Default::default(),
|
||||
resolver,
|
||||
codegen_callback: None,
|
||||
loc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,12 +240,11 @@ impl TopLevelComposer {
|
|||
let this = this.as_ref();
|
||||
let other = unifier.get_ty(other);
|
||||
let other = other.as_ref();
|
||||
if let (TypeEnum::TFunc(this_sig), TypeEnum::TFunc(other_sig)) = (this, other) {
|
||||
let (this_sig, other_sig) = (&*this_sig.borrow(), &*other_sig.borrow());
|
||||
let (
|
||||
FunSignature { args: this_args, ret: this_ret, vars: _this_vars },
|
||||
FunSignature { args: other_args, ret: other_ret, vars: _other_vars },
|
||||
) = (this_sig, other_sig);
|
||||
if let (
|
||||
TypeEnum::TFunc(FunSignature { args: this_args, ret: this_ret, .. }),
|
||||
TypeEnum::TFunc(FunSignature { args: other_args, ret: other_ret, .. }),
|
||||
) = (this, other)
|
||||
{
|
||||
// check args
|
||||
let args_ok = this_args
|
||||
.iter()
|
||||
|
@ -362,11 +357,19 @@ impl TopLevelComposer {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn parse_parameter_default_value(default: &ast::Expr, resolver: &(dyn SymbolResolver + Send + Sync)) -> Result<SymbolValue, String> {
|
||||
pub fn parse_parameter_default_value(
|
||||
default: &ast::Expr,
|
||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||
) -> Result<SymbolValue, String> {
|
||||
parse_parameter_default_value(default, resolver)
|
||||
}
|
||||
|
||||
pub fn check_default_param_type(val: &SymbolValue, ty: &TypeAnnotation, primitive: &PrimitiveStore, unifier: &mut Unifier) -> Result<(), String> {
|
||||
pub fn check_default_param_type(
|
||||
val: &SymbolValue,
|
||||
ty: &TypeAnnotation,
|
||||
primitive: &PrimitiveStore,
|
||||
unifier: &mut Unifier,
|
||||
) -> Result<(), String> {
|
||||
let res = match val {
|
||||
SymbolValue::Bool(..) => {
|
||||
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.bool) {
|
||||
|
@ -430,33 +433,26 @@ impl TopLevelComposer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_parameter_default_value(default: &ast::Expr, resolver: &(dyn SymbolResolver + Send + Sync)) -> Result<SymbolValue, String> {
|
||||
pub fn parse_parameter_default_value(
|
||||
default: &ast::Expr,
|
||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||
) -> Result<SymbolValue, String> {
|
||||
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, String> {
|
||||
match val {
|
||||
Constant::Int(v) => {
|
||||
match v {
|
||||
Some(v) => {
|
||||
if let Ok(v) = (*v).try_into() {
|
||||
Ok(SymbolValue::I32(v))
|
||||
} else {
|
||||
Err(format!(
|
||||
"integer value out of range at {}",
|
||||
loc
|
||||
))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
Err(format!(
|
||||
"integer value out of range at {}",
|
||||
loc
|
||||
))
|
||||
Constant::Int(v) => match v {
|
||||
Some(v) => {
|
||||
if let Ok(v) = (*v).try_into() {
|
||||
Ok(SymbolValue::I32(v))
|
||||
} else {
|
||||
Err(format!("integer value out of range at {}", loc))
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Err(format!("integer value out of range at {}", loc)),
|
||||
},
|
||||
Constant::Float(v) => Ok(SymbolValue::Double(*v)),
|
||||
Constant::Bool(v) => Ok(SymbolValue::Bool(*v)),
|
||||
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
|
||||
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?
|
||||
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
|
||||
)),
|
||||
_ => unimplemented!("this constant is not supported at {}", loc),
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
};
|
||||
use inkwell::values::BasicValueEnum;
|
||||
use itertools::{izip, Itertools};
|
||||
use nac3parser::ast::{self, Stmt, StrRef};
|
||||
use nac3parser::ast::{self, Location, Stmt, StrRef};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
|
||||
|
@ -39,7 +39,7 @@ type GenCallCallback = Box<
|
|||
(&FunSignature, DefinitionId),
|
||||
Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
&mut dyn CodeGenerator,
|
||||
) -> Option<BasicValueEnum<'ctx>>
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
|
@ -60,7 +60,7 @@ impl GenCall {
|
|||
fun: (&FunSignature, DefinitionId),
|
||||
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
(self.fp)(ctx, obj, fun, args, generator)
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,8 @@ pub enum TopLevelDef {
|
|||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||
// constructor type
|
||||
constructor: Option<Type>,
|
||||
// definition location
|
||||
loc: Option<Location>,
|
||||
},
|
||||
Function {
|
||||
// prefix for symbol, should be unique globally
|
||||
|
@ -124,6 +126,8 @@ pub enum TopLevelDef {
|
|||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||
// custom codegen callback
|
||||
codegen_callback: Option<Arc<GenCall>>,
|
||||
// definition location
|
||||
loc: Option<Location>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
---
|
||||
source: nac3core/src/toplevel/test.rs
|
||||
assertion_line: 541
|
||||
assertion_line: 540
|
||||
expression: res_vec
|
||||
|
||||
---
|
||||
[
|
||||
"Class {\nname: \"Generic_A\",\nancestors: [\"{class: Generic_A, params: [\\\"var6\\\"]}\", \"{class: B, params: []}\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b=var5], none]\"), (\"fun\", \"fn[[a=int32], var6]\")],\ntype_vars: [\"var6\"]\n}\n",
|
||||
"Class {\nname: \"Generic_A\",\nancestors: [\"{class: Generic_A, params: [\\\"V\\\"]}\", \"{class: B, params: []}\"],\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: [6]\n}\n",
|
||||
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a=int32], var6]\",\nvar_id: [6]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b=var5], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [6, 17]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\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=var5], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
|
||||
]
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
---
|
||||
source: nac3core/src/toplevel/test.rs
|
||||
assertion_line: 541
|
||||
assertion_line: 540
|
||||
expression: res_vec
|
||||
|
||||
---
|
||||
[
|
||||
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var5\\\"]}\"],\nfields: [\"a\", \"b\", \"c\"],\nmethods: [(\"__init__\", \"fn[[t=var5], none]\"), (\"fun\", \"fn[[a=int32, b=var5], list[virtual[B[6->bool]]]]\"), (\"foo\", \"fn[[c=C], none]\")],\ntype_vars: [\"var5\"]\n}\n",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[t=var5], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a=int32, b=var5], list[virtual[B[6->bool]]]]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"A.foo\",\nsig: \"fn[[c=C], none]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: [\\\"var6\\\"]}\", \"{class: A, params: [\\\"float\\\"]}\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a=int32, b=var5], list[virtual[B[6->bool]]]]\"), (\"foo\", \"fn[[c=C], none]\")],\ntype_vars: [\"var6\"]\n}\n",
|
||||
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"T\\\"]}\"],\nfields: [\"a\", \"b\", \"c\"],\nmethods: [(\"__init__\", \"fn[[t:T], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"T\"]\n}\n",
|
||||
"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: [\"{class: B, params: [\\\"var6\\\"]}\", \"{class: A, params: [\\\"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: [\"var6\"]\n}\n",
|
||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: [6]\n}\n",
|
||||
"Function {\nname: \"B.fun\",\nsig: \"fn[[a=int32, b=var5], list[virtual[B[6->bool]]]]\",\nvar_id: [6]\n}\n",
|
||||
"Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: B, params: [\\\"bool\\\"]}\", \"{class: A, params: [\\\"float\\\"]}\"],\nfields: [\"a\", \"b\", \"c\", \"d\", \"e\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a=int32, b=var5], list[virtual[B[6->bool]]]]\"), (\"foo\", \"fn[[c=C], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: [6]\n}\n",
|
||||
"Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: B, params: [\\\"bool\\\"]}\", \"{class: A, params: [\\\"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",
|
||||
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
]
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
---
|
||||
source: nac3core/src/toplevel/test.rs
|
||||
assertion_line: 541
|
||||
assertion_line: 540
|
||||
expression: res_vec
|
||||
|
||||
---
|
||||
[
|
||||
"Function {\nname: \"foo\",\nsig: \"fn[[a=list[int32], b=tuple[var5, float]], A[5->B, 6->bool]]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var5\\\", \\\"var6\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v=var6], none]\"), (\"fun\", \"fn[[a=var5], var6]\")],\ntype_vars: [\"var5\", \"var6\"]\n}\n",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v=var6], none]\",\nvar_id: [6]\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a=var5], var6]\",\nvar_id: [6]\n}\n",
|
||||
"Function {\nname: \"gfun\",\nsig: \"fn[[a=A[5->list[float], 6->int32]], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"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: [18, 19]\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [19, 24]\n}\n",
|
||||
"Function {\nname: \"gfun\",\nsig: \"fn[[a:A[int32, list[float]]], none]\",\nvar_id: []\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
]
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
---
|
||||
source: nac3core/src/toplevel/test.rs
|
||||
assertion_line: 541
|
||||
assertion_line: 540
|
||||
expression: res_vec
|
||||
|
||||
---
|
||||
[
|
||||
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var5\\\", \\\"var6\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a=A[5->float, 6->bool], b=B], none]\"), (\"fun\", \"fn[[a=A[5->float, 6->bool]], A[5->bool, 6->int32]]\")],\ntype_vars: [\"var5\", \"var6\"]\n}\n",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a=A[5->float, 6->bool], b=B], none]\",\nvar_id: [6]\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a=A[5->float, 6->bool]], A[5->bool, 6->int32]]\",\nvar_id: [6]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: A, params: [\\\"int64\\\", \\\"bool\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a=A[5->float, 6->bool]], A[5->bool, 6->int32]]\"), (\"foo\", \"fn[[b=B], B]\"), (\"bar\", \"fn[[a=A[5->list[B], 6->int32]], tuple[A[5->virtual[A[5->B, 6->int32]], 6->bool], B]]\")],\ntype_vars: []\n}\n",
|
||||
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var5\\\", \\\"var6\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"var5\", \"var6\"]\n}\n",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[bool, float], b:B], none]\",\nvar_id: [6]\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[bool, float]], A[bool, int32]]\",\nvar_id: [6]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: A, params: [\\\"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",
|
||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"B.foo\",\nsig: \"fn[[b=B], B]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"B.bar\",\nsig: \"fn[[a=A[5->list[B], 6->int32]], tuple[A[5->virtual[A[5->B, 6->int32]], 6->bool], B]]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:B], B]\",\nvar_id: []\n}\n",
|
||||
"Function {\nname: \"B.bar\",\nsig: \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\",\nvar_id: []\n}\n",
|
||||
]
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
---
|
||||
source: nac3core/src/toplevel/test.rs
|
||||
assertion_line: 541
|
||||
assertion_line: 540
|
||||
expression: res_vec
|
||||
|
||||
---
|
||||
[
|
||||
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b=B], none]\"), (\"foo\", \"fn[[a=var5, b=var6], none]\")],\ntype_vars: []\n}\n",
|
||||
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: []}\"],\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=var5, b=var6], none]\",\nvar_id: [6]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: C, params: []}\", \"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b=B], none]\"), (\"foo\", \"fn[[a=var5, b=var6], none]\")],\ntype_vars: []\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: [25]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: C, params: []}\", \"{class: A, params: []}\"],\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: [\"{class: C, params: []}\", \"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b=B], none]\"), (\"foo\", \"fn[[a=var5, b=var6], none]\")],\ntype_vars: []\n}\n",
|
||||
"Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: A, params: []}\"],\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=var5], var6]\",\nvar_id: [6]\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: [33]\n}\n",
|
||||
]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::{
|
||||
codegen::CodeGenContext,
|
||||
location::Location,
|
||||
symbol_resolver::{SymbolResolver, ValueEnum},
|
||||
toplevel::DefinitionId,
|
||||
typecheck::{
|
||||
|
@ -35,7 +34,10 @@ impl ResolverInternal {
|
|||
struct Resolver(Arc<ResolverInternal>);
|
||||
|
||||
impl SymbolResolver for Resolver {
|
||||
fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
fn get_default_param_value(
|
||||
&self,
|
||||
_: &nac3parser::ast::Expr,
|
||||
) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -62,12 +64,8 @@ impl SymbolResolver for Resolver {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_symbol_location(&self, _: StrRef) -> Option<Location> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
self.0.id_to_def.lock().get(&id).cloned()
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
self.0.id_to_def.lock().get(&id).cloned().ok_or("Unknown identifier".to_string())
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
|
@ -134,9 +132,9 @@ fn test_simple_register(source: Vec<&str>) {
|
|||
"},
|
||||
],
|
||||
vec![
|
||||
"fn[[a=0], 0]",
|
||||
"fn[[a=2], 4]",
|
||||
"fn[[b=1], 0]",
|
||||
"fn[[a:0], 0]",
|
||||
"fn[[a:2], 4]",
|
||||
"fn[[b:1], 0]",
|
||||
],
|
||||
vec![
|
||||
"fun",
|
||||
|
@ -174,10 +172,12 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
|
|||
{
|
||||
let def = &*def.read();
|
||||
if let TopLevelDef::Function { signature, name, .. } = def {
|
||||
let ty_str =
|
||||
composer
|
||||
.unifier
|
||||
.stringify(*signature, &mut |id| id.to_string(), &mut |id| id.to_string());
|
||||
let ty_str = composer.unifier.internal_stringify(
|
||||
*signature,
|
||||
&mut |id| id.to_string(),
|
||||
&mut |id| id.to_string(),
|
||||
&mut None,
|
||||
);
|
||||
assert_eq!(ty_str, tys[i]);
|
||||
assert_eq!(name, names[i]);
|
||||
}
|
||||
|
@ -757,7 +757,7 @@ fn make_internal_resolver_with_tvar(
|
|||
.into_iter()
|
||||
.map(|(name, range)| {
|
||||
(name, {
|
||||
let (ty, id) = unifier.get_fresh_var_with_range(range.as_slice());
|
||||
let (ty, id) = unifier.get_fresh_var_with_range(range.as_slice(), None, None);
|
||||
if print {
|
||||
println!("{}: {:?}, tvar{}", name, ty, id);
|
||||
}
|
||||
|
@ -784,9 +784,12 @@ impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
|
|||
type Error = String;
|
||||
fn map_user(&mut self, user: Option<Type>) -> Result<Self::TargetU, Self::Error> {
|
||||
Ok(if let Some(ty) = user {
|
||||
self.unifier.stringify(ty, &mut |id| format!("class{}", id.to_string()), &mut |id| {
|
||||
format!("tvar{}", id.to_string())
|
||||
})
|
||||
self.unifier.internal_stringify(
|
||||
ty,
|
||||
&mut |id| format!("class{}", id.to_string()),
|
||||
&mut |id| format!("tvar{}", id.to_string()),
|
||||
&mut None,
|
||||
)
|
||||
} else {
|
||||
"None".into()
|
||||
})
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
use std::cell::RefCell;
|
||||
|
||||
use crate::typecheck::typedef::TypeVarMeta;
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -23,20 +20,30 @@ impl TypeAnnotation {
|
|||
pub fn stringify(&self, unifier: &mut Unifier) -> String {
|
||||
use TypeAnnotation::*;
|
||||
match self {
|
||||
Primitive(ty) | TypeVar(ty) => unifier.default_stringify(*ty),
|
||||
Primitive(ty) | TypeVar(ty) => unifier.stringify(*ty),
|
||||
CustomClass { id, params } => {
|
||||
let class_name = match unifier.top_level {
|
||||
Some(ref top) => if let TopLevelDef::Class { name, .. } = &*top.definitions.read()[id.0].read() {
|
||||
(*name).into()
|
||||
} else {
|
||||
format!("def_{}", id.0)
|
||||
Some(ref top) => {
|
||||
if let TopLevelDef::Class { name, .. } =
|
||||
&*top.definitions.read()[id.0].read()
|
||||
{
|
||||
(*name).into()
|
||||
} else {
|
||||
format!("def_{}", id.0)
|
||||
}
|
||||
}
|
||||
None => format!("def_{}", id.0)
|
||||
None => format!("def_{}", id.0),
|
||||
};
|
||||
format!("{{class: {}, params: {:?}}}", class_name, params.iter().map(|p| p.stringify(unifier)).collect_vec())
|
||||
format!(
|
||||
"{{class: {}, params: {:?}}}",
|
||||
class_name,
|
||||
params.iter().map(|p| p.stringify(unifier)).collect_vec()
|
||||
)
|
||||
}
|
||||
Virtual(ty) | List(ty) => ty.stringify(unifier),
|
||||
Tuple(types) => format!("({:?})", types.iter().map(|p| p.stringify(unifier)).collect_vec()),
|
||||
Tuple(types) => {
|
||||
format!("({:?})", types.iter().map(|p| p.stringify(unifier)).collect_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +57,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
// the key stores the type_var of this topleveldef::class, we only need this field here
|
||||
locked: HashMap<DefinitionId, Vec<Type>>,
|
||||
) -> Result<TypeAnnotation, String> {
|
||||
let name_handle = |id: &StrRef, unifier: &mut Unifier, locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||
let name_handle = |id: &StrRef,
|
||||
unifier: &mut Unifier,
|
||||
locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||
if id == &"int32".into() {
|
||||
Ok(TypeAnnotation::Primitive(primitives.int32))
|
||||
} else if id == &"int64".into() {
|
||||
|
@ -65,7 +74,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
Ok(TypeAnnotation::Primitive(primitives.str))
|
||||
} else if id == &"Exception".into() {
|
||||
Ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() })
|
||||
} else if let Some(obj_id) = resolver.get_identifier_def(*id) {
|
||||
} else if let Ok(obj_id) = resolver.get_identifier_def(*id) {
|
||||
let type_vars = {
|
||||
let def_read = top_level_defs[obj_id.0].try_read();
|
||||
if let Some(def_read) = def_read {
|
||||
|
@ -92,13 +101,11 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] })
|
||||
} else if let Ok(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) {
|
||||
if let TypeEnum::TVar { .. } = unifier.get_ty(ty).as_ref() {
|
||||
let var = unifier.get_fresh_var(Some(*id), Some(expr.location)).0;
|
||||
unifier.unify(var, ty).unwrap();
|
||||
Ok(TypeAnnotation::TypeVar(ty))
|
||||
} else {
|
||||
Err(format!(
|
||||
"`{}` is not a valid type annotation (at {})",
|
||||
id,
|
||||
expr.location
|
||||
))
|
||||
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location))
|
||||
}
|
||||
} else {
|
||||
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location))
|
||||
|
@ -106,74 +113,73 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
};
|
||||
|
||||
let class_name_handle =
|
||||
|id: &StrRef, slice: &ast::Expr<T>, unifier: &mut Unifier, mut locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||
if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into()]
|
||||
.contains(id)
|
||||
|id: &StrRef,
|
||||
slice: &ast::Expr<T>,
|
||||
unifier: &mut Unifier,
|
||||
mut locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||
if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into()].contains(id)
|
||||
{
|
||||
return Err(format!("keywords cannot be class name (at {})", expr.location));
|
||||
}
|
||||
let obj_id = resolver
|
||||
.get_identifier_def(*id)
|
||||
.ok_or_else(|| "unknown class name".to_string())?;
|
||||
let type_vars = {
|
||||
let def_read = top_level_defs[obj_id.0].try_read();
|
||||
if let Some(def_read) = def_read {
|
||||
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
||||
type_vars.clone()
|
||||
let obj_id = resolver.get_identifier_def(*id)?;
|
||||
let type_vars = {
|
||||
let def_read = top_level_defs[obj_id.0].try_read();
|
||||
if let Some(def_read) = def_read {
|
||||
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
||||
type_vars.clone()
|
||||
} else {
|
||||
unreachable!("must be class here")
|
||||
}
|
||||
} else {
|
||||
unreachable!("must be class here")
|
||||
locked.get(&obj_id).unwrap().clone()
|
||||
}
|
||||
} else {
|
||||
locked.get(&obj_id).unwrap().clone()
|
||||
}
|
||||
};
|
||||
// we do not check whether the application of type variables are compatible here
|
||||
let param_type_infos = {
|
||||
let params_ast = if let ast::ExprKind::Tuple { elts, .. } = &slice.node {
|
||||
elts.iter().collect_vec()
|
||||
} else {
|
||||
vec![slice]
|
||||
};
|
||||
if type_vars.len() != params_ast.len() {
|
||||
return Err(format!(
|
||||
"expect {} type parameters but got {} (at {})",
|
||||
type_vars.len(),
|
||||
params_ast.len(),
|
||||
params_ast[0].location,
|
||||
));
|
||||
}
|
||||
let result = params_ast
|
||||
.iter()
|
||||
.map(|x| {
|
||||
parse_ast_to_type_annotation_kinds(
|
||||
resolver,
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
x,
|
||||
{
|
||||
locked.insert(obj_id, type_vars.clone());
|
||||
locked.clone()
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
// make sure the result do not contain any type vars
|
||||
let no_type_var = result
|
||||
.iter()
|
||||
.all(|x| get_type_var_contained_in_type_annotation(x).is_empty());
|
||||
if no_type_var {
|
||||
result
|
||||
} else {
|
||||
return Err(format!(
|
||||
"application of type vars to generic class \
|
||||
// we do not check whether the application of type variables are compatible here
|
||||
let param_type_infos = {
|
||||
let params_ast = if let ast::ExprKind::Tuple { elts, .. } = &slice.node {
|
||||
elts.iter().collect_vec()
|
||||
} else {
|
||||
vec![slice]
|
||||
};
|
||||
if type_vars.len() != params_ast.len() {
|
||||
return Err(format!(
|
||||
"expect {} type parameters but got {} (at {})",
|
||||
type_vars.len(),
|
||||
params_ast.len(),
|
||||
params_ast[0].location,
|
||||
));
|
||||
}
|
||||
let result = params_ast
|
||||
.iter()
|
||||
.map(|x| {
|
||||
parse_ast_to_type_annotation_kinds(
|
||||
resolver,
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
x,
|
||||
{
|
||||
locked.insert(obj_id, type_vars.clone());
|
||||
locked.clone()
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
// make sure the result do not contain any type vars
|
||||
let no_type_var =
|
||||
result.iter().all(|x| get_type_var_contained_in_type_annotation(x).is_empty());
|
||||
if no_type_var {
|
||||
result
|
||||
} else {
|
||||
return Err(format!(
|
||||
"application of type vars to generic class \
|
||||
is not currently supported (at {})",
|
||||
params_ast[0].location
|
||||
));
|
||||
}
|
||||
params_ast[0].location
|
||||
));
|
||||
}
|
||||
};
|
||||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
|
||||
};
|
||||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
|
||||
};
|
||||
match &expr.node {
|
||||
ast::ExprKind::Name { id, .. } => name_handle(id, unifier, locked),
|
||||
// virtual
|
||||
|
@ -293,14 +299,17 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
// TODO: if allow type var to be applied(now this disallowed in the parse_to_type_annotation), need more check
|
||||
let mut result: HashMap<u32, Type> = HashMap::new();
|
||||
for (tvar, p) in type_vars.iter().zip(param_ty) {
|
||||
if let TypeEnum::TVar { id, range, meta: TypeVarMeta::Generic } =
|
||||
if let TypeEnum::TVar { id, range, fields: None, name, loc } =
|
||||
unifier.get_ty(*tvar).as_ref()
|
||||
{
|
||||
let ok: bool = {
|
||||
// create a temp type var and unify to check compatibility
|
||||
p == *tvar || {
|
||||
let temp =
|
||||
unifier.get_fresh_var_with_range(range.borrow().as_slice());
|
||||
let temp = unifier.get_fresh_var_with_range(
|
||||
range.as_slice(),
|
||||
*name,
|
||||
*loc,
|
||||
);
|
||||
unifier.unify(temp.0, p).is_ok()
|
||||
}
|
||||
};
|
||||
|
@ -309,10 +318,11 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
} else {
|
||||
return Err(format!(
|
||||
"cannot apply type {} to type variable with id {:?}",
|
||||
unifier.stringify(
|
||||
unifier.internal_stringify(
|
||||
p,
|
||||
&mut |id| format!("class{}", id),
|
||||
&mut |id| format!("tvar{}", id)
|
||||
&mut |id| format!("tvar{}", id),
|
||||
&mut None
|
||||
),
|
||||
*id
|
||||
));
|
||||
|
@ -338,8 +348,8 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
|
||||
Ok(unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: *obj_id,
|
||||
fields: RefCell::new(tobj_fields),
|
||||
params: subst.into(),
|
||||
fields: tobj_fields,
|
||||
params: subst,
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
|
@ -438,8 +448,8 @@ pub fn check_overload_type_annotation_compatible(
|
|||
let b = unifier.get_ty(*b);
|
||||
let b = b.deref();
|
||||
if let (
|
||||
TypeEnum::TVar { id: a, meta: TypeVarMeta::Generic, .. },
|
||||
TypeEnum::TVar { id: b, meta: TypeVarMeta::Generic, .. },
|
||||
TypeEnum::TVar { id: a, fields: None, .. },
|
||||
TypeEnum::TVar { id: b, fields: None, .. },
|
||||
) = (a, b)
|
||||
{
|
||||
a == b
|
||||
|
|
|
@ -2,10 +2,10 @@ use crate::typecheck::{
|
|||
type_inferencer::*,
|
||||
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
|
||||
};
|
||||
use nac3parser::ast;
|
||||
use nac3parser::ast::{self, StrRef};
|
||||
use nac3parser::ast::{Cmpop, Operator, Unaryop};
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn binop_name(op: &Operator) -> &'static str {
|
||||
match op {
|
||||
|
@ -64,6 +64,23 @@ pub fn comparison_name(op: &Cmpop) -> Option<&'static str> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn with_fields<F>(unifier: &mut Unifier, ty: Type, f: F)
|
||||
where
|
||||
F: FnOnce(&mut Unifier, &mut HashMap<StrRef, (Type, bool)>),
|
||||
{
|
||||
let (id, mut fields, params) =
|
||||
if let TypeEnum::TObj { obj_id, fields, params } = &*unifier.get_ty(ty) {
|
||||
(*obj_id, fields.clone(), params.clone())
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
f(unifier, &mut fields);
|
||||
unsafe {
|
||||
let unification_table = unifier.get_unification_table();
|
||||
unification_table.set_value(ty, Rc::new(TypeEnum::TObj { obj_id: id, fields, params }));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impl_binop(
|
||||
unifier: &mut Unifier,
|
||||
store: &PrimitiveStore,
|
||||
|
@ -72,11 +89,11 @@ pub fn impl_binop(
|
|||
ret_ty: Type,
|
||||
ops: &[ast::Operator],
|
||||
) {
|
||||
if let TypeEnum::TObj { fields, .. } = unifier.get_ty(ty).borrow() {
|
||||
with_fields(unifier, ty, |unifier, fields| {
|
||||
let (other_ty, other_var_id) = if other_ty.len() == 1 {
|
||||
(other_ty[0], None)
|
||||
} else {
|
||||
let (ty, var_id) = unifier.get_fresh_var_with_range(other_ty);
|
||||
let (ty, var_id) = unifier.get_fresh_var_with_range(other_ty, Some("N".into()), None);
|
||||
(ty, Some(var_id))
|
||||
};
|
||||
let function_vars = if let Some(var_id) = other_var_id {
|
||||
|
@ -85,69 +102,55 @@ pub fn impl_binop(
|
|||
HashMap::new()
|
||||
};
|
||||
for op in ops {
|
||||
fields.borrow_mut().insert(binop_name(op).into(), {
|
||||
fields.insert(binop_name(op).into(), {
|
||||
(
|
||||
unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature {
|
||||
ret: ret_ty,
|
||||
vars: function_vars.clone(),
|
||||
args: vec![FuncArg {
|
||||
ty: other_ty,
|
||||
default_value: None,
|
||||
name: "other".into(),
|
||||
}],
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
ret: ret_ty,
|
||||
vars: function_vars.clone(),
|
||||
args: vec![FuncArg {
|
||||
ty: other_ty,
|
||||
default_value: None,
|
||||
name: "other".into(),
|
||||
}],
|
||||
})),
|
||||
false,
|
||||
)
|
||||
});
|
||||
|
||||
fields.borrow_mut().insert(binop_assign_name(op).into(), {
|
||||
fields.insert(binop_assign_name(op).into(), {
|
||||
(
|
||||
unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature {
|
||||
ret: store.none,
|
||||
vars: function_vars.clone(),
|
||||
args: vec![FuncArg {
|
||||
ty: other_ty,
|
||||
default_value: None,
|
||||
name: "other".into(),
|
||||
}],
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
ret: store.none,
|
||||
vars: function_vars.clone(),
|
||||
args: vec![FuncArg {
|
||||
ty: other_ty,
|
||||
default_value: None,
|
||||
name: "other".into(),
|
||||
}],
|
||||
})),
|
||||
false,
|
||||
)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
unreachable!("")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn impl_unaryop(
|
||||
unifier: &mut Unifier,
|
||||
_store: &PrimitiveStore,
|
||||
ty: Type,
|
||||
ret_ty: Type,
|
||||
ops: &[ast::Unaryop],
|
||||
) {
|
||||
if let TypeEnum::TObj { fields, .. } = unifier.get_ty(ty).borrow() {
|
||||
pub fn impl_unaryop(unifier: &mut Unifier, ty: Type, ret_ty: Type, ops: &[ast::Unaryop]) {
|
||||
with_fields(unifier, ty, |unifier, fields| {
|
||||
for op in ops {
|
||||
fields.borrow_mut().insert(
|
||||
fields.insert(
|
||||
unaryop_name(op).into(),
|
||||
(
|
||||
unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature { ret: ret_ty, vars: HashMap::new(), args: vec![] }.into(),
|
||||
)),
|
||||
unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
ret: ret_ty,
|
||||
vars: HashMap::new(),
|
||||
args: vec![],
|
||||
})),
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn impl_cmpop(
|
||||
|
@ -157,30 +160,25 @@ pub fn impl_cmpop(
|
|||
other_ty: Type,
|
||||
ops: &[ast::Cmpop],
|
||||
) {
|
||||
if let TypeEnum::TObj { fields, .. } = unifier.get_ty(ty).borrow() {
|
||||
with_fields(unifier, ty, |unifier, fields| {
|
||||
for op in ops {
|
||||
fields.borrow_mut().insert(
|
||||
fields.insert(
|
||||
comparison_name(op).unwrap().into(),
|
||||
(
|
||||
unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature {
|
||||
ret: store.bool,
|
||||
vars: HashMap::new(),
|
||||
args: vec![FuncArg {
|
||||
ty: other_ty,
|
||||
default_value: None,
|
||||
name: "other".into(),
|
||||
}],
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
ret: store.bool,
|
||||
vars: HashMap::new(),
|
||||
args: vec![FuncArg {
|
||||
ty: other_ty,
|
||||
default_value: None,
|
||||
name: "other".into(),
|
||||
}],
|
||||
})),
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Add, Sub, Mult
|
||||
|
@ -257,18 +255,18 @@ pub fn impl_mod(
|
|||
}
|
||||
|
||||
/// UAdd, USub
|
||||
pub fn impl_sign(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
|
||||
impl_unaryop(unifier, store, ty, ty, &[ast::Unaryop::UAdd, ast::Unaryop::USub])
|
||||
pub fn impl_sign(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type) {
|
||||
impl_unaryop(unifier, ty, ty, &[ast::Unaryop::UAdd, ast::Unaryop::USub])
|
||||
}
|
||||
|
||||
/// Invert
|
||||
pub fn impl_invert(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
|
||||
impl_unaryop(unifier, store, ty, ty, &[ast::Unaryop::Invert])
|
||||
pub fn impl_invert(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type) {
|
||||
impl_unaryop(unifier, ty, ty, &[ast::Unaryop::Invert])
|
||||
}
|
||||
|
||||
/// Not
|
||||
pub fn impl_not(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
|
||||
impl_unaryop(unifier, store, ty, store.bool, &[ast::Unaryop::Not])
|
||||
impl_unaryop(unifier, ty, store.bool, &[ast::Unaryop::Not])
|
||||
}
|
||||
|
||||
/// Lt, LtE, Gt, GtE
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod function_check;
|
||||
pub mod magic_methods;
|
||||
pub mod type_error;
|
||||
pub mod type_inferencer;
|
||||
pub mod typedef;
|
||||
mod unification_table;
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::typecheck::typedef::TypeEnum;
|
||||
|
||||
use super::typedef::{RecordKey, Type, Unifier};
|
||||
use nac3parser::ast::{Location, StrRef};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TypeErrorKind {
|
||||
TooManyArguments {
|
||||
expected: usize,
|
||||
got: usize,
|
||||
},
|
||||
MissingArgs(String),
|
||||
UnknownArgName(StrRef),
|
||||
IncorrectArgType {
|
||||
name: StrRef,
|
||||
expected: Type,
|
||||
got: Type,
|
||||
},
|
||||
FieldUnificationError {
|
||||
field: RecordKey,
|
||||
types: (Type, Type),
|
||||
loc: (Option<Location>, Option<Location>),
|
||||
},
|
||||
IncompatibleRange(Type, Vec<Type>),
|
||||
IncompatibleTypes(Type, Type),
|
||||
MutationError(RecordKey, Type),
|
||||
NoSuchField(RecordKey, Type),
|
||||
TupleIndexOutOfBounds {
|
||||
index: i32,
|
||||
len: i32,
|
||||
},
|
||||
RequiresTypeAnn,
|
||||
PolymorphicFunctionPointer,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TypeError {
|
||||
pub kind: TypeErrorKind,
|
||||
pub loc: Option<Location>,
|
||||
}
|
||||
|
||||
impl TypeError {
|
||||
pub fn new(kind: TypeErrorKind, loc: Option<Location>) -> TypeError {
|
||||
TypeError { kind, loc }
|
||||
}
|
||||
|
||||
pub fn at(mut self, loc: Option<Location>) -> TypeError {
|
||||
self.loc = self.loc.or(loc);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn to_display(self, unifier: &Unifier) -> DisplayTypeError {
|
||||
DisplayTypeError { err: self, unifier }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DisplayTypeError<'a> {
|
||||
pub err: TypeError,
|
||||
pub unifier: &'a Unifier,
|
||||
}
|
||||
|
||||
fn loc_to_str(loc: Option<Location>) -> String {
|
||||
match loc {
|
||||
Some(loc) => format!("(in {})", loc),
|
||||
None => "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for DisplayTypeError<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
use TypeErrorKind::*;
|
||||
let mut notes = Some(HashMap::new());
|
||||
match &self.err.kind {
|
||||
TooManyArguments { expected, got } => {
|
||||
write!(f, "Too many arguments. Expected {} but got {}", expected, got)
|
||||
}
|
||||
MissingArgs(args) => {
|
||||
write!(f, "Missing arguments: {}", args)
|
||||
}
|
||||
UnknownArgName(name) => {
|
||||
write!(f, "Unknown argument name: {}", name)
|
||||
}
|
||||
IncorrectArgType { name, expected, got } => {
|
||||
let expected = self.unifier.stringify_with_notes(*expected, &mut notes);
|
||||
let got = self.unifier.stringify_with_notes(*got, &mut notes);
|
||||
write!(
|
||||
f,
|
||||
"Incorrect argument type for {}. Expected {}, but got {}",
|
||||
name, expected, got
|
||||
)
|
||||
}
|
||||
FieldUnificationError { field, types, loc } => {
|
||||
let lhs = self.unifier.stringify_with_notes(types.0, &mut notes);
|
||||
let rhs = self.unifier.stringify_with_notes(types.1, &mut notes);
|
||||
write!(
|
||||
f,
|
||||
"Unable to unify field {}: Got types {}{} and {}{}",
|
||||
field,
|
||||
lhs,
|
||||
loc_to_str(loc.0),
|
||||
rhs,
|
||||
loc_to_str(loc.1)
|
||||
)
|
||||
}
|
||||
IncompatibleRange(t, ts) => {
|
||||
let t = self.unifier.stringify_with_notes(*t, &mut notes);
|
||||
let ts = ts
|
||||
.iter()
|
||||
.map(|t| self.unifier.stringify_with_notes(*t, &mut notes))
|
||||
.collect::<Vec<_>>();
|
||||
write!(f, "Expected any one of these types: {}, but got {}", ts.join(", "), t)
|
||||
}
|
||||
IncompatibleTypes(t1, t2) => {
|
||||
let type1 = self.unifier.get_ty_immutable(*t1);
|
||||
let type2 = self.unifier.get_ty_immutable(*t2);
|
||||
match (&*type1, &*type2) {
|
||||
(TypeEnum::TCall(calls), _) => {
|
||||
let loc = self.unifier.calls[calls[0].0].loc;
|
||||
let result = write!(
|
||||
f,
|
||||
"{} is not callable",
|
||||
self.unifier.stringify_with_notes(*t2, &mut notes)
|
||||
);
|
||||
if let Some(loc) = loc {
|
||||
result?;
|
||||
write!(f, " (in {})", loc)?;
|
||||
return Ok(());
|
||||
}
|
||||
result
|
||||
}
|
||||
(TypeEnum::TTuple { ty: ty1 }, TypeEnum::TTuple { ty: ty2 })
|
||||
if ty1.len() != ty2.len() =>
|
||||
{
|
||||
let t1 = self.unifier.stringify_with_notes(*t1, &mut notes);
|
||||
let t2 = self.unifier.stringify_with_notes(*t2, &mut notes);
|
||||
write!(f, "Tuple length mismatch: got {} and {}", t1, t2)
|
||||
}
|
||||
_ => {
|
||||
let t1 = self.unifier.stringify_with_notes(*t1, &mut notes);
|
||||
let t2 = self.unifier.stringify_with_notes(*t2, &mut notes);
|
||||
write!(f, "Incompatible types: {} and {}", t1, t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
MutationError(name, t) => {
|
||||
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty_immutable(*t) {
|
||||
write!(f, "Cannot assign to an element of a tuple")
|
||||
} else {
|
||||
let t = self.unifier.stringify_with_notes(*t, &mut notes);
|
||||
write!(f, "Cannot assign to field {} of {}, which is immutable", name, t)
|
||||
}
|
||||
}
|
||||
NoSuchField(name, t) => {
|
||||
let t = self.unifier.stringify_with_notes(*t, &mut notes);
|
||||
write!(f, "`{}::{}` field does not exist", t, name)
|
||||
}
|
||||
TupleIndexOutOfBounds { index, len } => {
|
||||
write!(
|
||||
f,
|
||||
"Tuple index out of bounds. Got {} but tuple has only {} elements",
|
||||
index, len
|
||||
)
|
||||
}
|
||||
RequiresTypeAnn => {
|
||||
write!(f, "Unable to infer virtual object type: Type annotation required")
|
||||
}
|
||||
PolymorphicFunctionPointer => {
|
||||
write!(f, "Polymorphic function pointers is not supported")
|
||||
}
|
||||
}?;
|
||||
if let Some(loc) = self.err.loc {
|
||||
write!(f, " at {}", loc)?;
|
||||
}
|
||||
let notes = notes.unwrap();
|
||||
if !notes.is_empty() {
|
||||
write!(f, "\n\nNotes:")?;
|
||||
for line in notes.values() {
|
||||
write!(f, "\n {}", line)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ use std::convert::{From, TryInto};
|
|||
use std::iter::once;
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
use super::typedef::{Call, FunSignature, FuncArg, Type, TypeEnum, Unifier};
|
||||
use super::typedef::{Call, FunSignature, FuncArg, RecordField, Type, TypeEnum, Unifier};
|
||||
use super::{magic_methods::*, typedef::CallId};
|
||||
use crate::{symbol_resolver::SymbolResolver, toplevel::TopLevelContext};
|
||||
use itertools::izip;
|
||||
|
@ -125,7 +125,10 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
}
|
||||
}
|
||||
ast::StmtKind::Try { body, handlers, orelse, finalbody, config_comment } => {
|
||||
let body = body.into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<Vec<_>, _>>()?;
|
||||
let body = body
|
||||
.into_iter()
|
||||
.map(|stmt| self.fold_stmt(stmt))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let outer_in_handler = self.in_handler;
|
||||
let mut exception_handlers = Vec::with_capacity(handlers.len());
|
||||
self.in_handler = true;
|
||||
|
@ -133,21 +136,29 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
let top_level_defs = self.top_level.definitions.read();
|
||||
let mut naive_folder = NaiveFolder();
|
||||
for handler in handlers.into_iter() {
|
||||
let ast::ExcepthandlerKind::ExceptHandler { type_, name, body } = handler.node;
|
||||
let ast::ExcepthandlerKind::ExceptHandler { type_, name, body } =
|
||||
handler.node;
|
||||
let type_ = if let Some(type_) = type_ {
|
||||
let typ = self.function_data.resolver.parse_type_annotation(
|
||||
top_level_defs.as_slice(),
|
||||
self.unifier,
|
||||
self.primitives,
|
||||
&type_
|
||||
&type_,
|
||||
)?;
|
||||
self.virtual_checks.push((typ, self.primitives.exception, handler.location));
|
||||
self.virtual_checks.push((
|
||||
typ,
|
||||
self.primitives.exception,
|
||||
handler.location,
|
||||
));
|
||||
if let Some(name) = name {
|
||||
if !self.defined_identifiers.contains(&name) {
|
||||
self.defined_identifiers.insert(name);
|
||||
}
|
||||
if let Some(old_typ) = self.variable_mapping.insert(name, typ) {
|
||||
self.unifier.unify(old_typ, typ)?;
|
||||
let loc = handler.location;
|
||||
self.unifier.unify(old_typ, typ).map_err(|e| {
|
||||
e.at(Some(loc)).to_display(self.unifier).to_string()
|
||||
})?;
|
||||
}
|
||||
}
|
||||
let mut type_ = naive_folder.fold_expr(*type_)?;
|
||||
|
@ -156,22 +167,32 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let body = body.into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<Vec<_>, _>>()?;
|
||||
let body = body
|
||||
.into_iter()
|
||||
.map(|stmt| self.fold_stmt(stmt))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
exception_handlers.push(Located {
|
||||
location: handler.location,
|
||||
node: ast::ExcepthandlerKind::ExceptHandler { type_, name, body },
|
||||
custom: None
|
||||
custom: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
self.in_handler = outer_in_handler;
|
||||
let handlers = exception_handlers;
|
||||
let orelse = orelse.into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<Vec<_>, _>>()?;
|
||||
let finalbody = finalbody .into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<Vec<_>, _>>()?;
|
||||
let orelse = orelse.into_iter().map(|stmt| self.fold_stmt(stmt)).collect::<Result<
|
||||
Vec<_>,
|
||||
_,
|
||||
>>(
|
||||
)?;
|
||||
let finalbody = finalbody
|
||||
.into_iter()
|
||||
.map(|stmt| self.fold_stmt(stmt))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Located {
|
||||
location: node.location,
|
||||
node: ast::StmtKind::Try { body, handlers, orelse, finalbody, config_comment },
|
||||
custom: None
|
||||
custom: None,
|
||||
}
|
||||
}
|
||||
ast::StmtKind::For { target, iter, body, orelse, config_comment, type_comment } => {
|
||||
|
@ -184,14 +205,10 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
let list = self.unifier.add_ty(TypeEnum::TList { ty: target.custom.unwrap() });
|
||||
self.unify(list, iter.custom.unwrap(), &iter.location)?;
|
||||
}
|
||||
let body = body
|
||||
.into_iter()
|
||||
.map(|b| self.fold_stmt(b))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let orelse = orelse
|
||||
.into_iter()
|
||||
.map(|o| self.fold_stmt(o))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let body =
|
||||
body.into_iter().map(|b| self.fold_stmt(b)).collect::<Result<Vec<_>, _>>()?;
|
||||
let orelse =
|
||||
orelse.into_iter().map(|o| self.fold_stmt(o)).collect::<Result<Vec<_>, _>>()?;
|
||||
Located {
|
||||
location: node.location,
|
||||
node: ast::StmtKind::For {
|
||||
|
@ -202,7 +219,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
config_comment,
|
||||
type_comment,
|
||||
},
|
||||
custom: None
|
||||
custom: None,
|
||||
}
|
||||
}
|
||||
ast::StmtKind::Assign { ref mut targets, ref config_comment, .. } => {
|
||||
|
@ -249,7 +266,9 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
}
|
||||
})
|
||||
.collect();
|
||||
let targets = targets?;
|
||||
let loc = node.location;
|
||||
let targets = targets
|
||||
.map_err(|e| e.at(Some(loc)).to_display(self.unifier).to_string())?;
|
||||
return Ok(Located {
|
||||
location: node.location,
|
||||
node: ast::StmtKind::Assign {
|
||||
|
@ -280,8 +299,8 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
_ => fold::fold_stmt(self, node)?,
|
||||
};
|
||||
match &stmt.node {
|
||||
ast::StmtKind::For { .. } => {},
|
||||
ast::StmtKind::Try { .. } => {},
|
||||
ast::StmtKind::For { .. } => {}
|
||||
ast::StmtKind::Try { .. } => {}
|
||||
ast::StmtKind::If { test, .. } | ast::StmtKind::While { test, .. } => {
|
||||
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
|
||||
}
|
||||
|
@ -299,9 +318,16 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
return report_error("raise ... from cause is not supported", cause.location);
|
||||
}
|
||||
if let Some(exc) = exc {
|
||||
self.virtual_checks.push((exc.custom.unwrap(), self.primitives.exception, exc.location));
|
||||
self.virtual_checks.push((
|
||||
exc.custom.unwrap(),
|
||||
self.primitives.exception,
|
||||
exc.location,
|
||||
));
|
||||
} else if !self.in_handler {
|
||||
return report_error("cannot reraise outside exception handlers", stmt.location);
|
||||
return report_error(
|
||||
"cannot reraise outside exception handlers",
|
||||
stmt.location,
|
||||
);
|
||||
}
|
||||
}
|
||||
ast::StmtKind::With { items, .. } => {
|
||||
|
@ -310,11 +336,9 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
// if we can simply unify without creating new types...
|
||||
let mut fast_path = false;
|
||||
if let TypeEnum::TObj { fields, .. } = &*self.unifier.get_ty(ty) {
|
||||
let fields = fields.borrow();
|
||||
fast_path = true;
|
||||
if let Some(enter) = fields.get(&"__enter__".into()).cloned() {
|
||||
if let TypeEnum::TFunc(signature) = &*self.unifier.get_ty(enter.0) {
|
||||
let signature = signature.borrow();
|
||||
if !signature.args.is_empty() {
|
||||
return report_error(
|
||||
"__enter__ method should take no argument other than self",
|
||||
|
@ -343,7 +367,6 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
}
|
||||
if let Some(exit) = fields.get(&"__exit__".into()).cloned() {
|
||||
if let TypeEnum::TFunc(signature) = &*self.unifier.get_ty(exit.0) {
|
||||
let signature = signature.borrow();
|
||||
if !signature.args.is_empty() {
|
||||
return report_error(
|
||||
"__exit__ method should take no argument other than self",
|
||||
|
@ -361,24 +384,24 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
}
|
||||
}
|
||||
if !fast_path {
|
||||
let enter = TypeEnum::TFunc(RefCell::new(FunSignature {
|
||||
let enter = TypeEnum::TFunc(FunSignature {
|
||||
args: vec![],
|
||||
ret: item.optional_vars.as_ref().map_or_else(
|
||||
|| self.unifier.get_fresh_var().0,
|
||||
|| self.unifier.get_dummy_var().0,
|
||||
|var| var.custom.unwrap(),
|
||||
),
|
||||
vars: Default::default(),
|
||||
}));
|
||||
});
|
||||
let enter = self.unifier.add_ty(enter);
|
||||
let exit = TypeEnum::TFunc(RefCell::new(FunSignature {
|
||||
let exit = TypeEnum::TFunc(FunSignature {
|
||||
args: vec![],
|
||||
ret: self.unifier.get_fresh_var().0,
|
||||
ret: self.unifier.get_dummy_var().0,
|
||||
vars: Default::default(),
|
||||
}));
|
||||
});
|
||||
let exit = self.unifier.add_ty(exit);
|
||||
let mut fields = HashMap::new();
|
||||
fields.insert("__enter__".into(), (enter, false));
|
||||
fields.insert("__exit__".into(), (exit, false));
|
||||
fields.insert("__enter__".into(), RecordField::new(enter, false, None));
|
||||
fields.insert("__exit__".into(), RecordField::new(exit, false, None));
|
||||
let record = self.unifier.add_record(fields);
|
||||
self.unify(ty, record, &stmt.location)?;
|
||||
}
|
||||
|
@ -419,8 +442,9 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
_ => fold::fold_expr(self, node)?,
|
||||
};
|
||||
let custom = match &expr.node {
|
||||
ast::ExprKind::Constant { value, .. } =>
|
||||
Some(self.infer_constant(value, &expr.location)?),
|
||||
ast::ExprKind::Constant { value, .. } => {
|
||||
Some(self.infer_constant(value, &expr.location)?)
|
||||
}
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
if !self.defined_identifiers.contains(id) {
|
||||
match self.function_data.resolver.get_symbol_type(
|
||||
|
@ -455,8 +479,8 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
ast::ExprKind::Compare { left, ops, comparators } => {
|
||||
Some(self.infer_compare(left, ops, comparators)?)
|
||||
}
|
||||
ast::ExprKind::Subscript { value, slice, .. } => {
|
||||
Some(self.infer_subscript(value.as_ref(), slice.as_ref())?)
|
||||
ast::ExprKind::Subscript { value, slice, ctx, .. } => {
|
||||
Some(self.infer_subscript(value.as_ref(), slice.as_ref(), ctx)?)
|
||||
}
|
||||
ast::ExprKind::IfExp { test, body, orelse } => {
|
||||
Some(self.infer_if_expr(test, body.as_ref(), orelse.as_ref())?)
|
||||
|
@ -477,11 +501,13 @@ impl<'a> Inferencer<'a> {
|
|||
/// Constrain a <: b
|
||||
/// Currently implemented as unification
|
||||
fn constrain(&mut self, a: Type, b: Type, location: &Location) -> Result<(), String> {
|
||||
self.unifier.unify(a, b).map_err(|old| format!("{} at {}", old, location))
|
||||
self.unify(a, b, location)
|
||||
}
|
||||
|
||||
fn unify(&mut self, a: Type, b: Type, location: &Location) -> Result<(), String> {
|
||||
self.unifier.unify(a, b).map_err(|old| format!("{} at {}", old, location))
|
||||
self.unifier
|
||||
.unify(a, b)
|
||||
.map_err(|e| e.at(Some(*location)).to_display(self.unifier).to_string())
|
||||
}
|
||||
|
||||
fn infer_pattern(&mut self, pattern: &ast::Expr<()>) -> Result<(), String> {
|
||||
|
@ -511,17 +537,17 @@ impl<'a> Inferencer<'a> {
|
|||
ret: Option<Type>,
|
||||
) -> InferenceResult {
|
||||
if let TypeEnum::TObj { params: class_params, fields, .. } = &*self.unifier.get_ty(obj) {
|
||||
if class_params.borrow().is_empty() {
|
||||
if let Some(ty) = fields.borrow().get(&method) {
|
||||
if class_params.is_empty() {
|
||||
if let Some(ty) = fields.get(&method) {
|
||||
let ty = ty.0;
|
||||
if let TypeEnum::TFunc(sign) = &*self.unifier.get_ty(ty) {
|
||||
let sign = sign.borrow();
|
||||
if sign.vars.is_empty() {
|
||||
let call = Call {
|
||||
posargs: params,
|
||||
kwargs: HashMap::new(),
|
||||
ret: sign.ret,
|
||||
fun: RefCell::new(None),
|
||||
loc: Some(location),
|
||||
};
|
||||
if let Some(ret) = ret {
|
||||
self.unifier.unify(sign.ret, ret).unwrap();
|
||||
|
@ -533,26 +559,27 @@ impl<'a> Inferencer<'a> {
|
|||
.map(|v| v.name)
|
||||
.rev()
|
||||
.collect();
|
||||
self.unifier
|
||||
.unify_call(&call, ty, &sign, &required)
|
||||
.map_err(|old| format!("{} at {}", old, location))?;
|
||||
self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| {
|
||||
e.at(Some(location)).to_display(self.unifier).to_string()
|
||||
})?;
|
||||
return Ok(sign.ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let ret = ret.unwrap_or_else(|| self.unifier.get_fresh_var().0);
|
||||
let ret = ret.unwrap_or_else(|| self.unifier.get_dummy_var().0);
|
||||
|
||||
let call = self.unifier.add_call(Call {
|
||||
posargs: params,
|
||||
kwargs: HashMap::new(),
|
||||
ret,
|
||||
fun: RefCell::new(None),
|
||||
loc: Some(location),
|
||||
});
|
||||
self.calls.insert(location.into(), call);
|
||||
let call = self.unifier.add_ty(TypeEnum::TCall(vec![call].into()));
|
||||
let fields = once((method, (call, false))).collect();
|
||||
let call = self.unifier.add_ty(TypeEnum::TCall(vec![call]));
|
||||
let fields = once((method.into(), RecordField::new(call, false, Some(location)))).collect();
|
||||
let record = self.unifier.add_record(fields);
|
||||
self.constrain(obj, record, &location)?;
|
||||
Ok(ret)
|
||||
|
@ -584,11 +611,14 @@ impl<'a> Inferencer<'a> {
|
|||
defined_identifiers.insert(*name);
|
||||
}
|
||||
}
|
||||
let fn_args: Vec<_> =
|
||||
args.args.iter().map(|v| (v.node.arg, self.unifier.get_fresh_var().0)).collect();
|
||||
let fn_args: Vec<_> = args
|
||||
.args
|
||||
.iter()
|
||||
.map(|v| (v.node.arg, self.unifier.get_fresh_var(Some(v.node.arg), Some(v.location)).0))
|
||||
.collect();
|
||||
let mut variable_mapping = self.variable_mapping.clone();
|
||||
variable_mapping.extend(fn_args.iter().cloned());
|
||||
let ret = self.unifier.get_fresh_var().0;
|
||||
let ret = self.unifier.get_dummy_var().0;
|
||||
|
||||
let mut new_context = Inferencer {
|
||||
function_data: self.function_data,
|
||||
|
@ -620,7 +650,7 @@ impl<'a> Inferencer<'a> {
|
|||
Ok(Located {
|
||||
location,
|
||||
node: ExprKind::Lambda { args: args.into(), body: body.into() },
|
||||
custom: Some(self.unifier.add_ty(TypeEnum::TFunc(fun.into()))),
|
||||
custom: Some(self.unifier.add_ty(TypeEnum::TFunc(fun))),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -648,7 +678,7 @@ impl<'a> Inferencer<'a> {
|
|||
calls: self.calls,
|
||||
defined_identifiers,
|
||||
// listcomp expr should not be considered as inside an exception handler...
|
||||
in_handler: false
|
||||
in_handler: false,
|
||||
};
|
||||
let generator = generators.pop().unwrap();
|
||||
if generator.is_async {
|
||||
|
@ -725,7 +755,7 @@ impl<'a> Inferencer<'a> {
|
|||
&arg,
|
||||
)?
|
||||
} else {
|
||||
self.unifier.get_fresh_var().0
|
||||
self.unifier.get_dummy_var().0
|
||||
};
|
||||
self.virtual_checks.push((arg0.custom.unwrap(), ty, func_location));
|
||||
let custom = Some(self.unifier.add_ty(TypeEnum::TVirtual { ty }));
|
||||
|
@ -774,7 +804,6 @@ impl<'a> Inferencer<'a> {
|
|||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
if let TypeEnum::TFunc(sign) = &*self.unifier.get_ty(func.custom.unwrap()) {
|
||||
let sign = sign.borrow();
|
||||
if sign.vars.is_empty() {
|
||||
let call = Call {
|
||||
posargs: args.iter().map(|v| v.custom.unwrap()).collect(),
|
||||
|
@ -784,6 +813,7 @@ impl<'a> Inferencer<'a> {
|
|||
.collect(),
|
||||
fun: RefCell::new(None),
|
||||
ret: sign.ret,
|
||||
loc: Some(location),
|
||||
};
|
||||
let required: Vec<_> = sign
|
||||
.args
|
||||
|
@ -793,8 +823,8 @@ impl<'a> Inferencer<'a> {
|
|||
.rev()
|
||||
.collect();
|
||||
self.unifier
|
||||
.unify_call(&call, func.custom.unwrap(), &sign, &required)
|
||||
.map_err(|old| format!("{} at {}", old, location))?;
|
||||
.unify_call(&call, func.custom.unwrap(), sign, &required)
|
||||
.map_err(|e| e.at(Some(location)).to_display(self.unifier).to_string())?;
|
||||
return Ok(Located {
|
||||
location,
|
||||
custom: Some(sign.ret),
|
||||
|
@ -803,7 +833,7 @@ impl<'a> Inferencer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let ret = self.unifier.get_fresh_var().0;
|
||||
let ret = self.unifier.get_dummy_var().0;
|
||||
let call = self.unifier.add_call(Call {
|
||||
posargs: args.iter().map(|v| v.custom.unwrap()).collect(),
|
||||
kwargs: keywords
|
||||
|
@ -812,9 +842,10 @@ impl<'a> Inferencer<'a> {
|
|||
.collect(),
|
||||
fun: RefCell::new(None),
|
||||
ret,
|
||||
loc: Some(location),
|
||||
});
|
||||
self.calls.insert(location.into(), call);
|
||||
let call = self.unifier.add_ty(TypeEnum::TCall(vec![call].into()));
|
||||
let call = self.unifier.add_ty(TypeEnum::TCall(vec![call]));
|
||||
self.unify(func.custom.unwrap(), call, &func.location)?;
|
||||
|
||||
Ok(Located { location, custom: Some(ret), node: ExprKind::Call { func, args, keywords } })
|
||||
|
@ -831,7 +862,7 @@ impl<'a> Inferencer<'a> {
|
|||
.resolver
|
||||
.get_symbol_type(unifier, &self.top_level.definitions.read(), self.primitives, id)
|
||||
.unwrap_or_else(|_| {
|
||||
let ty = unifier.get_fresh_var().0;
|
||||
let ty = unifier.get_dummy_var().0;
|
||||
variable_mapping.insert(id, ty);
|
||||
ty
|
||||
}))
|
||||
|
@ -851,8 +882,8 @@ impl<'a> Inferencer<'a> {
|
|||
} else {
|
||||
report_error("Integer out of bound", *loc)
|
||||
}
|
||||
},
|
||||
None => report_error("Integer out of bound", *loc)
|
||||
}
|
||||
None => report_error("Integer out of bound", *loc),
|
||||
}
|
||||
}
|
||||
ast::Constant::Float(_) => Ok(self.primitives.float),
|
||||
|
@ -867,7 +898,7 @@ impl<'a> Inferencer<'a> {
|
|||
}
|
||||
|
||||
fn infer_list(&mut self, elts: &[ast::Expr<Option<Type>>]) -> InferenceResult {
|
||||
let (ty, _) = self.unifier.get_fresh_var();
|
||||
let ty = self.unifier.get_dummy_var().0;
|
||||
for t in elts.iter() {
|
||||
self.unify(ty, t.custom.unwrap(), &t.location)?;
|
||||
}
|
||||
|
@ -888,7 +919,6 @@ impl<'a> Inferencer<'a> {
|
|||
let ty = value.custom.unwrap();
|
||||
if let TypeEnum::TObj { fields, .. } = &*self.unifier.get_ty(ty) {
|
||||
// just a fast path
|
||||
let fields = fields.borrow();
|
||||
match (fields.get(&attr), ctx == &ExprContext::Store) {
|
||||
(Some((ty, true)), _) => Ok(*ty),
|
||||
(Some((ty, false)), false) => Ok(*ty),
|
||||
|
@ -898,8 +928,12 @@ impl<'a> Inferencer<'a> {
|
|||
(None, _) => report_error(&format!("No such field {}", attr), value.location),
|
||||
}
|
||||
} else {
|
||||
let (attr_ty, _) = self.unifier.get_fresh_var();
|
||||
let fields = once((attr, (attr_ty, ctx == &ExprContext::Store))).collect();
|
||||
let attr_ty = self.unifier.get_dummy_var().0;
|
||||
let fields = once((
|
||||
attr.into(),
|
||||
RecordField::new(attr_ty, ctx == &ExprContext::Store, Some(value.location)),
|
||||
))
|
||||
.collect();
|
||||
let record = self.unifier.add_record(fields);
|
||||
self.constrain(value.custom.unwrap(), record, &value.location)?;
|
||||
Ok(attr_ty)
|
||||
|
@ -965,8 +999,9 @@ impl<'a> Inferencer<'a> {
|
|||
&mut self,
|
||||
value: &ast::Expr<Option<Type>>,
|
||||
slice: &ast::Expr<Option<Type>>,
|
||||
ctx: &ExprContext,
|
||||
) -> InferenceResult {
|
||||
let ty = self.unifier.get_fresh_var().0;
|
||||
let ty = self.unifier.get_dummy_var().0;
|
||||
match &slice.node {
|
||||
ast::ExprKind::Slice { lower, upper, step } => {
|
||||
for v in [lower.as_ref(), upper.as_ref(), step.as_ref()].iter().flatten() {
|
||||
|
@ -983,12 +1018,20 @@ impl<'a> Inferencer<'a> {
|
|||
None => None,
|
||||
};
|
||||
let ind = ind.ok_or_else(|| "Index must be int32".to_string())?;
|
||||
let map = once((ind, ty)).collect();
|
||||
let seq = self.unifier.add_sequence(map);
|
||||
let map = once((
|
||||
ind.into(),
|
||||
RecordField::new(ty, ctx == &ExprContext::Store, Some(value.location)),
|
||||
))
|
||||
.collect();
|
||||
let seq = self.unifier.add_record(map);
|
||||
self.constrain(value.custom.unwrap(), seq, &value.location)?;
|
||||
Ok(ty)
|
||||
}
|
||||
_ => {
|
||||
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap())
|
||||
{
|
||||
return report_error("Tuple index must be a constant (KernelInvariant is also not supported)", slice.location)
|
||||
}
|
||||
// the index is not a constant, so value can only be a list
|
||||
self.constrain(slice.custom.unwrap(), self.primitives.int32, &slice.location)?;
|
||||
let list = self.unifier.add_ty(TypeEnum::TList { ty });
|
||||
|
@ -1005,9 +1048,7 @@ impl<'a> Inferencer<'a> {
|
|||
orelse: &ast::Expr<Option<Type>>,
|
||||
) -> InferenceResult {
|
||||
self.constrain(test.custom.unwrap(), self.primitives.bool, &test.location)?;
|
||||
let ty = self.unifier.get_fresh_var().0;
|
||||
self.constrain(body.custom.unwrap(), ty, &body.location)?;
|
||||
self.constrain(orelse.custom.unwrap(), ty, &orelse.location)?;
|
||||
Ok(ty)
|
||||
self.constrain(body.custom.unwrap(), orelse.custom.unwrap(), &body.location)?;
|
||||
Ok(body.custom.unwrap())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use super::super::typedef::*;
|
||||
use super::super::{magic_methods::with_fields, typedef::*};
|
||||
use super::*;
|
||||
use crate::{
|
||||
codegen::CodeGenContext,
|
||||
location::Location,
|
||||
symbol_resolver::ValueEnum,
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
};
|
||||
|
@ -19,7 +18,10 @@ struct Resolver {
|
|||
}
|
||||
|
||||
impl SymbolResolver for Resolver {
|
||||
fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
fn get_default_param_value(
|
||||
&self,
|
||||
_: &nac3parser::ast::Expr,
|
||||
) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -41,12 +43,8 @@ impl SymbolResolver for Resolver {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_symbol_location(&self, _: StrRef) -> Option<Location> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
self.id_to_def.get(&id).cloned()
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
self.id_to_def.get(&id).cloned().ok_or("Unknown identifier".to_string())
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
|
@ -71,54 +69,51 @@ impl TestEnvironment {
|
|||
|
||||
let int32 = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(0),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
with_fields(&mut unifier, int32, |unifier, fields| {
|
||||
let add_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![FuncArg { name: "other".into(), ty: int32, default_value: None }],
|
||||
ret: int32,
|
||||
vars: HashMap::new(),
|
||||
}));
|
||||
fields.insert("__add__".into(), (add_ty, false));
|
||||
});
|
||||
if let TypeEnum::TObj { fields, .. } = &*unifier.get_ty(int32) {
|
||||
let add_ty = unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature {
|
||||
args: vec![FuncArg { name: "other".into(), ty: int32, default_value: None }],
|
||||
ret: int32,
|
||||
vars: HashMap::new(),
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
fields.borrow_mut().insert("__add__".into(), (add_ty, false));
|
||||
}
|
||||
let int64 = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(1),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let float = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(2),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let bool = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(3),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let none = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(4),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let range = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(5),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let str = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(6),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let exception = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(7),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
|
||||
set_primitives_magic_methods(&primitives, &mut unifier);
|
||||
|
@ -172,58 +167,56 @@ impl TestEnvironment {
|
|||
let mut top_level_defs: Vec<Arc<RwLock<TopLevelDef>>> = Vec::new();
|
||||
let int32 = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(0),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
with_fields(&mut unifier, int32, |unifier, fields| {
|
||||
let add_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![FuncArg { name: "other".into(), ty: int32, default_value: None }],
|
||||
ret: int32,
|
||||
vars: HashMap::new(),
|
||||
}));
|
||||
fields.insert("__add__".into(), (add_ty, false));
|
||||
});
|
||||
if let TypeEnum::TObj { fields, .. } = &*unifier.get_ty(int32) {
|
||||
let add_ty = unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature {
|
||||
args: vec![FuncArg { name: "other".into(), ty: int32, default_value: None }],
|
||||
ret: int32,
|
||||
vars: HashMap::new(),
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
fields.borrow_mut().insert("__add__".into(), (add_ty, false));
|
||||
}
|
||||
let int64 = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(1),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let float = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(2),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let bool = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(3),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let none = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(4),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let range = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(5),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let str = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(6),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let exception = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(7),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::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"]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
top_level_defs.push(
|
||||
RwLock::new(TopLevelDef::Class {
|
||||
|
@ -235,6 +228,7 @@ impl TestEnvironment {
|
|||
ancestors: Default::default(),
|
||||
resolver: None,
|
||||
constructor: None,
|
||||
loc: None,
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
|
@ -243,12 +237,12 @@ impl TestEnvironment {
|
|||
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
|
||||
|
||||
let (v0, id) = unifier.get_fresh_var();
|
||||
let (v0, id) = unifier.get_dummy_var();
|
||||
|
||||
let foo_ty = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(defs + 1),
|
||||
fields: [("a".into(), (v0, true))].iter().cloned().collect::<HashMap<_, _>>().into(),
|
||||
params: [(id, v0)].iter().cloned().collect::<HashMap<_, _>>().into(),
|
||||
fields: [("a".into(), (v0, true))].iter().cloned().collect::<HashMap<_, _>>(),
|
||||
params: [(id, v0)].iter().cloned().collect::<HashMap<_, _>>(),
|
||||
});
|
||||
top_level_defs.push(
|
||||
RwLock::new(TopLevelDef::Class {
|
||||
|
@ -260,32 +254,31 @@ impl TestEnvironment {
|
|||
ancestors: Default::default(),
|
||||
resolver: None,
|
||||
constructor: None,
|
||||
loc: None,
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
|
||||
identifier_mapping.insert(
|
||||
"Foo".into(),
|
||||
unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature {
|
||||
args: vec![],
|
||||
ret: foo_ty,
|
||||
vars: [(id, v0)].iter().cloned().collect(),
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![],
|
||||
ret: foo_ty,
|
||||
vars: [(id, v0)].iter().cloned().collect(),
|
||||
})),
|
||||
);
|
||||
|
||||
let fun = unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature { args: vec![], ret: int32, vars: Default::default() }.into(),
|
||||
));
|
||||
let fun = unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![],
|
||||
ret: int32,
|
||||
vars: Default::default(),
|
||||
}));
|
||||
let bar = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(defs + 2),
|
||||
fields: [("a".into(), (int32, true)), ("b".into(), (fun, true))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashMap<_, _>>()
|
||||
.into(),
|
||||
.collect::<HashMap<_, _>>(),
|
||||
params: Default::default(),
|
||||
});
|
||||
top_level_defs.push(
|
||||
|
@ -298,14 +291,17 @@ impl TestEnvironment {
|
|||
ancestors: Default::default(),
|
||||
resolver: None,
|
||||
constructor: None,
|
||||
loc: None,
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
identifier_mapping.insert(
|
||||
"Bar".into(),
|
||||
unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature { args: vec![], ret: bar, vars: Default::default() }.into(),
|
||||
)),
|
||||
unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![],
|
||||
ret: bar,
|
||||
vars: Default::default(),
|
||||
})),
|
||||
);
|
||||
|
||||
let bar2 = unifier.add_ty(TypeEnum::TObj {
|
||||
|
@ -313,8 +309,7 @@ impl TestEnvironment {
|
|||
fields: [("a".into(), (bool, true)), ("b".into(), (fun, false))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashMap<_, _>>()
|
||||
.into(),
|
||||
.collect::<HashMap<_, _>>(),
|
||||
params: Default::default(),
|
||||
});
|
||||
top_level_defs.push(
|
||||
|
@ -327,14 +322,17 @@ impl TestEnvironment {
|
|||
ancestors: Default::default(),
|
||||
resolver: None,
|
||||
constructor: None,
|
||||
loc: None,
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
identifier_mapping.insert(
|
||||
"Bar2".into(),
|
||||
unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature { args: vec![], ret: bar2, vars: Default::default() }.into(),
|
||||
)),
|
||||
unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![],
|
||||
ret: bar2,
|
||||
vars: Default::default(),
|
||||
})),
|
||||
);
|
||||
let class_names = [("Bar".into(), bar), ("Bar2".into(), bar2)].iter().cloned().collect();
|
||||
|
||||
|
@ -401,7 +399,7 @@ impl TestEnvironment {
|
|||
virtual_checks: &mut self.virtual_checks,
|
||||
calls: &mut self.calls,
|
||||
defined_identifiers: Default::default(),
|
||||
in_handler: false
|
||||
in_handler: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -421,7 +419,7 @@ impl TestEnvironment {
|
|||
c = 1.234
|
||||
d = b(c)
|
||||
"},
|
||||
[("a", "fn[[x=float, y=float], float]"), ("b", "fn[[x=float], float]"), ("c", "float"), ("d", "float")].iter().cloned().collect(),
|
||||
[("a", "fn[[x:float, y:float], float]"), ("b", "fn[[x:float], float]"), ("c", "float"), ("d", "float")].iter().cloned().collect(),
|
||||
&[]
|
||||
; "lambda test")]
|
||||
#[test_case(indoc! {"
|
||||
|
@ -430,7 +428,7 @@ impl TestEnvironment {
|
|||
a = b
|
||||
c = b(1)
|
||||
"},
|
||||
[("a", "fn[[x=int32], int32]"), ("b", "fn[[x=int32], int32]"), ("c", "int32")].iter().cloned().collect(),
|
||||
[("a", "fn[[x:int32], int32]"), ("b", "fn[[x:int32], int32]"), ("c", "int32")].iter().cloned().collect(),
|
||||
&[]
|
||||
; "lambda test 2")]
|
||||
#[test_case(indoc! {"
|
||||
|
@ -446,8 +444,8 @@ impl TestEnvironment {
|
|||
b(123)
|
||||
|
||||
"},
|
||||
[("a", "fn[[x=bool], bool]"), ("b", "fn[[x=int32], int32]"), ("c", "bool"),
|
||||
("d", "int32"), ("foo1", "Foo[1->bool]"), ("foo2", "Foo[1->int32]")].iter().cloned().collect(),
|
||||
[("a", "fn[[x:bool], bool]"), ("b", "fn[[x:int32], int32]"), ("c", "bool"),
|
||||
("d", "int32"), ("foo1", "Foo[bool]"), ("foo2", "Foo[int32]")].iter().cloned().collect(),
|
||||
&[]
|
||||
; "obj test")]
|
||||
#[test_case(indoc! {"
|
||||
|
@ -490,33 +488,37 @@ fn test_basic(source: &str, mapping: HashMap<&str, &str>, virtuals: &[(&str, &st
|
|||
inferencer.check_block(&statements, &mut defined_identifiers).unwrap();
|
||||
|
||||
for (k, v) in inferencer.variable_mapping.iter() {
|
||||
let name = inferencer.unifier.stringify(
|
||||
let name = inferencer.unifier.internal_stringify(
|
||||
*v,
|
||||
&mut |v| (*id_to_name.get(&v).unwrap()).into(),
|
||||
&mut |v| format!("v{}", v),
|
||||
&mut None,
|
||||
);
|
||||
println!("{}: {}", k, name);
|
||||
}
|
||||
for (k, v) in mapping.iter() {
|
||||
let ty = inferencer.variable_mapping.get(&(*k).into()).unwrap();
|
||||
let name = inferencer.unifier.stringify(
|
||||
let name = inferencer.unifier.internal_stringify(
|
||||
*ty,
|
||||
&mut |v| (*id_to_name.get(&v).unwrap()).into(),
|
||||
&mut |v| format!("v{}", v),
|
||||
&mut None,
|
||||
);
|
||||
assert_eq!(format!("{}: {}", k, v), format!("{}: {}", k, name));
|
||||
}
|
||||
assert_eq!(inferencer.virtual_checks.len(), virtuals.len());
|
||||
for ((a, b, _), (x, y)) in zip(inferencer.virtual_checks.iter(), virtuals) {
|
||||
let a = inferencer.unifier.stringify(
|
||||
let a = inferencer.unifier.internal_stringify(
|
||||
*a,
|
||||
&mut |v| (*id_to_name.get(&v).unwrap()).into(),
|
||||
&mut |v| format!("v{}", v),
|
||||
&mut None,
|
||||
);
|
||||
let b = inferencer.unifier.stringify(
|
||||
let b = inferencer.unifier.internal_stringify(
|
||||
*b,
|
||||
&mut |v| (*id_to_name.get(&v).unwrap()).into(),
|
||||
&mut |v| format!("v{}", v),
|
||||
&mut None,
|
||||
);
|
||||
|
||||
assert_eq!(&a, x);
|
||||
|
@ -632,19 +634,21 @@ fn test_primitive_magic_methods(source: &str, mapping: HashMap<&str, &str>) {
|
|||
inferencer.check_block(&statements, &mut defined_identifiers).unwrap();
|
||||
|
||||
for (k, v) in inferencer.variable_mapping.iter() {
|
||||
let name = inferencer.unifier.stringify(
|
||||
let name = inferencer.unifier.internal_stringify(
|
||||
*v,
|
||||
&mut |v| (*id_to_name.get(&v).unwrap()).into(),
|
||||
&mut |v| format!("v{}", v),
|
||||
&mut None,
|
||||
);
|
||||
println!("{}: {}", k, name);
|
||||
}
|
||||
for (k, v) in mapping.iter() {
|
||||
let ty = inferencer.variable_mapping.get(&(*k).into()).unwrap();
|
||||
let name = inferencer.unifier.stringify(
|
||||
let name = inferencer.unifier.internal_stringify(
|
||||
*ty,
|
||||
&mut |v| (*id_to_name.get(&v).unwrap()).into(),
|
||||
&mut |v| format!("v{}", v),
|
||||
&mut None,
|
||||
);
|
||||
assert_eq!(format!("{}: {}", k, v), format!("{}: {}", k, name));
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,4 @@
|
|||
use super::super::magic_methods::with_fields;
|
||||
use super::*;
|
||||
use indoc::indoc;
|
||||
use itertools::Itertools;
|
||||
|
@ -7,7 +8,6 @@ use test_case::test_case;
|
|||
impl Unifier {
|
||||
/// Check whether two types are equal.
|
||||
fn eq(&mut self, a: Type, b: Type) -> bool {
|
||||
use TypeVarMeta::*;
|
||||
if a == b {
|
||||
return true;
|
||||
}
|
||||
|
@ -21,13 +21,13 @@ impl Unifier {
|
|||
|
||||
match (&*ty_a, &*ty_b) {
|
||||
(
|
||||
TypeEnum::TVar { meta: Generic, id: id1, .. },
|
||||
TypeEnum::TVar { meta: Generic, id: id2, .. },
|
||||
TypeEnum::TVar { fields: None, id: id1, .. },
|
||||
TypeEnum::TVar { fields: None, id: id2, .. },
|
||||
) => id1 == id2,
|
||||
(
|
||||
TypeEnum::TVar { meta: Sequence(map1), .. },
|
||||
TypeEnum::TVar { meta: Sequence(map2), .. },
|
||||
) => self.map_eq(&map1.borrow(), &map2.borrow()),
|
||||
TypeEnum::TVar { fields: Some(map1), .. },
|
||||
TypeEnum::TVar { fields: Some(map2), .. },
|
||||
) => self.map_eq2(map1, map2),
|
||||
(TypeEnum::TTuple { ty: ty1 }, TypeEnum::TTuple { ty: ty2 }) => {
|
||||
ty1.len() == ty2.len()
|
||||
&& ty1.iter().zip(ty2.iter()).all(|(t1, t2)| self.eq(*t1, *t2))
|
||||
|
@ -36,14 +36,10 @@ impl Unifier {
|
|||
| (TypeEnum::TVirtual { ty: ty1 }, TypeEnum::TVirtual { ty: ty2 }) => {
|
||||
self.eq(*ty1, *ty2)
|
||||
}
|
||||
(
|
||||
TypeEnum::TVar { meta: Record(fields1), .. },
|
||||
TypeEnum::TVar { meta: Record(fields2), .. },
|
||||
) => self.map_eq2(&fields1.borrow(), &fields2.borrow()),
|
||||
(
|
||||
TypeEnum::TObj { obj_id: id1, params: params1, .. },
|
||||
TypeEnum::TObj { obj_id: id2, params: params2, .. },
|
||||
) => id1 == id2 && self.map_eq(¶ms1.borrow(), ¶ms2.borrow()),
|
||||
) => id1 == id2 && self.map_eq(params1, params2),
|
||||
// TCall and TFunc are not yet implemented
|
||||
_ => false,
|
||||
}
|
||||
|
@ -64,19 +60,15 @@ impl Unifier {
|
|||
true
|
||||
}
|
||||
|
||||
fn map_eq2<K>(
|
||||
&mut self,
|
||||
map1: &Mapping<K, (Type, bool)>,
|
||||
map2: &Mapping<K, (Type, bool)>,
|
||||
) -> bool
|
||||
fn map_eq2<K>(&mut self, map1: &Mapping<K, RecordField>, map2: &Mapping<K, RecordField>) -> bool
|
||||
where
|
||||
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
|
||||
{
|
||||
if map1.len() != map2.len() {
|
||||
return false;
|
||||
}
|
||||
for (k, (ty1, m1)) in map1.iter() {
|
||||
if !map2.get(k).map(|(ty2, m2)| m1 == m2 && self.eq(*ty1, *ty2)).unwrap_or(false) {
|
||||
for (k, v) in map1.iter() {
|
||||
if !map2.get(k).map(|v1| self.eq(v.ty, v1.ty)).unwrap_or(false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -98,37 +90,33 @@ impl TestEnvironment {
|
|||
"int".into(),
|
||||
unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(0),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
}),
|
||||
);
|
||||
type_mapping.insert(
|
||||
"float".into(),
|
||||
unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(1),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
}),
|
||||
);
|
||||
type_mapping.insert(
|
||||
"bool".into(),
|
||||
unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(2),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
fields: HashMap::new(),
|
||||
params: HashMap::new(),
|
||||
}),
|
||||
);
|
||||
let (v0, id) = unifier.get_fresh_var();
|
||||
let (v0, id) = unifier.get_dummy_var();
|
||||
type_mapping.insert(
|
||||
"Foo".into(),
|
||||
unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(3),
|
||||
fields: [("a".into(), (v0, true))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashMap<_, _>>()
|
||||
.into(),
|
||||
params: [(id, v0)].iter().cloned().collect::<HashMap<_, _>>().into(),
|
||||
fields: [("a".into(), (v0, true))].iter().cloned().collect::<HashMap<_, _>>(),
|
||||
params: [(id, v0)].iter().cloned().collect::<HashMap<_, _>>(),
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -174,7 +162,7 @@ impl TestEnvironment {
|
|||
let eq = s.find('=').unwrap();
|
||||
let key = s[1..eq].into();
|
||||
let result = self.internal_parse(&s[eq + 1..], mapping);
|
||||
fields.insert(key, (result.0, true));
|
||||
fields.insert(key, RecordField::new(result.0, true, None));
|
||||
s = result.1;
|
||||
}
|
||||
(self.unifier.add_record(fields), &s[1..])
|
||||
|
@ -187,7 +175,6 @@ impl TestEnvironment {
|
|||
let mut ty = *self.type_mapping.get(x).unwrap();
|
||||
let te = self.unifier.get_ty(ty);
|
||||
if let TypeEnum::TObj { params, .. } = &*te.as_ref() {
|
||||
let params = params.borrow();
|
||||
if !params.is_empty() {
|
||||
assert!(&s[0..1] == "[");
|
||||
let mut p = Vec::new();
|
||||
|
@ -209,6 +196,10 @@ impl TestEnvironment {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unify(&mut self, typ1: Type, typ2: Type) -> Result<(), String> {
|
||||
self.unifier.unify(typ1, typ2).map_err(|e| e.to_display(&self.unifier).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[test_case(2,
|
||||
|
@ -258,7 +249,7 @@ fn test_unify(
|
|||
let mut env = TestEnvironment::new();
|
||||
let mut mapping = HashMap::new();
|
||||
for i in 1..=variable_count {
|
||||
let v = env.unifier.get_fresh_var();
|
||||
let v = env.unifier.get_dummy_var();
|
||||
mapping.insert(format!("v{}", i), v.0);
|
||||
}
|
||||
// unification may have side effect when we do type resolution, so freeze the types
|
||||
|
@ -276,6 +267,7 @@ fn test_unify(
|
|||
println!("{} = {}", a, b);
|
||||
let t1 = env.parse(a, &mapping);
|
||||
let t2 = env.parse(b, &mapping);
|
||||
println!("a = {}, b = {}", env.unifier.stringify(t1), env.unifier.stringify(t2));
|
||||
assert!(env.unifier.eq(t1, t2));
|
||||
}
|
||||
}
|
||||
|
@ -286,7 +278,7 @@ fn test_unify(
|
|||
("v1", "tuple[int]"),
|
||||
("v2", "list[int]"),
|
||||
],
|
||||
(("v1", "v2"), "Cannot unify list[0] with tuple[0]")
|
||||
(("v1", "v2"), "Incompatible types: list[0] and tuple[0]")
|
||||
; "type mismatch"
|
||||
)]
|
||||
#[test_case(2,
|
||||
|
@ -294,7 +286,7 @@ fn test_unify(
|
|||
("v1", "tuple[int]"),
|
||||
("v2", "tuple[float]"),
|
||||
],
|
||||
(("v1", "v2"), "Cannot unify 0 with 1")
|
||||
(("v1", "v2"), "Incompatible types: 0 and 1")
|
||||
; "tuple parameter mismatch"
|
||||
)]
|
||||
#[test_case(2,
|
||||
|
@ -302,7 +294,7 @@ fn test_unify(
|
|||
("v1", "tuple[int,int]"),
|
||||
("v2", "tuple[int]"),
|
||||
],
|
||||
(("v1", "v2"), "Cannot unify tuples with length 2 and 1")
|
||||
(("v1", "v2"), "Tuple length mismatch: got tuple[0, 0] and tuple[0]")
|
||||
; "tuple length mismatch"
|
||||
)]
|
||||
#[test_case(3,
|
||||
|
@ -310,7 +302,7 @@ fn test_unify(
|
|||
("v1", "Record[a=float,b=int]"),
|
||||
("v2", "Foo[v3]"),
|
||||
],
|
||||
(("v1", "v2"), "No such attribute b")
|
||||
(("v1", "v2"), "`3[var4]::b` field does not exist")
|
||||
; "record obj merge"
|
||||
)]
|
||||
/// Test cases for invalid unifications.
|
||||
|
@ -322,7 +314,7 @@ fn test_invalid_unification(
|
|||
let mut env = TestEnvironment::new();
|
||||
let mut mapping = HashMap::new();
|
||||
for i in 1..=variable_count {
|
||||
let v = env.unifier.get_fresh_var();
|
||||
let v = env.unifier.get_dummy_var();
|
||||
mapping.insert(format!("v{}", i), v.0);
|
||||
}
|
||||
// unification may have side effect when we do type resolution, so freeze the types
|
||||
|
@ -338,7 +330,7 @@ fn test_invalid_unification(
|
|||
for (a, b) in pairs {
|
||||
env.unifier.unify(a, b).unwrap();
|
||||
}
|
||||
assert_eq!(env.unifier.unify(t1, t2), Err(errornous_pair.1.to_string()));
|
||||
assert_eq!(env.unify(t1, t2), Err(errornous_pair.1.to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -348,16 +340,17 @@ fn test_recursive_subst() {
|
|||
let foo_id = *env.type_mapping.get("Foo").unwrap();
|
||||
let foo_ty = env.unifier.get_ty(foo_id);
|
||||
let mapping: HashMap<_, _>;
|
||||
if let TypeEnum::TObj { fields, params, .. } = &*foo_ty {
|
||||
fields.borrow_mut().insert("rec".into(), (foo_id, true));
|
||||
mapping = params.borrow().iter().map(|(id, _)| (*id, int)).collect();
|
||||
with_fields(&mut env.unifier, foo_id, |_unifier, fields| {
|
||||
fields.insert("rec".into(), (foo_id, true));
|
||||
});
|
||||
if let TypeEnum::TObj { params, .. } = &*foo_ty {
|
||||
mapping = params.iter().map(|(id, _)| (*id, int)).collect();
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
let instantiated = env.unifier.subst(foo_id, &mapping).unwrap();
|
||||
let instantiated_ty = env.unifier.get_ty(instantiated);
|
||||
if let TypeEnum::TObj { fields, .. } = &*instantiated_ty {
|
||||
let fields = fields.borrow();
|
||||
assert!(env.unifier.unioned(fields.get(&"a".into()).unwrap().0, int));
|
||||
assert!(env.unifier.unioned(fields.get(&"rec".into()).unwrap().0, instantiated));
|
||||
} else {
|
||||
|
@ -369,33 +362,40 @@ fn test_recursive_subst() {
|
|||
fn test_virtual() {
|
||||
let mut env = TestEnvironment::new();
|
||||
let int = env.parse("int", &HashMap::new());
|
||||
let fun = env.unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature { args: vec![], ret: int, vars: HashMap::new() }.into(),
|
||||
));
|
||||
let fun = env.unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![],
|
||||
ret: int,
|
||||
vars: HashMap::new(),
|
||||
}));
|
||||
let bar = env.unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(5),
|
||||
fields: [("f".into(), (fun, false)), ("a".into(), (int, false))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashMap<StrRef, _>>()
|
||||
.into(),
|
||||
params: HashMap::new().into(),
|
||||
.collect::<HashMap<StrRef, _>>(),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
let v0 = env.unifier.get_fresh_var().0;
|
||||
let v1 = env.unifier.get_fresh_var().0;
|
||||
let v0 = env.unifier.get_dummy_var().0;
|
||||
let v1 = env.unifier.get_dummy_var().0;
|
||||
|
||||
let a = env.unifier.add_ty(TypeEnum::TVirtual { ty: bar });
|
||||
let b = env.unifier.add_ty(TypeEnum::TVirtual { ty: v0 });
|
||||
let c = env.unifier.add_record([("f".into(), (v1, false))].iter().cloned().collect());
|
||||
let c = env
|
||||
.unifier
|
||||
.add_record([("f".into(), RecordField::new(v1, false, None))].iter().cloned().collect());
|
||||
env.unifier.unify(a, b).unwrap();
|
||||
env.unifier.unify(b, c).unwrap();
|
||||
assert!(env.unifier.eq(v1, fun));
|
||||
|
||||
let d = env.unifier.add_record([("a".into(), (v1, true))].iter().cloned().collect());
|
||||
assert_eq!(env.unifier.unify(b, d), Err("Cannot access field a for virtual type".to_string()));
|
||||
let d = env
|
||||
.unifier
|
||||
.add_record([("a".into(), RecordField::new(v1, true, None))].iter().cloned().collect());
|
||||
assert_eq!(env.unify(b, d), Err("`virtual[5]::a` field does not exist".to_string()));
|
||||
|
||||
let d = env.unifier.add_record([("b".into(), (v1, true))].iter().cloned().collect());
|
||||
assert_eq!(env.unifier.unify(b, d), Err("No such attribute b".to_string()));
|
||||
let d = env
|
||||
.unifier
|
||||
.add_record([("b".into(), RecordField::new(v1, true, None))].iter().cloned().collect());
|
||||
assert_eq!(env.unify(b, d), Err("`virtual[5]::b` field does not exist".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -409,107 +409,104 @@ fn test_typevar_range() {
|
|||
|
||||
// unification between v and int
|
||||
// where v in (int, bool)
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean]).0;
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).0;
|
||||
env.unifier.unify(int, v).unwrap();
|
||||
|
||||
// unification between v and list[int]
|
||||
// where v in (int, bool)
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean]).0;
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).0;
|
||||
assert_eq!(
|
||||
env.unifier.unify(int_list, v),
|
||||
Err("Cannot unify variable 3 with list[0] due to incompatible value range".to_string())
|
||||
env.unify(int_list, v),
|
||||
Err("Expected any one of these types: 0, 2, but got list[0]".to_string())
|
||||
);
|
||||
|
||||
// unification between v and float
|
||||
// where v in (int, bool)
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean]).0;
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).0;
|
||||
assert_eq!(
|
||||
env.unifier.unify(float, v),
|
||||
Err("Cannot unify variable 4 with 1 due to incompatible value range".to_string())
|
||||
env.unify(float, v),
|
||||
Err("Expected any one of these types: 0, 2, but got 1".to_string())
|
||||
);
|
||||
|
||||
let v1 = env.unifier.get_fresh_var_with_range(&[int, boolean]).0;
|
||||
let v1 = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).0;
|
||||
let v1_list = env.unifier.add_ty(TypeEnum::TList { ty: v1 });
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, v1_list]).0;
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, v1_list], None, None).0;
|
||||
// unification between v and int
|
||||
// where v in (int, list[v1]), v1 in (int, bool)
|
||||
env.unifier.unify(int, v).unwrap();
|
||||
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, v1_list]).0;
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, v1_list], None, None).0;
|
||||
// unification between v and list[int]
|
||||
// where v in (int, list[v1]), v1 in (int, bool)
|
||||
env.unifier.unify(int_list, v).unwrap();
|
||||
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, v1_list]).0;
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, v1_list], None, None).0;
|
||||
// unification between v and list[float]
|
||||
// where v in (int, list[v1]), v1 in (int, bool)
|
||||
assert_eq!(
|
||||
env.unifier.unify(float_list, v),
|
||||
Err("Cannot unify variable 8 with list[1] due to incompatible value range".to_string())
|
||||
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())
|
||||
);
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float]).0;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float]).0;
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float], None, None).0;
|
||||
env.unifier.unify(a, b).unwrap();
|
||||
env.unifier.unify(a, float).unwrap();
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float]).0;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float]).0;
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float], None, None).0;
|
||||
env.unifier.unify(a, b).unwrap();
|
||||
assert_eq!(
|
||||
env.unifier.unify(a, int),
|
||||
Err("Cannot unify variable 12 with 0 due to incompatible value range".into())
|
||||
);
|
||||
assert_eq!(env.unify(a, int), Err("Expected any one of these types: 1, but got 0".into()));
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float]).0;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float]).0;
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float], None, None).0;
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TList { ty: a });
|
||||
let a_list = env.unifier.get_fresh_var_with_range(&[a_list]).0;
|
||||
let a_list = env.unifier.get_fresh_var_with_range(&[a_list], None, None).0;
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TList { ty: b });
|
||||
let b_list = env.unifier.get_fresh_var_with_range(&[b_list]).0;
|
||||
let b_list = env.unifier.get_fresh_var_with_range(&[b_list], None, None).0;
|
||||
env.unifier.unify(a_list, b_list).unwrap();
|
||||
let float_list = env.unifier.add_ty(TypeEnum::TList { 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]).0;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float]).0;
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
|
||||
let b = env.unifier.get_fresh_var_with_range(&[boolean, float], None, None).0;
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TList { ty: a });
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TList { ty: b });
|
||||
env.unifier.unify(a_list, b_list).unwrap();
|
||||
let int_list = env.unifier.add_ty(TypeEnum::TList { ty: int });
|
||||
assert_eq!(
|
||||
env.unifier.unify(a_list, int_list),
|
||||
Err("Cannot unify variable 19 with 0 due to incompatible value range".into())
|
||||
env.unify(a_list, int_list),
|
||||
Err("Expected any one of these types: 1, but got 0".into())
|
||||
);
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float]).0;
|
||||
let b = env.unifier.get_fresh_var().0;
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
|
||||
let b = env.unifier.get_dummy_var().0;
|
||||
let a_list = env.unifier.add_ty(TypeEnum::TList { ty: a });
|
||||
let a_list = env.unifier.get_fresh_var_with_range(&[a_list]).0;
|
||||
let a_list = env.unifier.get_fresh_var_with_range(&[a_list], None, None).0;
|
||||
let b_list = env.unifier.add_ty(TypeEnum::TList { ty: b });
|
||||
env.unifier.unify(a_list, b_list).unwrap();
|
||||
assert_eq!(
|
||||
env.unifier.unify(b, boolean),
|
||||
Err("Cannot unify variable 21 with 2 due to incompatible value range".into())
|
||||
env.unify(b, boolean),
|
||||
Err("Expected any one of these types: 0, 1, but got 2".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rigid_var() {
|
||||
let mut env = TestEnvironment::new();
|
||||
let a = env.unifier.get_fresh_rigid_var().0;
|
||||
let b = env.unifier.get_fresh_rigid_var().0;
|
||||
let x = env.unifier.get_fresh_var().0;
|
||||
let a = env.unifier.get_fresh_rigid_var(None, None).0;
|
||||
let b = env.unifier.get_fresh_rigid_var(None, None).0;
|
||||
let x = env.unifier.get_dummy_var().0;
|
||||
let list_a = env.unifier.add_ty(TypeEnum::TList { ty: a });
|
||||
let list_x = env.unifier.add_ty(TypeEnum::TList { ty: x });
|
||||
let int = env.parse("int", &HashMap::new());
|
||||
let list_int = env.parse("list[int]", &HashMap::new());
|
||||
|
||||
assert_eq!(env.unifier.unify(a, b), Err("Cannot unify var3 with var2".to_string()));
|
||||
assert_eq!(env.unify(a, b), Err("Incompatible types: var3 and var2".to_string()));
|
||||
env.unifier.unify(list_a, list_x).unwrap();
|
||||
assert_eq!(env.unifier.unify(list_x, list_int), Err("Cannot unify 0 with var2".to_string()));
|
||||
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: 0 and var2".to_string()));
|
||||
|
||||
env.unifier.replace_rigid_var(a, int);
|
||||
env.unifier.unify(list_x, list_int).unwrap();
|
||||
|
@ -526,13 +523,13 @@ fn test_instantiation() {
|
|||
let obj_map: HashMap<_, _> =
|
||||
[(0usize, "int"), (1, "float"), (2, "bool")].iter().cloned().collect();
|
||||
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean]).0;
|
||||
let v = env.unifier.get_fresh_var_with_range(&[int, boolean], None, None).0;
|
||||
let list_v = env.unifier.add_ty(TypeEnum::TList { ty: v });
|
||||
let v1 = env.unifier.get_fresh_var_with_range(&[list_v, int]).0;
|
||||
let v2 = env.unifier.get_fresh_var_with_range(&[list_int, float]).0;
|
||||
let t = env.unifier.get_fresh_rigid_var().0;
|
||||
let v1 = env.unifier.get_fresh_var_with_range(&[list_v, int], None, None).0;
|
||||
let v2 = env.unifier.get_fresh_var_with_range(&[list_int, float], None, None).0;
|
||||
let t = env.unifier.get_dummy_var().0;
|
||||
let tuple = env.unifier.add_ty(TypeEnum::TTuple { ty: vec![v, v1, v2] });
|
||||
let v3 = env.unifier.get_fresh_var_with_range(&[tuple, t]).0;
|
||||
let v3 = env.unifier.get_fresh_var_with_range(&[tuple, t], None, None).0;
|
||||
// t = TypeVar('t')
|
||||
// v = TypeVar('v', int, bool)
|
||||
// v1 = TypeVar('v1', 'list[v]', int)
|
||||
|
@ -561,9 +558,12 @@ fn test_instantiation() {
|
|||
let types = types
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
env.unifier.stringify(*ty, &mut |i| obj_map.get(&i).unwrap().to_string(), &mut |i| {
|
||||
format!("v{}", i)
|
||||
})
|
||||
env.unifier.internal_stringify(
|
||||
*ty,
|
||||
&mut |i| obj_map.get(&i).unwrap().to_string(),
|
||||
&mut |i| format!("v{}", i),
|
||||
&mut None,
|
||||
)
|
||||
})
|
||||
.sorted()
|
||||
.collect_vec();
|
||||
|
|
|
@ -46,6 +46,17 @@ impl<V> UnificationTable<V> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn probe_value_immutable(&self, key: UnificationKey) -> &V {
|
||||
let mut root = key.0;
|
||||
let mut parent = self.parents[root];
|
||||
while root != parent {
|
||||
root = parent;
|
||||
// parent = root.parent
|
||||
parent = self.parents[parent];
|
||||
}
|
||||
self.values[parent].as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn probe_value(&mut self, a: UnificationKey) -> &V {
|
||||
let index = self.find(a);
|
||||
self.values[index].as_ref().unwrap()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod cslice { // copied from https://github.com/dherman/cslice
|
||||
mod cslice {
|
||||
// copied from https://github.com/dherman/cslice
|
||||
use std::marker::PhantomData;
|
||||
use std::slice;
|
||||
|
||||
|
@ -7,14 +8,12 @@ mod cslice { // copied from https://github.com/dherman/cslice
|
|||
pub struct CSlice<'a, T> {
|
||||
base: *const T,
|
||||
len: usize,
|
||||
marker: PhantomData<&'a ()>
|
||||
marker: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, T> AsRef<[T]> for CSlice<'a, T> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self.base, self.len)
|
||||
}
|
||||
unsafe { slice::from_raw_parts(self.base, self.len) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +42,7 @@ pub extern "C" fn output_asciiart(x: i32) {
|
|||
pub extern "C" fn output_int32_list(x: &cslice::CSlice<i32>) {
|
||||
print!("[");
|
||||
let mut it = x.as_ref().iter().peekable();
|
||||
while let Some(e) = it.next() {
|
||||
while let Some(e) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
print!("{}", e);
|
||||
} else {
|
||||
|
@ -58,7 +57,6 @@ pub extern "C" fn __artiq_personality(_state: u32, _exception_object: u32, _cont
|
|||
unimplemented!();
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
fn run() -> i32;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use nac3core::{
|
||||
codegen::CodeGenContext,
|
||||
location::Location,
|
||||
symbol_resolver::{SymbolResolver, SymbolValue, ValueEnum},
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
typecheck::{
|
||||
|
@ -39,10 +38,8 @@ pub struct Resolver(pub Arc<ResolverInternal>);
|
|||
impl SymbolResolver for Resolver {
|
||||
fn get_default_param_value(&self, expr: &ast::Expr) -> Option<SymbolValue> {
|
||||
match &expr.node {
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
self.0.module_globals.lock().get(id).cloned()
|
||||
}
|
||||
_ => unimplemented!("other type of expr not supported at {}", expr.location)
|
||||
ast::ExprKind::Name { id, .. } => self.0.module_globals.lock().get(id).cloned(),
|
||||
_ => unimplemented!("other type of expr not supported at {}", expr.location),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,12 +61,8 @@ impl SymbolResolver for Resolver {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_symbol_location(&self, _: StrRef) -> Option<Location> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
self.0.id_to_def.lock().get(&id).cloned()
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Undefined identifier".to_string())
|
||||
}
|
||||
|
||||
fn get_string_id(&self, s: &str) -> i32 {
|
||||
|
|
|
@ -1,24 +1,30 @@
|
|||
use inkwell::{
|
||||
memory_buffer::MemoryBuffer,
|
||||
passes::{PassManager, PassManagerBuilder},
|
||||
targets::*,
|
||||
OptimizationLevel, memory_buffer::MemoryBuffer,
|
||||
OptimizationLevel,
|
||||
};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use std::{borrow::Borrow, collections::HashMap, env, fs, path::Path, sync::Arc};
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
|
||||
use nac3parser::{ast::{Expr, ExprKind, StmtKind}, parser};
|
||||
use nac3core::{
|
||||
codegen::{
|
||||
concrete_type::ConcreteTypeStore, CodeGenTask, DefaultCodeGenerator, WithCall,
|
||||
WorkerRegistry, irrt::load_irrt,
|
||||
concrete_type::ConcreteTypeStore, irrt::load_irrt, CodeGenTask, DefaultCodeGenerator,
|
||||
WithCall, WorkerRegistry,
|
||||
},
|
||||
symbol_resolver::SymbolResolver,
|
||||
toplevel::{
|
||||
composer::TopLevelComposer,
|
||||
TopLevelDef, helper::parse_parameter_default_value,
|
||||
type_annotation::*,
|
||||
composer::TopLevelComposer, helper::parse_parameter_default_value, type_annotation::*,
|
||||
TopLevelDef,
|
||||
},
|
||||
typecheck::{type_inferencer::PrimitiveStore, typedef::{Type, Unifier, FunSignature}}
|
||||
typecheck::{
|
||||
type_inferencer::PrimitiveStore,
|
||||
typedef::{FunSignature, Type, Unifier},
|
||||
},
|
||||
};
|
||||
use nac3parser::{
|
||||
ast::{Expr, ExprKind, StmtKind},
|
||||
parser,
|
||||
};
|
||||
|
||||
mod basic_symbol_resolver;
|
||||
|
@ -26,10 +32,7 @@ use basic_symbol_resolver::*;
|
|||
|
||||
fn main() {
|
||||
let file_name = env::args().nth(1).unwrap();
|
||||
let threads: u32 = env::args()
|
||||
.nth(2)
|
||||
.map(|s| str::parse(&s).unwrap())
|
||||
.unwrap_or(1);
|
||||
let threads: u32 = env::args().nth(2).map(|s| str::parse(&s).unwrap()).unwrap_or(1);
|
||||
|
||||
Target::initialize_all(&InitializationConfig::default());
|
||||
|
||||
|
@ -42,10 +45,8 @@ fn main() {
|
|||
};
|
||||
|
||||
let primitive: PrimitiveStore = TopLevelComposer::make_primitives().0;
|
||||
let (mut composer, builtins_def, builtins_ty) = TopLevelComposer::new(
|
||||
vec![],
|
||||
Default::default()
|
||||
);
|
||||
let (mut composer, builtins_def, builtins_ty) =
|
||||
TopLevelComposer::new(vec![], Default::default());
|
||||
|
||||
let internal_resolver: Arc<ResolverInternal> = ResolverInternal {
|
||||
id_to_type: builtins_ty.into(),
|
||||
|
@ -83,15 +84,23 @@ fn main() {
|
|||
x,
|
||||
Default::default(),
|
||||
)?;
|
||||
get_type_from_type_annotation_kinds(def_list, unifier, primitives, &ty)
|
||||
get_type_from_type_annotation_kinds(
|
||||
def_list, unifier, primitives, &ty,
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(unifier.get_fresh_var_with_range(&constraints).0)
|
||||
Ok(unifier.get_fresh_var_with_range(&constraints, None, None).0)
|
||||
} else {
|
||||
Err(format!("expression {:?} cannot be handled as a TypeVar in global scope", var))
|
||||
Err(format!(
|
||||
"expression {:?} cannot be handled as a TypeVar in global scope",
|
||||
var
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(format!("expression {:?} cannot be handled as a TypeVar in global scope", var))
|
||||
Err(format!(
|
||||
"expression {:?} cannot be handled as a TypeVar in global scope",
|
||||
var
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +125,9 @@ fn main() {
|
|||
) {
|
||||
internal_resolver.add_id_type(*id, var);
|
||||
Ok(())
|
||||
} else if let Ok(val) = parse_parameter_default_value(value.borrow(), resolver) {
|
||||
} else if let Ok(val) =
|
||||
parse_parameter_default_value(value.borrow(), resolver)
|
||||
{
|
||||
internal_resolver.add_module_global(*id, val);
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -126,8 +137,7 @@ fn main() {
|
|||
))
|
||||
}
|
||||
}
|
||||
ExprKind::List { elts, .. }
|
||||
| ExprKind::Tuple { elts, .. } => {
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
handle_assignment_pattern(
|
||||
elts,
|
||||
value,
|
||||
|
@ -135,16 +145,18 @@ fn main() {
|
|||
internal_resolver,
|
||||
def_list,
|
||||
unifier,
|
||||
primitives
|
||||
primitives,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(format!("assignment to {:?} is not supported at {}", targets[0], targets[0].location))
|
||||
_ => Err(format!(
|
||||
"assignment to {:?} is not supported at {}",
|
||||
targets[0], targets[0].location
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
match &value.node {
|
||||
ExprKind::List { elts, .. }
|
||||
| ExprKind::Tuple { elts, .. } => {
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
if elts.len() != targets.len() {
|
||||
Err(format!(
|
||||
"number of elements to unpack does not match (expect {}, found {}) at {}",
|
||||
|
@ -161,13 +173,16 @@ fn main() {
|
|||
internal_resolver,
|
||||
def_list,
|
||||
unifier,
|
||||
primitives
|
||||
primitives,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
_ => Err(format!("unpack of this expression is not supported at {}", value.location))
|
||||
}
|
||||
_ => Err(format!(
|
||||
"unpack of this expression is not supported at {}",
|
||||
value.location
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,9 +205,8 @@ fn main() {
|
|||
continue;
|
||||
}
|
||||
|
||||
let (name, def_id, ty) = composer
|
||||
.register_top_level(stmt, Some(resolver.clone()), "__main__".into())
|
||||
.unwrap();
|
||||
let (name, def_id, ty) =
|
||||
composer.register_top_level(stmt, Some(resolver.clone()), "__main__".into()).unwrap();
|
||||
|
||||
internal_resolver.add_id_def(name, def_id);
|
||||
if let Some(ty) = ty {
|
||||
|
@ -200,11 +214,7 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
let signature = FunSignature {
|
||||
args: vec![],
|
||||
ret: primitive.int32,
|
||||
vars: HashMap::new(),
|
||||
};
|
||||
let signature = FunSignature { args: vec![], ret: primitive.int32, vars: HashMap::new() };
|
||||
let mut store = ConcreteTypeStore::new();
|
||||
let mut cache = HashMap::new();
|
||||
let signature = store.from_signature(&mut composer.unifier, &primitive, &signature, &mut cache);
|
||||
|
@ -216,17 +226,12 @@ fn main() {
|
|||
|
||||
let instance = {
|
||||
let defs = top_level.definitions.read();
|
||||
let mut instance =
|
||||
defs[resolver
|
||||
.get_identifier_def("run".into())
|
||||
.unwrap_or_else(|| panic!("cannot find run() entry point")).0
|
||||
].write();
|
||||
if let TopLevelDef::Function {
|
||||
instance_to_stmt,
|
||||
instance_to_symbol,
|
||||
..
|
||||
} = &mut *instance
|
||||
{
|
||||
let mut instance = defs[resolver
|
||||
.get_identifier_def("run".into())
|
||||
.unwrap_or_else(|_| panic!("cannot find run() entry point"))
|
||||
.0]
|
||||
.write();
|
||||
if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } = &mut *instance {
|
||||
instance_to_symbol.insert("".to_string(), "run".to_string());
|
||||
instance_to_stmt[""].clone()
|
||||
} else {
|
||||
|
@ -291,8 +296,7 @@ fn main() {
|
|||
passes.run_on(&main);
|
||||
|
||||
let triple = TargetMachine::get_default_triple();
|
||||
let target =
|
||||
Target::from_triple(&triple).expect("couldn't create target from target triple");
|
||||
let target = Target::from_triple(&triple).expect("couldn't create target from target triple");
|
||||
let target_machine = target
|
||||
.create_target_machine(
|
||||
&triple,
|
||||
|
@ -304,10 +308,6 @@ fn main() {
|
|||
)
|
||||
.expect("couldn't create target machine");
|
||||
target_machine
|
||||
.write_to_file(
|
||||
&main,
|
||||
FileType::Object,
|
||||
Path::new("module.o"),
|
||||
)
|
||||
.write_to_file(&main, FileType::Object, Path::new("module.o"))
|
||||
.expect("couldn't write module to file");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue