Exception and RPC implementation #181
|
@ -0,0 +1,57 @@
|
|||
class EmbeddingMap:
|
||||
def __init__(self):
|
||||
self.object_inverse_map = {}
|
||||
self.object_map = {}
|
||||
self.string_map = {}
|
||||
self.string_reverse_map = {}
|
||||
self.function_map = {}
|
||||
|
||||
# preallocate exception names
|
||||
self.preallocate_runtime_exception_names(["RuntimeError",
|
||||
"RTIOUnderflow",
|
||||
"RTIOOverflow",
|
||||
"RTIODestinationUnreachable",
|
||||
"DMAError",
|
||||
"I2CError",
|
||||
"CacheError",
|
||||
"SPIError",
|
||||
"0:ZeroDivisionError",
|
||||
"0:IndexError"])
|
||||
|
||||
def preallocate_runtime_exception_names(self, names):
|
||||
for i, name in enumerate(names):
|
||||
if ":" not in name:
|
||||
name = "0:artiq.coredevice.exceptions." + name
|
||||
exn_id = self.store_str(name)
|
||||
assert exn_id == i
|
||||
|
||||
def store_function(self, key, fun):
|
||||
self.function_map[key] = fun
|
||||
return key
|
||||
|
||||
def store_object(self, obj):
|
||||
obj_id = id(obj)
|
||||
if obj_id in self.object_inverse_map:
|
||||
return self.object_inverse_map[obj_id]
|
||||
key = len(self.object_map)
|
||||
self.object_map[key] = obj
|
||||
self.object_inverse_map[obj_id] = key
|
||||
return key
|
||||
|
||||
def store_str(self, s):
|
||||
if s in self.string_reverse_map:
|
||||
return self.string_reverse_map[s]
|
||||
key = len(self.string_map)
|
||||
self.string_map[key] = s
|
||||
self.string_reverse_map[s] = key
|
||||
return key
|
||||
|
||||
def retrieve_function(self, key):
|
||||
return self.function_map[key]
|
||||
|
||||
def retrieve_object(self, key):
|
||||
return self.object_map[key]
|
||||
|
||||
def retrieve_str(self, key):
|
||||
return self.string_map[key]
|
||||
|
|
@ -6,13 +6,14 @@ from typing import Generic, TypeVar
|
|||
from math import floor, ceil
|
||||
|
||||
import nac3artiq
|
||||
from embedding_map import EmbeddingMap
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Kernel", "KernelInvariant", "virtual",
|
||||
"round64", "floor64", "ceil64",
|
||||
"extern", "kernel", "portable", "nac3",
|
||||
"ms", "us", "ns",
|
||||
"rpc", "ms", "us", "ns",
|
||||
"print_int32", "print_int64",
|
||||
"Core", "TTLOut",
|
||||
"parallel", "sequential"
|
||||
|
@ -65,6 +66,10 @@ def extern(function):
|
|||
register_function(function)
|
||||
return function
|
||||
|
||||
def rpc(function):
|
||||
"""Decorates a function declaration defined by the core device runtime."""
|
||||
register_function(function)
|
||||
return function
|
||||
|
||||
def kernel(function_or_method):
|
||||
"""Decorates a function or method to be executed on the core device."""
|
||||
|
@ -146,6 +151,9 @@ class Core:
|
|||
|
||||
def run(self, method, *args, **kwargs):
|
||||
global allow_registration
|
||||
|
||||
embedding = EmbeddingMap()
|
||||
|
||||
if allow_registration:
|
||||
compiler.analyze(registered_functions, registered_classes)
|
||||
allow_registration = False
|
||||
|
@ -157,7 +165,7 @@ class Core:
|
|||
obj = method
|
||||
name = ""
|
||||
|
||||
compiler.compile_method_to_file(obj, name, args, "module.elf")
|
||||
compiler.compile_method_to_file(obj, name, args, "module.elf", embedding)
|
||||
|
||||
@kernel
|
||||
def reset(self):
|
||||
|
|
|
@ -1,16 +1,30 @@
|
|||
use nac3core::{
|
||||
codegen::{expr::gen_call, stmt::gen_with, CodeGenContext, CodeGenerator},
|
||||
codegen::{
|
||||
expr::gen_call,
|
||||
stmt::{gen_block, gen_with},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
symbol_resolver::ValueEnum,
|
||||
toplevel::DefinitionId,
|
||||
toplevel::{DefinitionId, GenCall},
|
||||
typecheck::typedef::{FunSignature, Type},
|
||||
};
|
||||
|
||||
use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef};
|
||||
|
||||
use inkwell::{context::Context, types::IntType, values::BasicValueEnum};
|
||||
use inkwell::{
|
||||
context::Context, module::Linkage, types::IntType, values::BasicValueEnum, AddressSpace,
|
||||
};
|
||||
|
||||
use crate::timeline::TimeFns;
|
||||
|
||||
use std::{
|
||||
collections::hash_map::DefaultHasher,
|
||||
collections::HashMap,
|
||||
convert::TryInto,
|
||||
hash::{Hash, Hasher},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub struct ArtiqCodeGenerator<'a> {
|
||||
name: String,
|
||||
size_t: u32,
|
||||
|
@ -21,16 +35,13 @@ pub struct ArtiqCodeGenerator<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ArtiqCodeGenerator<'a> {
|
||||
pub fn new(name: String, size_t: u32, timeline: &'a (dyn TimeFns + Sync)) -> ArtiqCodeGenerator<'a> {
|
||||
pub fn new(
|
||||
name: String,
|
||||
size_t: u32,
|
||||
timeline: &'a (dyn TimeFns + Sync),
|
||||
) -> ArtiqCodeGenerator<'a> {
|
||||
assert!(size_t == 32 || size_t == 64);
|
||||
ArtiqCodeGenerator {
|
||||
name,
|
||||
size_t,
|
||||
name_counter: 0,
|
||||
start: None,
|
||||
end: None,
|
||||
timeline,
|
||||
}
|
||||
ArtiqCodeGenerator { name, size_t, name_counter: 0, start: None, end: None, timeline }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,7 +97,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> bool {
|
||||
) {
|
||||
if let StmtKind::With { items, body, .. } = &stmt.node {
|
||||
if items.len() == 1 && items[0].optional_vars.is_none() {
|
||||
let item = &items[0];
|
||||
|
@ -108,9 +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)
|
||||
};
|
||||
|
@ -126,10 +135,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
let start_expr = Located {
|
||||
// location does not matter at this point
|
||||
location: stmt.location,
|
||||
node: ExprKind::Name {
|
||||
id: start,
|
||||
ctx: name_ctx.clone(),
|
||||
},
|
||||
node: ExprKind::Name { id: start, ctx: name_ctx.clone() },
|
||||
custom: Some(ctx.primitives.int64),
|
||||
};
|
||||
let start = self.gen_store_target(ctx, &start_expr);
|
||||
|
@ -140,40 +146,41 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
let end_expr = Located {
|
||||
// location does not matter at this point
|
||||
location: stmt.location,
|
||||
node: ExprKind::Name {
|
||||
id: end,
|
||||
ctx: name_ctx.clone(),
|
||||
},
|
||||
node: ExprKind::Name { id: end, ctx: name_ctx.clone() },
|
||||
custom: Some(ctx.primitives.int64),
|
||||
};
|
||||
let end = self.gen_store_target(ctx, &end_expr);
|
||||
ctx.builder.build_store(end, now);
|
||||
self.end = Some(end_expr);
|
||||
self.name_counter += 1;
|
||||
let mut exited = false;
|
||||
for stmt in body.iter() {
|
||||
if self.gen_stmt(ctx, stmt) {
|
||||
exited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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
|
||||
// TODO: This may be unsound if there are multiple exit paths in the
|
||||
// block... e.g.
|
||||
// if ...:
|
||||
// return
|
||||
// Perhaps we can fix this by using actual with block?
|
||||
let reset_position = if let Some(terminator) = current.get_terminator() {
|
||||
ctx.builder.position_before(&terminator);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// set duration
|
||||
let end_expr = self.end.take().unwrap();
|
||||
let end_val = self
|
||||
.gen_expr(ctx, &end_expr)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, self);
|
||||
let end_val =
|
||||
self.gen_expr(ctx, &end_expr).unwrap().to_basic_value_enum(ctx, self);
|
||||
|
||||
// inside an sequential block
|
||||
// inside a sequential block
|
||||
if old_start.is_none() {
|
||||
self.timeline.emit_at_mu(ctx, end_val);
|
||||
}
|
||||
// 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();
|
||||
|
@ -194,24 +201,294 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
}
|
||||
self.start = old_start;
|
||||
self.end = old_end;
|
||||
return exited;
|
||||
if reset_position {
|
||||
ctx.builder.position_at_end(current);
|
||||
}
|
||||
return;
|
||||
} else if id == &"sequential".into() {
|
||||
let start = self.start.take();
|
||||
for stmt in body.iter() {
|
||||
if self.gen_stmt(ctx, stmt) {
|
||||
self.start = start;
|
||||
return true;
|
||||
self.gen_stmt(ctx, stmt);
|
||||
if ctx.is_terminated() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.start = start;
|
||||
return false;
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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>) {
|
||||
use nac3core::typecheck::typedef::TypeEnum::*;
|
||||
|
||||
let int32 = ctx.primitives.int32;
|
||||
let int64 = ctx.primitives.int64;
|
||||
let float = ctx.primitives.float;
|
||||
let bool = ctx.primitives.bool;
|
||||
let str = ctx.primitives.str;
|
||||
let none = ctx.primitives.none;
|
||||
|
||||
if ctx.unifier.unioned(ty, int32) {
|
||||
buffer.push(b'i');
|
||||
} else if ctx.unifier.unioned(ty, int64) {
|
||||
buffer.push(b'I');
|
||||
} else if ctx.unifier.unioned(ty, float) {
|
||||
buffer.push(b'f');
|
||||
} else if ctx.unifier.unioned(ty, bool) {
|
||||
buffer.push(b'b');
|
||||
} else if ctx.unifier.unioned(ty, str) {
|
||||
buffer.push(b's');
|
||||
} else if ctx.unifier.unioned(ty, none) {
|
||||
buffer.push(b'n');
|
||||
} else {
|
||||
let ty = ctx.unifier.get_ty(ty);
|
||||
match &*ty {
|
||||
TTuple { ty } => {
|
||||
buffer.push(b't');
|
||||
buffer.push(ty.len() as u8);
|
||||
for ty in ty {
|
||||
gen_rpc_tag(ctx, *ty, buffer);
|
||||
}
|
||||
}
|
||||
TList { ty } => {
|
||||
buffer.push(b'l');
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rpc_codegen_callback_fn<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
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);
|
||||
// -- 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);
|
||||
}
|
||||
tag.push(b':');
|
||||
gen_rpc_tag(ctx, fun.0.ret, &mut tag);
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
tag.hash(&mut hasher);
|
||||
let hash = format!("{}", hasher.finish());
|
||||
|
||||
let tag_ptr = ctx
|
||||
.module
|
||||
.get_global(hash.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
let tag_arr_ptr = ctx.module.add_global(
|
||||
int8.array_type(tag.len() as u32),
|
||||
None,
|
||||
format!("tagptr{}", fun.1 .0).as_str(),
|
||||
);
|
||||
tag_arr_ptr.set_initializer(&int8.const_array(
|
||||
&tag.iter().map(|v| int8.const_int(*v as u64, false)).collect::<Vec<_>>(),
|
||||
));
|
||||
tag_arr_ptr.set_linkage(Linkage::Private);
|
||||
let tag_ptr = ctx.module.add_global(tag_ptr_type, None, &hash);
|
||||
tag_ptr.set_linkage(Linkage::Private);
|
||||
tag_ptr.set_initializer(&ctx.ctx.const_struct(
|
||||
&[
|
||||
tag_arr_ptr.as_pointer_value().const_cast(ptr_type).into(),
|
||||
size_type.const_int(tag.len() as u64, false).into(),
|
||||
],
|
||||
false,
|
||||
));
|
||||
tag_ptr
|
||||
})
|
||||
.as_pointer_value();
|
||||
|
||||
let arg_length = args.len() + if obj.is_some() { 1 } else { 0 };
|
||||
|
||||
let stacksave = ctx.module.get_function("llvm.stacksave").unwrap_or_else(|| {
|
||||
ctx.module.add_function("llvm.stacksave", ptr_type.fn_type(&[], false), None)
|
||||
});
|
||||
let stackrestore = ctx.module.get_function("llvm.stackrestore").unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
"llvm.stackrestore",
|
||||
ctx.ctx.void_type().fn_type(&[ptr_type.into()], false),
|
||||
None,
|
||||
)
|
||||
});
|
||||
|
||||
let stackptr = ctx.builder.build_call(stacksave, &[], "rpc.stack");
|
||||
let args_ptr = ctx.builder.build_array_alloca(
|
||||
ptr_type,
|
||||
ctx.ctx.i32_type().const_int(arg_length as u64, false),
|
||||
"argptr",
|
||||
);
|
||||
|
||||
// -- rpc args handling
|
||||
let mut keys = fun.0.args.clone();
|
||||
let mut mapping = HashMap::new();
|
||||
for (key, value) in args.into_iter() {
|
||||
mapping.insert(key.unwrap_or_else(|| keys.remove(0).name), value);
|
||||
}
|
||||
// default value handling
|
||||
for k in keys.into_iter() {
|
||||
mapping.insert(k.name, ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into());
|
||||
}
|
||||
// reorder the parameters
|
||||
let mut real_params = fun
|
||||
.0
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| mapping.remove(&arg.name).unwrap().to_basic_value_enum(ctx, generator))
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(obj) = obj {
|
||||
if let ValueEnum::Static(obj) = obj.1 {
|
||||
real_params.insert(0, obj.get_const_obj(ctx, generator));
|
||||
} else {
|
||||
// should be an error here...
|
||||
panic!("only host object is allowed");
|
||||
}
|
||||
}
|
||||
|
||||
for (i, arg) in real_params.iter().enumerate() {
|
||||
let arg_slot = if arg.is_pointer_value() {
|
||||
arg.into_pointer_value()
|
||||
} else {
|
||||
let arg_slot = ctx.builder.build_alloca(arg.get_type(), &format!("rpc.arg{}", i));
|
||||
ctx.builder.build_store(arg_slot, *arg);
|
||||
arg_slot
|
||||
};
|
||||
let arg_slot = ctx.builder.build_bitcast(arg_slot, ptr_type, "rpc.arg");
|
||||
let arg_ptr = unsafe {
|
||||
ctx.builder.build_gep(
|
||||
args_ptr,
|
||||
&[int32.const_int(i as u64, false)],
|
||||
&format!("rpc.arg{}", i),
|
||||
)
|
||||
};
|
||||
ctx.builder.build_store(arg_ptr, arg_slot);
|
||||
}
|
||||
|
||||
// call
|
||||
let rpc_send = ctx.module.get_function("rpc_send").unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
"rpc_send",
|
||||
ctx.ctx.void_type().fn_type(
|
||||
&[
|
||||
int32.into(),
|
||||
tag_ptr_type.ptr_type(AddressSpace::Generic).into(),
|
||||
ptr_type.ptr_type(AddressSpace::Generic).into(),
|
||||
],
|
||||
false,
|
||||
),
|
||||
None,
|
||||
)
|
||||
});
|
||||
ctx.builder.build_call(
|
||||
rpc_send,
|
||||
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
|
||||
"rpc.send",
|
||||
);
|
||||
|
||||
// reclaim stack space used by arguments
|
||||
ctx.builder.build_call(
|
||||
stackrestore,
|
||||
&[stackptr.try_as_basic_value().unwrap_left().into()],
|
||||
"rpc.stackrestore",
|
||||
);
|
||||
|
||||
// -- receive value:
|
||||
// T result = {
|
||||
// void *ret_ptr = alloca(sizeof(T));
|
||||
// void *ptr = ret_ptr;
|
||||
// loop: int size = rpc_recv(ptr);
|
||||
// // Non-zero: Provide `size` bytes of extra storage for variable-length data.
|
||||
// if(size) { ptr = alloca(size); goto loop; }
|
||||
// else *(T*)ret_ptr
|
||||
// }
|
||||
let rpc_recv = ctx.module.get_function("rpc_recv").unwrap_or_else(|| {
|
||||
ctx.module.add_function("rpc_recv", int32.fn_type(&[ptr_type.into()], false), None)
|
||||
});
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
let prehead_bb = ctx.builder.get_insert_block().unwrap();
|
||||
let current_function = prehead_bb.get_parent().unwrap();
|
||||
let head_bb = ctx.ctx.append_basic_block(current_function, "rpc.head");
|
||||
let alloc_bb = ctx.ctx.append_basic_block(current_function, "rpc.continue");
|
||||
let tail_bb = ctx.ctx.append_basic_block(current_function, "rpc.tail");
|
||||
|
||||
let mut ret_ty = ctx.get_llvm_type(generator, fun.0.ret);
|
||||
let need_load = !ret_ty.is_pointer_type();
|
||||
if ret_ty.is_pointer_type() {
|
||||
ret_ty = ret_ty.into_pointer_type().get_element_type().try_into().unwrap();
|
||||
}
|
||||
let slot = ctx.builder.build_alloca(ret_ty, "rpc.ret.slot");
|
||||
let slotgen = ctx.builder.build_bitcast(slot, ptr_type, "rpc.ret.ptr");
|
||||
ctx.builder.build_unconditional_branch(head_bb);
|
||||
ctx.builder.position_at_end(head_bb);
|
||||
|
||||
let phi = ctx.builder.build_phi(ptr_type, "rpc.ptr");
|
||||
phi.add_incoming(&[(&slotgen, prehead_bb)]);
|
||||
let alloc_size = ctx
|
||||
.build_call_or_invoke(rpc_recv, &[phi.as_basic_value()], "rpc.size.next")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
let is_done = ctx.builder.build_int_compare(
|
||||
inkwell::IntPredicate::EQ,
|
||||
int32.const_zero(),
|
||||
alloc_size,
|
||||
"rpc.done",
|
||||
);
|
||||
|
||||
ctx.builder.build_conditional_branch(is_done, tail_bb, alloc_bb);
|
||||
ctx.builder.position_at_end(alloc_bb);
|
||||
|
||||
let alloc_ptr = ctx.builder.build_array_alloca(ptr_type, alloc_size, "rpc.alloc");
|
||||
let alloc_ptr = ctx.builder.build_bitcast(alloc_ptr, ptr_type, "rpc.alloc.ptr");
|
||||
phi.add_incoming(&[(&alloc_ptr, alloc_bb)]);
|
||||
ctx.builder.build_unconditional_branch(head_bb);
|
||||
|
||||
ctx.builder.position_at_end(tail_bb);
|
||||
|
||||
if need_load {
|
||||
let result = ctx.builder.build_load(slot, "rpc.result");
|
||||
ctx.builder.build_call(
|
||||
stackrestore,
|
||||
&[stackptr.try_as_basic_value().unwrap_left().into()],
|
||||
"rpc.stackrestore",
|
||||
);
|
||||
Some(result)
|
||||
} else {
|
||||
Some(slot.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rpc_codegen_callback() -> Arc<GenCall> {
|
||||
Arc::new(GenCall::new(Box::new(|ctx, obj, fun, args, generator| {
|
||||
rpc_codegen_callback_fn(ctx, obj, fun, args, generator)
|
||||
})))
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use inkwell::{
|
|||
};
|
||||
use nac3core::typecheck::typedef::{Unifier, TypeEnum};
|
||||
use nac3parser::{
|
||||
ast::{self, Stmt, StrRef},
|
||||
ast::{self, ExprKind, Stmt, StmtKind, StrRef},
|
||||
parser::{self, parse_program},
|
||||
};
|
||||
use pyo3::prelude::*;
|
||||
|
@ -24,7 +24,10 @@ use nac3core::{
|
|||
codegen::{concrete_type::ConcreteTypeStore, CodeGenTask, WithCall, WorkerRegistry},
|
||||
codegen::irrt::load_irrt,
|
||||
symbol_resolver::SymbolResolver,
|
||||
toplevel::{composer::{TopLevelComposer, ComposerConfig}, DefinitionId, GenCall, TopLevelDef},
|
||||
toplevel::{
|
||||
composer::{ComposerConfig, TopLevelComposer},
|
||||
DefinitionId, GenCall, TopLevelDef,
|
||||
},
|
||||
typecheck::typedef::{FunSignature, FuncArg},
|
||||
typecheck::{type_inferencer::PrimitiveStore, typedef::Type},
|
||||
};
|
||||
|
@ -32,7 +35,7 @@ use nac3core::{
|
|||
use tempfile::{self, TempDir};
|
||||
|
||||
use crate::{
|
||||
codegen::ArtiqCodeGenerator,
|
||||
codegen::{rpc_codegen_callback, ArtiqCodeGenerator},
|
||||
symbol_resolver::{InnerResolver, PythonHelper, Resolver},
|
||||
};
|
||||
|
||||
|
@ -61,6 +64,7 @@ pub struct PrimitivePythonId {
|
|||
tuple: u64,
|
||||
typevar: u64,
|
||||
none: u64,
|
||||
exception: u64,
|
||||
generic_alias: (u64, u64),
|
||||
virtual_id: u64,
|
||||
}
|
||||
|
@ -81,6 +85,7 @@ struct Nac3 {
|
|||
primitive_ids: PrimitivePythonId,
|
||||
working_directory: TempDir,
|
||||
top_levels: Vec<TopLevelComponent>,
|
||||
string_store: Arc<RwLock<HashMap<String, i32>>>,
|
||||
}
|
||||
|
||||
impl Nac3 {
|
||||
|
@ -127,9 +132,13 @@ impl Nac3 {
|
|||
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
|
||||
match &base.node {
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
let base_obj = module.getattr(py, id.to_string())?;
|
||||
let base_id = id_fn.call1((base_obj,))?.extract()?;
|
||||
Ok(registered_class_ids.contains(&base_id))
|
||||
if *id == "Exception".into() {
|
||||
Ok(true)
|
||||
} else {
|
||||
let base_obj = module.getattr(py, id.to_string())?;
|
||||
let base_id = id_fn.call1((base_obj,))?.extract()?;
|
||||
Ok(registered_class_ids.contains(&base_id))
|
||||
}
|
||||
}
|
||||
_ => Ok(true),
|
||||
}
|
||||
|
@ -143,7 +152,9 @@ impl Nac3 {
|
|||
{
|
||||
decorator_list.iter().any(|decorator| {
|
||||
if let ast::ExprKind::Name { id, .. } = decorator.node {
|
||||
id.to_string() == "kernel" || id.to_string() == "portable"
|
||||
id.to_string() == "kernel"
|
||||
|| id.to_string() == "portable"
|
||||
|| id.to_string() == "rpc"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -159,7 +170,7 @@ impl Nac3 {
|
|||
} => 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 == "extern" || id == "portable" || id == "kernel" || id == "rpc"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -188,7 +199,7 @@ impl Nac3 {
|
|||
Ok(ty) => ty,
|
||||
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) {
|
||||
|
@ -201,7 +212,7 @@ impl Nac3 {
|
|||
} else {
|
||||
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 arg_names.len() > args.len() {
|
||||
|
@ -269,7 +280,7 @@ impl Nac3 {
|
|||
ret: primitive.int64,
|
||||
vars: HashMap::new(),
|
||||
},
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, _| {
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, _, _| {
|
||||
Some(time_fns.emit_now_mu(ctx))
|
||||
}))),
|
||||
),
|
||||
|
@ -284,8 +295,9 @@ impl Nac3 {
|
|||
ret: primitive.none,
|
||||
vars: HashMap::new(),
|
||||
},
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args| {
|
||||
time_fns.emit_at_mu(ctx, args[0].1);
|
||||
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
|
||||
}))),
|
||||
),
|
||||
|
@ -300,16 +312,20 @@ impl Nac3 {
|
|||
ret: primitive.none,
|
||||
vars: HashMap::new(),
|
||||
},
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args| {
|
||||
time_fns.emit_delay_mu(ctx, args[0].1);
|
||||
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
|
||||
}))),
|
||||
),
|
||||
];
|
||||
let (_, builtins_def, builtins_ty) = TopLevelComposer::new(builtins.clone(), ComposerConfig {
|
||||
kernel_ann: Some("Kernel"),
|
||||
kernel_invariant_ann: "KernelInvariant"
|
||||
});
|
||||
let (_, builtins_def, builtins_ty) = TopLevelComposer::new(
|
||||
builtins.clone(),
|
||||
ComposerConfig {
|
||||
kernel_ann: Some("Kernel"),
|
||||
kernel_invariant_ann: "KernelInvariant",
|
||||
},
|
||||
);
|
||||
|
||||
let builtins_mod = PyModule::import(py, "builtins").unwrap();
|
||||
let id_fn = builtins_mod.getattr("id").unwrap();
|
||||
|
@ -385,6 +401,11 @@ impl Nac3 {
|
|||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
exception: id_fn
|
||||
.call1((builtins_mod.getattr("tuple").unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
|
||||
|
@ -405,6 +426,7 @@ impl Nac3 {
|
|||
top_levels: Default::default(),
|
||||
pyid_to_def: Default::default(),
|
||||
working_directory,
|
||||
string_store: Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -441,6 +463,7 @@ impl Nac3 {
|
|||
method_name: &str,
|
||||
args: Vec<&PyAny>,
|
||||
filename: &str,
|
||||
embedding_map: &PyAny,
|
||||
py: Python,
|
||||
) -> PyResult<()> {
|
||||
let (mut composer, _, _) = TopLevelComposer::new(self.builtins.clone(), ComposerConfig {
|
||||
|
@ -451,17 +474,26 @@ impl Nac3 {
|
|||
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 helper = PythonHelper {
|
||||
id_fn: builtins.getattr("id").unwrap().to_object(py),
|
||||
len_fn: builtins.getattr("len").unwrap().to_object(py),
|
||||
type_fn: builtins.getattr("type").unwrap().to_object(py),
|
||||
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
|
||||
};
|
||||
let mut module_to_resolver_cache: HashMap<u64, _> = HashMap::new();
|
||||
|
||||
let pyid_to_type = Arc::new(RwLock::new(HashMap::<u64, Type>::new()));
|
||||
let global_value_ids = Arc::new(RwLock::new(HashSet::<u64>::new()));
|
||||
let mut rpc_ids = vec![];
|
||||
for (stmt, path, module) in self.top_levels.iter() {
|
||||
let py_module: &PyAny = module.extract(py)?;
|
||||
let module_id: u64 = id_fn.call1((py_module,))?.extract()?;
|
||||
|
@ -492,6 +524,7 @@ impl Nac3 {
|
|||
id_to_primitive: Default::default(),
|
||||
field_to_val: Default::default(),
|
||||
helper,
|
||||
string_store: self.string_store.clone(),
|
||||
})))
|
||||
as Arc<dyn SymbolResolver + Send + Sync>;
|
||||
let name_to_pyid = Rc::new(name_to_pyid);
|
||||
|
@ -502,7 +535,30 @@ 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)))?;
|
||||
.map_err(|e| {
|
||||
exceptions::PyRuntimeError::new_err(format!("nac3 compilation failure: {}", e))
|
||||
})?;
|
||||
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { decorator_list, .. } => {
|
||||
if decorator_list.iter().any(|decorator| matches!(decorator.node, ExprKind::Name { id, .. } if id == "rpc".into())) {
|
||||
store_fun.call1(py, (def_id.0.into_py(py), module.getattr(py, name.to_string()).unwrap())).unwrap();
|
||||
rpc_ids.push((None, def_id));
|
||||
}
|
||||
}
|
||||
StmtKind::ClassDef { name, body, .. } => {
|
||||
let class_obj = module.getattr(py, name.to_string()).unwrap();
|
||||
for stmt in body.iter() {
|
||||
if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node {
|
||||
if decorator_list.iter().any(|decorator| matches!(decorator.node, ExprKind::Name { id, .. } if id == "rpc".into())) {
|
||||
rpc_ids.push((Some((class_obj.clone(), *name)), def_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
let id = *name_to_pyid.get(&name).unwrap();
|
||||
self.pyid_to_def.write().insert(id, def_id);
|
||||
{
|
||||
|
@ -552,6 +608,7 @@ impl Nac3 {
|
|||
name_to_pyid,
|
||||
module: module.to_object(py),
|
||||
helper,
|
||||
string_store: self.string_store.clone(),
|
||||
}))) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||
let (_, def_id, _) = composer
|
||||
.register_top_level(
|
||||
|
@ -595,6 +652,45 @@ impl Nac3 {
|
|||
}
|
||||
}
|
||||
let top_level = Arc::new(composer.make_top_level_context());
|
||||
|
||||
{
|
||||
let rpc_codegen = rpc_codegen_callback();
|
||||
let defs = top_level.definitions.read();
|
||||
for (class_data, id) in rpc_ids.iter() {
|
||||
let mut def = defs[id.0].write();
|
||||
match &mut *def {
|
||||
TopLevelDef::Function {
|
||||
codegen_callback, ..
|
||||
} => {
|
||||
*codegen_callback = Some(rpc_codegen.clone());
|
||||
}
|
||||
TopLevelDef::Class { methods, .. } => {
|
||||
let (class_def, method_name) = class_data.as_ref().unwrap();
|
||||
for (name, _, id) in methods.iter() {
|
||||
if name != method_name {
|
||||
continue;
|
||||
}
|
||||
if let TopLevelDef::Function {
|
||||
codegen_callback, ..
|
||||
} = &mut *defs[id.0].write()
|
||||
{
|
||||
*codegen_callback = Some(rpc_codegen.clone());
|
||||
store_fun
|
||||
.call1(
|
||||
py,
|
||||
(
|
||||
id.0.into_py(py),
|
||||
class_def.getattr(py, name.to_string()).unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let instance = {
|
||||
let defs = top_level.definitions.read();
|
||||
let mut definition = defs[def_id.0].write();
|
||||
|
@ -634,15 +730,17 @@ impl Nac3 {
|
|||
let buffer = buffer.as_slice().into();
|
||||
membuffer.lock().push(buffer);
|
||||
})));
|
||||
let size_t = if self.isa == Isa::Host {
|
||||
64
|
||||
} else {
|
||||
32
|
||||
};
|
||||
let size_t = if self.isa == Isa::Host { 64 } else { 32 };
|
||||
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(|| {
|
||||
|
@ -759,11 +857,12 @@ impl Nac3 {
|
|||
obj: &PyAny,
|
||||
method_name: &str,
|
||||
args: Vec<&PyAny>,
|
||||
embedding_map: &PyAny,
|
||||
py: Python,
|
||||
) -> PyResult<PyObject> {
|
||||
let filename_path = self.working_directory.path().join("module.elf");
|
||||
let filename = filename_path.to_str().unwrap();
|
||||
self.compile_method_to_file(obj, method_name, args, filename, py)?;
|
||||
self.compile_method_to_file(obj, method_name, args, filename, embedding_map, py)?;
|
||||
Ok(PyBytes::new(py, &fs::read(filename).unwrap()).into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ pub struct InnerResolver {
|
|||
pub pyid_to_type: Arc<RwLock<HashMap<u64, Type>>>,
|
||||
pub primitive_ids: PrimitivePythonId,
|
||||
pub helper: PythonHelper,
|
||||
pub string_store: Arc<RwLock<HashMap<String, i32>>>,
|
||||
// module specific
|
||||
pub name_to_pyid: HashMap<StrRef, u64>,
|
||||
pub module: PyObject,
|
||||
|
@ -56,11 +57,14 @@ pub struct PythonHelper {
|
|||
pub id_fn: PyObject,
|
||||
pub origin_ty_fn: PyObject,
|
||||
pub args_ty_fn: PyObject,
|
||||
pub store_obj: PyObject,
|
||||
pub store_str: PyObject,
|
||||
}
|
||||
|
||||
struct PythonValue {
|
||||
id: u64,
|
||||
value: PyObject,
|
||||
store_obj: PyObject,
|
||||
resolver: Arc<InnerResolver>,
|
||||
}
|
||||
|
||||
|
@ -69,6 +73,36 @@ impl StaticValue for PythonValue {
|
|||
self.id
|
||||
}
|
||||
|
||||
fn get_const_obj<'ctx, 'a>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
_: &mut dyn CodeGenerator,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
ctx.module
|
||||
.get_global(self.id.to_string().as_str())
|
||||
.map(|val| val.as_pointer_value().into())
|
||||
.unwrap_or_else(|| {
|
||||
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());
|
||||
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());
|
||||
global2.set_initializer(&global.as_pointer_value());
|
||||
Ok(global2.as_pointer_value().into())
|
||||
})
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
fn to_basic_value_enum<'ctx, 'a>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
|
@ -140,6 +174,7 @@ impl StaticValue for PythonValue {
|
|||
ValueEnum::Static(Arc::new(PythonValue {
|
||||
id,
|
||||
value: obj,
|
||||
store_obj: self.store_obj.clone(),
|
||||
resolver: self.resolver.clone(),
|
||||
}))
|
||||
})
|
||||
|
@ -208,7 +243,9 @@ impl InnerResolver {
|
|||
Ok(Ok((primitives.bool, true)))
|
||||
} else if ty_id == self.primitive_ids.float {
|
||||
Ok(Ok((primitives.float, true)))
|
||||
} else if ty_id == self.primitive_ids.list {
|
||||
} else if ty_id == self.primitive_ids.exception {
|
||||
Ok(Ok((primitives.exception, true)))
|
||||
}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 list = unifier.add_ty(TypeEnum::TList { ty: var });
|
||||
|
@ -755,9 +792,7 @@ impl InnerResolver {
|
|||
.get_llvm_type(generator, ty)
|
||||
.into_pointer_type()
|
||||
.get_element_type()
|
||||
.into_struct_type()
|
||||
.as_basic_type_enum();
|
||||
|
||||
.into_struct_type();
|
||||
{
|
||||
if self.global_value_ids.read().contains(&id) {
|
||||
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
|
||||
|
@ -783,7 +818,7 @@ impl InnerResolver {
|
|||
.collect();
|
||||
let values = values?;
|
||||
if let Some(values) = values {
|
||||
let val = ctx.ctx.const_struct(&values, false);
|
||||
let val = ty.const_named_struct(&values);
|
||||
let global = ctx
|
||||
.module
|
||||
.add_global(ty, Some(AddressSpace::Generic), &id_str);
|
||||
|
@ -948,6 +983,7 @@ impl SymbolResolver for Resolver {
|
|||
ValueEnum::Static(Arc::new(PythonValue {
|
||||
id,
|
||||
value: v,
|
||||
store_obj: self.0.helper.store_obj.clone(),
|
||||
resolver: self.0.clone(),
|
||||
}))
|
||||
})
|
||||
|
@ -971,4 +1007,17 @@ impl SymbolResolver for Resolver {
|
|||
result
|
||||
})
|
||||
}
|
||||
|
||||
fn get_string_id(&self, s: &str) -> i32 {
|
||||
let mut string_store = self.0.string_store.write();
|
||||
if let Some(id) = string_store.get(s) {
|
||||
*id
|
||||
} else {
|
||||
let id = Python::with_gil(|py| -> PyResult<i32> {
|
||||
self.0.helper.store_str.call1(py, (s, ))?.extract(py)
|
||||
}).unwrap();
|
||||
string_store.insert(s.into(), id);
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::ast_gen::StrRef;
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct FileName(StrRef);
|
||||
pub struct FileName(pub StrRef);
|
||||
impl Default for FileName {
|
||||
fn default() -> Self {
|
||||
FileName("unknown".into())
|
||||
|
@ -19,9 +19,9 @@ impl From<String> for FileName {
|
|||
/// A location somewhere in the sourcecode.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct Location {
|
||||
row: usize,
|
||||
column: usize,
|
||||
file: FileName
|
||||
pub row: usize,
|
||||
pub column: usize,
|
||||
pub file: FileName
|
||||
}
|
||||
|
||||
impl fmt::Display for Location {
|
||||
|
|
|
@ -31,6 +31,9 @@ pub enum Primitive {
|
|||
Float,
|
||||
Bool,
|
||||
None,
|
||||
Range,
|
||||
Str,
|
||||
Exception
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -66,6 +69,9 @@ impl ConcreteTypeStore {
|
|||
ConcreteTypeEnum::TPrimitive(Primitive::Float),
|
||||
ConcreteTypeEnum::TPrimitive(Primitive::Bool),
|
||||
ConcreteTypeEnum::TPrimitive(Primitive::None),
|
||||
ConcreteTypeEnum::TPrimitive(Primitive::Range),
|
||||
ConcreteTypeEnum::TPrimitive(Primitive::Str),
|
||||
ConcreteTypeEnum::TPrimitive(Primitive::Exception),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
@ -118,6 +124,12 @@ impl ConcreteTypeStore {
|
|||
ConcreteType(3)
|
||||
} else if unifier.unioned(ty, primitives.none) {
|
||||
ConcreteType(4)
|
||||
} else if unifier.unioned(ty, primitives.range) {
|
||||
ConcreteType(5)
|
||||
} else if unifier.unioned(ty, primitives.str) {
|
||||
ConcreteType(6)
|
||||
} else if unifier.unioned(ty, primitives.exception) {
|
||||
ConcreteType(7)
|
||||
} else if let Some(cty) = cache.get(&ty) {
|
||||
if let Some(cty) = cty {
|
||||
*cty
|
||||
|
@ -211,6 +223,9 @@ impl ConcreteTypeStore {
|
|||
Primitive::Float => primitives.float,
|
||||
Primitive::Bool => primitives.bool,
|
||||
Primitive::None => primitives.none,
|
||||
Primitive::Range => primitives.range,
|
||||
Primitive::Str => primitives.str,
|
||||
Primitive::Exception => primitives.exception,
|
||||
};
|
||||
*cache.get_mut(&cty).unwrap() = Some(ty);
|
||||
return ty;
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::{collections::HashMap, convert::TryInto, iter::once};
|
|||
use crate::{
|
||||
codegen::{
|
||||
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
||||
stmt::gen_raise,
|
||||
get_llvm_type,
|
||||
irrt::*,
|
||||
CodeGenContext, CodeGenTask,
|
||||
|
@ -12,12 +13,12 @@ use crate::{
|
|||
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
|
||||
};
|
||||
use inkwell::{
|
||||
types::{BasicType, BasicTypeEnum},
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
types::{BasicType, BasicTypeEnum},
|
||||
values::{BasicValueEnum, FunctionValue, IntValue, PointerValue}
|
||||
};
|
||||
use itertools::{chain, izip, zip, Itertools};
|
||||
use nac3parser::ast::{self, Boolop, Comprehension, Constant, Expr, ExprKind, Operator, StrRef};
|
||||
use nac3parser::ast::{self, Boolop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef};
|
||||
|
||||
use super::CodeGenerator;
|
||||
|
||||
|
@ -76,14 +77,20 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
index
|
||||
}
|
||||
|
||||
fn gen_symbol_val(&mut self, 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 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()
|
||||
}
|
||||
SymbolValue::Tuple(ls) => {
|
||||
let vals = ls.iter().map(|v| self.gen_symbol_val(v)).collect_vec();
|
||||
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v)).collect_vec();
|
||||
let fields = vals.iter().map(|v| v.get_type()).collect_vec();
|
||||
let ty = self.ctx.struct_type(&fields, false);
|
||||
let ptr = self.builder.build_alloca(ty, "tuple");
|
||||
|
@ -118,7 +125,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
)
|
||||
}
|
||||
|
||||
fn gen_const(&mut self, 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));
|
||||
|
@ -145,7 +152,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
let types =
|
||||
if let TypeEnum::TTuple { ty } = &*ty { ty.clone() } else { unreachable!() };
|
||||
let values = zip(types.into_iter(), v.iter())
|
||||
.map(|(ty, v)| self.gen_const(v, ty))
|
||||
.map(|(ty, v)| self.gen_const(generator, v, ty))
|
||||
.collect_vec();
|
||||
let types = values.iter().map(BasicValueEnum::get_type).collect_vec();
|
||||
let ty = self.ctx.struct_type(&types, false);
|
||||
|
@ -153,7 +160,16 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
}
|
||||
Constant::Str(v) => {
|
||||
assert!(self.unifier.unioned(ty, self.primitives.str));
|
||||
self.builder.build_global_string_ptr(v, "const").as_pointer_value().into()
|
||||
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 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();
|
||||
self.const_strings.insert(v.to_string(), val);
|
||||
val
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -241,6 +257,93 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_call_or_invoke(
|
||||
&self,
|
||||
fun: FunctionValue<'ctx>,
|
||||
params: &[BasicValueEnum<'ctx>],
|
||||
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();
|
||||
self.builder.position_at_end(then_block);
|
||||
result
|
||||
} else {
|
||||
let param: Vec<_> = params.iter().map(|v| (*v).into()).collect();
|
||||
self.builder.build_call(fun, ¶m, call_name).try_as_basic_value().left()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_string<G: CodeGenerator, S: Into<String>>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
s: S
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str)
|
||||
}
|
||||
|
||||
pub fn raise_exn<G: CodeGenerator>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
name: &str,
|
||||
msg: BasicValueEnum<'ctx>,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
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();
|
||||
let zelf = self.builder.build_alloca(zelf_ty, "alloca");
|
||||
let int32 = self.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
unsafe {
|
||||
let id_ptr = self.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id");
|
||||
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");
|
||||
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"));
|
||||
self.builder.build_store(ptr, val);
|
||||
}
|
||||
}
|
||||
gen_raise(generator, self, Some(&zelf.into()), loc);
|
||||
}
|
||||
|
||||
pub fn make_assert<G: CodeGenerator>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
cond: IntValue<'ctx>,
|
||||
err_name: &str,
|
||||
err_msg: &str,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
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)
|
||||
});
|
||||
// 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 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");
|
||||
self.builder.build_conditional_branch(cond, then_block, exn_block);
|
||||
self.builder.position_at_end(exn_block);
|
||||
let err_msg = self.gen_string(generator, err_msg);
|
||||
self.raise_exn(generator, err_name, err_msg, params, loc);
|
||||
self.builder.position_at_end(then_block);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
|
||||
|
@ -371,13 +474,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
..
|
||||
} => {
|
||||
if let Some(callback) = codegen_callback {
|
||||
// TODO: Change signature
|
||||
let obj = obj.map(|(t, v)| (t, v.to_basic_value_enum(ctx, generator)));
|
||||
let params = params
|
||||
.into_iter()
|
||||
.map(|(name, val)| (name, val.to_basic_value_enum(ctx, generator)))
|
||||
.collect();
|
||||
return callback.run(ctx, obj, fun, params);
|
||||
return callback.run(ctx, obj, fun, params, generator);
|
||||
}
|
||||
let old_key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), fun.0, None);
|
||||
let mut keys = fun.0.args.clone();
|
||||
|
@ -387,7 +484,7 @@ 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(&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 =
|
||||
|
@ -431,7 +528,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
};
|
||||
param_vals = real_params
|
||||
.into_iter()
|
||||
.map(|p| p.to_basic_value_enum(ctx, generator).into())
|
||||
.map(|p| p.to_basic_value_enum(ctx, generator))
|
||||
.collect_vec();
|
||||
instance_to_symbol.get(&key).cloned()
|
||||
}
|
||||
|
@ -457,7 +554,8 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
};
|
||||
ctx.module.add_function(&symbol, fun_ty, None)
|
||||
});
|
||||
ctx.builder.build_call(fun_val, ¶m_vals, "call").try_as_basic_value().left()
|
||||
|
||||
ctx.build_call_or_invoke(fun_val, ¶m_vals, "call")
|
||||
}
|
||||
|
||||
pub fn destructure_range<'ctx, 'a>(
|
||||
|
@ -698,7 +796,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
Some(match &expr.node {
|
||||
ExprKind::Constant { value, .. } => {
|
||||
let ty = expr.custom.unwrap();
|
||||
ctx.gen_const(value, ty).into()
|
||||
ctx.gen_const(generator, value, ty).into()
|
||||
}
|
||||
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
|
||||
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(),
|
||||
|
@ -1088,12 +1186,23 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
);
|
||||
res_array_ret.into()
|
||||
} else {
|
||||
// TODO: bound check
|
||||
let index = generator
|
||||
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)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)
|
||||
.into_int_value();
|
||||
// handle negative index
|
||||
let is_negative = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, raw_index,
|
||||
generator.get_size_type(ctx.ctx).const_zero(), "is_neg");
|
||||
let adjusted = ctx.builder.build_int_add(raw_index, len, "adjusted");
|
||||
let index = ctx.builder.build_select(is_negative, adjusted, raw_index, "index").into_int_value();
|
||||
// unsigned less than is enough, because negative index after adjustment is
|
||||
// bigger than the length (for unsigned cmp)
|
||||
let bound_check = ctx.builder.build_int_compare(inkwell::IntPredicate::ULT, index, len, "inbound");
|
||||
ctx.make_assert(generator, bound_check, "0:IndexError", "index {0} out of bounds 0:{1}",
|
||||
[Some(raw_index), Some(len), None], expr.location);
|
||||
ctx.build_gep_and_load(arr_ptr, &[index])
|
||||
}
|
||||
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||
|
|
|
@ -117,67 +117,45 @@ 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>>,
|
||||
) -> bool
|
||||
fn gen_while<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_while(self, ctx, stmt);
|
||||
false
|
||||
}
|
||||
|
||||
/// 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>>,
|
||||
) -> bool
|
||||
fn gen_for<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_for(self, ctx, stmt);
|
||||
false
|
||||
}
|
||||
|
||||
/// 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>>,
|
||||
) -> bool
|
||||
fn gen_if<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
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>>,
|
||||
) -> bool
|
||||
fn gen_with<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
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>>,
|
||||
) -> bool
|
||||
fn gen_stmt<'ctx, 'a>(&mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt<Option<Type>>)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_stmt(self, ctx, stmt)
|
||||
gen_stmt(self, ctx, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use inkwell::{
|
|||
module::Module,
|
||||
passes::{PassManager, PassManagerBuilder},
|
||||
types::{BasicType, BasicTypeEnum},
|
||||
values::{FunctionValue, PointerValue},
|
||||
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue},
|
||||
AddressSpace, OptimizationLevel,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
@ -60,11 +60,28 @@ pub struct CodeGenContext<'ctx, 'a> {
|
|||
pub primitives: PrimitiveStore,
|
||||
pub calls: Arc<HashMap<CodeLocation, CallId>>,
|
||||
pub registry: &'a WorkerRegistry,
|
||||
// const string cache
|
||||
pub const_strings: HashMap<String, BasicValueEnum<'ctx>>,
|
||||
// stores the alloca for variables
|
||||
pub init_bb: BasicBlock<'ctx>,
|
||||
// where continue and break should go to respectively
|
||||
// the first one is the test_bb, and the second one is bb after the loop
|
||||
pub loop_bb: Option<(BasicBlock<'ctx>, BasicBlock<'ctx>)>,
|
||||
pub loop_target: Option<(BasicBlock<'ctx>, BasicBlock<'ctx>)>,
|
||||
// unwind target bb
|
||||
pub unwind_target: Option<BasicBlock<'ctx>>,
|
||||
// return target bb, just emit ret if no such target
|
||||
pub return_target: Option<BasicBlock<'ctx>>,
|
||||
pub return_buffer: Option<PointerValue<'ctx>>,
|
||||
// outer finally block function
|
||||
pub outer_final: Option<(PointerValue<'ctx>, Vec<BasicBlock<'ctx>>, Vec<BasicBlock<'ctx>>)>,
|
||||
// outer catch clauses
|
||||
pub outer_catch_clauses:
|
||||
Option<(Vec<Option<BasicValueEnum<'ctx>>>, BasicBlock<'ctx>, PhiValue<'ctx>)>,
|
||||
}
|
||||
|
||||
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||
pub fn is_terminated(&self) -> bool {
|
||||
self.builder.get_insert_block().unwrap().get_terminator().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
type Fp = Box<dyn Fn(&Module) + Send + Sync>;
|
||||
|
@ -182,7 +199,7 @@ impl WorkerRegistry {
|
|||
fn worker_thread<G: CodeGenerator>(&self, generator: &mut G, f: Arc<WithCall>) {
|
||||
let context = Context::create();
|
||||
let mut builder = context.create_builder();
|
||||
let mut module = context.create_module(generator.get_name());
|
||||
let module = context.create_module(generator.get_name());
|
||||
|
||||
let pass_builder = PassManagerBuilder::create();
|
||||
pass_builder.set_optimization_level(OptimizationLevel::Default);
|
||||
|
@ -190,10 +207,12 @@ impl WorkerRegistry {
|
|||
pass_builder.populate_function_pass_manager(&passes);
|
||||
|
||||
while let Some(task) = self.receiver.recv().unwrap() {
|
||||
let result = gen_func(&context, generator, self, builder, module, task);
|
||||
let tmp_module = context.create_module("tmp");
|
||||
let result = gen_func(&context, generator, self, builder, tmp_module, task);
|
||||
builder = result.0;
|
||||
module = result.1;
|
||||
passes.run_on(&result.2);
|
||||
module.link_in_module(result.1).unwrap();
|
||||
// module = result.1;
|
||||
*self.task_count.lock() -= 1;
|
||||
self.wait_condvar.notify_all();
|
||||
}
|
||||
|
@ -235,20 +254,26 @@ fn get_llvm_type<'ctx>(
|
|||
// we assume the type cache should already contain primitive types,
|
||||
// and they should be passed by value instead of passing as pointer.
|
||||
type_cache.get(&unifier.get_representative(ty)).cloned().unwrap_or_else(|| {
|
||||
let ty = unifier.get_ty(ty);
|
||||
match &*ty {
|
||||
let ty_enum = unifier.get_ty(ty);
|
||||
let result = match &*ty_enum {
|
||||
TObj { obj_id, fields, .. } => {
|
||||
// check to avoid treating primitives as classes
|
||||
if obj_id.0 <= 7 {
|
||||
unreachable!();
|
||||
}
|
||||
// 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 { 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))
|
||||
.collect_vec();
|
||||
ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into()
|
||||
struct_type.set_body(&fields, false);
|
||||
struct_type.ptr_type(AddressSpace::Generic).into()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
@ -270,8 +295,10 @@ fn get_llvm_type<'ctx>(
|
|||
ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
TVirtual { .. } => unimplemented!(),
|
||||
_ => unreachable!("{}", ty.get_type_name()),
|
||||
}
|
||||
_ => unreachable!("{}", ty_enum.get_type_name()),
|
||||
};
|
||||
type_cache.insert(unifier.get_representative(ty), result);
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -314,25 +341,50 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
none: unifier.get_representative(primitives.none),
|
||||
range: unifier.get_representative(primitives.range),
|
||||
str: unifier.get_representative(primitives.str),
|
||||
exception: unifier.get_representative(primitives.exception),
|
||||
};
|
||||
|
||||
let mut type_cache: HashMap<_, _> = [
|
||||
(unifier.get_representative(primitives.int32), context.i32_type().into()),
|
||||
(unifier.get_representative(primitives.int64), context.i64_type().into()),
|
||||
(unifier.get_representative(primitives.float), context.f64_type().into()),
|
||||
(unifier.get_representative(primitives.bool), context.bool_type().into()),
|
||||
(primitives.int32, context.i32_type().into()),
|
||||
(primitives.int64, context.i64_type().into()),
|
||||
(primitives.float, context.f64_type().into()),
|
||||
(primitives.bool, context.bool_type().into()),
|
||||
(primitives.str, {
|
||||
let str_type = context.opaque_struct_type("str");
|
||||
let fields = [
|
||||
context.i8_type().ptr_type(AddressSpace::Generic).into(),
|
||||
generator.get_size_type(context).into(),
|
||||
];
|
||||
str_type.set_body(&fields, false);
|
||||
str_type.into()
|
||||
}),
|
||||
(
|
||||
unifier.get_representative(primitives.str),
|
||||
context.i8_type().ptr_type(AddressSpace::Generic).into(),
|
||||
),
|
||||
(
|
||||
unifier.get_representative(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()
|
||||
.collect();
|
||||
type_cache.insert(primitives.exception, {
|
||||
let exception = context.opaque_struct_type("Exception");
|
||||
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
|
||||
];
|
||||
exception.set_body(&fields, false);
|
||||
exception.ptr_type(AddressSpace::Generic).into()
|
||||
});
|
||||
|
||||
let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } =
|
||||
task.store.get(task.signature)
|
||||
|
@ -390,6 +442,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
builder.build_store(alloca, param);
|
||||
var_assignment.insert(arg.name, (alloca, None, 0));
|
||||
}
|
||||
let return_buffer = fn_type.get_return_type().map(|v| builder.build_alloca(v, "$ret"));
|
||||
let static_values = {
|
||||
let store = registry.static_value_store.lock();
|
||||
store.store[task.id].clone()
|
||||
|
@ -407,7 +460,13 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
resolver: task.resolver,
|
||||
top_level: top_level_ctx.as_ref(),
|
||||
calls: task.calls,
|
||||
loop_bb: None,
|
||||
loop_target: None,
|
||||
return_target: None,
|
||||
return_buffer,
|
||||
unwind_target: None,
|
||||
outer_final: None,
|
||||
outer_catch_clauses: None,
|
||||
const_strings: Default::default(),
|
||||
registry,
|
||||
var_assignment,
|
||||
type_cache,
|
||||
|
@ -419,15 +478,14 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
static_value_store,
|
||||
};
|
||||
|
||||
let mut returned = false;
|
||||
for stmt in task.body.iter() {
|
||||
returned = generator.gen_stmt(&mut code_gen_context, stmt);
|
||||
if returned {
|
||||
generator.gen_stmt(&mut code_gen_context, stmt);
|
||||
if code_gen_context.is_terminated() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// after static analysis, only void functions can have no return at the end.
|
||||
if !returned {
|
||||
if !code_gen_context.is_terminated() {
|
||||
code_gen_context.builder.build_return(None);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,17 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
codegen::expr::gen_binop_expr,
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
typecheck::typedef::{Type, TypeEnum, FunSignature}
|
||||
};
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
basic_block::BasicBlock,
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValue, BasicValueEnum, PointerValue},
|
||||
values::{BasicValue, BasicValueEnum, FunctionValue, PointerValue},
|
||||
IntPredicate::EQ,
|
||||
};
|
||||
use nac3parser::ast::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use nac3parser::ast::{ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef, Constant};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub fn gen_var<'ctx, 'a>(
|
||||
|
@ -173,7 +177,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
let orelse_bb =
|
||||
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") };
|
||||
// store loop bb information and restore it later
|
||||
let loop_bb = ctx.loop_bb.replace((test_bb, cont_bb));
|
||||
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);
|
||||
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
|
||||
|
@ -234,22 +238,22 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
generator.gen_assign(ctx, target, val.into());
|
||||
}
|
||||
|
||||
for stmt in body.iter() {
|
||||
generator.gen_stmt(ctx, stmt);
|
||||
}
|
||||
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 {
|
||||
*static_val = None;
|
||||
}
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
}
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
for stmt in orelse.iter() {
|
||||
generator.gen_stmt(ctx, stmt);
|
||||
gen_block(generator, ctx, orelse.iter());
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
|
@ -258,7 +262,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
ctx.builder.position_at_end(cont_bb);
|
||||
ctx.loop_bb = loop_bb;
|
||||
ctx.loop_target = loop_bb;
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -282,7 +286,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
let orelse_bb =
|
||||
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") };
|
||||
// store loop bb information and restore it later
|
||||
let loop_bb = ctx.loop_bb.replace((test_bb, cont_bb));
|
||||
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);
|
||||
|
@ -292,22 +296,22 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
unreachable!()
|
||||
};
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
for stmt in body.iter() {
|
||||
generator.gen_stmt(ctx, stmt);
|
||||
}
|
||||
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 {
|
||||
*static_val = None;
|
||||
}
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
}
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
for stmt in orelse.iter() {
|
||||
generator.gen_stmt(ctx, stmt);
|
||||
gen_block(generator, ctx, orelse.iter());
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
|
@ -316,7 +320,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
ctx.builder.position_at_end(cont_bb);
|
||||
ctx.loop_bb = loop_bb;
|
||||
ctx.loop_target = loop_bb;
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -326,7 +330,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> bool {
|
||||
) {
|
||||
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
|
||||
|
@ -352,13 +356,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
unreachable!()
|
||||
};
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
let mut exited = false;
|
||||
for stmt in body.iter() {
|
||||
exited = generator.gen_stmt(ctx, stmt);
|
||||
if exited {
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
|
@ -366,32 +364,22 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
|
||||
if !exited {
|
||||
if !ctx.is_terminated() {
|
||||
if cont_bb.is_none() {
|
||||
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
|
||||
}
|
||||
let then_exited = exited;
|
||||
let else_exited = if !orelse.is_empty() {
|
||||
exited = false;
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
for stmt in orelse.iter() {
|
||||
exited = generator.gen_stmt(ctx, stmt);
|
||||
if exited {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !exited {
|
||||
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"));
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
|
||||
}
|
||||
exited
|
||||
} else {
|
||||
false
|
||||
};
|
||||
}
|
||||
if let Some(cont_bb) = cont_bb {
|
||||
ctx.builder.position_at_end(cont_bb);
|
||||
}
|
||||
|
@ -401,7 +389,464 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
*static_val = None;
|
||||
}
|
||||
}
|
||||
then_exited && else_exited
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final_proxy<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
target: BasicBlock<'ctx>,
|
||||
block: BasicBlock<'ctx>,
|
||||
) {
|
||||
let (final_state, final_targets, final_paths) = ctx.outer_final.as_mut().unwrap();
|
||||
let prev = ctx.builder.get_insert_block().unwrap();
|
||||
ctx.builder.position_at_end(block);
|
||||
unsafe {
|
||||
ctx.builder.build_store(*final_state, target.get_address().unwrap());
|
||||
}
|
||||
ctx.builder.position_at_end(prev);
|
||||
final_targets.push(target);
|
||||
final_paths.push(block);
|
||||
}
|
||||
|
||||
pub fn get_builtins<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
symbol: &str,
|
||||
) -> 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_resume" => ctx.ctx.void_type().fn_type(&[], false),
|
||||
"__artiq_end_catch" => ctx.ctx.void_type().fn_type(&[], false),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let fun = ctx.module.add_function(symbol, ty, None);
|
||||
if symbol == "__artiq_raise" || symbol == "__artiq_resume" {
|
||||
fun.add_attribute(
|
||||
AttributeLoc::Function,
|
||||
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 1),
|
||||
);
|
||||
}
|
||||
fun
|
||||
})
|
||||
}
|
||||
|
||||
pub fn exn_constructor<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
_fun: (&FunSignature, DefinitionId),
|
||||
mut args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
let (zelf_ty, zelf) = obj.unwrap();
|
||||
let zelf = zelf.to_basic_value_enum(ctx, generator).into_pointer_value();
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let zelf_id = {
|
||||
if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(zelf_ty) {
|
||||
obj_id.0
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
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 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 msg = if !args.is_empty() {
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator)
|
||||
} else {
|
||||
empty_string
|
||||
};
|
||||
ctx.builder.build_store(ptr, msg);
|
||||
for i in [6, 7, 8].iter() {
|
||||
let value = if !args.is_empty() {
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator)
|
||||
} else {
|
||||
ctx.ctx.i64_type().const_zero().into()
|
||||
};
|
||||
let ptr = ctx.builder.build_in_bounds_gep(
|
||||
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");
|
||||
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");
|
||||
ctx.builder.build_store(ptr, zero);
|
||||
}
|
||||
}
|
||||
Some(zelf.into())
|
||||
}
|
||||
|
||||
pub fn gen_raise<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
exception: Option<&BasicValueEnum<'ctx>>,
|
||||
loc: Location,
|
||||
) {
|
||||
if let Some(exception) = exception {
|
||||
unsafe {
|
||||
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 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");
|
||||
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");
|
||||
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");
|
||||
ctx.builder.build_store(name_ptr, fun_name);
|
||||
}
|
||||
|
||||
let raise = get_builtins(generator, ctx, "__artiq_raise");
|
||||
let exception = *exception;
|
||||
ctx.build_call_or_invoke(raise, &[exception], "raise");
|
||||
} else {
|
||||
let resume = get_builtins(generator, ctx, "__artiq_resume");
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
}
|
||||
ctx.builder.build_unreachable();
|
||||
}
|
||||
|
||||
pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
target: &Stmt<Option<Type>>,
|
||||
) {
|
||||
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();
|
||||
let personality = ctx.module.get_function(personality_symbol).unwrap_or_else(|| {
|
||||
let ty = ctx.ctx.i32_type().fn_type(&[], true);
|
||||
ctx.module.add_function(personality_symbol, ty, None)
|
||||
});
|
||||
let exception_type = ctx.get_llvm_type(generator, ctx.primitives.exception);
|
||||
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::Generic);
|
||||
let current_block = ctx.builder.get_insert_block().unwrap();
|
||||
let current_fun = current_block.get_parent().unwrap();
|
||||
let landingpad = ctx.ctx.append_basic_block(current_fun, "try.landingpad");
|
||||
let dispatcher = ctx.ctx.append_basic_block(current_fun, "try.dispatch");
|
||||
let mut dispatcher_end = dispatcher;
|
||||
ctx.builder.position_at_end(dispatcher);
|
||||
let exn = ctx.builder.build_phi(exception_type, "exn");
|
||||
ctx.builder.position_at_end(current_block);
|
||||
|
||||
let mut cleanup = None;
|
||||
let mut old_loop_target = None;
|
||||
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());
|
||||
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");
|
||||
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "try.continue");
|
||||
final_proxy(ctx, break_target, break_proxy);
|
||||
final_proxy(ctx, continue_target, continue_proxy);
|
||||
old_loop_target = ctx.loop_target.replace((continue_proxy, break_proxy));
|
||||
}
|
||||
let return_proxy = ctx.ctx.append_basic_block(current_fun, "try.return");
|
||||
if let Some(return_target) = ctx.return_target {
|
||||
final_proxy(ctx, return_target, return_proxy);
|
||||
} else {
|
||||
let return_target = ctx.ctx.append_basic_block(current_fun, "try.return_target");
|
||||
ctx.builder.position_at_end(return_target);
|
||||
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret"));
|
||||
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue));
|
||||
ctx.builder.position_at_end(current_block);
|
||||
final_proxy(ctx, return_target, return_proxy);
|
||||
}
|
||||
old_return = ctx.return_target.replace(return_proxy);
|
||||
cleanup = Some(ctx.ctx.append_basic_block(current_fun, "try.cleanup"));
|
||||
true
|
||||
} else {
|
||||
ctx.outer_final.is_some()
|
||||
};
|
||||
|
||||
let mut clauses = Vec::new();
|
||||
let mut found_catch_all = false;
|
||||
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) {
|
||||
clauses.push(None);
|
||||
found_catch_all = true;
|
||||
break;
|
||||
} else {
|
||||
let type_ = type_.as_ref().unwrap();
|
||||
let exn_name = ctx.resolver.get_type_name(
|
||||
&ctx.top_level.definitions.read(),
|
||||
&mut ctx.unifier,
|
||||
type_.custom.unwrap(),
|
||||
);
|
||||
let exn_id = ctx.resolver.get_string_id(&format!("0:{}", exn_name));
|
||||
let exn_id_global =
|
||||
ctx.module.add_global(ctx.ctx.i32_type(), None, &format!("exn.{}", exn_id));
|
||||
exn_id_global.set_initializer(&ctx.ctx.i32_type().const_int(exn_id as u64, false));
|
||||
clauses.push(Some(exn_id_global.as_pointer_value().as_basic_value_enum()));
|
||||
}
|
||||
}
|
||||
let mut all_clauses = clauses.clone();
|
||||
if let Some(old_clauses) = &ctx.outer_catch_clauses {
|
||||
if !found_catch_all {
|
||||
all_clauses.extend_from_slice(&old_clauses.0)
|
||||
}
|
||||
}
|
||||
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());
|
||||
if ctx.builder.get_insert_block().unwrap().get_terminator().is_none() {
|
||||
gen_block(generator, ctx, orelse.iter());
|
||||
}
|
||||
let body = ctx.builder.get_insert_block().unwrap();
|
||||
// reset old_clauses and old_unwind
|
||||
let (all_clauses, _, _) = ctx.outer_catch_clauses.take().unwrap();
|
||||
ctx.outer_catch_clauses = old_clauses;
|
||||
ctx.unwind_target = old_unwind;
|
||||
ctx.return_target = old_return;
|
||||
ctx.loop_target = old_loop_target;
|
||||
old_loop_target = None;
|
||||
|
||||
let old_unwind = if !finalbody.is_empty() {
|
||||
let final_landingpad = ctx.ctx.append_basic_block(current_fun, "try.catch.final");
|
||||
ctx.builder.position_at_end(final_landingpad);
|
||||
ctx.builder.build_landing_pad(
|
||||
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
|
||||
personality,
|
||||
&[],
|
||||
true,
|
||||
"try.catch.final",
|
||||
);
|
||||
ctx.builder.build_unconditional_branch(cleanup.unwrap());
|
||||
ctx.builder.position_at_end(body);
|
||||
ctx.unwind_target.replace(final_landingpad)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// run end_catch before continue/break/return
|
||||
let mut final_proxy_lambda =
|
||||
|ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
target: BasicBlock<'ctx>,
|
||||
block: BasicBlock<'ctx>| final_proxy(ctx, target, block);
|
||||
let mut redirect_lambda = |ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
target: BasicBlock<'ctx>,
|
||||
block: BasicBlock<'ctx>| {
|
||||
ctx.builder.position_at_end(block);
|
||||
ctx.builder.build_unconditional_branch(target);
|
||||
ctx.builder.position_at_end(body);
|
||||
};
|
||||
let redirect = if ctx.outer_final.is_some() {
|
||||
&mut final_proxy_lambda
|
||||
as &mut dyn FnMut(&mut CodeGenContext<'ctx, 'a>, BasicBlock<'ctx>, BasicBlock<'ctx>)
|
||||
} else {
|
||||
&mut redirect_lambda
|
||||
as &mut dyn FnMut(&mut CodeGenContext<'ctx, 'a>, BasicBlock<'ctx>, BasicBlock<'ctx>)
|
||||
};
|
||||
let resume = get_builtins(generator, ctx, "__artiq_resume");
|
||||
let end_catch = get_builtins(generator, ctx, "__artiq_end_catch");
|
||||
if let Some((continue_target, break_target)) = ctx.loop_target.take() {
|
||||
let break_proxy = ctx.ctx.append_basic_block(current_fun, "try.break");
|
||||
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "try.continue");
|
||||
ctx.builder.position_at_end(break_proxy);
|
||||
ctx.builder.build_call(end_catch, &[], "end_catch");
|
||||
ctx.builder.position_at_end(continue_proxy);
|
||||
ctx.builder.build_call(end_catch, &[], "end_catch");
|
||||
ctx.builder.position_at_end(body);
|
||||
redirect(ctx, break_target, break_proxy);
|
||||
redirect(ctx, continue_target, continue_proxy);
|
||||
ctx.loop_target = Some((continue_proxy, break_proxy));
|
||||
old_loop_target = Some((continue_target, break_target));
|
||||
}
|
||||
let return_proxy = ctx.ctx.append_basic_block(current_fun, "try.return");
|
||||
ctx.builder.position_at_end(return_proxy);
|
||||
ctx.builder.build_call(end_catch, &[], "end_catch");
|
||||
let return_target = ctx.return_target.take().unwrap_or_else(|| {
|
||||
let doreturn = ctx.ctx.append_basic_block(current_fun, "try.doreturn");
|
||||
ctx.builder.position_at_end(doreturn);
|
||||
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret"));
|
||||
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue));
|
||||
doreturn
|
||||
});
|
||||
redirect(ctx, return_target, return_proxy);
|
||||
ctx.return_target = Some(return_proxy);
|
||||
old_return = Some(return_target);
|
||||
|
||||
let mut post_handlers = Vec::new();
|
||||
|
||||
let exnid = if !handlers.is_empty() {
|
||||
ctx.builder.position_at_end(dispatcher);
|
||||
unsafe {
|
||||
let zero = ctx.ctx.i32_type().const_zero();
|
||||
let exnid_ptr = ctx.builder.build_gep(
|
||||
exn.as_basic_value().into_pointer_value(),
|
||||
&[zero, zero],
|
||||
"exnidptr",
|
||||
);
|
||||
Some(ctx.builder.build_load(exnid_ptr, "exnid"))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
for (handler_node, exn_type) in handlers.iter().zip(clauses.iter()) {
|
||||
let ExcepthandlerKind::ExceptHandler { type_, name, body } = &handler_node.node;
|
||||
let handler_bb = ctx.ctx.append_basic_block(current_fun, "try.handler");
|
||||
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);
|
||||
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());
|
||||
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
|
||||
if current.get_terminator().is_none() {
|
||||
ctx.builder.build_call(end_catch, &[], "end_catch");
|
||||
}
|
||||
post_handlers.push(current);
|
||||
ctx.builder.position_at_end(dispatcher_end);
|
||||
if let Some(exn_type) = exn_type {
|
||||
let dispatcher_cont =
|
||||
ctx.ctx.append_basic_block(current_fun, "try.dispatcher_cont");
|
||||
let actual_id = exnid.unwrap().into_int_value();
|
||||
let expected_id = ctx
|
||||
.builder
|
||||
.build_load(exn_type.into_pointer_value(), "expected_id")
|
||||
.into_int_value();
|
||||
let result = ctx.builder.build_int_compare(EQ, actual_id, expected_id, "exncheck");
|
||||
ctx.builder.build_conditional_branch(result, handler_bb, dispatcher_cont);
|
||||
dispatcher_end = dispatcher_cont;
|
||||
} else {
|
||||
ctx.builder.build_unconditional_branch(handler_bb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.unwind_target = old_unwind;
|
||||
ctx.loop_target = old_loop_target;
|
||||
ctx.return_target = old_return;
|
||||
|
||||
ctx.builder.position_at_end(landingpad);
|
||||
let clauses: Vec<_> = if finalbody.is_empty() { &all_clauses } else { &clauses }
|
||||
.iter()
|
||||
.map(|v| v.unwrap_or(ptr_type.const_zero().into()))
|
||||
.collect();
|
||||
let landingpad_value = ctx
|
||||
.builder
|
||||
.build_landing_pad(
|
||||
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
|
||||
personality,
|
||||
&clauses,
|
||||
has_cleanup,
|
||||
"try.landingpad",
|
||||
)
|
||||
.into_struct_value();
|
||||
let exn_val = ctx.builder.build_extract_value(landingpad_value, 1, "exn").unwrap();
|
||||
ctx.builder.build_unconditional_branch(dispatcher);
|
||||
exn.add_incoming(&[(&exn_val, landingpad)]);
|
||||
|
||||
if dispatcher_end.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(dispatcher_end);
|
||||
if let Some(cleanup) = cleanup {
|
||||
ctx.builder.build_unconditional_branch(cleanup);
|
||||
} else if let Some((_, outer_dispatcher, phi)) = ctx.outer_catch_clauses {
|
||||
phi.add_incoming(&[(&exn_val, dispatcher_end)]);
|
||||
ctx.builder.build_unconditional_branch(outer_dispatcher);
|
||||
} else {
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
ctx.builder.build_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
if finalbody.is_empty() {
|
||||
let tail = ctx.ctx.append_basic_block(current_fun, "try.tail");
|
||||
if body.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(body);
|
||||
ctx.builder.build_unconditional_branch(tail);
|
||||
}
|
||||
if matches!(cleanup, Some(cleanup) if cleanup.get_terminator().is_none()) {
|
||||
ctx.builder.position_at_end(cleanup.unwrap());
|
||||
ctx.builder.build_unconditional_branch(tail);
|
||||
}
|
||||
for post_handler in post_handlers {
|
||||
if post_handler.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(post_handler);
|
||||
ctx.builder.build_unconditional_branch(tail);
|
||||
}
|
||||
}
|
||||
ctx.builder.position_at_end(tail);
|
||||
} else {
|
||||
let final_branches = ctx.outer_final.take().unwrap();
|
||||
ctx.outer_final = old_outer_final;
|
||||
|
||||
// exception path
|
||||
let cleanup = cleanup.unwrap();
|
||||
ctx.builder.position_at_end(cleanup);
|
||||
gen_block(generator, ctx, finalbody.iter());
|
||||
if !ctx.is_terminated() {
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
ctx.builder.build_unreachable();
|
||||
}
|
||||
|
||||
// normal path
|
||||
let (final_state, mut final_targets, final_paths) = final_branches;
|
||||
let tail = ctx.ctx.append_basic_block(current_fun, "try.tail");
|
||||
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());
|
||||
if !ctx.is_terminated() {
|
||||
let dest = ctx.builder.build_load(final_state, "final_dest");
|
||||
ctx.builder.build_indirect_branch(dest, &final_targets);
|
||||
}
|
||||
for block in final_paths.iter() {
|
||||
if block.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(*block);
|
||||
ctx.builder.build_unconditional_branch(finalizer);
|
||||
}
|
||||
}
|
||||
for block in [body].iter().chain(post_handlers.iter()) {
|
||||
if block.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(*block);
|
||||
unsafe {
|
||||
ctx.builder.build_store(final_state, tail.get_address().unwrap());
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(finalizer);
|
||||
}
|
||||
}
|
||||
ctx.builder.position_at_end(tail);
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -416,23 +861,37 @@ pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
value: &Option<Box<Expr<Option<Type>>>>,
|
||||
) {
|
||||
let value = value
|
||||
.as_ref()
|
||||
.map(|v| generator.gen_expr(ctx, v).unwrap().to_basic_value_enum(ctx, generator));
|
||||
if let Some(return_target) = ctx.return_target {
|
||||
if let Some(value) = value {
|
||||
ctx.builder.build_store(ctx.return_buffer.unwrap(), value);
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(return_target);
|
||||
} else {
|
||||
let value = value.as_ref().map(|v| v as &dyn BasicValue);
|
||||
ctx.builder.build_return(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> bool {
|
||||
) {
|
||||
match &stmt.node {
|
||||
StmtKind::Pass { .. } => {}
|
||||
StmtKind::Expr { value, .. } => {
|
||||
generator.gen_expr(ctx, value);
|
||||
}
|
||||
StmtKind::Return { value, .. } => {
|
||||
let value = value
|
||||
.as_ref()
|
||||
.map(|v| generator.gen_expr(ctx, v).unwrap().to_basic_value_enum(ctx, generator));
|
||||
let value = value.as_ref().map(|v| v as &dyn BasicValue);
|
||||
ctx.builder.build_return(value);
|
||||
return true;
|
||||
gen_return(generator, ctx, value);
|
||||
}
|
||||
StmtKind::AnnAssign { target, value, .. } => {
|
||||
if let Some(value) = value {
|
||||
|
@ -447,22 +906,37 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
StmtKind::Continue { .. } => {
|
||||
ctx.builder.build_unconditional_branch(ctx.loop_bb.unwrap().0);
|
||||
return true;
|
||||
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().0);
|
||||
}
|
||||
StmtKind::Break { .. } => {
|
||||
ctx.builder.build_unconditional_branch(ctx.loop_bb.unwrap().1);
|
||||
return true;
|
||||
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().1);
|
||||
}
|
||||
StmtKind::If { .. } => return generator.gen_if(ctx, stmt),
|
||||
StmtKind::While { .. } => return generator.gen_while(ctx, stmt),
|
||||
StmtKind::For { .. } => return generator.gen_for(ctx, stmt),
|
||||
StmtKind::With { .. } => return 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);
|
||||
}
|
||||
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)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
false
|
||||
}
|
||||
|
||||
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,
|
||||
) {
|
||||
for stmt in stmts {
|
||||
generator.gen_stmt(ctx, stmt);
|
||||
if ctx.is_terminated() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,10 @@ impl SymbolResolver for Resolver {
|
|||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
self.id_to_def.read().get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -120,6 +124,7 @@ fn test_primitives() {
|
|||
virtual_checks: &mut virtual_checks,
|
||||
calls: &mut calls,
|
||||
defined_identifiers: identifiers.clone(),
|
||||
in_handler: false
|
||||
};
|
||||
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
|
||||
inferencer.variable_mapping.insert("b".into(), inferencer.primitives.int32);
|
||||
|
@ -263,6 +268,7 @@ fn test_simple_call() {
|
|||
virtual_checks: &mut virtual_checks,
|
||||
calls: &mut calls,
|
||||
defined_identifiers: identifiers.clone(),
|
||||
in_handler: false
|
||||
};
|
||||
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
|
||||
inferencer.variable_mapping.insert("foo".into(), fun_ty);
|
||||
|
|
|
@ -2,14 +2,17 @@ use std::collections::HashMap;
|
|||
use std::fmt::Debug;
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
use crate::{codegen::CodeGenerator, typecheck::{
|
||||
type_inferencer::PrimitiveStore,
|
||||
typedef::{Type, Unifier},
|
||||
}};
|
||||
use crate::{
|
||||
codegen::CodeGenContext,
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
};
|
||||
use crate::{
|
||||
codegen::CodeGenerator,
|
||||
typecheck::{
|
||||
type_inferencer::PrimitiveStore,
|
||||
typedef::{Type, Unifier},
|
||||
},
|
||||
};
|
||||
use crate::{location::Location, typecheck::typedef::TypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue};
|
||||
use itertools::{chain, izip};
|
||||
|
@ -20,6 +23,7 @@ use parking_lot::RwLock;
|
|||
pub enum SymbolValue {
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
Str(String),
|
||||
Double(f64),
|
||||
Bool(bool),
|
||||
Tuple(Vec<SymbolValue>),
|
||||
|
@ -28,10 +32,16 @@ pub enum SymbolValue {
|
|||
pub trait StaticValue {
|
||||
fn get_unique_identifier(&self) -> u64;
|
||||
|
||||
fn get_const_obj<'ctx, 'a>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> BasicValueEnum<'ctx>;
|
||||
|
||||
fn to_basic_value_enum<'ctx, 'a>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut dyn CodeGenerator
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> BasicValueEnum<'ctx>;
|
||||
|
||||
fn get_field<'ctx, 'a>(
|
||||
|
@ -105,11 +115,12 @@ pub trait SymbolResolver {
|
|||
|
||||
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.
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static IDENTIFIER_ID: [StrRef; 8] = [
|
||||
static IDENTIFIER_ID: [StrRef; 10] = [
|
||||
"int32".into(),
|
||||
"int64".into(),
|
||||
"float".into(),
|
||||
|
@ -117,7 +128,9 @@ thread_local! {
|
|||
"None".into(),
|
||||
"virtual".into(),
|
||||
"list".into(),
|
||||
"tuple".into()
|
||||
"tuple".into(),
|
||||
"str".into(),
|
||||
"Exception".into(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -139,6 +152,8 @@ pub fn parse_type_annotation<T>(
|
|||
let virtual_id = ids[5];
|
||||
let list_id = ids[6];
|
||||
let tuple_id = ids[7];
|
||||
let str_id = ids[8];
|
||||
let exn_id = ids[9];
|
||||
|
||||
let name_handling = |id: &StrRef, unifier: &mut Unifier| {
|
||||
if *id == int32_id {
|
||||
|
@ -151,6 +166,10 @@ pub fn parse_type_annotation<T>(
|
|||
Ok(primitives.bool)
|
||||
} else if *id == none_id {
|
||||
Ok(primitives.none)
|
||||
} else if *id == str_id {
|
||||
Ok(primitives.str)
|
||||
} else if *id == exn_id {
|
||||
Ok(primitives.exception)
|
||||
} else {
|
||||
let obj_id = resolver.get_identifier_def(*id);
|
||||
if let Some(obj_id) = obj_id {
|
||||
|
@ -179,8 +198,7 @@ pub fn parse_type_annotation<T>(
|
|||
}
|
||||
} else {
|
||||
// it could be a type variable
|
||||
let ty = resolver
|
||||
.get_symbol_type(unifier, top_level_defs, primitives, *id)?;
|
||||
let ty = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id)?;
|
||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
||||
Ok(ty)
|
||||
} else {
|
||||
|
@ -192,35 +210,17 @@ pub fn parse_type_annotation<T>(
|
|||
|
||||
let subscript_name_handle = |id: &StrRef, slice: &Expr<T>, unifier: &mut Unifier| {
|
||||
if *id == virtual_id {
|
||||
let ty = parse_type_annotation(
|
||||
resolver,
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
slice,
|
||||
)?;
|
||||
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
|
||||
Ok(unifier.add_ty(TypeEnum::TVirtual { ty }))
|
||||
} else if *id == list_id {
|
||||
let ty = parse_type_annotation(
|
||||
resolver,
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
slice,
|
||||
)?;
|
||||
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
|
||||
Ok(unifier.add_ty(TypeEnum::TList { ty }))
|
||||
} else if *id == tuple_id {
|
||||
if let Tuple { elts, .. } = &slice.node {
|
||||
let ty = elts
|
||||
.iter()
|
||||
.map(|elt| {
|
||||
parse_type_annotation(
|
||||
resolver,
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
elt,
|
||||
)
|
||||
parse_type_annotation(resolver, top_level_defs, unifier, primitives, elt)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
|
||||
|
@ -231,23 +231,11 @@ pub fn parse_type_annotation<T>(
|
|||
let types = if let Tuple { elts, .. } = &slice.node {
|
||||
elts.iter()
|
||||
.map(|v| {
|
||||
parse_type_annotation(
|
||||
resolver,
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
v,
|
||||
)
|
||||
parse_type_annotation(resolver, top_level_defs, unifier, primitives, v)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
} else {
|
||||
vec![parse_type_annotation(
|
||||
resolver,
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
slice,
|
||||
)?]
|
||||
vec![parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?]
|
||||
};
|
||||
|
||||
let obj_id = resolver
|
||||
|
@ -316,6 +304,25 @@ impl dyn SymbolResolver + Send + Sync {
|
|||
) -> Result<Type, String> {
|
||||
parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
|
||||
}
|
||||
|
||||
pub fn get_type_name(
|
||||
&self,
|
||||
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
|
||||
unifier: &mut Unifier,
|
||||
ty: Type,
|
||||
) -> String {
|
||||
unifier.stringify(
|
||||
ty,
|
||||
&mut |id| {
|
||||
if let TopLevelDef::Class { name, .. } = &*top_level_defs[id].read() {
|
||||
name.to_string()
|
||||
} else {
|
||||
unreachable!("expected class definition")
|
||||
}
|
||||
},
|
||||
&mut |id| format!("var{}", id),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for dyn SymbolResolver + Send + Sync {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::*;
|
||||
use crate::{
|
||||
codegen::{expr::destructure_range, irrt::calculate_len_for_slice_range},
|
||||
codegen::{expr::destructure_range, irrt::calculate_len_for_slice_range, stmt::exn_constructor},
|
||||
symbol_resolver::SymbolValue,
|
||||
};
|
||||
use inkwell::{FloatPredicate, IntPredicate};
|
||||
|
@ -21,6 +21,47 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let num_ty = primitives.1.get_fresh_var_with_range(&[int32, int64, float, boolean]);
|
||||
let var_map: HashMap<_, _> = vec![(num_ty.1, num_ty.0)].into_iter().collect();
|
||||
|
||||
let exception_fields = vec![
|
||||
("__name__".into(), int32, true),
|
||||
("__file__".into(), string, true),
|
||||
("__line__".into(), int32, true),
|
||||
("__col__".into(), int32, true),
|
||||
("__func__".into(), string, true),
|
||||
("__message__".into(), string, true),
|
||||
("__param0__".into(), int64, true),
|
||||
("__param1__".into(), int64, true),
|
||||
("__param2__".into(), int64, true),
|
||||
];
|
||||
let div_by_zero = primitives.1.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(10),
|
||||
fields: RefCell::new(exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect()),
|
||||
params: Default::default()
|
||||
});
|
||||
let index_error = primitives.1.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(11),
|
||||
fields: RefCell::new(exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect()),
|
||||
params: Default::default()
|
||||
});
|
||||
let exn_cons_args = vec![
|
||||
FuncArg { name: "msg".into(), ty: string,
|
||||
default_value: Some(SymbolValue::Str("".into()))},
|
||||
FuncArg { name: "param0".into(), ty: int64,
|
||||
default_value: Some(SymbolValue::I64(0))},
|
||||
FuncArg { name: "param1".into(), ty: int64,
|
||||
default_value: Some(SymbolValue::I64(0))},
|
||||
FuncArg { name: "param2".into(), ty: int64,
|
||||
default_value: Some(SymbolValue::I64(0))},
|
||||
];
|
||||
let div_by_zero_signature = primitives.1.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature {
|
||||
args: exn_cons_args.clone(),
|
||||
ret: div_by_zero,
|
||||
vars: Default::default()
|
||||
})));
|
||||
let index_error_signature = primitives.1.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature {
|
||||
args: exn_cons_args,
|
||||
ret: index_error,
|
||||
vars: Default::default()
|
||||
})));
|
||||
let top_level_def_list = vec![
|
||||
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
|
||||
0,
|
||||
|
@ -49,6 +90,62 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
None,
|
||||
))),
|
||||
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(6, None, "str".into(), None))),
|
||||
Arc::new(RwLock::new(TopLevelDef::Class {
|
||||
name: "Exception".into(),
|
||||
object_id: DefinitionId(7),
|
||||
type_vars: Default::default(),
|
||||
fields: exception_fields.clone(),
|
||||
methods: Default::default(),
|
||||
ancestors: vec![],
|
||||
constructor: None,
|
||||
resolver: None,
|
||||
})),
|
||||
Arc::new(RwLock::new(TopLevelDef::Function {
|
||||
name: "ZeroDivisionError.__init__".into(),
|
||||
simple_name: "__init__".into(),
|
||||
signature: div_by_zero_signature,
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor))))
|
||||
})),
|
||||
Arc::new(RwLock::new(TopLevelDef::Function {
|
||||
name: "IndexError.__init__".into(),
|
||||
simple_name: "__init__".into(),
|
||||
signature: index_error_signature,
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor))))
|
||||
})),
|
||||
Arc::new(RwLock::new(TopLevelDef::Class {
|
||||
name: "ZeroDivisionError".into(),
|
||||
object_id: DefinitionId(10),
|
||||
type_vars: Default::default(),
|
||||
fields: exception_fields.clone(),
|
||||
methods: vec![("__init__".into(), div_by_zero_signature, DefinitionId(8))],
|
||||
ancestors: vec![
|
||||
TypeAnnotation::CustomClass { id: DefinitionId(10), params: Default::default() },
|
||||
TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }
|
||||
],
|
||||
constructor: Some(div_by_zero_signature),
|
||||
resolver: None,
|
||||
})),
|
||||
Arc::new(RwLock::new(TopLevelDef::Class {
|
||||
name: "IndexError".into(),
|
||||
object_id: DefinitionId(11),
|
||||
type_vars: Default::default(),
|
||||
fields: exception_fields,
|
||||
methods: vec![("__init__".into(), index_error_signature, DefinitionId(9))],
|
||||
ancestors: vec![
|
||||
TypeAnnotation::CustomClass { id: DefinitionId(11), params: Default::default() },
|
||||
TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }
|
||||
],
|
||||
constructor: Some(index_error_signature),
|
||||
resolver: None,
|
||||
})),
|
||||
Arc::new(RwLock::new(TopLevelDef::Function {
|
||||
name: "int32".into(),
|
||||
simple_name: "int32".into(),
|
||||
|
@ -62,13 +159,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args| {
|
||||
|ctx, _, fun, args, generator| {
|
||||
let int32 = ctx.primitives.int32;
|
||||
let int64 = ctx.primitives.int64;
|
||||
let float = ctx.primitives.float;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
if ctx.unifier.unioned(arg_ty, boolean) {
|
||||
Some(
|
||||
ctx.builder
|
||||
|
@ -120,13 +217,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args| {
|
||||
|ctx, _, fun, args, generator| {
|
||||
let int32 = ctx.primitives.int32;
|
||||
let int64 = ctx.primitives.int64;
|
||||
let float = ctx.primitives.float;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
if ctx.unifier.unioned(arg_ty, boolean)
|
||||
|| ctx.unifier.unioned(arg_ty, int32)
|
||||
{
|
||||
|
@ -170,18 +267,18 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args| {
|
||||
|ctx, _, fun, args, generator| {
|
||||
let int32 = ctx.primitives.int32;
|
||||
let int64 = ctx.primitives.int64;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let float = ctx.primitives.float;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
if ctx.unifier.unioned(arg_ty, boolean)
|
||||
|| ctx.unifier.unioned(arg_ty, int32)
|
||||
|| ctx.unifier.unioned(arg_ty, int64)
|
||||
{
|
||||
let arg = args[0].1.into_int_value();
|
||||
let arg = arg.into_int_value();
|
||||
let val = ctx
|
||||
.builder
|
||||
.build_signed_int_to_float(arg, ctx.ctx.f64_type(), "sitofp")
|
||||
|
@ -207,8 +304,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
|
||||
let arg = args[0].1;
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
let round_intrinsic =
|
||||
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -244,8 +341,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
|
||||
let arg = args[0].1;
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
let round_intrinsic =
|
||||
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -294,7 +391,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
||||
let mut start = None;
|
||||
let mut stop = None;
|
||||
let mut step = None;
|
||||
|
@ -302,17 +399,17 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let zero = int32.const_zero();
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if arg.0 == Some("start".into()) {
|
||||
start = Some(arg.1);
|
||||
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
||||
} else if arg.0 == Some("stop".into()) {
|
||||
stop = Some(arg.1);
|
||||
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
||||
} else if arg.0 == Some("step".into()) {
|
||||
step = Some(arg.1);
|
||||
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
||||
} else if i == 0 {
|
||||
start = Some(arg.1);
|
||||
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
||||
} else if i == 1 {
|
||||
stop = Some(arg.1);
|
||||
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
||||
} else if i == 2 {
|
||||
step = Some(arg.1);
|
||||
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
||||
}
|
||||
}
|
||||
// TODO: error when step == 0
|
||||
|
@ -356,8 +453,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|_, _, _, args| {
|
||||
Some(args[0].1)
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
||||
Some(args[0].1.clone().to_basic_value_enum(ctx, generator))
|
||||
})))),
|
||||
})),
|
||||
Arc::new(RwLock::new(TopLevelDef::Function {
|
||||
|
@ -373,13 +470,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args| {
|
||||
|ctx, _, fun, args, generator| {
|
||||
let int32 = ctx.primitives.int32;
|
||||
let int64 = ctx.primitives.int64;
|
||||
let float = ctx.primitives.float;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
if ctx.unifier.unioned(arg_ty, boolean) {
|
||||
Some(arg)
|
||||
} else if ctx.unifier.unioned(arg_ty, int32) {
|
||||
|
@ -424,8 +521,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
|
||||
let arg = args[0].1;
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
let floor_intrinsic =
|
||||
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -461,8 +558,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
|
||||
let arg = args[0].1;
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
let floor_intrinsic =
|
||||
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -498,8 +595,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
|
||||
let arg = args[0].1;
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
let ceil_intrinsic =
|
||||
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -535,8 +632,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args| {
|
||||
let arg = args[0].1;
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
let ceil_intrinsic =
|
||||
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -581,10 +678,10 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args| {
|
||||
|ctx, _, fun, args, generator| {
|
||||
let range_ty = ctx.primitives.range;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
||||
if ctx.unifier.unioned(arg_ty, range_ty) {
|
||||
let arg = arg.into_pointer_value();
|
||||
let (start, end, step) = destructure_range(ctx, arg);
|
||||
|
@ -609,6 +706,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
(
|
||||
izip!(top_level_def_list, ast_list).collect_vec(),
|
||||
&[
|
||||
"ZeroDivisionError",
|
||||
"IndexError",
|
||||
"int32",
|
||||
"int64",
|
||||
"float",
|
||||
|
|
|
@ -4,7 +4,8 @@ use nac3parser::ast::fold::Fold;
|
|||
|
||||
use crate::{
|
||||
typecheck::type_inferencer::{FunctionData, Inferencer},
|
||||
codegen::expr::get_subst_key,
|
||||
codegen::{expr::get_subst_key, stmt::exn_constructor},
|
||||
symbol_resolver::SymbolValue,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
@ -90,8 +91,13 @@ impl TopLevelComposer {
|
|||
assert!(name == *simple_name);
|
||||
builtin_ty.insert(name, *signature);
|
||||
builtin_id.insert(name, DefinitionId(id));
|
||||
} else {
|
||||
unreachable!()
|
||||
} else if let TopLevelDef::Class { name, constructor, object_id, type_vars, .. } = &*def {
|
||||
assert!(id == object_id.0);
|
||||
assert!(type_vars.is_empty());
|
||||
if let Some(constructor) = constructor {
|
||||
builtin_ty.insert(*name, *constructor);
|
||||
}
|
||||
builtin_id.insert(*name, DefinitionId(id));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,7 +148,7 @@ impl TopLevelComposer {
|
|||
self.unifier.get_shared_unifier(),
|
||||
self.primitives_ty,
|
||||
)])),
|
||||
personality_symbol: None,
|
||||
personality_symbol: Some("__artiq_personality".into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,7 +166,7 @@ impl TopLevelComposer {
|
|||
) -> Result<(StrRef, DefinitionId, Option<Type>), String> {
|
||||
let defined_names = &mut self.defined_names;
|
||||
match &ast.node {
|
||||
ast::StmtKind::ClassDef { name: class_name, body, .. } => {
|
||||
ast::StmtKind::ClassDef { name: class_name, bases, body, .. } => {
|
||||
if self.keyword_list.contains(class_name) {
|
||||
return Err(format!(
|
||||
"cannot use keyword `{}` as a class name (at {})",
|
||||
|
@ -168,11 +174,12 @@ impl TopLevelComposer {
|
|||
ast.location
|
||||
));
|
||||
}
|
||||
if !defined_names.insert({
|
||||
let mut n = mod_path.clone();
|
||||
n.push_str(&class_name.to_string());
|
||||
n
|
||||
}) {
|
||||
let fully_qualified_class_name = if mod_path.is_empty() {
|
||||
*class_name
|
||||
} else {
|
||||
format!("{}.{}", &mod_path, class_name).into()
|
||||
};
|
||||
if !defined_names.insert(fully_qualified_class_name.into()) {
|
||||
return Err(format!(
|
||||
"duplicate definition of class `{}` (at {})",
|
||||
class_name,
|
||||
|
@ -190,7 +197,7 @@ impl TopLevelComposer {
|
|||
Arc::new(RwLock::new(Self::make_top_level_class_def(
|
||||
class_def_id,
|
||||
resolver.clone(),
|
||||
class_name,
|
||||
fully_qualified_class_name,
|
||||
Some(constructor_ty),
|
||||
))),
|
||||
None,
|
||||
|
@ -212,8 +219,13 @@ impl TopLevelComposer {
|
|||
// we do not push anything to the def list, so we keep track of the index
|
||||
// and then push in the correct order after the for loop
|
||||
let mut class_method_index_offset = 0;
|
||||
let mut contains_constructor = false;
|
||||
let init_id = "__init__".into();
|
||||
let exception_id = "Exception".into();
|
||||
// TODO: Fix this hack. We will generate constructor for classes that inherit
|
||||
// from Exception class (directly or indirectly), but this code cannot handle
|
||||
// subclass of other exception classes.
|
||||
let mut contains_constructor = bases
|
||||
.iter().any(|base| matches!(base.node, ast::ExprKind::Name { id, .. } if id == exception_id));
|
||||
for b in body {
|
||||
if let ast::StmtKind::FunctionDef { name: method_name, .. } = &b.node {
|
||||
if method_name == &init_id {
|
||||
|
@ -226,21 +238,14 @@ impl TopLevelComposer {
|
|||
b.location
|
||||
));
|
||||
}
|
||||
let global_class_method_name = {
|
||||
let mut n = mod_path.clone();
|
||||
n.push_str(
|
||||
Self::make_class_method_name(
|
||||
class_name.into(),
|
||||
&method_name.to_string(),
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
n
|
||||
};
|
||||
let global_class_method_name = Self::make_class_method_name(
|
||||
fully_qualified_class_name.into(),
|
||||
&method_name.to_string(),
|
||||
);
|
||||
if !defined_names.insert(global_class_method_name.clone()) {
|
||||
return Err(format!(
|
||||
"class method `{}` defined twice (at {})",
|
||||
&global_class_method_name[mod_path.len()..],
|
||||
global_class_method_name,
|
||||
b.location
|
||||
));
|
||||
}
|
||||
|
@ -298,15 +303,15 @@ impl TopLevelComposer {
|
|||
// if self.keyword_list.contains(name) {
|
||||
// return Err("cannot use keyword as a top level function name".into());
|
||||
// }
|
||||
let global_fun_name = {
|
||||
let mut n = mod_path.clone();
|
||||
n.push_str(&name.to_string());
|
||||
n
|
||||
let global_fun_name = if mod_path.is_empty() {
|
||||
name.to_string()
|
||||
} else {
|
||||
format!("{}.{}", mod_path, name)
|
||||
};
|
||||
if !defined_names.insert(global_fun_name.clone()) {
|
||||
return Err(format!(
|
||||
"top level function `{}` defined twice (at {})",
|
||||
&global_fun_name[mod_path.len()..],
|
||||
global_fun_name,
|
||||
ast.location
|
||||
));
|
||||
}
|
||||
|
@ -471,7 +476,6 @@ impl TopLevelComposer {
|
|||
let unifier = self.unifier.borrow_mut();
|
||||
|
||||
// first, only push direct parent into the list
|
||||
// skip 5 to skip analyzing the primitives
|
||||
for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
|
||||
let mut class_def = class_def.write();
|
||||
let (class_def_id, class_bases, class_ancestors, class_resolver, class_type_vars) = {
|
||||
|
@ -540,7 +544,6 @@ impl TopLevelComposer {
|
|||
|
||||
// second, get all ancestors
|
||||
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = Default::default();
|
||||
// skip 5 to skip analyzing the primitives
|
||||
for (class_def, _) in self.definition_ast_list.iter().skip(self.builtin_num) {
|
||||
let class_def = class_def.read();
|
||||
let (class_ancestors, class_id) = {
|
||||
|
@ -562,8 +565,7 @@ impl TopLevelComposer {
|
|||
}
|
||||
|
||||
// insert the ancestors to the def list
|
||||
// skip 5 to skip analyzing the primitives
|
||||
for (class_def, _) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
|
||||
for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
|
||||
let mut class_def = class_def.write();
|
||||
let (class_ancestors, class_id, class_type_vars) = {
|
||||
if let TopLevelDef::Class { ancestors, object_id, type_vars, .. } =
|
||||
|
@ -581,6 +583,26 @@ impl TopLevelComposer {
|
|||
// insert self type annotation to the front of the vector to maintain the order
|
||||
class_ancestors
|
||||
.insert(0, make_self_type_annotation(class_type_vars.as_slice(), class_id));
|
||||
|
||||
// special case classes that inherit from Exception
|
||||
if class_ancestors.iter().any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) {
|
||||
// if inherited from Exception, the body should be a pass
|
||||
if let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node {
|
||||
if body.len() != 1 || !matches!(body[0].node, ast::StmtKind::Pass { .. }) {
|
||||
return Err("Classes inherited from exception should have `pass` as body".into());
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deal with ancestor of Exception object
|
||||
if let TopLevelDef::Class { name, ancestors, object_id, .. } = &mut *self.definition_ast_list[7].0.write() {
|
||||
assert_eq!(*name, "Exception".into());
|
||||
ancestors.push(make_self_type_annotation(&[], *object_id));
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -595,7 +617,6 @@ impl TopLevelComposer {
|
|||
|
||||
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new();
|
||||
|
||||
// skip 5 to skip analyzing the primitives
|
||||
for (class_def, class_ast) in def_ast_list.iter().skip(self.builtin_num) {
|
||||
if matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
|
||||
Self::analyze_single_class_methods_fields(
|
||||
|
@ -610,8 +631,6 @@ impl TopLevelComposer {
|
|||
}
|
||||
}
|
||||
|
||||
// println!("type_var_to_concrete_def1: {:?}", type_var_to_concrete_def);
|
||||
|
||||
// handle the inheritanced methods and fields
|
||||
let mut current_ancestor_depth: usize = 2;
|
||||
loop {
|
||||
|
@ -646,19 +665,8 @@ impl TopLevelComposer {
|
|||
}
|
||||
}
|
||||
|
||||
// println!("type_var_to_concrete_def3: {:?}\n", type_var_to_concrete_def);
|
||||
|
||||
// unification of previously assigned typevar
|
||||
for (ty, def) in type_var_to_concrete_def {
|
||||
// println!(
|
||||
// "{:?}_{} -> {:?}\n",
|
||||
// ty,
|
||||
// unifier.stringify(ty,
|
||||
// &mut |id| format!("class{}", id),
|
||||
// &mut |id| format!("tvar{}", id)
|
||||
// ),
|
||||
// def
|
||||
// );
|
||||
let target_ty =
|
||||
get_type_from_type_annotation_kinds(&temp_def_list, unifier, primitives, &def)?;
|
||||
unifier.unify(ty, target_ty)?;
|
||||
|
@ -946,7 +954,7 @@ impl TopLevelComposer {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if name == &"__init__".into() && !defined_paramter_name.contains(&zelf) {
|
||||
return Err(format!("__init__ method must have a `self` parameter (at {})", b.location));
|
||||
}
|
||||
|
@ -1301,10 +1309,14 @@ impl TopLevelComposer {
|
|||
fn analyze_function_instance(&mut self) -> Result<(), String> {
|
||||
// first get the class contructor type correct for the following type check in function body
|
||||
// also do class field instantiation check
|
||||
for (def, ast) in self.definition_ast_list.iter().skip(self.builtin_num) {
|
||||
let init_str_id = "__init__".into();
|
||||
let mut definition_extension = Vec::new();
|
||||
let mut constructors = Vec::new();
|
||||
for (i, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.builtin_num) {
|
||||
let class_def = def.read();
|
||||
if let TopLevelDef::Class {
|
||||
constructor,
|
||||
ancestors,
|
||||
methods,
|
||||
fields,
|
||||
type_vars,
|
||||
|
@ -1314,13 +1326,54 @@ impl TopLevelComposer {
|
|||
..
|
||||
} = &*class_def
|
||||
{
|
||||
let self_type = get_type_from_type_annotation_kinds(
|
||||
self.extract_def_list().as_slice(),
|
||||
&mut self.unifier,
|
||||
&self.primitives_ty,
|
||||
&make_self_type_annotation(type_vars, *object_id),
|
||||
)?;
|
||||
if ancestors.iter().any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) {
|
||||
// create constructor for these classes
|
||||
let string = self.primitives_ty.str;
|
||||
let int64 = self.primitives_ty.int64;
|
||||
let signature = self.unifier.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature {
|
||||
args: vec![
|
||||
FuncArg { name: "msg".into(), ty: string,
|
||||
default_value: Some(SymbolValue::Str("".into()))},
|
||||
FuncArg { name: "param0".into(), ty: int64,
|
||||
default_value: Some(SymbolValue::I64(0))},
|
||||
FuncArg { name: "param1".into(), ty: int64,
|
||||
default_value: Some(SymbolValue::I64(0))},
|
||||
FuncArg { name: "param2".into(), ty: int64,
|
||||
default_value: Some(SymbolValue::I64(0))},
|
||||
],
|
||||
ret: self_type,
|
||||
vars: Default::default()
|
||||
})));
|
||||
let cons_fun = TopLevelDef::Function {
|
||||
name: format!("{}.{}", class_name, "__init__"),
|
||||
simple_name: init_str_id,
|
||||
signature,
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor))))
|
||||
};
|
||||
constructors.push((i, signature, definition_extension.len()));
|
||||
definition_extension.push((Arc::new(RwLock::new(cons_fun)), None));
|
||||
self.unifier
|
||||
.unify(constructor.unwrap(), signature)
|
||||
.map_err(|old| format!("{} (at {})", old, ast.as_ref().unwrap().location))?;
|
||||
continue;
|
||||
}
|
||||
let mut init_id: Option<DefinitionId> = None;
|
||||
// get the class contructor type correct
|
||||
let (contor_args, contor_type_vars) = {
|
||||
let mut constructor_args: Vec<FuncArg> = Vec::new();
|
||||
let mut type_vars: HashMap<u32, Type> = HashMap::new();
|
||||
for (name, func_sig, id) in methods {
|
||||
if name == &"__init__".into() {
|
||||
if *name == init_str_id {
|
||||
init_id = Some(*id);
|
||||
if let TypeEnum::TFunc(sig) = self.unifier.get_ty(*func_sig).as_ref() {
|
||||
let FunSignature { args, vars, .. } = &*sig.borrow();
|
||||
|
@ -1333,12 +1386,6 @@ impl TopLevelComposer {
|
|||
}
|
||||
(constructor_args, type_vars)
|
||||
};
|
||||
let self_type = get_type_from_type_annotation_kinds(
|
||||
self.extract_def_list().as_slice(),
|
||||
&mut self.unifier,
|
||||
&self.primitives_ty,
|
||||
&make_self_type_annotation(type_vars, *object_id),
|
||||
)?;
|
||||
let contor_type = self.unifier.add_ty(TypeEnum::TFunc(
|
||||
FunSignature { args: contor_args, ret: self_type, vars: contor_type_vars }
|
||||
.into(),
|
||||
|
@ -1352,7 +1399,7 @@ impl TopLevelComposer {
|
|||
let init_ast =
|
||||
self.definition_ast_list.get(init_id.0).unwrap().1.as_ref().unwrap();
|
||||
if let ast::StmtKind::FunctionDef { name, body, .. } = &init_ast.node {
|
||||
if name != &"__init__".into() {
|
||||
if *name != init_str_id {
|
||||
unreachable!("must be init function here")
|
||||
}
|
||||
let all_inited = Self::get_all_assigned_field(body.as_slice())?;
|
||||
|
@ -1370,11 +1417,23 @@ impl TopLevelComposer {
|
|||
}
|
||||
}
|
||||
}
|
||||
for (i, signature, id) in constructors.into_iter() {
|
||||
if let TopLevelDef::Class { methods, .. } = &mut *self.definition_ast_list[i].0.write() {
|
||||
methods.push((init_str_id, signature,
|
||||
DefinitionId(self.definition_ast_list.len() + id)));
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
self.definition_ast_list.extend_from_slice(&definition_extension);
|
||||
|
||||
let ctx = Arc::new(self.make_top_level_context());
|
||||
// type inference inside function body
|
||||
for (id, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.builtin_num)
|
||||
{
|
||||
if ast.is_none() {
|
||||
continue;
|
||||
}
|
||||
let mut function_def = def.write();
|
||||
if let TopLevelDef::Function {
|
||||
instance_to_stmt,
|
||||
|
@ -1522,6 +1581,7 @@ impl TopLevelComposer {
|
|||
primitives: &self.primitives_ty,
|
||||
virtual_checks: &mut Vec::new(),
|
||||
calls: &mut calls,
|
||||
in_handler: false
|
||||
};
|
||||
|
||||
let fun_body =
|
||||
|
@ -1535,6 +1595,13 @@ impl TopLevelComposer {
|
|||
instance_to_symbol.insert("".into(), simple_name.to_string());
|
||||
continue;
|
||||
}
|
||||
if !decorator_list.is_empty()
|
||||
&& matches!(&decorator_list[0].node,
|
||||
ast::ExprKind::Name{ id, .. } if id == &"rpc".into())
|
||||
{
|
||||
instance_to_symbol.insert("".into(), simple_name.to_string());
|
||||
continue;
|
||||
}
|
||||
body
|
||||
} else {
|
||||
unreachable!("must be function def ast")
|
||||
|
@ -1545,7 +1612,42 @@ impl TopLevelComposer {
|
|||
|
||||
let returned =
|
||||
inferencer.check_block(fun_body.as_slice(), &mut identifiers)?;
|
||||
|
||||
{
|
||||
// check virtuals
|
||||
let defs = ctx.definitions.read();
|
||||
for (subtype, base, loc) in inferencer.virtual_checks.iter() {
|
||||
let base_id = {
|
||||
let base = inferencer.unifier.get_ty(*base);
|
||||
if let TypeEnum::TObj { obj_id, .. } = &*base {
|
||||
*obj_id
|
||||
} else {
|
||||
return Err(format!("Base type should be a class (at {})", loc))
|
||||
}
|
||||
};
|
||||
let subtype_id = {
|
||||
let ty = inferencer.unifier.get_ty(*subtype);
|
||||
if let TypeEnum::TObj { obj_id, .. } = &*ty {
|
||||
*obj_id
|
||||
} else {
|
||||
let base_repr = inferencer.unifier.default_stringify(*base);
|
||||
let subtype_repr = inferencer.unifier.default_stringify(*subtype);
|
||||
return Err(format!("Expected a subtype of {}, but got {} (at {})", base_repr, subtype_repr, loc))
|
||||
}
|
||||
};
|
||||
let subtype_entry = defs[subtype_id.0].read();
|
||||
if let TopLevelDef::Class { ancestors, .. } = &*subtype_entry {
|
||||
let m = ancestors.iter()
|
||||
.find(|kind| matches!(kind, TypeAnnotation::CustomClass { id, .. } if *id == base_id));
|
||||
if m.is_none() {
|
||||
let base_repr = inferencer.unifier.default_stringify(*base);
|
||||
let subtype_repr = inferencer.unifier.default_stringify(*subtype);
|
||||
return Err(format!("Expected a subtype of {}, but got {} (at {})", base_repr, subtype_repr, loc))
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.unifier.unioned(inst_ret, self.primitives_ty.none) && !returned {
|
||||
let def_ast_list = &self.definition_ast_list;
|
||||
let ret_str = self.unifier.stringify(
|
||||
|
|
|
@ -90,7 +90,22 @@ impl TopLevelComposer {
|
|||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
});
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str };
|
||||
let exception = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(7),
|
||||
fields: vec![
|
||||
("__name__".into(), (int32, true)),
|
||||
("__file__".into(), (int32, true)),
|
||||
("__line__".into(), (int32, true)),
|
||||
("__col__".into(), (int32, true)),
|
||||
("__func__".into(), (str, true)),
|
||||
("__message__".into(), (str, true)),
|
||||
("__param0__".into(), (int64, true)),
|
||||
("__param1__".into(), (int64, true)),
|
||||
("__param2__".into(), (int64, true)),
|
||||
].into_iter().collect::<HashMap<_, _>>().into(),
|
||||
params: HashMap::new().into(),
|
||||
});
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
|
||||
crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier);
|
||||
(primitives, unifier)
|
||||
}
|
||||
|
@ -381,6 +396,13 @@ impl TopLevelComposer {
|
|||
Some("int64".to_string())
|
||||
}
|
||||
}
|
||||
SymbolValue::Str(..) => {
|
||||
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.str) {
|
||||
None
|
||||
} else {
|
||||
Some("str".to_string())
|
||||
}
|
||||
}
|
||||
SymbolValue::Tuple(elts) => {
|
||||
if let TypeAnnotation::Tuple(elts_ty) = ty {
|
||||
for (e, t) in elts.iter().zip(elts_ty.iter()) {
|
||||
|
|
|
@ -11,20 +11,21 @@ use super::codegen::CodeGenContext;
|
|||
use super::typecheck::type_inferencer::PrimitiveStore;
|
||||
use super::typecheck::typedef::{FunSignature, FuncArg, SharedUnifier, Type, TypeEnum, Unifier};
|
||||
use crate::{
|
||||
symbol_resolver::SymbolResolver,
|
||||
codegen::CodeGenerator,
|
||||
symbol_resolver::{SymbolResolver, ValueEnum},
|
||||
typecheck::{type_inferencer::CodeLocation, typedef::CallId},
|
||||
};
|
||||
use itertools::{izip, Itertools};
|
||||
use parking_lot::RwLock;
|
||||
use nac3parser::ast::{self, Stmt, StrRef};
|
||||
use inkwell::values::BasicValueEnum;
|
||||
use itertools::{izip, Itertools};
|
||||
use nac3parser::ast::{self, Stmt, StrRef};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
|
||||
pub struct DefinitionId(pub usize);
|
||||
|
||||
pub mod builtins;
|
||||
pub mod composer;
|
||||
pub mod helper;
|
||||
pub mod builtins;
|
||||
pub mod type_annotation;
|
||||
use composer::*;
|
||||
use type_annotation::*;
|
||||
|
@ -34,9 +35,10 @@ mod test;
|
|||
type GenCallCallback = Box<
|
||||
dyn for<'ctx, 'a> Fn(
|
||||
&mut CodeGenContext<'ctx, 'a>,
|
||||
Option<(Type, BasicValueEnum)>,
|
||||
Option<(Type, ValueEnum<'ctx>)>,
|
||||
(&FunSignature, DefinitionId),
|
||||
Vec<(Option<StrRef>, BasicValueEnum<'ctx>)>,
|
||||
Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
&mut dyn CodeGenerator,
|
||||
) -> Option<BasicValueEnum<'ctx>>
|
||||
+ Send
|
||||
+ Sync,
|
||||
|
@ -54,11 +56,12 @@ impl GenCall {
|
|||
pub fn run<'ctx, 'a>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
obj: Option<(Type, BasicValueEnum<'ctx>)>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
args: Vec<(Option<StrRef>, BasicValueEnum<'ctx>)>,
|
||||
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
(self.fp)(ctx, obj, fun, args)
|
||||
(self.fp)(ctx, obj, fun, args, generator)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,7 +123,7 @@ pub enum TopLevelDef {
|
|||
// symbol resolver of the module defined the class
|
||||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||
// custom codegen callback
|
||||
codegen_callback: Option<Arc<GenCall>>
|
||||
codegen_callback: Option<Arc<GenCall>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ impl SymbolResolver for Resolver {
|
|||
fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
||||
fn get_symbol_type(
|
||||
&self,
|
||||
_: &mut Unifier,
|
||||
|
@ -69,6 +69,10 @@ impl SymbolResolver for Resolver {
|
|||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
self.0.id_to_def.lock().get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test_case(
|
||||
|
|
|
@ -63,6 +63,8 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
Ok(TypeAnnotation::Primitive(primitives.none))
|
||||
} else if id == &"str".into() {
|
||||
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) {
|
||||
let type_vars = {
|
||||
let def_read = top_level_defs[obj_id.0].try_read();
|
||||
|
|
|
@ -227,6 +227,20 @@ impl<'a> Inferencer<'a> {
|
|||
self.check_block(body, &mut new_defined_identifiers)?;
|
||||
Ok(false)
|
||||
}
|
||||
StmtKind::Try { body, handlers, orelse, finalbody, .. } => {
|
||||
self.check_block(body, &mut defined_identifiers.clone())?;
|
||||
self.check_block(orelse, &mut defined_identifiers.clone())?;
|
||||
for handler in handlers.iter() {
|
||||
let mut defined_identifiers = defined_identifiers.clone();
|
||||
let ast::ExcepthandlerKind::ExceptHandler { name, body, .. } = &handler.node;
|
||||
if let Some(name) = name {
|
||||
defined_identifiers.insert(*name);
|
||||
}
|
||||
self.check_block(body, &mut defined_identifiers)?;
|
||||
}
|
||||
self.check_block(finalbody, defined_identifiers)?;
|
||||
Ok(false)
|
||||
}
|
||||
StmtKind::Expr { value, .. } => {
|
||||
self.check_expr(value, defined_identifiers)?;
|
||||
Ok(false)
|
||||
|
|
|
@ -37,6 +37,7 @@ pub struct PrimitiveStore {
|
|||
pub none: Type,
|
||||
pub range: Type,
|
||||
pub str: Type,
|
||||
pub exception: Type,
|
||||
}
|
||||
|
||||
pub struct FunctionData {
|
||||
|
@ -51,9 +52,10 @@ pub struct Inferencer<'a> {
|
|||
pub function_data: &'a mut FunctionData,
|
||||
pub unifier: &'a mut Unifier,
|
||||
pub primitives: &'a PrimitiveStore,
|
||||
pub virtual_checks: &'a mut Vec<(Type, Type)>,
|
||||
pub virtual_checks: &'a mut Vec<(Type, Type, Location)>,
|
||||
pub variable_mapping: HashMap<StrRef, Type>,
|
||||
pub calls: &'a mut HashMap<CodeLocation, CallId>,
|
||||
pub in_handler: bool,
|
||||
}
|
||||
|
||||
struct NaiveFolder();
|
||||
|
@ -122,6 +124,56 @@ 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 outer_in_handler = self.in_handler;
|
||||
let mut exception_handlers = Vec::with_capacity(handlers.len());
|
||||
self.in_handler = true;
|
||||
{
|
||||
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 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_
|
||||
)?;
|
||||
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 mut type_ = naive_folder.fold_expr(*type_)?;
|
||||
type_.custom = Some(typ);
|
||||
Some(Box::new(type_))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
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<_>, _>>()?;
|
||||
Located {
|
||||
location: node.location,
|
||||
node: ast::StmtKind::Try { body, handlers, orelse, finalbody, config_comment },
|
||||
custom: None
|
||||
}
|
||||
}
|
||||
ast::StmtKind::For { target, iter, body, orelse, config_comment, type_comment } => {
|
||||
self.infer_pattern(&target)?;
|
||||
let target = self.fold_expr(*target)?;
|
||||
|
@ -228,7 +280,8 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
_ => fold::fold_stmt(self, node)?,
|
||||
};
|
||||
match &stmt.node {
|
||||
ast::StmtKind::For { .. } => {}
|
||||
ast::StmtKind::For { .. } => {},
|
||||
ast::StmtKind::Try { .. } => {},
|
||||
ast::StmtKind::If { test, .. } | ast::StmtKind::While { test, .. } => {
|
||||
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
|
||||
}
|
||||
|
@ -241,6 +294,16 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
ast::StmtKind::Break { .. }
|
||||
| ast::StmtKind::Continue { .. }
|
||||
| ast::StmtKind::Pass { .. } => {}
|
||||
ast::StmtKind::Raise { exc, cause, .. } => {
|
||||
if let Some(cause) = cause {
|
||||
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));
|
||||
} else if !self.in_handler {
|
||||
return report_error("cannot reraise outside exception handlers", stmt.location);
|
||||
}
|
||||
}
|
||||
ast::StmtKind::With { items, .. } => {
|
||||
for item in items.iter() {
|
||||
let ty = item.context_expr.custom.unwrap();
|
||||
|
@ -536,6 +599,8 @@ impl<'a> Inferencer<'a> {
|
|||
top_level: self.top_level,
|
||||
defined_identifiers,
|
||||
variable_mapping,
|
||||
// lambda should not be considered in exception handler
|
||||
in_handler: false,
|
||||
};
|
||||
let fun = FunSignature {
|
||||
args: fn_args
|
||||
|
@ -582,6 +647,8 @@ impl<'a> Inferencer<'a> {
|
|||
primitives: self.primitives,
|
||||
calls: self.calls,
|
||||
defined_identifiers,
|
||||
// listcomp expr should not be considered as inside an exception handler...
|
||||
in_handler: false
|
||||
};
|
||||
let generator = generators.pop().unwrap();
|
||||
if generator.is_async {
|
||||
|
@ -660,7 +727,7 @@ impl<'a> Inferencer<'a> {
|
|||
} else {
|
||||
self.unifier.get_fresh_var().0
|
||||
};
|
||||
self.virtual_checks.push((arg0.custom.unwrap(), ty));
|
||||
self.virtual_checks.push((arg0.custom.unwrap(), ty, func_location));
|
||||
let custom = Some(self.unifier.add_ty(TypeEnum::TVirtual { ty }));
|
||||
return Ok(Located {
|
||||
location,
|
||||
|
|
|
@ -22,7 +22,7 @@ impl SymbolResolver for Resolver {
|
|||
fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
||||
fn get_symbol_type(
|
||||
&self,
|
||||
_: &mut Unifier,
|
||||
|
@ -48,6 +48,10 @@ impl SymbolResolver for Resolver {
|
|||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
self.id_to_def.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct TestEnvironment {
|
||||
|
@ -56,7 +60,7 @@ struct TestEnvironment {
|
|||
pub primitives: PrimitiveStore,
|
||||
pub id_to_name: HashMap<usize, StrRef>,
|
||||
pub identifier_mapping: HashMap<StrRef, Type>,
|
||||
pub virtual_checks: Vec<(Type, Type)>,
|
||||
pub virtual_checks: Vec<(Type, Type, nac3parser::ast::Location)>,
|
||||
pub calls: HashMap<CodeLocation, CallId>,
|
||||
pub top_level: TopLevelContext,
|
||||
}
|
||||
|
@ -111,7 +115,12 @@ impl TestEnvironment {
|
|||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
});
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str };
|
||||
let exception = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(7),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
});
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
|
||||
set_primitives_magic_methods(&primitives, &mut unifier);
|
||||
|
||||
let id_to_name = [
|
||||
|
@ -122,6 +131,7 @@ impl TestEnvironment {
|
|||
(4, "none".into()),
|
||||
(5, "range".into()),
|
||||
(6, "str".into()),
|
||||
(7, "exception".into()),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -206,9 +216,14 @@ impl TestEnvironment {
|
|||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
});
|
||||
let exception = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(7),
|
||||
fields: HashMap::new().into(),
|
||||
params: HashMap::new().into(),
|
||||
});
|
||||
identifier_mapping.insert("None".into(), none);
|
||||
for (i, name) in
|
||||
["int32", "int64", "float", "bool", "none", "range", "str"].iter().enumerate()
|
||||
["int32", "int64", "float", "bool", "none", "range", "str", "Exception"].iter().enumerate()
|
||||
{
|
||||
top_level_defs.push(
|
||||
RwLock::new(TopLevelDef::Class {
|
||||
|
@ -224,20 +239,21 @@ impl TestEnvironment {
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
let defs = 7;
|
||||
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str };
|
||||
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception };
|
||||
|
||||
let (v0, id) = unifier.get_fresh_var();
|
||||
|
||||
let foo_ty = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(7),
|
||||
obj_id: DefinitionId(defs + 1),
|
||||
fields: [("a".into(), (v0, true))].iter().cloned().collect::<HashMap<_, _>>().into(),
|
||||
params: [(id, v0)].iter().cloned().collect::<HashMap<_, _>>().into(),
|
||||
});
|
||||
top_level_defs.push(
|
||||
RwLock::new(TopLevelDef::Class {
|
||||
name: "Foo".into(),
|
||||
object_id: DefinitionId(7),
|
||||
object_id: DefinitionId(defs + 1),
|
||||
type_vars: vec![v0],
|
||||
fields: [("a".into(), v0, true)].into(),
|
||||
methods: Default::default(),
|
||||
|
@ -264,7 +280,7 @@ impl TestEnvironment {
|
|||
FunSignature { args: vec![], ret: int32, vars: Default::default() }.into(),
|
||||
));
|
||||
let bar = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(8),
|
||||
obj_id: DefinitionId(defs + 2),
|
||||
fields: [("a".into(), (int32, true)), ("b".into(), (fun, true))]
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -275,7 +291,7 @@ impl TestEnvironment {
|
|||
top_level_defs.push(
|
||||
RwLock::new(TopLevelDef::Class {
|
||||
name: "Bar".into(),
|
||||
object_id: DefinitionId(8),
|
||||
object_id: DefinitionId(defs + 2),
|
||||
type_vars: Default::default(),
|
||||
fields: [("a".into(), int32, true), ("b".into(), fun, true)].into(),
|
||||
methods: Default::default(),
|
||||
|
@ -293,7 +309,7 @@ impl TestEnvironment {
|
|||
);
|
||||
|
||||
let bar2 = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(9),
|
||||
obj_id: DefinitionId(defs + 3),
|
||||
fields: [("a".into(), (bool, true)), ("b".into(), (fun, false))]
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -304,7 +320,7 @@ impl TestEnvironment {
|
|||
top_level_defs.push(
|
||||
RwLock::new(TopLevelDef::Class {
|
||||
name: "Bar2".into(),
|
||||
object_id: DefinitionId(9),
|
||||
object_id: DefinitionId(defs + 3),
|
||||
type_vars: Default::default(),
|
||||
fields: [("a".into(), bool, true), ("b".into(), fun, false)].into(),
|
||||
methods: Default::default(),
|
||||
|
@ -323,19 +339,21 @@ impl TestEnvironment {
|
|||
let class_names = [("Bar".into(), bar), ("Bar2".into(), bar2)].iter().cloned().collect();
|
||||
|
||||
let id_to_name = [
|
||||
(0, "int32".into()),
|
||||
(1, "int64".into()),
|
||||
(2, "float".into()),
|
||||
(3, "bool".into()),
|
||||
(4, "none".into()),
|
||||
(5, "range".into()),
|
||||
(6, "str".into()),
|
||||
(7, "Foo".into()),
|
||||
(8, "Bar".into()),
|
||||
(9, "Bar2".into()),
|
||||
"int32".into(),
|
||||
"int64".into(),
|
||||
"float".into(),
|
||||
"bool".into(),
|
||||
"none".into(),
|
||||
"range".into(),
|
||||
"str".into(),
|
||||
"exception".into(),
|
||||
"Foo".into(),
|
||||
"Bar".into(),
|
||||
"Bar2".into(),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.enumerate()
|
||||
.map(|(a, b)| (a, *b))
|
||||
.collect();
|
||||
|
||||
let top_level = TopLevelContext {
|
||||
|
@ -347,9 +365,9 @@ impl TestEnvironment {
|
|||
let resolver = Arc::new(Resolver {
|
||||
id_to_type: identifier_mapping.clone(),
|
||||
id_to_def: [
|
||||
("Foo".into(), DefinitionId(7)),
|
||||
("Bar".into(), DefinitionId(8)),
|
||||
("Bar2".into(), DefinitionId(9)),
|
||||
("Foo".into(), DefinitionId(defs + 1)),
|
||||
("Bar".into(), DefinitionId(defs + 2)),
|
||||
("Bar2".into(), DefinitionId(defs + 3)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -383,6 +401,7 @@ impl TestEnvironment {
|
|||
virtual_checks: &mut self.virtual_checks,
|
||||
calls: &mut self.calls,
|
||||
defined_identifiers: Default::default(),
|
||||
in_handler: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +507,7 @@ fn test_basic(source: &str, mapping: HashMap<&str, &str>, virtuals: &[(&str, &st
|
|||
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) {
|
||||
for ((a, b, _), (x, y)) in zip(inferencer.virtual_checks.iter(), virtuals) {
|
||||
let a = inferencer.unifier.stringify(
|
||||
*a,
|
||||
&mut |v| (*id_to_name.get(&v).unwrap()).into(),
|
||||
|
|
|
@ -70,4 +70,8 @@ impl SymbolResolver for Resolver {
|
|||
fn get_identifier_def(&self, id: StrRef) -> Option<DefinitionId> {
|
||||
self.0.id_to_def.lock().get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue