Compare commits
19 Commits
2244f347c3
...
710cfd7bb9
Author | SHA1 | Date |
---|---|---|
David Mak | 710cfd7bb9 | |
David Mak | 0c976aca4d | |
Sebastien Bourdeauducq | 5bf05c6a69 | |
David Mak | 32746c37be | |
David Mak | 1d6291b9ba | |
David Mak | 16655959f2 | |
David Mak | beee3e1f7e | |
David Mak | d4c109b6ef | |
David Mak | 5ffd06dd61 | |
David Mak | 95d0c3c93c | |
David Mak | bd3d67f3d6 | |
David Mak | ddfb532b80 | |
David Mak | 02933753ca | |
David Mak | a1f244834f | |
David Mak | d304afd333 | |
David Mak | ef04696b02 | |
David Mak | 4dc5dbb856 | |
David Mak | fd9f66b8d9 | |
David Mak | 5182453bd9 |
|
@ -502,9 +502,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
@ -641,6 +641,7 @@ name = "nac3artiq"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"inkwell",
|
||||
"itertools 0.12.0",
|
||||
"nac3core",
|
||||
"nac3ld",
|
||||
"nac3parser",
|
||||
|
@ -667,7 +668,7 @@ dependencies = [
|
|||
"indoc",
|
||||
"inkwell",
|
||||
"insta",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.0",
|
||||
"nac3parser",
|
||||
"parking_lot",
|
||||
"rayon",
|
||||
|
|
|
@ -92,8 +92,8 @@
|
|||
(pkgs.fetchFromGitHub {
|
||||
owner = "m-labs";
|
||||
repo = "artiq";
|
||||
rev = "4c189f8c0576111733bb6ff934035c080c8ccc58";
|
||||
sha256 = "sha256-gYGzmfaIoftKFDwn8AybUenYtIpux+tHGMu51WgwA8A=";
|
||||
rev = "8b4572f9cad34ac0c2b6f6bba9382e7b59b2f93b";
|
||||
sha256 = "sha256-O/0sUSxxXU1AL9cmT9qdzCkzdOKREBNftz22/8ouQcc=";
|
||||
})
|
||||
];
|
||||
buildInputs = [
|
||||
|
|
|
@ -9,6 +9,7 @@ name = "nac3artiq"
|
|||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.12"
|
||||
pyo3 = { version = "0.20", features = ["extension-module"] }
|
||||
parking_lot = "0.12"
|
||||
tempfile = "3.8"
|
||||
|
|
|
@ -86,7 +86,13 @@ def ceil64(x):
|
|||
import device_db
|
||||
core_arguments = device_db.device_db["core"]["arguments"]
|
||||
|
||||
compiler = nac3artiq.NAC3(core_arguments["target"])
|
||||
artiq_builtins = {
|
||||
"none": none,
|
||||
"virtual": virtual,
|
||||
"_ConstGenericMarker": _ConstGenericMarker,
|
||||
"Option": Option,
|
||||
}
|
||||
compiler = nac3artiq.NAC3(core_arguments["target"], artiq_builtins)
|
||||
allow_registration = True
|
||||
# Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side.
|
||||
registered_functions = set()
|
||||
|
|
|
@ -91,9 +91,9 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
|||
///
|
||||
/// Direct-`parallel` block context refers to when the generator is generating statements whose
|
||||
/// closest parent `with` statement is a `with parallel` block.
|
||||
fn timeline_reset_start<'ctx, 'b>(
|
||||
fn timeline_reset_start(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'b>
|
||||
ctx: &mut CodeGenContext<'_, '_>
|
||||
) -> Result<(), String> {
|
||||
if let Some(start) = self.start.clone() {
|
||||
let start_val = self.gen_expr(ctx, &start)?
|
||||
|
@ -117,9 +117,9 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
|||
///
|
||||
/// * `store_name` - The LLVM value name for the pointer to `end`. `.addr` will be appended to
|
||||
/// the end of the provided value name.
|
||||
fn timeline_update_end_max<'ctx, 'b>(
|
||||
fn timeline_update_end_max(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'b>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
end: Option<Expr<Option<Type>>>,
|
||||
store_name: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
|
@ -192,9 +192,9 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_call<'ctx, 'a>(
|
||||
fn gen_call<'ctx>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
|
@ -210,9 +210,9 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
fn gen_with<'ctx, 'a>(
|
||||
fn gen_with(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::With { items, body, .. } = &stmt.node {
|
||||
|
@ -360,8 +360,8 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_rpc_tag<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
fn gen_rpc_tag(
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
ty: Type,
|
||||
buffer: &mut Vec<u8>,
|
||||
) -> Result<(), String> {
|
||||
|
@ -406,14 +406,14 @@ fn gen_rpc_tag<'ctx, 'a>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn rpc_codegen_callback_fn<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
fn rpc_codegen_callback_fn<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::default());
|
||||
let ptr_type = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
|
||||
let size_type = generator.get_size_type(ctx.ctx);
|
||||
let int8 = ctx.ctx.i8_type();
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
|
@ -425,7 +425,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
if obj.is_some() {
|
||||
tag.push(b'O');
|
||||
}
|
||||
for arg in fun.0.args.iter() {
|
||||
for arg in &fun.0.args {
|
||||
gen_rpc_tag(ctx, arg.ty, &mut tag)?;
|
||||
}
|
||||
tag.push(b':');
|
||||
|
@ -461,7 +461,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
})
|
||||
.as_pointer_value();
|
||||
|
||||
let arg_length = args.len() + if obj.is_some() { 1 } else { 0 };
|
||||
let arg_length = args.len() + usize::from(obj.is_some());
|
||||
|
||||
let stacksave = ctx.module.get_function("llvm.stacksave").unwrap_or_else(|| {
|
||||
ctx.module.add_function("llvm.stacksave", ptr_type.fn_type(&[], false), None)
|
||||
|
@ -484,11 +484,11 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
// -- rpc args handling
|
||||
let mut keys = fun.0.args.clone();
|
||||
let mut mapping = HashMap::new();
|
||||
for (key, value) in args.into_iter() {
|
||||
for (key, value) in args {
|
||||
mapping.insert(key.unwrap_or_else(|| keys.remove(0).name), value);
|
||||
}
|
||||
// default value handling
|
||||
for k in keys.into_iter() {
|
||||
for k in keys {
|
||||
mapping.insert(
|
||||
k.name,
|
||||
ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into()
|
||||
|
@ -518,7 +518,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
ctx.builder.build_gep(
|
||||
args_ptr,
|
||||
&[int32.const_int(i as u64, false)],
|
||||
&format!("rpc.arg{}", i),
|
||||
&format!("rpc.arg{i}"),
|
||||
)
|
||||
};
|
||||
ctx.builder.build_store(arg_ptr, arg_slot);
|
||||
|
@ -617,11 +617,11 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
Ok(Some(result))
|
||||
}
|
||||
|
||||
pub fn attributes_writeback<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
pub fn attributes_writeback(
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
inner_resolver: &InnerResolver,
|
||||
host_attributes: PyObject,
|
||||
host_attributes: &PyObject,
|
||||
) -> Result<(), String> {
|
||||
Python::with_gil(|py| -> PyResult<Result<(), String>> {
|
||||
let host_attributes: &PyList = host_attributes.downcast(py)?;
|
||||
|
@ -631,7 +631,7 @@ pub fn attributes_writeback<'ctx, 'a>(
|
|||
let zero = int32.const_zero();
|
||||
let mut values = Vec::new();
|
||||
let mut scratch_buffer = Vec::new();
|
||||
for (_, val) in globals.iter() {
|
||||
for val in (*globals).values() {
|
||||
let val = val.as_ref(py);
|
||||
let ty = inner_resolver.get_obj_type(py, val, &mut ctx.unifier, &top_levels, &ctx.primitives)?;
|
||||
if let Err(ty) = ty {
|
||||
|
@ -646,7 +646,7 @@ pub fn attributes_writeback<'ctx, 'a>(
|
|||
// for non-primitive attributes, they should be in another global
|
||||
let mut attributes = Vec::new();
|
||||
let obj = inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap();
|
||||
for (name, (field_ty, is_mutable)) in fields.iter() {
|
||||
for (name, (field_ty, is_mutable)) in fields {
|
||||
if !is_mutable {
|
||||
continue
|
||||
}
|
||||
|
@ -683,7 +683,7 @@ pub fn attributes_writeback<'ctx, 'a>(
|
|||
default_value: None
|
||||
}).collect(),
|
||||
ret: ctx.primitives.none,
|
||||
vars: Default::default()
|
||||
vars: HashMap::default()
|
||||
};
|
||||
let args: Vec<_> = values.into_iter().map(|(_, val)| (None, ValueEnum::Dynamic(val))).collect();
|
||||
if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, DefinitionId(0)), args, generator) {
|
||||
|
|
|
@ -13,12 +13,13 @@ use inkwell::{
|
|||
targets::*,
|
||||
OptimizationLevel,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nac3core::codegen::{CodeGenLLVMOptions, CodeGenTargetMachineOptions, gen_func_impl};
|
||||
use nac3core::toplevel::builtins::get_exn_constructor;
|
||||
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
|
||||
use nac3parser::{
|
||||
ast::{self, ExprKind, Stmt, StmtKind, StrRef},
|
||||
parser::{self, parse_program},
|
||||
ast::{ExprKind, Stmt, StmtKind, StrRef},
|
||||
parser::parse_program,
|
||||
};
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::{exceptions, types::PyBytes, types::PyDict, types::PySet};
|
||||
|
@ -75,7 +76,7 @@ pub struct PrimitivePythonId {
|
|||
list: u64,
|
||||
tuple: u64,
|
||||
typevar: u64,
|
||||
const_generic_dummy: u64,
|
||||
const_generic_marker: u64,
|
||||
none: u64,
|
||||
exception: u64,
|
||||
generic_alias: (u64, u64),
|
||||
|
@ -109,7 +110,7 @@ create_exception!(nac3artiq, CompileError, exceptions::PyException);
|
|||
impl Nac3 {
|
||||
fn register_module(
|
||||
&mut self,
|
||||
module: PyObject,
|
||||
module: &PyObject,
|
||||
registered_class_ids: &HashSet<u64>,
|
||||
) -> PyResult<()> {
|
||||
let (module_name, source_file) = Python::with_gil(|py| -> PyResult<(String, String)> {
|
||||
|
@ -118,18 +119,18 @@ impl Nac3 {
|
|||
})?;
|
||||
|
||||
let source = fs::read_to_string(&source_file).map_err(|e| {
|
||||
exceptions::PyIOError::new_err(format!("failed to read input file: {}", e))
|
||||
exceptions::PyIOError::new_err(format!("failed to read input file: {e}"))
|
||||
})?;
|
||||
let parser_result = parser::parse_program(&source, source_file.into())
|
||||
.map_err(|e| exceptions::PySyntaxError::new_err(format!("parse error: {}", e)))?;
|
||||
let parser_result = parse_program(&source, source_file.into())
|
||||
.map_err(|e| exceptions::PySyntaxError::new_err(format!("parse error: {e}")))?;
|
||||
|
||||
for mut stmt in parser_result.into_iter() {
|
||||
for mut stmt in parser_result {
|
||||
let include = match stmt.node {
|
||||
ast::StmtKind::ClassDef {
|
||||
StmtKind::ClassDef {
|
||||
ref decorator_list, ref mut body, ref mut bases, ..
|
||||
} => {
|
||||
let nac3_class = decorator_list.iter().any(|decorator| {
|
||||
if let ast::ExprKind::Name { id, .. } = decorator.node {
|
||||
if let ExprKind::Name { id, .. } = decorator.node {
|
||||
id.to_string() == "nac3"
|
||||
} else {
|
||||
false
|
||||
|
@ -143,7 +144,7 @@ impl Nac3 {
|
|||
Python::with_gil(|py| -> PyResult<bool> {
|
||||
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
|
||||
match &base.node {
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
ExprKind::Name { id, .. } => {
|
||||
if *id == "Exception".into() {
|
||||
Ok(true)
|
||||
} else {
|
||||
|
@ -158,9 +159,9 @@ impl Nac3 {
|
|||
.unwrap()
|
||||
});
|
||||
body.retain(|stmt| {
|
||||
if let ast::StmtKind::FunctionDef { ref decorator_list, .. } = stmt.node {
|
||||
if let StmtKind::FunctionDef { ref decorator_list, .. } = stmt.node {
|
||||
decorator_list.iter().any(|decorator| {
|
||||
if let ast::ExprKind::Name { id, .. } = decorator.node {
|
||||
if let ExprKind::Name { id, .. } = decorator.node {
|
||||
id.to_string() == "kernel"
|
||||
|| id.to_string() == "portable"
|
||||
|| id.to_string() == "rpc"
|
||||
|
@ -174,9 +175,9 @@ impl Nac3 {
|
|||
});
|
||||
true
|
||||
}
|
||||
ast::StmtKind::FunctionDef { ref decorator_list, .. } => {
|
||||
StmtKind::FunctionDef { ref decorator_list, .. } => {
|
||||
decorator_list.iter().any(|decorator| {
|
||||
if let ast::ExprKind::Name { id, .. } = decorator.node {
|
||||
if let ExprKind::Name { id, .. } = decorator.node {
|
||||
let id = id.to_string();
|
||||
id == "extern" || id == "portable" || id == "kernel" || id == "rpc"
|
||||
} else {
|
||||
|
@ -197,7 +198,7 @@ impl Nac3 {
|
|||
fn report_modinit(
|
||||
arg_names: &[String],
|
||||
method_name: &str,
|
||||
resolver: Arc<dyn SymbolResolver + Send + Sync>,
|
||||
resolver: &Arc<dyn SymbolResolver + Send + Sync>,
|
||||
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
|
||||
unifier: &mut Unifier,
|
||||
primitives: &PrimitiveStore,
|
||||
|
@ -205,7 +206,7 @@ impl Nac3 {
|
|||
let base_ty =
|
||||
match resolver.get_symbol_type(unifier, top_level_defs, primitives, "base".into()) {
|
||||
Ok(ty) => ty,
|
||||
Err(e) => return Some(format!("type error inside object launching kernel: {}", e)),
|
||||
Err(e) => return Some(format!("type error inside object launching kernel: {e}")),
|
||||
};
|
||||
|
||||
let fun_ty = if method_name.is_empty() {
|
||||
|
@ -215,8 +216,7 @@ impl Nac3 {
|
|||
Some(t) => t.0,
|
||||
None => {
|
||||
return Some(format!(
|
||||
"object launching kernel does not have method `{}`",
|
||||
method_name
|
||||
"object launching kernel does not have method `{method_name}`"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -237,8 +237,7 @@ impl Nac3 {
|
|||
Some(n) => n,
|
||||
None if default_value.is_none() => {
|
||||
return Some(format!(
|
||||
"argument `{}` not provided when launching kernel function",
|
||||
name
|
||||
"argument `{name}` not provided when launching kernel function"
|
||||
))
|
||||
}
|
||||
_ => break,
|
||||
|
@ -252,16 +251,14 @@ impl Nac3 {
|
|||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Some(format!(
|
||||
"type error ({}) at parameter #{} when calling kernel function",
|
||||
e, i
|
||||
"type error ({e}) at parameter #{i} when calling kernel function"
|
||||
))
|
||||
}
|
||||
};
|
||||
if let Err(e) = unifier.unify(in_ty, *ty) {
|
||||
return Some(format!(
|
||||
"type error ({}) at parameter #{} when calling kernel function",
|
||||
e.to_display(unifier).to_string(),
|
||||
i
|
||||
"type error ({}) at parameter #{i} when calling kernel function",
|
||||
e.to_display(unifier),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +320,7 @@ impl Nac3 {
|
|||
let mut module_to_resolver_cache: HashMap<u64, _> = HashMap::new();
|
||||
|
||||
let mut rpc_ids = vec![];
|
||||
for (stmt, path, module) in self.top_levels.iter() {
|
||||
for (stmt, path, module) in &self.top_levels {
|
||||
let py_module: &PyAny = module.extract(py)?;
|
||||
let module_id: u64 = id_fn.call1((py_module,))?.extract()?;
|
||||
let helper = helper.clone();
|
||||
|
@ -344,7 +341,7 @@ impl Nac3 {
|
|||
let mut name_to_pyid: HashMap<StrRef, u64> = HashMap::new();
|
||||
let members: &PyDict =
|
||||
py_module.getattr("__dict__").unwrap().downcast().unwrap();
|
||||
for (key, val) in members.iter() {
|
||||
for (key, val) in members {
|
||||
let key: &str = key.extract().unwrap();
|
||||
let val = id_fn.call1((val,)).unwrap().extract().unwrap();
|
||||
name_to_pyid.insert(key.into(), val);
|
||||
|
@ -356,12 +353,12 @@ impl Nac3 {
|
|||
pyid_to_type: pyid_to_type.clone(),
|
||||
primitive_ids: self.primitive_ids.clone(),
|
||||
global_value_ids: global_value_ids.clone(),
|
||||
class_names: Default::default(),
|
||||
class_names: Mutex::default(),
|
||||
name_to_pyid: name_to_pyid.clone(),
|
||||
module: module.clone(),
|
||||
id_to_pyval: Default::default(),
|
||||
id_to_primitive: Default::default(),
|
||||
field_to_val: Default::default(),
|
||||
id_to_pyval: RwLock::default(),
|
||||
id_to_primitive: RwLock::default(),
|
||||
field_to_val: RwLock::default(),
|
||||
helper,
|
||||
string_store: self.string_store.clone(),
|
||||
exception_ids: self.exception_ids.clone(),
|
||||
|
@ -375,11 +372,10 @@ impl Nac3 {
|
|||
});
|
||||
|
||||
let (name, def_id, ty) = composer
|
||||
.register_top_level(stmt.clone(), Some(resolver.clone()), path.clone(), false)
|
||||
.register_top_level(stmt.clone(), Some(resolver.clone()), path, false)
|
||||
.map_err(|e| {
|
||||
CompileError::new_err(format!(
|
||||
"compilation failed\n----------\n{}",
|
||||
e
|
||||
"compilation failed\n----------\n{e}"
|
||||
))
|
||||
})?;
|
||||
if let Some(class_obj) = class_obj {
|
||||
|
@ -396,7 +392,7 @@ impl Nac3 {
|
|||
StmtKind::ClassDef { name, body, .. } => {
|
||||
let class_name = name.to_string();
|
||||
let class_obj = module.getattr(py, class_name.as_str()).unwrap();
|
||||
for stmt in body.iter() {
|
||||
for stmt in body {
|
||||
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())) {
|
||||
if name == &"__init__".into() {
|
||||
|
@ -430,7 +426,7 @@ impl Nac3 {
|
|||
name_to_pyid.insert("base".into(), id_fun.call1((obj,))?.extract()?);
|
||||
let mut arg_names = vec![];
|
||||
for (i, arg) in args.into_iter().enumerate() {
|
||||
let name = format!("tmp{}", i);
|
||||
let name = format!("tmp{i}");
|
||||
module.add(&name, arg)?;
|
||||
name_to_pyid.insert(name.clone().into(), id_fun.call1((arg,))?.extract()?);
|
||||
arg_names.push(name);
|
||||
|
@ -449,10 +445,10 @@ impl Nac3 {
|
|||
pyid_to_type: pyid_to_type.clone(),
|
||||
primitive_ids: self.primitive_ids.clone(),
|
||||
global_value_ids: global_value_ids.clone(),
|
||||
class_names: Default::default(),
|
||||
id_to_pyval: Default::default(),
|
||||
id_to_primitive: Default::default(),
|
||||
field_to_val: Default::default(),
|
||||
class_names: Mutex::default(),
|
||||
id_to_pyval: RwLock::default(),
|
||||
id_to_primitive: RwLock::default(),
|
||||
field_to_val: RwLock::default(),
|
||||
name_to_pyid,
|
||||
module: module.to_object(py),
|
||||
helper,
|
||||
|
@ -462,7 +458,7 @@ impl Nac3 {
|
|||
});
|
||||
let resolver = Arc::new(Resolver(inner_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||
let (_, def_id, _) = composer
|
||||
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into(), false)
|
||||
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "", false)
|
||||
.unwrap();
|
||||
|
||||
let fun_signature =
|
||||
|
@ -475,24 +471,26 @@ impl Nac3 {
|
|||
|
||||
if let Err(e) = composer.start_analysis(true) {
|
||||
// report error of __modinit__ separately
|
||||
if !e.contains("<nac3_synthesized_modinit>") {
|
||||
return Err(CompileError::new_err(format!(
|
||||
"compilation failed\n----------\n{}",
|
||||
e
|
||||
)));
|
||||
} else {
|
||||
return if e.iter().any(|err| err.contains("<nac3_synthesized_modinit>")) {
|
||||
let msg = Self::report_modinit(
|
||||
&arg_names,
|
||||
method_name,
|
||||
resolver.clone(),
|
||||
&resolver,
|
||||
&composer.extract_def_list(),
|
||||
&mut composer.unifier,
|
||||
&self.primitive,
|
||||
);
|
||||
return Err(CompileError::new_err(format!(
|
||||
Err(CompileError::new_err(format!(
|
||||
"compilation failed\n----------\n{}",
|
||||
msg.unwrap_or(e)
|
||||
)));
|
||||
msg.unwrap_or(e.iter().sorted().join("\n----------\n"))
|
||||
)))
|
||||
} else {
|
||||
Err(CompileError::new_err(
|
||||
format!(
|
||||
"compilation failed\n----------\n{}",
|
||||
e.iter().sorted().join("\n----------\n"),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
let top_level = Arc::new(composer.make_top_level_context());
|
||||
|
@ -500,7 +498,7 @@ impl Nac3 {
|
|||
{
|
||||
let rpc_codegen = rpc_codegen_callback();
|
||||
let defs = top_level.definitions.read();
|
||||
for (class_data, id) in rpc_ids.iter() {
|
||||
for (class_data, id) in &rpc_ids {
|
||||
let mut def = defs[id.0].write();
|
||||
match &mut *def {
|
||||
TopLevelDef::Function { codegen_callback, .. } => {
|
||||
|
@ -508,7 +506,7 @@ impl Nac3 {
|
|||
}
|
||||
TopLevelDef::Class { methods, .. } => {
|
||||
let (class_def, method_name) = class_data.as_ref().unwrap();
|
||||
for (name, _, id) in methods.iter() {
|
||||
for (name, _, id) in &*methods {
|
||||
if name != method_name {
|
||||
continue;
|
||||
}
|
||||
|
@ -538,7 +536,7 @@ impl Nac3 {
|
|||
if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } =
|
||||
&mut *definition
|
||||
{
|
||||
instance_to_symbol.insert("".to_string(), "__modinit__".into());
|
||||
instance_to_symbol.insert(String::new(), "__modinit__".into());
|
||||
instance_to_stmt[""].clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
|
@ -546,7 +544,7 @@ impl Nac3 {
|
|||
};
|
||||
|
||||
let task = CodeGenTask {
|
||||
subst: Default::default(),
|
||||
subst: Vec::default(),
|
||||
symbol_name: "__modinit__".to_string(),
|
||||
body: instance.body,
|
||||
signature,
|
||||
|
@ -563,18 +561,18 @@ impl Nac3 {
|
|||
store.from_signature(&mut composer.unifier, &self.primitive, &fun_signature, &mut cache);
|
||||
let signature = store.add_cty(signature);
|
||||
let attributes_writeback_task = CodeGenTask {
|
||||
subst: Default::default(),
|
||||
subst: Vec::default(),
|
||||
symbol_name: "attributes_writeback".to_string(),
|
||||
body: Arc::new(Default::default()),
|
||||
body: Arc::new(Vec::default()),
|
||||
signature,
|
||||
resolver,
|
||||
store,
|
||||
unifier_index: instance.unifier_id,
|
||||
calls: Arc::new(Default::default()),
|
||||
calls: Arc::new(HashMap::default()),
|
||||
id: 0,
|
||||
};
|
||||
|
||||
let membuffers: Arc<Mutex<Vec<Vec<u8>>>> = Default::default();
|
||||
let membuffers: Arc<Mutex<Vec<Vec<u8>>>> = Arc::default();
|
||||
|
||||
let membuffer = membuffers.clone();
|
||||
|
||||
|
@ -597,7 +595,7 @@ impl Nac3 {
|
|||
threads,
|
||||
top_level.clone(),
|
||||
&self.llvm_options,
|
||||
f
|
||||
&f
|
||||
);
|
||||
registry.add_task(task);
|
||||
registry.wait_tasks_complete(handles);
|
||||
|
@ -608,7 +606,7 @@ impl Nac3 {
|
|||
let builder = context.create_builder();
|
||||
let (_, module, _) = gen_func_impl(&context, &mut generator, ®istry, builder, module,
|
||||
attributes_writeback_task, |generator, ctx| {
|
||||
attributes_writeback(ctx, generator, inner_resolver.as_ref(), host_attributes)
|
||||
attributes_writeback(ctx, generator, inner_resolver.as_ref(), &host_attributes)
|
||||
}).unwrap();
|
||||
let buffer = module.write_bitcode_to_memory();
|
||||
let buffer = buffer.as_slice().into();
|
||||
|
@ -639,7 +637,7 @@ impl Nac3 {
|
|||
let mut function_iter = main.get_first_function();
|
||||
while let Some(func) = function_iter {
|
||||
if func.count_basic_blocks() > 0 && func.get_name().to_str().unwrap() != "__modinit__" {
|
||||
func.set_linkage(inkwell::module::Linkage::Private);
|
||||
func.set_linkage(Linkage::Private);
|
||||
}
|
||||
function_iter = func.get_next_function();
|
||||
}
|
||||
|
@ -672,7 +670,7 @@ impl Nac3 {
|
|||
link_fn(&main)
|
||||
}
|
||||
|
||||
/// Returns the [TargetTriple] used for compiling to [isa].
|
||||
/// Returns the [`TargetTriple`] used for compiling to [isa].
|
||||
fn get_llvm_target_triple(isa: Isa) -> TargetTriple {
|
||||
match isa {
|
||||
Isa::Host => TargetMachine::get_default_triple(),
|
||||
|
@ -681,7 +679,7 @@ impl Nac3 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the [String] representing the target CPU used for compiling to [isa].
|
||||
/// Returns the [`String`] representing the target CPU used for compiling to [isa].
|
||||
fn get_llvm_target_cpu(isa: Isa) -> String {
|
||||
match isa {
|
||||
Isa::Host => TargetMachine::get_host_cpu_name().to_string(),
|
||||
|
@ -690,7 +688,7 @@ impl Nac3 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the [String] representing the target features used for compiling to [isa].
|
||||
/// Returns the [`String`] representing the target features used for compiling to [isa].
|
||||
fn get_llvm_target_features(isa: Isa) -> String {
|
||||
match isa {
|
||||
Isa::Host => TargetMachine::get_host_cpu_features().to_string(),
|
||||
|
@ -700,7 +698,7 @@ impl Nac3 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns an instance of [CodeGenTargetMachineOptions] representing the target machine
|
||||
/// Returns an instance of [`CodeGenTargetMachineOptions`] representing the target machine
|
||||
/// options used for compiling to [isa].
|
||||
fn get_llvm_target_options(isa: Isa) -> CodeGenTargetMachineOptions {
|
||||
CodeGenTargetMachineOptions {
|
||||
|
@ -712,7 +710,7 @@ impl Nac3 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns an instance of [TargetMachine] used in compiling and linking of a program to the
|
||||
/// Returns an instance of [`TargetMachine`] used in compiling and linking of a program to the
|
||||
/// target [isa].
|
||||
fn get_llvm_target_machine(&self) -> TargetMachine {
|
||||
Nac3::get_llvm_target_options(self.isa)
|
||||
|
@ -782,7 +780,7 @@ fn add_exceptions(
|
|||
#[pymethods]
|
||||
impl Nac3 {
|
||||
#[new]
|
||||
fn new(isa: &str, py: Python) -> PyResult<Self> {
|
||||
fn new(isa: &str, artiq_builtins: &PyDict, py: Python) -> PyResult<Self> {
|
||||
let isa = match isa {
|
||||
"host" => Isa::Host,
|
||||
"rv32g" => Isa::RiscV32G,
|
||||
|
@ -791,10 +789,9 @@ impl Nac3 {
|
|||
_ => return Err(exceptions::PyValueError::new_err("invalid ISA")),
|
||||
};
|
||||
let time_fns: &(dyn TimeFns + Sync) = match isa {
|
||||
Isa::Host => &timeline::EXTERN_TIME_FNS,
|
||||
Isa::RiscV32G => &timeline::NOW_PINNING_TIME_FNS_64,
|
||||
Isa::RiscV32IMA => &timeline::NOW_PINNING_TIME_FNS,
|
||||
Isa::CortexA9 => &timeline::EXTERN_TIME_FNS,
|
||||
Isa::CortexA9 | Isa::Host => &timeline::EXTERN_TIME_FNS,
|
||||
};
|
||||
let primitive: PrimitiveStore = TopLevelComposer::make_primitives().0;
|
||||
let builtins = vec![
|
||||
|
@ -849,44 +846,18 @@ impl Nac3 {
|
|||
let typing_mod = PyModule::import(py, "typing").unwrap();
|
||||
let types_mod = PyModule::import(py, "types").unwrap();
|
||||
|
||||
let get_id = |x| id_fn.call1((x,)).unwrap().extract().unwrap();
|
||||
let get_id = |x: &PyAny| id_fn.call1((x,)).and_then(PyAny::extract).unwrap();
|
||||
let get_attr_id = |obj: &PyModule, attr| id_fn.call1((obj.getattr(attr).unwrap(),))
|
||||
.unwrap().extract().unwrap();
|
||||
let primitive_ids = PrimitivePythonId {
|
||||
virtual_id: get_id(
|
||||
builtins_mod
|
||||
.getattr("globals")
|
||||
.unwrap()
|
||||
.call0()
|
||||
.unwrap()
|
||||
.get_item("virtual")
|
||||
.unwrap(
|
||||
)),
|
||||
virtual_id: get_id(artiq_builtins.get_item("virtual").ok().flatten().unwrap()),
|
||||
generic_alias: (
|
||||
get_attr_id(typing_mod, "_GenericAlias"),
|
||||
get_attr_id(types_mod, "GenericAlias"),
|
||||
),
|
||||
none: id_fn
|
||||
.call1((builtins_mod
|
||||
.getattr("globals")
|
||||
.unwrap()
|
||||
.call0()
|
||||
.unwrap()
|
||||
.get_item("none")
|
||||
.unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
none: get_id(artiq_builtins.get_item("none").ok().flatten().unwrap()),
|
||||
typevar: get_attr_id(typing_mod, "TypeVar"),
|
||||
const_generic_dummy: id_fn
|
||||
.call1((
|
||||
builtins_mod.getattr("globals")
|
||||
.and_then(|v| v.call0())
|
||||
.and_then(|v| v.get_item("_ConstGenericMarker"))
|
||||
.unwrap(),
|
||||
))
|
||||
.and_then(|v| v.extract())
|
||||
.unwrap(),
|
||||
const_generic_marker: get_id(artiq_builtins.get_item("_ConstGenericMarker").ok().flatten().unwrap()),
|
||||
int: get_attr_id(builtins_mod, "int"),
|
||||
int32: get_attr_id(numpy_mod, "int32"),
|
||||
int64: get_attr_id(numpy_mod, "int64"),
|
||||
|
@ -898,17 +869,7 @@ impl Nac3 {
|
|||
list: get_attr_id(builtins_mod, "list"),
|
||||
tuple: get_attr_id(builtins_mod, "tuple"),
|
||||
exception: get_attr_id(builtins_mod, "Exception"),
|
||||
option: id_fn
|
||||
.call1((builtins_mod
|
||||
.getattr("globals")
|
||||
.unwrap()
|
||||
.call0()
|
||||
.unwrap()
|
||||
.get_item("Option")
|
||||
.unwrap(),))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
option: get_id(artiq_builtins.get_item("Option").ok().flatten().unwrap()),
|
||||
};
|
||||
|
||||
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
|
||||
|
@ -920,11 +881,11 @@ impl Nac3 {
|
|||
primitive,
|
||||
builtins,
|
||||
primitive_ids,
|
||||
top_levels: Default::default(),
|
||||
pyid_to_def: Default::default(),
|
||||
top_levels: Vec::default(),
|
||||
pyid_to_def: Arc::default(),
|
||||
working_directory,
|
||||
string_store: Default::default(),
|
||||
exception_ids: Default::default(),
|
||||
string_store: Arc::default(),
|
||||
exception_ids: Arc::default(),
|
||||
deferred_eval_store: DeferredEvaluationStore::new(),
|
||||
llvm_options: CodeGenLLVMOptions {
|
||||
opt_level: OptimizationLevel::Default,
|
||||
|
@ -942,11 +903,11 @@ impl Nac3 {
|
|||
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
|
||||
let getmodule_fn = PyModule::import(py, "inspect")?.getattr("getmodule")?;
|
||||
|
||||
for function in functions.iter() {
|
||||
for function in functions {
|
||||
let module = getmodule_fn.call1((function,))?.extract()?;
|
||||
modules.insert(id_fn.call1((&module,))?.extract()?, module);
|
||||
}
|
||||
for class in classes.iter() {
|
||||
for class in classes {
|
||||
let module = getmodule_fn.call1((class,))?.extract()?;
|
||||
modules.insert(id_fn.call1((&module,))?.extract()?, module);
|
||||
class_ids.insert(id_fn.call1((class,))?.extract()?);
|
||||
|
@ -955,7 +916,7 @@ impl Nac3 {
|
|||
})?;
|
||||
|
||||
for module in modules.into_values() {
|
||||
self.register_module(module, &class_ids)?;
|
||||
self.register_module(&module, &class_ids)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1020,7 +981,7 @@ impl Nac3 {
|
|||
let link_fn = |module: &Module| {
|
||||
let working_directory = self.working_directory.path().to_owned();
|
||||
target_machine
|
||||
.write_to_file(&module, FileType::Object, &working_directory.join("module.o"))
|
||||
.write_to_file(module, FileType::Object, &working_directory.join("module.o"))
|
||||
.expect("couldn't write module to file");
|
||||
|
||||
let filename_path = self.working_directory.path().join("module.elf");
|
||||
|
@ -1037,7 +998,7 @@ impl Nac3 {
|
|||
} else {
|
||||
let link_fn = |module: &Module| {
|
||||
let object_mem = target_machine
|
||||
.write_to_memory_buffer(&module, FileType::Object)
|
||||
.write_to_memory_buffer(module, FileType::Object)
|
||||
.expect("couldn't write module to object file buffer");
|
||||
if let Ok(dyn_lib) = Linker::ld(object_mem.as_slice()) {
|
||||
Ok(PyBytes::new(py, &dyn_lib).into())
|
||||
|
|
|
@ -15,7 +15,7 @@ use pyo3::{
|
|||
PyAny, PyObject, PyResult, Python,
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering::Relaxed}
|
||||
|
@ -93,32 +93,32 @@ impl StaticValue for PythonValue {
|
|||
self.id
|
||||
}
|
||||
|
||||
fn get_const_obj<'ctx, 'a>(
|
||||
fn get_const_obj<'ctx>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
_: &mut dyn CodeGenerator,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
ctx.module
|
||||
.get_global(format!("{}_const", self.id).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,
|
||||
));
|
||||
Ok(global.as_pointer_value().into())
|
||||
})
|
||||
.unwrap()
|
||||
})
|
||||
.map_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,
|
||||
));
|
||||
Ok(global.as_pointer_value().into())
|
||||
})
|
||||
.unwrap(),
|
||||
|val| val.as_pointer_value().into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_basic_value_enum<'ctx, 'a>(
|
||||
|
@ -132,7 +132,7 @@ impl StaticValue for PythonValue {
|
|||
PrimitiveValue::I32(val) => ctx.ctx.i32_type().const_int(*val as u64, false).into(),
|
||||
PrimitiveValue::I64(val) => ctx.ctx.i64_type().const_int(*val as u64, false).into(),
|
||||
PrimitiveValue::U32(val) => ctx.ctx.i32_type().const_int(*val as u64, false).into(),
|
||||
PrimitiveValue::U64(val) => ctx.ctx.i64_type().const_int(*val as u64, false).into(),
|
||||
PrimitiveValue::U64(val) => ctx.ctx.i64_type().const_int(*val, false).into(),
|
||||
PrimitiveValue::F64(val) => ctx.ctx.f64_type().const_float(*val).into(),
|
||||
PrimitiveValue::Bool(val) => ctx.ctx.i8_type().const_int(*val as u64, false).into(),
|
||||
});
|
||||
|
@ -148,10 +148,10 @@ impl StaticValue for PythonValue {
|
|||
}).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
fn get_field<'ctx, 'a>(
|
||||
fn get_field<'ctx>(
|
||||
&self,
|
||||
name: StrRef,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> Option<ValueEnum<'ctx>> {
|
||||
{
|
||||
let field_to_val = self.resolver.field_to_val.read();
|
||||
|
@ -166,17 +166,17 @@ impl StaticValue for PythonValue {
|
|||
if ty_id == self.resolver.primitive_ids.option && name == "_nac3_option".into() {
|
||||
let obj = self.value.getattr(py, name.to_string().as_str())?;
|
||||
let id = self.resolver.helper.id_fn.call1(py, (&obj,))?.extract(py)?;
|
||||
if self.id == self.resolver.primitive_ids.none {
|
||||
return Ok(None)
|
||||
return if self.id == self.resolver.primitive_ids.none {
|
||||
Ok(None)
|
||||
} else {
|
||||
return Ok(Some((id, obj)))
|
||||
Ok(Some((id, obj)))
|
||||
}
|
||||
}
|
||||
let def_id = { *self.resolver.pyid_to_def.read().get(&ty_id).unwrap() };
|
||||
let mut mutable = true;
|
||||
let defs = ctx.top_level.definitions.read();
|
||||
if let TopLevelDef::Class { fields, .. } = &*defs[def_id.0].read() {
|
||||
for (field_name, _, is_mutable) in fields.iter() {
|
||||
for (field_name, _, is_mutable) in fields {
|
||||
if field_name == &name {
|
||||
mutable = *is_mutable;
|
||||
break;
|
||||
|
@ -240,7 +240,7 @@ impl InnerResolver {
|
|||
) -> PyResult<Result<Type, String>> {
|
||||
let mut ty = match self.get_obj_type(py, list.get_item(0)?, unifier, defs, primitives)? {
|
||||
Ok(t) => t,
|
||||
Err(e) => return Ok(Err(format!("type error ({}) at element #0 of the list", e))),
|
||||
Err(e) => return Ok(Err(format!("type error ({e}) at element #0 of the list"))),
|
||||
};
|
||||
for i in 1..len {
|
||||
let b = match list
|
||||
|
@ -249,16 +249,15 @@ impl InnerResolver {
|
|||
{
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Ok(Err(format!("type error ({}) at element #{} of the list", e, i)))
|
||||
return Ok(Err(format!("type error ({e}) at element #{i} of the list")))
|
||||
}
|
||||
};
|
||||
ty = match unifier.unify(ty, b) {
|
||||
Ok(_) => ty,
|
||||
Ok(()) => ty,
|
||||
Err(e) => {
|
||||
return Ok(Err(format!(
|
||||
"inhomogeneous type ({}) at element #{} of the list",
|
||||
e.to_display(unifier).to_string(),
|
||||
i
|
||||
"inhomogeneous type ({}) at element #{i} of the list",
|
||||
e.to_display(unifier)
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
@ -266,12 +265,12 @@ impl InnerResolver {
|
|||
Ok(Ok(ty))
|
||||
}
|
||||
|
||||
/// handle python objects that represent types themselves
|
||||
/// Handles python objects that represent types themselves,
|
||||
///
|
||||
/// primitives and class types should be themselves, use `ty_id` to check,
|
||||
/// TypeVars and GenericAlias(`A[int, bool]`) should use `ty_ty_id` to check
|
||||
/// Primitives and class types should be themselves, use `ty_id` to check;
|
||||
/// `TypeVars` and `GenericAlias`(`A[int, bool]`) should use `ty_ty_id` to check.
|
||||
///
|
||||
/// the `bool` value returned indicates whether they are instantiated or not
|
||||
/// The `bool` value returned indicates whether they are instantiated or not
|
||||
fn get_pyty_obj_type(
|
||||
&self,
|
||||
py: Python,
|
||||
|
@ -294,9 +293,7 @@ impl InnerResolver {
|
|||
Ok(Ok((primitives.uint64, true)))
|
||||
} else if ty_id == self.primitive_ids.bool {
|
||||
Ok(Ok((primitives.bool, true)))
|
||||
} else if ty_id == self.primitive_ids.float {
|
||||
Ok(Ok((primitives.float, true)))
|
||||
} else if ty_id == self.primitive_ids.float64 {
|
||||
} else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 {
|
||||
Ok(Ok((primitives.float, true)))
|
||||
} else if ty_id == self.primitive_ids.exception {
|
||||
Ok(Ok((primitives.exception, true)))
|
||||
|
@ -312,7 +309,7 @@ impl InnerResolver {
|
|||
Ok(Ok((primitives.option, false)))
|
||||
} else if ty_id == self.primitive_ids.none {
|
||||
unreachable!("none cannot be typeid")
|
||||
} else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).cloned() {
|
||||
} else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).copied() {
|
||||
let def = defs[def_id.0].read();
|
||||
if let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def {
|
||||
// do not handle type var param and concrete check here, and no subst
|
||||
|
@ -356,7 +353,7 @@ impl InnerResolver {
|
|||
for i in 0usize.. {
|
||||
if let Ok(constr) = constraints.get_item(i) {
|
||||
let constr_id: u64 = self.helper.id_fn.call1(py, (constr,))?.extract(py)?;
|
||||
if constr_id == self.primitive_ids.const_generic_dummy {
|
||||
if constr_id == self.primitive_ids.const_generic_marker {
|
||||
is_const_generic = true;
|
||||
continue
|
||||
}
|
||||
|
@ -379,7 +376,7 @@ impl InnerResolver {
|
|||
}
|
||||
Err(err) => return Ok(Err(err)),
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
@ -391,7 +388,7 @@ impl InnerResolver {
|
|||
.push((result.clone(),
|
||||
constraints.extract()?,
|
||||
pyty.getattr("__name__")?.extract::<String>()?
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
(result, is_const_generic)
|
||||
|
@ -517,11 +514,10 @@ impl InnerResolver {
|
|||
Ok(ty) => ty,
|
||||
Err(err) => return Ok(Err(err)),
|
||||
};
|
||||
if !unifier.is_concrete(ty.0, &[]) && !ty.1 {
|
||||
panic!(
|
||||
"virtual class should take concrete parameters in type var ranges"
|
||||
)
|
||||
}
|
||||
assert!(
|
||||
unifier.is_concrete(ty.0, &[]) || ty.1,
|
||||
"virtual class should take concrete parameters in type var ranges"
|
||||
);
|
||||
Ok(Ok((unifier.add_ty(TypeEnum::TVirtual { ty: ty.0 }), true)))
|
||||
} else {
|
||||
return Ok(Err(format!(
|
||||
|
@ -545,8 +541,7 @@ impl InnerResolver {
|
|||
pyo3::types::PyModule::import(py, "builtins").unwrap().getattr("repr").unwrap();
|
||||
let str_repr: String = str_fn.call1((pyty,)).unwrap().extract().unwrap();
|
||||
Ok(Err(format!(
|
||||
"{} is not registered with NAC3 (@nac3 decorator missing?)",
|
||||
str_repr
|
||||
"{str_repr} is not registered with NAC3 (@nac3 decorator missing?)"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
@ -577,7 +572,7 @@ impl InnerResolver {
|
|||
object_id, methods, constructor, ..
|
||||
} = &*def.read() {
|
||||
if object_id == def_id && constructor.is_some() && methods.iter().any(|(s, _, _)| s == &"__init__".into()) {
|
||||
return constructor.clone();
|
||||
return *constructor;
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -627,10 +622,10 @@ impl InnerResolver {
|
|||
self.get_list_elem_type(py, obj, len, unifier, defs, primitives)?;
|
||||
match actual_ty {
|
||||
Ok(t) => match unifier.unify(*ty, t) {
|
||||
Ok(_) => Ok(Ok(unifier.add_ty(TypeEnum::TList { ty: *ty }))),
|
||||
Ok(()) => Ok(Ok(unifier.add_ty(TypeEnum::TList { ty: *ty }))),
|
||||
Err(e) => Ok(Err(format!(
|
||||
"type error ({}) for the list",
|
||||
e.to_display(unifier).to_string()
|
||||
e.to_display(unifier)
|
||||
))),
|
||||
},
|
||||
Err(e) => Ok(Err(e)),
|
||||
|
@ -651,10 +646,8 @@ impl InnerResolver {
|
|||
(TypeEnum::TObj { obj_id, params, .. }, false)
|
||||
if *obj_id == primitives.option.get_obj_id(unifier) =>
|
||||
{
|
||||
let field_data = match obj.getattr("_nac3_option") {
|
||||
Ok(d) => d,
|
||||
// we use `none = Option(None)`, so the obj always have attr `_nac3_option`
|
||||
Err(_) => unreachable!("cannot be None")
|
||||
let Ok(field_data) = obj.getattr("_nac3_option") else {
|
||||
unreachable!("cannot be None")
|
||||
};
|
||||
// if is `none`
|
||||
let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
|
||||
|
@ -674,17 +667,15 @@ impl InnerResolver {
|
|||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()))
|
||||
} else {
|
||||
unreachable!("must be tobj")
|
||||
}
|
||||
unreachable!("must be tobj")
|
||||
}
|
||||
|
||||
let ty = match self.get_obj_type(py, field_data, unifier, defs, primitives)? {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Ok(Err(format!(
|
||||
"error when getting type of the option object ({})",
|
||||
e
|
||||
"error when getting type of the option object ({e})"
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
@ -709,39 +700,36 @@ impl InnerResolver {
|
|||
.collect::<HashMap<_, _>>();
|
||||
let mut instantiate_obj = || {
|
||||
// loop through non-function fields of the class to get the instantiated value
|
||||
for field in fields.iter() {
|
||||
for field in fields {
|
||||
let name: String = (*field.0).into();
|
||||
if let TypeEnum::TFunc(..) = &*unifier.get_ty(field.1.0) {
|
||||
continue;
|
||||
} else {
|
||||
let field_data = match obj.getattr(name.as_str()) {
|
||||
Ok(d) => d,
|
||||
Err(e) => return Ok(Err(format!("{}", e))),
|
||||
};
|
||||
let ty = match self
|
||||
.get_obj_type(py, field_data, unifier, defs, primitives)?
|
||||
{
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Ok(Err(format!(
|
||||
"error when getting type of field `{}` ({})",
|
||||
name, e
|
||||
)))
|
||||
}
|
||||
};
|
||||
let field_ty =
|
||||
unifier.subst(field.1.0, &var_map).unwrap_or(field.1.0);
|
||||
if let Err(e) = unifier.unify(ty, field_ty) {
|
||||
// field type mismatch
|
||||
}
|
||||
let field_data = match obj.getattr(name.as_str()) {
|
||||
Ok(d) => d,
|
||||
Err(e) => return Ok(Err(format!("{e}"))),
|
||||
};
|
||||
let ty = match self
|
||||
.get_obj_type(py, field_data, unifier, defs, primitives)?
|
||||
{
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Ok(Err(format!(
|
||||
"error when getting type of field `{}` ({})",
|
||||
name,
|
||||
e.to_display(unifier).to_string()
|
||||
)));
|
||||
"error when getting type of field `{name}` ({e})"
|
||||
)))
|
||||
}
|
||||
};
|
||||
let field_ty =
|
||||
unifier.subst(field.1.0, &var_map).unwrap_or(field.1.0);
|
||||
if let Err(e) = unifier.unify(ty, field_ty) {
|
||||
// field type mismatch
|
||||
return Ok(Err(format!(
|
||||
"error when getting type of field `{name}` ({})",
|
||||
e.to_display(unifier)
|
||||
)));
|
||||
}
|
||||
}
|
||||
for (_, ty) in var_map.iter() {
|
||||
for ty in var_map.values() {
|
||||
// must be concrete type
|
||||
if !unifier.is_concrete(*ty, &[]) {
|
||||
return Ok(Err("object is not of concrete type".into()));
|
||||
|
@ -762,32 +750,32 @@ impl InnerResolver {
|
|||
// check integer bounds
|
||||
if unifier.unioned(extracted_ty, primitives.int32) {
|
||||
obj.extract::<i32>().map_or_else(
|
||||
|_| Ok(Err(format!("{} is not in the range of int32", obj))),
|
||||
|_| Ok(Err(format!("{obj} is not in the range of int32"))),
|
||||
|_| Ok(Ok(extracted_ty))
|
||||
)
|
||||
} else if unifier.unioned(extracted_ty, primitives.int64) {
|
||||
obj.extract::<i64>().map_or_else(
|
||||
|_| Ok(Err(format!("{} is not in the range of int64", obj))),
|
||||
|_| Ok(Err(format!("{obj} is not in the range of int64"))),
|
||||
|_| Ok(Ok(extracted_ty))
|
||||
)
|
||||
} else if unifier.unioned(extracted_ty, primitives.uint32) {
|
||||
obj.extract::<u32>().map_or_else(
|
||||
|_| Ok(Err(format!("{} is not in the range of uint32", obj))),
|
||||
|_| Ok(Err(format!("{obj} is not in the range of uint32"))),
|
||||
|_| Ok(Ok(extracted_ty))
|
||||
)
|
||||
} else if unifier.unioned(extracted_ty, primitives.uint64) {
|
||||
obj.extract::<u64>().map_or_else(
|
||||
|_| Ok(Err(format!("{} is not in the range of uint64", obj))),
|
||||
|_| Ok(Err(format!("{obj} is not in the range of uint64"))),
|
||||
|_| Ok(Ok(extracted_ty))
|
||||
)
|
||||
} else if unifier.unioned(extracted_ty, primitives.bool) {
|
||||
obj.extract::<bool>().map_or_else(
|
||||
|_| Ok(Err(format!("{} is not in the range of bool", obj))),
|
||||
|_| Ok(Err(format!("{obj} is not in the range of bool"))),
|
||||
|_| Ok(Ok(extracted_ty))
|
||||
)
|
||||
} else if unifier.unioned(extracted_ty, primitives.float) {
|
||||
obj.extract::<f64>().map_or_else(
|
||||
|_| Ok(Err(format!("{} is not in the range of float64", obj))),
|
||||
|_| Ok(Err(format!("{obj} is not in the range of float64"))),
|
||||
|_| Ok(Ok(extracted_ty))
|
||||
)
|
||||
} else {
|
||||
|
@ -797,11 +785,11 @@ impl InnerResolver {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_obj_value<'ctx, 'a>(
|
||||
pub fn get_obj_value<'ctx>(
|
||||
&self,
|
||||
py: Python,
|
||||
obj: &PyAny,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
expected_ty: Type,
|
||||
) -> PyResult<Option<BasicValueEnum<'ctx>>> {
|
||||
|
@ -859,9 +847,8 @@ impl InnerResolver {
|
|||
ctx.module.add_global(arr_ty, Some(AddressSpace::default()), &id_str)
|
||||
});
|
||||
return Ok(Some(global.as_pointer_value().into()));
|
||||
} else {
|
||||
self.global_value_ids.write().insert(id, obj.into());
|
||||
}
|
||||
self.global_value_ids.write().insert(id, obj.into());
|
||||
}
|
||||
|
||||
let arr: Result<Option<Vec<_>>, _> = (0..len)
|
||||
|
@ -871,7 +858,7 @@ impl InnerResolver {
|
|||
.and_then(|elem| self.get_obj_value(py, elem, ctx, generator, elem_ty)
|
||||
.map_err(
|
||||
|e| super::CompileError::new_err(
|
||||
format!("Error getting element {}: {}", i, e))
|
||||
format!("Error getting element {i}: {e}"))
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
|
@ -925,7 +912,7 @@ impl InnerResolver {
|
|||
.map(|((i, elem), ty)| self
|
||||
.get_obj_value(py, elem, ctx, generator, *ty).map_err(|e|
|
||||
super::CompileError::new_err(
|
||||
format!("Error getting element {}: {}", i, e)
|
||||
format!("Error getting element {i}: {e}")
|
||||
)
|
||||
)
|
||||
).collect();
|
||||
|
@ -957,21 +944,19 @@ impl InnerResolver {
|
|||
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator, option_val_ty)
|
||||
.map_err(|e| {
|
||||
super::CompileError::new_err(format!(
|
||||
"Error getting value of Option object: {}",
|
||||
e
|
||||
"Error getting value of Option object: {e}"
|
||||
))
|
||||
})? {
|
||||
Some(v) => {
|
||||
let global_str = format!("{}_option", id);
|
||||
let global_str = format!("{id}_option");
|
||||
{
|
||||
if self.global_value_ids.read().contains_key(&id) {
|
||||
let global = ctx.module.get_global(&global_str).unwrap_or_else(|| {
|
||||
ctx.module.add_global(v.get_type(), Some(AddressSpace::default()), &global_str)
|
||||
});
|
||||
return Ok(Some(global.as_pointer_value().into()));
|
||||
} else {
|
||||
self.global_value_ids.write().insert(id, obj.into());
|
||||
}
|
||||
self.global_value_ids.write().insert(id, obj.into());
|
||||
}
|
||||
let global = ctx.module.add_global(v.get_type(), Some(AddressSpace::default()), &global_str);
|
||||
global.set_initializer(&v);
|
||||
|
@ -1002,9 +987,8 @@ impl InnerResolver {
|
|||
ctx.module.add_global(ty, Some(AddressSpace::default()), &id_str)
|
||||
});
|
||||
return Ok(Some(global.as_pointer_value().into()));
|
||||
} else {
|
||||
self.global_value_ids.write().insert(id, obj.into());
|
||||
}
|
||||
self.global_value_ids.write().insert(id, obj.into());
|
||||
}
|
||||
// should be classes
|
||||
let definition =
|
||||
|
@ -1014,7 +998,7 @@ impl InnerResolver {
|
|||
.iter()
|
||||
.map(|(name, ty, _)| {
|
||||
self.get_obj_value(py, obj.getattr(name.to_string().as_str())?, ctx, generator, *ty)
|
||||
.map_err(|e| super::CompileError::new_err(format!("Error getting field {}: {}", name, e)))
|
||||
.map_err(|e| super::CompileError::new_err(format!("Error getting field {name}: {e}")))
|
||||
})
|
||||
.collect();
|
||||
let values = values?;
|
||||
|
@ -1087,11 +1071,11 @@ impl SymbolResolver for Resolver {
|
|||
let obj: &PyAny = self.0.module.extract(py)?;
|
||||
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
|
||||
let mut sym_value = None;
|
||||
for (key, val) in members.iter() {
|
||||
for (key, val) in members {
|
||||
let key: &str = key.extract()?;
|
||||
if key == id.to_string() {
|
||||
if let Ok(Ok(v)) = self.0.get_default_param_obj_value(py, val) {
|
||||
sym_value = Some(v)
|
||||
sym_value = Some(v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1111,50 +1095,48 @@ impl SymbolResolver for Resolver {
|
|||
primitives: &PrimitiveStore,
|
||||
str: StrRef,
|
||||
) -> Result<Type, String> {
|
||||
match {
|
||||
if let Some(ty) = {
|
||||
let id_to_type = self.0.id_to_type.read();
|
||||
id_to_type.get(&str).cloned()
|
||||
id_to_type.get(&str).copied()
|
||||
} {
|
||||
Some(ty) => Ok(ty),
|
||||
None => {
|
||||
let id = match self.0.name_to_pyid.get(&str) {
|
||||
Some(id) => id,
|
||||
None => return Err(format!("cannot find symbol `{}`", str)),
|
||||
};
|
||||
let result = match {
|
||||
let pyid_to_type = self.0.pyid_to_type.read();
|
||||
pyid_to_type.get(id).copied()
|
||||
} {
|
||||
Some(t) => Ok(t),
|
||||
None => Python::with_gil(|py| -> PyResult<Result<Type, String>> {
|
||||
let obj: &PyAny = self.0.module.extract(py)?;
|
||||
let mut sym_ty = Err(format!("cannot find symbol `{}`", str));
|
||||
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
|
||||
for (key, val) in members.iter() {
|
||||
let key: &str = key.extract()?;
|
||||
if key == str.to_string() {
|
||||
sym_ty = self.0.get_obj_type(py, val, unifier, defs, primitives)?;
|
||||
break;
|
||||
}
|
||||
Ok(ty)
|
||||
} else {
|
||||
let Some(id) = self.0.name_to_pyid.get(&str) else {
|
||||
return Err(format!("cannot find symbol `{str}`"))
|
||||
};
|
||||
let result = if let Some(t) = {
|
||||
let pyid_to_type = self.0.pyid_to_type.read();
|
||||
pyid_to_type.get(id).copied()
|
||||
} {
|
||||
Ok(t)
|
||||
} else {
|
||||
Python::with_gil(|py| -> PyResult<Result<Type, String>> {
|
||||
let obj: &PyAny = self.0.module.extract(py)?;
|
||||
let mut sym_ty = Err(format!("cannot find symbol `{str}`"));
|
||||
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
|
||||
for (key, val) in members {
|
||||
let key: &str = key.extract()?;
|
||||
if key == str.to_string() {
|
||||
sym_ty = self.0.get_obj_type(py, val, unifier, defs, primitives)?;
|
||||
break;
|
||||
}
|
||||
if let Ok(t) = sym_ty {
|
||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(t) {
|
||||
self.0.pyid_to_type.write().insert(*id, t);
|
||||
}
|
||||
}
|
||||
if let Ok(t) = sym_ty {
|
||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(t) {
|
||||
self.0.pyid_to_type.write().insert(*id, t);
|
||||
}
|
||||
Ok(sym_ty)
|
||||
})
|
||||
.unwrap(),
|
||||
};
|
||||
result
|
||||
}
|
||||
}
|
||||
Ok(sym_ty)
|
||||
}).unwrap()
|
||||
};
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn get_symbol_value<'ctx, 'a>(
|
||||
fn get_symbol_value<'ctx>(
|
||||
&self,
|
||||
id: StrRef,
|
||||
_: &mut CodeGenContext<'ctx, 'a>,
|
||||
_: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> Option<ValueEnum<'ctx>> {
|
||||
let sym_value = {
|
||||
let id_to_val = self.0.id_to_pyval.read();
|
||||
|
@ -1165,7 +1147,7 @@ impl SymbolResolver for Resolver {
|
|||
let obj: &PyAny = self.0.module.extract(py)?;
|
||||
let mut sym_value: Option<(u64, PyObject)> = None;
|
||||
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
|
||||
for (key, val) in members.iter() {
|
||||
for (key, val) in members {
|
||||
let key: &str = key.extract()?;
|
||||
if key == id.to_string() {
|
||||
let id = self.0.helper.id_fn.call1(py, (val,))?.extract(py)?;
|
||||
|
@ -1190,18 +1172,21 @@ impl SymbolResolver for Resolver {
|
|||
})
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||
{
|
||||
let id_to_def = self.0.id_to_def.read();
|
||||
id_to_def.get(&id).cloned().ok_or_else(|| "".to_string())
|
||||
id_to_def.get(&id).copied().ok_or_else(String::new)
|
||||
}
|
||||
.or_else(|_| {
|
||||
let py_id =
|
||||
self.0.name_to_pyid.get(&id).ok_or(format!("Undefined identifier `{}`", id))?;
|
||||
let result = self.0.pyid_to_def.read().get(py_id).copied().ok_or(format!(
|
||||
"`{}` is not registered with NAC3 (@nac3 decorator missing?)",
|
||||
id
|
||||
))?;
|
||||
let py_id = self.0.name_to_pyid.get(&id)
|
||||
.ok_or_else(|| HashSet::from([
|
||||
format!("Undefined identifier `{id}`"),
|
||||
]))?;
|
||||
let result = self.0.pyid_to_def.read().get(py_id)
|
||||
.copied()
|
||||
.ok_or_else(|| HashSet::from([
|
||||
format!("`{id}` is not registered with NAC3 (@nac3 decorator missing?)"),
|
||||
]))?;
|
||||
self.0.id_to_def.write().insert(id, result);
|
||||
Ok(result)
|
||||
})
|
||||
|
@ -1245,7 +1230,7 @@ impl SymbolResolver for Resolver {
|
|||
name,
|
||||
)));
|
||||
}
|
||||
unifier.unify(ty, *var).unwrap()
|
||||
unifier.unify(ty, *var).unwrap();
|
||||
}
|
||||
Err(err) => return Ok(Err(err)),
|
||||
}
|
||||
|
@ -1255,13 +1240,13 @@ impl SymbolResolver for Resolver {
|
|||
}
|
||||
}
|
||||
Ok(Ok(()))
|
||||
}).unwrap()?
|
||||
}).unwrap()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_exception_id(&self, tyid: usize) -> usize {
|
||||
let exn_ids = self.0.exception_ids.read();
|
||||
exn_ids.get(&tyid).cloned().unwrap_or(0)
|
||||
exn_ids.get(&tyid).copied().unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@ use nac3core::codegen::CodeGenContext;
|
|||
pub trait TimeFns {
|
||||
|
||||
/// Emits LLVM IR for `now_mu`.
|
||||
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx>;
|
||||
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx>;
|
||||
|
||||
/// Emits LLVM IR for `at_mu`.
|
||||
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>);
|
||||
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>);
|
||||
|
||||
/// Emits LLVM IR for `delay_mu`.
|
||||
fn emit_delay_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, dt: BasicValueEnum<'ctx>);
|
||||
fn emit_delay_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, dt: BasicValueEnum<'ctx>);
|
||||
}
|
||||
|
||||
pub struct NowPinningTimeFns64 {}
|
||||
|
@ -19,7 +19,7 @@ pub struct NowPinningTimeFns64 {}
|
|||
// For FPGA design reasons, on VexRiscv with 64-bit data bus, the "now" CSR is split into two 32-bit
|
||||
// values that are each padded to 64-bits.
|
||||
impl TimeFns for NowPinningTimeFns64 {
|
||||
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx> {
|
||||
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
|
||||
let i64_type = ctx.ctx.i64_type();
|
||||
let i32_type = ctx.ctx.i32_type();
|
||||
let now = ctx
|
||||
|
@ -54,7 +54,7 @@ impl TimeFns for NowPinningTimeFns64 {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
|
||||
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
|
||||
let i32_type = ctx.ctx.i32_type();
|
||||
let i64_type = ctx.ctx.i64_type();
|
||||
|
||||
|
@ -96,9 +96,9 @@ impl TimeFns for NowPinningTimeFns64 {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_delay_mu<'ctx, 'a>(
|
||||
fn emit_delay_mu<'ctx>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
dt: BasicValueEnum<'ctx>,
|
||||
) {
|
||||
let i64_type = ctx.ctx.i64_type();
|
||||
|
@ -168,7 +168,7 @@ pub static NOW_PINNING_TIME_FNS_64: NowPinningTimeFns64 = NowPinningTimeFns64 {}
|
|||
pub struct NowPinningTimeFns {}
|
||||
|
||||
impl TimeFns for NowPinningTimeFns {
|
||||
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx> {
|
||||
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
|
||||
let i64_type = ctx.ctx.i64_type();
|
||||
let now = ctx
|
||||
.module
|
||||
|
@ -186,7 +186,7 @@ impl TimeFns for NowPinningTimeFns {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
|
||||
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
|
||||
let i32_type = ctx.ctx.i32_type();
|
||||
let i64_type = ctx.ctx.i64_type();
|
||||
let i64_32 = i64_type.const_int(32, false);
|
||||
|
@ -228,9 +228,9 @@ impl TimeFns for NowPinningTimeFns {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_delay_mu<'ctx, 'a>(
|
||||
fn emit_delay_mu<'ctx>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
dt: BasicValueEnum<'ctx>,
|
||||
) {
|
||||
let i32_type = ctx.ctx.i32_type();
|
||||
|
@ -285,14 +285,14 @@ pub static NOW_PINNING_TIME_FNS: NowPinningTimeFns = NowPinningTimeFns {};
|
|||
pub struct ExternTimeFns {}
|
||||
|
||||
impl TimeFns for ExternTimeFns {
|
||||
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx> {
|
||||
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
|
||||
let now_mu = ctx.module.get_function("now_mu").unwrap_or_else(|| {
|
||||
ctx.module.add_function("now_mu", ctx.ctx.i64_type().fn_type(&[], false), None)
|
||||
});
|
||||
ctx.builder.build_call(now_mu, &[], "now_mu").try_as_basic_value().left().unwrap()
|
||||
}
|
||||
|
||||
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
|
||||
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
|
||||
let at_mu = ctx.module.get_function("at_mu").unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
"at_mu",
|
||||
|
@ -303,9 +303,9 @@ impl TimeFns for ExternTimeFns {
|
|||
ctx.builder.build_call(at_mu, &[t.into()], "at_mu");
|
||||
}
|
||||
|
||||
fn emit_delay_mu<'ctx, 'a>(
|
||||
fn emit_delay_mu<'ctx>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
dt: BasicValueEnum<'ctx>,
|
||||
) {
|
||||
let delay_mu = ctx.module.get_function("delay_mu").unwrap_or_else(|| {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
//! Datatypes to support source location information.
|
||||
use std::cmp::Ordering;
|
||||
use crate::ast_gen::StrRef;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct FileName(pub StrRef);
|
||||
impl Default for FileName {
|
||||
fn default() -> Self {
|
||||
|
@ -17,7 +18,7 @@ impl From<String> for FileName {
|
|||
}
|
||||
|
||||
/// A location somewhere in the sourcecode.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Location {
|
||||
pub row: usize,
|
||||
pub column: usize,
|
||||
|
@ -30,6 +31,28 @@ impl fmt::Display for Location {
|
|||
}
|
||||
}
|
||||
|
||||
impl Ord for Location {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let file_cmp = self.file.0.to_string().cmp(&other.file.0.to_string());
|
||||
if file_cmp != Ordering::Equal {
|
||||
return file_cmp
|
||||
}
|
||||
|
||||
let row_cmp = self.row.cmp(&other.row);
|
||||
if row_cmp != Ordering::Equal {
|
||||
return row_cmp
|
||||
}
|
||||
|
||||
self.column.cmp(&other.column)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Location {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Location {
|
||||
pub fn visualize<'a>(
|
||||
&self,
|
||||
|
|
|
@ -5,7 +5,7 @@ authors = ["M-Labs"]
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
crossbeam = "0.8"
|
||||
parking_lot = "0.12"
|
||||
rayon = "1.5"
|
||||
|
|
|
@ -9,15 +9,11 @@ use std::{
|
|||
|
||||
fn main() {
|
||||
const FILE: &str = "src/codegen/irrt/irrt.c";
|
||||
println!("cargo:rerun-if-changed={}", FILE);
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let out_path = Path::new(&out_dir);
|
||||
|
||||
/*
|
||||
* HACK: Sadly, clang doesn't let us emit generic LLVM bitcode.
|
||||
* Compiling for WASM32 and filtering the output with regex is the closest we can get.
|
||||
*/
|
||||
|
||||
const FLAG: &[&str] = &[
|
||||
"--target=wasm32",
|
||||
FILE,
|
||||
|
@ -29,6 +25,11 @@ fn main() {
|
|||
"-o",
|
||||
"-",
|
||||
];
|
||||
|
||||
println!("cargo:rerun-if-changed={FILE}");
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let out_path = Path::new(&out_dir);
|
||||
|
||||
let output = Command::new("clang-irrt")
|
||||
.args(FLAG)
|
||||
.output()
|
||||
|
@ -42,9 +43,9 @@ fn main() {
|
|||
let output = std::str::from_utf8(&output.stdout).unwrap().replace("\r\n", "\n");
|
||||
let mut filtered_output = String::with_capacity(output.len());
|
||||
|
||||
let regex_filter = regex::Regex::new(r"(?ms:^define.*?\}$)|(?m:^declare.*?$)").unwrap();
|
||||
let regex_filter = Regex::new(r"(?ms:^define.*?\}$)|(?m:^declare.*?$)").unwrap();
|
||||
for f in regex_filter.captures_iter(&output) {
|
||||
assert!(f.len() == 1);
|
||||
assert_eq!(f.len(), 1);
|
||||
filtered_output.push_str(&f[0]);
|
||||
filtered_output.push('\n');
|
||||
}
|
||||
|
@ -68,5 +69,5 @@ fn main() {
|
|||
.spawn()
|
||||
.unwrap();
|
||||
llvm_as.stdin.as_mut().unwrap().write_all(filtered_output.as_bytes()).unwrap();
|
||||
assert!(llvm_as.wait().unwrap().success())
|
||||
assert!(llvm_as.wait().unwrap().success());
|
||||
}
|
||||
|
|
|
@ -60,9 +60,14 @@ pub enum ConcreteTypeEnum {
|
|||
ret: ConcreteType,
|
||||
vars: HashMap<u32, ConcreteType>,
|
||||
},
|
||||
TConstant {
|
||||
value: SymbolValue,
|
||||
ty: ConcreteType,
|
||||
},
|
||||
}
|
||||
|
||||
impl ConcreteTypeStore {
|
||||
#[must_use]
|
||||
pub fn new() -> ConcreteTypeStore {
|
||||
ConcreteTypeStore {
|
||||
store: vec![
|
||||
|
@ -80,6 +85,7 @@ impl ConcreteTypeStore {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get(&self, cty: ConcreteType) -> &ConcreteTypeEnum {
|
||||
&self.store[cty.0]
|
||||
}
|
||||
|
@ -194,9 +200,13 @@ impl ConcreteTypeStore {
|
|||
ty: self.from_unifier_type(unifier, primitives, *ty, cache),
|
||||
},
|
||||
TypeEnum::TFunc(signature) => {
|
||||
self.from_signature(unifier, primitives, &*signature, cache)
|
||||
self.from_signature(unifier, primitives, signature, cache)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
TypeEnum::TConstant { value, ty, .. } => ConcreteTypeEnum::TConstant {
|
||||
value: value.clone(),
|
||||
ty: self.from_unifier_type(unifier, primitives, *ty, cache),
|
||||
},
|
||||
_ => unreachable!("{:?}", ty_enum.get_type_name()),
|
||||
};
|
||||
let index = if let Some(ConcreteType(index)) = cache.get(&ty).unwrap() {
|
||||
self.store[*index] = result;
|
||||
|
@ -283,6 +293,11 @@ impl ConcreteTypeStore {
|
|||
.map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache)))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
}),
|
||||
ConcreteTypeEnum::TConstant { value, ty } => TypeEnum::TConstant {
|
||||
value: value.clone(),
|
||||
ty: self.to_unifier_type(unifier, primitives, *ty, cache),
|
||||
loc: None,
|
||||
}
|
||||
};
|
||||
let result = unifier.add_ty(result);
|
||||
if let Some(ty) = cache.get(&cty).unwrap() {
|
||||
|
|
|
@ -47,7 +47,7 @@ pub fn get_subst_key(
|
|||
})
|
||||
.unwrap_or_default();
|
||||
vars.extend(fun_vars.iter());
|
||||
let sorted = vars.keys().filter(|id| filter.map(|v| v.contains(id)).unwrap_or(true)).sorted();
|
||||
let sorted = vars.keys().filter(|id| filter.map_or(true, |v| v.contains(id))).sorted();
|
||||
sorted
|
||||
.map(|id| {
|
||||
unifier.internal_stringify(
|
||||
|
@ -107,7 +107,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
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::U32(v) => self.ctx.i32_type().const_int(*v as u64, false).into(),
|
||||
SymbolValue::U64(v) => self.ctx.i64_type().const_int(*v as u64, false).into(),
|
||||
SymbolValue::U64(v) => self.ctx.i64_type().const_int(*v, false).into(),
|
||||
SymbolValue::Bool(v) => self.ctx.i8_type().const_int(*v as u64, true).into(),
|
||||
SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(),
|
||||
SymbolValue::Str(v) => {
|
||||
|
@ -119,7 +119,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
}
|
||||
SymbolValue::Tuple(ls) => {
|
||||
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec();
|
||||
let fields = vals.iter().map(|v| v.get_type()).collect_vec();
|
||||
let fields = vals.iter().map(BasicValueEnum::get_type).collect_vec();
|
||||
let ty = self.ctx.struct_type(&fields, false);
|
||||
let ptr = gen_var(self, ty.into(), Some("tuple")).unwrap();
|
||||
let zero = self.ctx.i32_type().const_zero();
|
||||
|
@ -165,7 +165,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// See [get_llvm_type].
|
||||
/// See [`get_llvm_type`].
|
||||
pub fn get_llvm_type(
|
||||
&mut self,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
|
@ -183,7 +183,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
)
|
||||
}
|
||||
|
||||
/// See [get_llvm_abi_type].
|
||||
/// See [`get_llvm_abi_type`].
|
||||
pub fn get_llvm_abi_type(
|
||||
&mut self,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
|
@ -212,7 +212,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
Constant::Bool(v) => {
|
||||
assert!(self.unifier.unioned(ty, self.primitives.bool));
|
||||
let ty = self.ctx.i8_type();
|
||||
Some(ty.const_int(if *v { 1 } else { 0 }, false).into())
|
||||
Some(ty.const_int(u64::from(*v), false).into())
|
||||
}
|
||||
Constant::Int(val) => {
|
||||
let ty = if self.unifier.unioned(ty, self.primitives.int32)
|
||||
|
@ -237,7 +237,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
let ty = self.unifier.get_ty(ty);
|
||||
let types =
|
||||
if let TypeEnum::TTuple { ty } = &*ty { ty.clone() } else { unreachable!() };
|
||||
let values = zip(types.into_iter(), v.iter())
|
||||
let values = zip(types, v.iter())
|
||||
.map_while(|(ty, v)| self.gen_const(generator, v, ty))
|
||||
.collect_vec();
|
||||
|
||||
|
@ -290,12 +290,9 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
rhs: BasicValueEnum<'ctx>,
|
||||
signed: bool
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let (lhs, rhs) =
|
||||
if let (BasicValueEnum::IntValue(lhs), BasicValueEnum::IntValue(rhs)) = (lhs, rhs) {
|
||||
(lhs, rhs)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let (BasicValueEnum::IntValue(lhs), BasicValueEnum::IntValue(rhs)) = (lhs, rhs) else {
|
||||
unreachable!()
|
||||
};
|
||||
let float = self.ctx.f64_type();
|
||||
match (op, signed) {
|
||||
(Operator::Add, _) => self.builder.build_int_add(lhs, rhs, "add").into(),
|
||||
|
@ -318,7 +315,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
(Operator::BitAnd, _) => self.builder.build_and(lhs, rhs, "and").into(),
|
||||
|
||||
// Sign-ness of bitshift operators are always determined by the left operand
|
||||
(Operator::LShift, signed) | (Operator::RShift, signed) => {
|
||||
(Operator::LShift | Operator::RShift, signed) => {
|
||||
// RHS operand is always 32 bits
|
||||
assert_eq!(rhs.get_type().get_bit_width(), 32);
|
||||
|
||||
|
@ -365,11 +362,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
lhs: BasicValueEnum<'ctx>,
|
||||
rhs: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let (lhs, rhs) = if let (BasicValueEnum::FloatValue(lhs), BasicValueEnum::FloatValue(rhs)) =
|
||||
(lhs, rhs)
|
||||
{
|
||||
(lhs, rhs)
|
||||
} else {
|
||||
let (BasicValueEnum::FloatValue(lhs), BasicValueEnum::FloatValue(rhs)) = (lhs, rhs) else {
|
||||
unreachable!()
|
||||
};
|
||||
let float = self.ctx.f64_type();
|
||||
|
@ -474,7 +467,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
.collect_vec();
|
||||
let result = 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 then_block = self.ctx.append_basic_block(current, &format!("after.{call_name}"));
|
||||
let result = self
|
||||
.builder
|
||||
.build_invoke(fun, ¶ms, then_block, target, call_name)
|
||||
|
@ -499,7 +492,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
generator: &mut dyn CodeGenerator,
|
||||
s: S,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str).unwrap()
|
||||
self.gen_const(generator, &Constant::Str(s.into()), self.primitives.str).unwrap()
|
||||
}
|
||||
|
||||
pub fn raise_exn(
|
||||
|
@ -516,7 +509,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
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 = generator.gen_var_alloc(self, zelf_ty, Some("exn")).unwrap();
|
||||
self.exception_val.insert(zelf).to_owned()
|
||||
*self.exception_val.insert(zelf)
|
||||
};
|
||||
let int32 = self.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
|
@ -556,7 +549,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
loc: Location,
|
||||
) {
|
||||
let err_msg = self.gen_string(generator, err_msg);
|
||||
self.make_assert_impl(generator, cond, err_name, err_msg, params, loc)
|
||||
self.make_assert_impl(generator, cond, err_name, err_msg, params, loc);
|
||||
}
|
||||
|
||||
pub fn make_assert_impl(
|
||||
|
@ -598,7 +591,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_constructor].
|
||||
/// See [`CodeGenerator::gen_constructor`].
|
||||
pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
|
@ -609,7 +602,7 @@ pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
|
|||
match def {
|
||||
TopLevelDef::Class { methods, .. } => {
|
||||
// TODO: what about other fields that require alloca?
|
||||
let fun_id = methods.iter().find(|method| method.0 == "__init__".into()).and_then(|method| Some(method.2));
|
||||
let fun_id = methods.iter().find(|method| method.0 == "__init__".into()).map(|method| method.2);
|
||||
let ty = ctx.get_llvm_type(generator, signature.ret).into_pointer_type();
|
||||
let zelf_ty: BasicTypeEnum = ty.get_element_type().try_into().unwrap();
|
||||
let zelf: BasicValueEnum<'ctx> = ctx.builder.build_alloca(zelf_ty, "alloca").into();
|
||||
|
@ -626,14 +619,14 @@ pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
Ok(zelf)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
TopLevelDef::Function { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_func_instance].
|
||||
pub fn gen_func_instance<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
/// See [`CodeGenerator::gen_func_instance`].
|
||||
pub fn gen_func_instance<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
obj: &Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, &mut TopLevelDef, String),
|
||||
id: usize,
|
||||
) -> Result<String, String> {
|
||||
|
@ -683,7 +676,7 @@ pub fn gen_func_instance<'ctx, 'a>(
|
|||
args.insert(
|
||||
0,
|
||||
ConcreteFuncArg { name: "self".into(), ty: zelf, default_value: None },
|
||||
)
|
||||
);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -707,10 +700,10 @@ pub fn gen_func_instance<'ctx, 'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_call].
|
||||
pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_call`].
|
||||
pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
|
@ -738,11 +731,11 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
let old_key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), fun.0, None);
|
||||
let mut keys = fun.0.args.clone();
|
||||
let mut mapping = HashMap::new();
|
||||
for (key, value) in params.into_iter() {
|
||||
for (key, value) in params {
|
||||
mapping.insert(key.unwrap_or_else(|| keys.remove(0).name), value);
|
||||
}
|
||||
// default value handling
|
||||
for k in keys.into_iter() {
|
||||
for k in keys {
|
||||
if mapping.get(&k.name).is_some() {
|
||||
continue;
|
||||
}
|
||||
|
@ -774,30 +767,29 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
.map(|(i, v)| (*i, v.get_unique_identifier()))
|
||||
.collect_vec();
|
||||
let mut store = ctx.static_value_store.lock();
|
||||
match store.lookup.get(&ids) {
|
||||
Some(index) => *index,
|
||||
None => {
|
||||
let length = store.store.len();
|
||||
store.lookup.insert(ids, length);
|
||||
store.store.push(static_params.into_iter().collect());
|
||||
length
|
||||
}
|
||||
if let Some(index) = store.lookup.get(&ids) {
|
||||
*index
|
||||
} else {
|
||||
let length = store.store.len();
|
||||
store.lookup.insert(ids, length);
|
||||
store.store.push(static_params.into_iter().collect());
|
||||
length
|
||||
}
|
||||
};
|
||||
// special case: extern functions
|
||||
key = if instance_to_stmt.is_empty() {
|
||||
"".to_string()
|
||||
String::new()
|
||||
} else {
|
||||
format!("{}:{}", id, old_key)
|
||||
format!("{id}:{old_key}")
|
||||
};
|
||||
param_vals = real_params
|
||||
.into_iter()
|
||||
.map(|(p, t)| p.to_basic_value_enum(ctx, generator, t))
|
||||
.collect::<Result<Vec<_>, String>>()?;
|
||||
instance_to_symbol.get(&key).cloned().ok_or_else(|| "".into())
|
||||
instance_to_symbol.get(&key).cloned().ok_or_else(String::new)
|
||||
}
|
||||
TopLevelDef::Class { .. } => {
|
||||
return Ok(Some(generator.gen_constructor(ctx, fun.0, &*def, params)?))
|
||||
return Ok(Some(generator.gen_constructor(ctx, fun.0, &def, params)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -814,7 +806,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
} else {
|
||||
Some(ctx.get_llvm_abi_type(generator, fun.0.ret))
|
||||
};
|
||||
let has_sret = ret_type.map_or(false, |ret_type| need_sret(ctx.ctx, ret_type));
|
||||
let has_sret = ret_type.map_or(false, |ret_type| need_sret(ret_type));
|
||||
let mut byrefs = Vec::new();
|
||||
let mut params = args.iter().enumerate()
|
||||
.map(|(i, arg)| match ctx.get_llvm_abi_type(generator, arg.ty) {
|
||||
|
@ -858,7 +850,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
});
|
||||
|
||||
// Convert boolean parameter values into i1
|
||||
let param_vals = (&fun_val.get_params()).iter().zip(param_vals)
|
||||
let param_vals = fun_val.get_params().iter().zip(param_vals)
|
||||
.map(|(p, v)| {
|
||||
if p.is_int_value() && v.is_int_value() {
|
||||
let expected_ty = p.into_int_value().get_type();
|
||||
|
@ -880,8 +872,8 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
|
||||
/// Generates three LLVM variables representing the start, stop, and step values of a [range] class
|
||||
/// respectively.
|
||||
pub fn destructure_range<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
pub fn destructure_range<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
range: PointerValue<'ctx>,
|
||||
) -> (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>) {
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
|
@ -900,12 +892,12 @@ pub fn destructure_range<'ctx, 'a>(
|
|||
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting
|
||||
/// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
|
||||
///
|
||||
/// Returns an instance of [PointerValue] pointing to the List structure. The List structure is
|
||||
/// Returns an instance of [`PointerValue`] pointing to the List structure. The List structure is
|
||||
/// defined as `type { ty*, size_t }` in LLVM, where the first element stores the pointer to the
|
||||
/// data, and the second element stores the size of the List.
|
||||
pub fn allocate_list<'ctx, 'a, G: CodeGenerator>(
|
||||
pub fn allocate_list<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
length: IntValue<'ctx>,
|
||||
name: Option<&str>,
|
||||
|
@ -949,9 +941,9 @@ pub fn allocate_list<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
|
||||
/// Generates LLVM IR for a [list comprehension expression][expr].
|
||||
pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
||||
pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
expr: &Expr<Option<Type>>,
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
if let ExprKind::ListComp { elt, generators } = &expr.node {
|
||||
|
@ -1083,7 +1075,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx.builder.build_store(len_ptr, ctx.builder.build_load(index, "index"));
|
||||
};
|
||||
|
||||
for cond in ifs.iter() {
|
||||
for cond in ifs {
|
||||
let result = if let Some(v) = generator.gen_expr(ctx, cond)? {
|
||||
v.to_basic_value_enum(ctx, generator, cond.custom.unwrap())?.into_int_value()
|
||||
} else {
|
||||
|
@ -1129,9 +1121,9 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
/// * `right` - The right-hand side of the binary operator.
|
||||
/// * `loc` - The location of the full expression.
|
||||
/// * `is_aug_assign` - Whether the binary operator expression is also an assignment operator.
|
||||
pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
|
||||
pub fn gen_binop_expr<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
left: &Expr<Option<Type>>,
|
||||
op: &Operator,
|
||||
right: &Expr<Option<Type>>,
|
||||
|
@ -1165,7 +1157,7 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
Ok(Some(ctx.gen_float_ops(op, left_val, right_val).into()))
|
||||
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
|
||||
// Pow is the only operator that would pass typecheck between float and int
|
||||
assert!(*op == Operator::Pow);
|
||||
assert_eq!(*op, Operator::Pow);
|
||||
let i32_t = ctx.ctx.i32_type();
|
||||
let pow_intr = ctx.module.get_function("llvm.powi.f64.i32").unwrap_or_else(|| {
|
||||
let f64_t = ctx.ctx.f64_type();
|
||||
|
@ -1226,14 +1218,14 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
Some((left.custom.unwrap(), left_val.into())),
|
||||
(&signature, fun_id),
|
||||
vec![(None, right_val.into())],
|
||||
).map(|f| f.map(|f| f.into()))
|
||||
).map(|f| f.map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_expr].
|
||||
pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_expr`].
|
||||
pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
expr: &Expr<Option<Type>>,
|
||||
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
||||
ctx.current_loc = expr.location;
|
||||
|
@ -1462,7 +1454,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
ast::Unaryop::USub => ctx.builder.build_int_neg(val, "neg").into(),
|
||||
ast::Unaryop::Invert => ctx.builder.build_not(val, "not").into(),
|
||||
ast::Unaryop::Not => ctx.builder.build_xor(val, val.get_type().const_all_ones(), "not").into(),
|
||||
_ => val.into(),
|
||||
ast::Unaryop::UAdd => val.into(),
|
||||
}
|
||||
} else if ty == ctx.primitives.float {
|
||||
let val = val.into_float_value();
|
||||
|
@ -1574,11 +1566,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let test = generator.bool_to_i1(ctx, test);
|
||||
let body_ty = body.custom.unwrap();
|
||||
let is_none = ctx.unifier.get_representative(body_ty) == ctx.primitives.none;
|
||||
let result = if !is_none {
|
||||
let result = if is_none {
|
||||
None
|
||||
} else {
|
||||
let llvm_ty = ctx.get_llvm_type(generator, body_ty);
|
||||
Some(ctx.builder.build_alloca(llvm_ty, "if_exp_result"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let then_bb = ctx.ctx.append_basic_block(current, "then");
|
||||
|
@ -1640,15 +1632,14 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let kw_iter = kw_iter.collect::<Result<Vec<_>, _>>()?;
|
||||
params.extend(kw_iter);
|
||||
let call = ctx.calls.get(&expr.location.into());
|
||||
let signature = match call {
|
||||
Some(call) => ctx.unifier.get_call_signature(*call).unwrap(),
|
||||
None => {
|
||||
let ty = func.custom.unwrap();
|
||||
if let TypeEnum::TFunc(sign) = &*ctx.unifier.get_ty(ty) {
|
||||
sign.clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
let signature = if let Some(call) = call {
|
||||
ctx.unifier.get_call_signature(*call).unwrap()
|
||||
} else {
|
||||
let ty = func.custom.unwrap();
|
||||
if let TypeEnum::TFunc(sign) = &*ctx.unifier.get_ty(ty) {
|
||||
sign.clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
let func = func.as_ref();
|
||||
|
@ -1658,15 +1649,14 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let fun = ctx
|
||||
.resolver
|
||||
.get_identifier_def(*id)
|
||||
.map_err(|e| format!("{} (at {})", e, func.location))?;
|
||||
.map_err(|e| format!("{} (at {})", e.iter().next().unwrap(), func.location))?;
|
||||
return Ok(generator
|
||||
.gen_call(ctx, None, (&signature, fun), params)?
|
||||
.map(|v| v.into()));
|
||||
.map(Into::into));
|
||||
}
|
||||
ExprKind::Attribute { value, attr, .. } => {
|
||||
let val = match generator.gen_expr(ctx, value)? {
|
||||
Some(v) => v,
|
||||
None => return Ok(None),
|
||||
let Some(val) = generator.gen_expr(ctx, value)? else {
|
||||
return Ok(None)
|
||||
};
|
||||
|
||||
let id = if let TypeEnum::TObj { obj_id, .. } =
|
||||
|
@ -1691,7 +1681,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
&& id == ctx.primitives.option.get_obj_id(&ctx.unifier)
|
||||
{
|
||||
match val {
|
||||
ValueEnum::Static(v) => match v.get_field("_nac3_option".into(), ctx) {
|
||||
ValueEnum::Static(v) => return match v.get_field("_nac3_option".into(), ctx) {
|
||||
// if is none, raise exception directly
|
||||
None => {
|
||||
let err_msg = ctx.gen_string(generator, "");
|
||||
|
@ -1723,13 +1713,13 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
.get_llvm_type(generator, value.custom.unwrap())
|
||||
.into_pointer_type()
|
||||
.const_null();
|
||||
return Ok(Some(ctx.builder.build_load(
|
||||
Ok(Some(ctx.builder.build_load(
|
||||
ptr,
|
||||
"unwrap_none_unreachable_load"
|
||||
).into()));
|
||||
).into()))
|
||||
}
|
||||
Some(v) => return Ok(Some(v)),
|
||||
}
|
||||
Some(v) => Ok(Some(v)),
|
||||
},
|
||||
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
|
||||
let not_null = ctx.builder.build_is_not_null(ptr, "unwrap_not_null");
|
||||
ctx.make_assert(
|
||||
|
@ -1745,7 +1735,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
"unwrap_some_load"
|
||||
).into()))
|
||||
}
|
||||
_ => unreachable!("option must be static or ptr")
|
||||
ValueEnum::Dynamic(_) => unreachable!("option must be static or ptr")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1759,7 +1749,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
(&signature, fun_id),
|
||||
params,
|
||||
)?
|
||||
.map(|v| v.into()));
|
||||
.map(Into::into));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
@ -1860,7 +1850,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||
let index: u32 =
|
||||
if let ExprKind::Constant { value: ast::Constant::Int(v), .. } = &slice.node {
|
||||
if let ExprKind::Constant { value: Constant::Int(v), .. } = &slice.node {
|
||||
(*v).try_into().unwrap()
|
||||
} else {
|
||||
unreachable!("tuple subscript must be const int after type check");
|
||||
|
@ -1871,14 +1861,13 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap().into()
|
||||
}
|
||||
Some(ValueEnum::Static(v)) => {
|
||||
match v.get_tuple_element(index) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
let tup = v
|
||||
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
||||
.into_struct_value();
|
||||
ctx.builder.build_extract_value(tup, index, "tup_elem").unwrap().into()
|
||||
}
|
||||
if let Some(v) = v.get_tuple_element(index) {
|
||||
v
|
||||
} else {
|
||||
let tup = v
|
||||
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
||||
.into_struct_value();
|
||||
ctx.builder.build_extract_value(tup, index, "tup_elem").unwrap().into()
|
||||
}
|
||||
}
|
||||
None => return Ok(None),
|
||||
|
|
|
@ -22,9 +22,9 @@ pub trait CodeGenerator {
|
|||
/// - fun: Function signature and definition ID.
|
||||
/// - params: Function parameters. Note that this does not include the object even if the
|
||||
/// function is a class method.
|
||||
fn gen_call<'ctx, 'a>(
|
||||
fn gen_call<'ctx>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
|
@ -39,9 +39,9 @@ pub trait CodeGenerator {
|
|||
/// - signature: Function signature of the constructor.
|
||||
/// - def: Class definition for the constructor class.
|
||||
/// - params: Function parameters.
|
||||
fn gen_constructor<'ctx, 'a>(
|
||||
fn gen_constructor<'ctx>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
signature: &FunSignature,
|
||||
def: &TopLevelDef,
|
||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
|
@ -59,20 +59,20 @@ pub trait CodeGenerator {
|
|||
/// function is a class method.
|
||||
/// Note that this function should check if the function is generated in another thread (due to
|
||||
/// possible race condition), see the default implementation for an example.
|
||||
fn gen_func_instance<'ctx, 'a>(
|
||||
fn gen_func_instance<'ctx>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, &mut TopLevelDef, String),
|
||||
id: usize,
|
||||
) -> Result<String, String> {
|
||||
gen_func_instance(ctx, obj, fun, id)
|
||||
gen_func_instance(ctx, &obj, fun, id)
|
||||
}
|
||||
|
||||
/// Generate the code for an expression.
|
||||
fn gen_expr<'ctx, 'a>(
|
||||
fn gen_expr<'ctx>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
expr: &Expr<Option<Type>>,
|
||||
) -> Result<Option<ValueEnum<'ctx>>, String>
|
||||
where
|
||||
|
@ -83,9 +83,9 @@ pub trait CodeGenerator {
|
|||
|
||||
/// Allocate memory for a variable and return a pointer pointing to it.
|
||||
/// The default implementation places the allocations at the start of the function.
|
||||
fn gen_var_alloc<'ctx, 'a>(
|
||||
fn gen_var_alloc<'ctx>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
name: Option<&str>,
|
||||
) -> Result<PointerValue<'ctx>, String> {
|
||||
|
@ -93,9 +93,9 @@ pub trait CodeGenerator {
|
|||
}
|
||||
|
||||
/// Return a pointer pointing to the target of the expression.
|
||||
fn gen_store_target<'ctx, 'a>(
|
||||
fn gen_store_target<'ctx>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
pattern: &Expr<Option<Type>>,
|
||||
name: Option<&str>,
|
||||
) -> Result<Option<PointerValue<'ctx>>, String>
|
||||
|
@ -106,9 +106,9 @@ pub trait CodeGenerator {
|
|||
}
|
||||
|
||||
/// Generate code for an assignment expression.
|
||||
fn gen_assign<'ctx, 'a>(
|
||||
fn gen_assign<'ctx>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
target: &Expr<Option<Type>>,
|
||||
value: ValueEnum<'ctx>,
|
||||
) -> Result<(), String>
|
||||
|
@ -120,9 +120,9 @@ pub trait CodeGenerator {
|
|||
|
||||
/// Generate code for a while expression.
|
||||
/// Return true if the while loop must early return
|
||||
fn gen_while<'ctx, 'a>(
|
||||
fn gen_while(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
|
@ -133,9 +133,9 @@ pub trait CodeGenerator {
|
|||
|
||||
/// Generate code for a while expression.
|
||||
/// Return true if the while loop must early return
|
||||
fn gen_for<'ctx, 'a>(
|
||||
fn gen_for(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
|
@ -146,9 +146,9 @@ pub trait CodeGenerator {
|
|||
|
||||
/// Generate code for an if expression.
|
||||
/// Return true if the statement must early return
|
||||
fn gen_if<'ctx, 'a>(
|
||||
fn gen_if(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
|
@ -157,9 +157,9 @@ pub trait CodeGenerator {
|
|||
gen_if(self, ctx, stmt)
|
||||
}
|
||||
|
||||
fn gen_with<'ctx, 'a>(
|
||||
fn gen_with(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
|
@ -171,9 +171,9 @@ pub trait CodeGenerator {
|
|||
/// Generate code for a statement
|
||||
///
|
||||
/// Return true if the statement must early return
|
||||
fn gen_stmt<'ctx, 'a>(
|
||||
fn gen_stmt(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
|
@ -183,9 +183,9 @@ pub trait CodeGenerator {
|
|||
}
|
||||
|
||||
/// Generates code for a block statement.
|
||||
fn gen_block<'ctx, 'a, 'b, I: Iterator<Item = &'b Stmt<Option<Type>>>>(
|
||||
fn gen_block<'a, I: Iterator<Item = &'a Stmt<Option<Type>>>>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmts: I,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
|
@ -194,22 +194,22 @@ pub trait CodeGenerator {
|
|||
gen_block(self, ctx, stmts)
|
||||
}
|
||||
|
||||
/// See [bool_to_i1].
|
||||
fn bool_to_i1<'ctx, 'a>(
|
||||
/// See [`bool_to_i1`].
|
||||
fn bool_to_i1<'ctx>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, 'a>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
bool_value: IntValue<'ctx>
|
||||
) -> IntValue<'ctx> {
|
||||
bool_to_i1(&ctx.builder, bool_value)
|
||||
}
|
||||
|
||||
/// See [bool_to_i8].
|
||||
fn bool_to_i8<'ctx, 'a>(
|
||||
/// See [`bool_to_i8`].
|
||||
fn bool_to_i8<'ctx>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, 'a>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
bool_value: IntValue<'ctx>
|
||||
) -> IntValue<'ctx> {
|
||||
bool_to_i8(&ctx.builder, &ctx.ctx, bool_value)
|
||||
bool_to_i8(&ctx.builder, ctx.ctx, bool_value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,17 +219,21 @@ pub struct DefaultCodeGenerator {
|
|||
}
|
||||
|
||||
impl DefaultCodeGenerator {
|
||||
#[must_use]
|
||||
pub fn new(name: String, size_t: u32) -> DefaultCodeGenerator {
|
||||
assert!(size_t == 32 || size_t == 64);
|
||||
assert!(matches!(size_t, 32 | 64));
|
||||
DefaultCodeGenerator { name, size_t }
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeGenerator for DefaultCodeGenerator {
|
||||
|
||||
/// Returns the name for this [`CodeGenerator`].
|
||||
fn get_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Returns an LLVM integer type representing `size_t`.
|
||||
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx> {
|
||||
// it should be unsigned, but we don't really need unsigned and this could save us from
|
||||
// having to do a bit cast...
|
||||
|
|
|
@ -12,6 +12,7 @@ use inkwell::{
|
|||
};
|
||||
use nac3parser::ast::Expr;
|
||||
|
||||
#[must_use]
|
||||
pub fn load_irrt(ctx: &Context) -> Module {
|
||||
let bitcode_buf = MemoryBuffer::create_from_memory_range(
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/irrt.bc")),
|
||||
|
@ -33,9 +34,9 @@ pub fn load_irrt(ctx: &Context) -> Module {
|
|||
|
||||
// repeated squaring method adapted from GNU Scientific Library:
|
||||
// https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
|
||||
pub fn integer_power<'ctx, 'a>(
|
||||
pub fn integer_power<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
base: IntValue<'ctx>,
|
||||
exp: IntValue<'ctx>,
|
||||
signed: bool,
|
||||
|
@ -74,9 +75,9 @@ pub fn integer_power<'ctx, 'a>(
|
|||
.into_int_value()
|
||||
}
|
||||
|
||||
pub fn calculate_len_for_slice_range<'ctx, 'a>(
|
||||
pub fn calculate_len_for_slice_range<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
start: IntValue<'ctx>,
|
||||
end: IntValue<'ctx>,
|
||||
step: IntValue<'ctx>,
|
||||
|
@ -151,11 +152,11 @@ pub fn calculate_len_for_slice_range<'ctx, 'a>(
|
|||
/// ,step
|
||||
/// )
|
||||
/// ```
|
||||
pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
||||
pub fn handle_slice_indices<'ctx, G: CodeGenerator>(
|
||||
start: &Option<Box<Expr<Option<Type>>>>,
|
||||
end: &Option<Box<Expr<Option<Type>>>>,
|
||||
step: &Option<Box<Expr<Option<Type>>>>,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut G,
|
||||
list: PointerValue<'ctx>,
|
||||
) -> Result<Option<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>)>, String> {
|
||||
|
@ -260,9 +261,9 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
|||
|
||||
/// this function allows index out of range, since python
|
||||
/// allows index out of range in slice (`a = [1,2,3]; a[1:10] == [2,3]`).
|
||||
pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
|
||||
pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>(
|
||||
i: &Expr<Option<Type>>,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut G,
|
||||
length: IntValue<'ctx>,
|
||||
) -> Result<Option<IntValue<'ctx>>, String> {
|
||||
|
@ -288,11 +289,11 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
|
|||
}
|
||||
|
||||
/// This function handles 'end' **inclusively**.
|
||||
/// Order of tuples assign_idx and value_idx is ('start', 'end', 'step').
|
||||
/// Order of tuples `assign_idx` and `value_idx` is ('start', 'end', 'step').
|
||||
/// Negative index should be handled before entering this function
|
||||
pub fn list_slice_assignment<'ctx, 'a>(
|
||||
pub fn list_slice_assignment<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
dest_arr: PointerValue<'ctx>,
|
||||
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
||||
|
@ -348,7 +349,7 @@ pub fn list_slice_assignment<'ctx, 'a>(
|
|||
let src_end = ctx.builder
|
||||
.build_select(
|
||||
ctx.builder.build_int_compare(
|
||||
inkwell::IntPredicate::SLT,
|
||||
IntPredicate::SLT,
|
||||
src_idx.2,
|
||||
zero,
|
||||
"is_neg",
|
||||
|
@ -361,7 +362,7 @@ pub fn list_slice_assignment<'ctx, 'a>(
|
|||
let dest_end = ctx.builder
|
||||
.build_select(
|
||||
ctx.builder.build_int_compare(
|
||||
inkwell::IntPredicate::SLT,
|
||||
IntPredicate::SLT,
|
||||
dest_idx.2,
|
||||
zero,
|
||||
"is_neg",
|
||||
|
@ -450,9 +451,9 @@ pub fn list_slice_assignment<'ctx, 'a>(
|
|||
}
|
||||
|
||||
/// Generates a call to `isinf` in IR. Returns an `i1` representing the result.
|
||||
pub fn call_isinf<'ctx, 'a>(
|
||||
pub fn call_isinf<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &CodeGenContext<'ctx, 'a>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
v: FloatValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let intrinsic_fn = ctx.module.get_function("__nac3_isinf").unwrap_or_else(|| {
|
||||
|
@ -470,9 +471,9 @@ pub fn call_isinf<'ctx, 'a>(
|
|||
}
|
||||
|
||||
/// Generates a call to `isnan` in IR. Returns an `i1` representing the result.
|
||||
pub fn call_isnan<'ctx, 'a>(
|
||||
pub fn call_isnan<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &CodeGenContext<'ctx, 'a>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
v: FloatValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let intrinsic_fn = ctx.module.get_function("__nac3_isnan").unwrap_or_else(|| {
|
||||
|
@ -490,8 +491,8 @@ pub fn call_isnan<'ctx, 'a>(
|
|||
}
|
||||
|
||||
/// Generates a call to `gamma` in IR. Returns an `f64` representing the result.
|
||||
pub fn call_gamma<'ctx, 'a>(
|
||||
ctx: &CodeGenContext<'ctx, 'a>,
|
||||
pub fn call_gamma<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
v: FloatValue<'ctx>,
|
||||
) -> FloatValue<'ctx> {
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
|
@ -509,8 +510,8 @@ pub fn call_gamma<'ctx, 'a>(
|
|||
}
|
||||
|
||||
/// Generates a call to `gammaln` in IR. Returns an `f64` representing the result.
|
||||
pub fn call_gammaln<'ctx, 'a>(
|
||||
ctx: &CodeGenContext<'ctx, 'a>,
|
||||
pub fn call_gammaln<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
v: FloatValue<'ctx>,
|
||||
) -> FloatValue<'ctx> {
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
|
@ -528,8 +529,8 @@ pub fn call_gammaln<'ctx, 'a>(
|
|||
}
|
||||
|
||||
/// Generates a call to `j0` in IR. Returns an `f64` representing the result.
|
||||
pub fn call_j0<'ctx, 'a>(
|
||||
ctx: &CodeGenContext<'ctx, 'a>,
|
||||
pub fn call_j0<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
v: FloatValue<'ctx>,
|
||||
) -> FloatValue<'ctx> {
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
|
|
|
@ -81,8 +81,9 @@ pub struct CodeGenTargetMachineOptions {
|
|||
|
||||
impl CodeGenTargetMachineOptions {
|
||||
|
||||
/// Creates an instance of [CodeGenTargetMachineOptions] using the triple of the host machine.
|
||||
/// Creates an instance of [`CodeGenTargetMachineOptions`] using the triple of the host machine.
|
||||
/// Other options are set to defaults.
|
||||
#[must_use]
|
||||
pub fn from_host_triple() -> CodeGenTargetMachineOptions {
|
||||
CodeGenTargetMachineOptions {
|
||||
triple: TargetMachine::get_default_triple().as_str().to_string_lossy().into_owned(),
|
||||
|
@ -93,8 +94,9 @@ impl CodeGenTargetMachineOptions {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates an instance of [CodeGenTargetMachineOptions] using the properties of the host
|
||||
/// Creates an instance of [`CodeGenTargetMachineOptions`] using the properties of the host
|
||||
/// machine. Other options are set to defaults.
|
||||
#[must_use]
|
||||
pub fn from_host() -> CodeGenTargetMachineOptions {
|
||||
CodeGenTargetMachineOptions {
|
||||
cpu: TargetMachine::get_host_cpu_name().to_string(),
|
||||
|
@ -103,16 +105,17 @@ impl CodeGenTargetMachineOptions {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a [TargetMachine] using the target options specified by this struct.
|
||||
/// Creates a [`TargetMachine`] using the target options specified by this struct.
|
||||
///
|
||||
/// See [Target::create_target_machine].
|
||||
/// See [`Target::create_target_machine`].
|
||||
#[must_use]
|
||||
pub fn create_target_machine(
|
||||
&self,
|
||||
level: OptimizationLevel,
|
||||
) -> Option<TargetMachine> {
|
||||
let triple = TargetTriple::create(self.triple.as_str());
|
||||
let target = Target::from_triple(&triple)
|
||||
.expect(format!("could not create target from target triple {}", self.triple).as_str());
|
||||
.unwrap_or_else(|_| panic!("could not create target from target triple {}", self.triple));
|
||||
|
||||
target.create_target_machine(
|
||||
&triple,
|
||||
|
@ -126,44 +129,76 @@ impl CodeGenTargetMachineOptions {
|
|||
}
|
||||
|
||||
pub struct CodeGenContext<'ctx, 'a> {
|
||||
/// The LLVM context associated with [this context][CodeGenContext].
|
||||
pub ctx: &'ctx Context,
|
||||
|
||||
/// The [Builder] instance for creating LLVM IR statements.
|
||||
pub builder: Builder<'ctx>,
|
||||
/// The [DebugInfoBuilder], [compilation unit information][DICompileUnit], and
|
||||
/// [scope information][DIScope] of this context.
|
||||
pub debug_info: (DebugInfoBuilder<'ctx>, DICompileUnit<'ctx>, DIScope<'ctx>),
|
||||
|
||||
/// The module for which [this context][CodeGenContext] is generating into.
|
||||
pub module: Module<'ctx>,
|
||||
|
||||
/// The [TopLevelContext] associated with [this context][CodeGenContext].
|
||||
pub top_level: &'a TopLevelContext,
|
||||
pub unifier: Unifier,
|
||||
pub resolver: Arc<dyn SymbolResolver + Send + Sync>,
|
||||
pub static_value_store: Arc<Mutex<StaticValueStore>>,
|
||||
|
||||
/// A [HashMap] containing the mapping between the names of variables currently in-scope and
|
||||
/// its value information.
|
||||
pub var_assignment: HashMap<StrRef, VarValue<'ctx>>,
|
||||
|
||||
///
|
||||
pub type_cache: HashMap<Type, BasicTypeEnum<'ctx>>,
|
||||
pub primitives: PrimitiveStore,
|
||||
pub calls: Arc<HashMap<CodeLocation, CallId>>,
|
||||
pub registry: &'a WorkerRegistry,
|
||||
// const string cache
|
||||
|
||||
/// Cache for constant strings.
|
||||
pub const_strings: HashMap<String, BasicValueEnum<'ctx>>,
|
||||
// stores the alloca for variables
|
||||
|
||||
/// [BasicBlock] containing all `alloca` statements for the current function.
|
||||
pub init_bb: BasicBlock<'ctx>,
|
||||
pub exception_val: Option<PointerValue<'ctx>>,
|
||||
|
||||
/// The header and exit basic blocks of a loop in this context. See
|
||||
/// https://llvm.org/docs/LoopTerminology.html for explanation of these terminology.
|
||||
pub loop_target: Option<(BasicBlock<'ctx>, BasicBlock<'ctx>)>,
|
||||
// unwind target bb
|
||||
|
||||
/// The target [BasicBlock] to jump to when performing stack unwind.
|
||||
pub unwind_target: Option<BasicBlock<'ctx>>,
|
||||
// return target bb, just emit ret if no such target
|
||||
|
||||
/// The target [BasicBlock] to jump to before returning from the function.
|
||||
///
|
||||
/// If this field is [None] when generating a return from a function, `ret` with no argument can
|
||||
/// be emitted.
|
||||
pub return_target: Option<BasicBlock<'ctx>>,
|
||||
|
||||
/// The [PointerValue] containing the return value of the function.
|
||||
pub return_buffer: Option<PointerValue<'ctx>>,
|
||||
|
||||
// outer catch clauses
|
||||
pub outer_catch_clauses:
|
||||
Option<(Vec<Option<BasicValueEnum<'ctx>>>, BasicBlock<'ctx>, PhiValue<'ctx>)>,
|
||||
|
||||
/// Whether `sret` is needed for the first parameter of the function.
|
||||
///
|
||||
/// See [need_sret].
|
||||
pub need_sret: bool,
|
||||
|
||||
/// The current source location.
|
||||
pub current_loc: Location,
|
||||
}
|
||||
|
||||
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||
|
||||
/// Whether the [current basic block][Builder::get_insert_block] referenced by `builder`
|
||||
/// contains a [terminator statement][BasicBlock::get_terminator].
|
||||
pub fn is_terminated(&self) -> bool {
|
||||
self.builder.get_insert_block().and_then(|bb| bb.get_terminator()).is_some()
|
||||
self.builder.get_insert_block().and_then(BasicBlock::get_terminator).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,43 +209,54 @@ pub struct WithCall {
|
|||
}
|
||||
|
||||
impl WithCall {
|
||||
#[must_use]
|
||||
pub fn new(fp: Fp) -> WithCall {
|
||||
WithCall { fp }
|
||||
}
|
||||
|
||||
pub fn run<'ctx>(&self, m: &Module<'ctx>) {
|
||||
(self.fp)(m)
|
||||
pub fn run(&self, m: &Module) {
|
||||
(self.fp)(m);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkerRegistry {
|
||||
sender: Arc<Sender<Option<CodeGenTask>>>,
|
||||
receiver: Arc<Receiver<Option<CodeGenTask>>>,
|
||||
|
||||
/// Whether any thread in this registry has panicked.
|
||||
panicked: AtomicBool,
|
||||
|
||||
/// The total number of tasks queued or completed in the registry.
|
||||
task_count: Mutex<usize>,
|
||||
|
||||
/// The number of threads available for this registry.
|
||||
thread_count: usize,
|
||||
wait_condvar: Condvar,
|
||||
top_level_ctx: Arc<TopLevelContext>,
|
||||
static_value_store: Arc<Mutex<StaticValueStore>>,
|
||||
|
||||
/// LLVM-related options for code generation.
|
||||
llvm_options: CodeGenLLVMOptions,
|
||||
}
|
||||
|
||||
impl WorkerRegistry {
|
||||
|
||||
/// Creates workers for this registry.
|
||||
#[must_use]
|
||||
pub fn create_workers<G: CodeGenerator + Send + 'static>(
|
||||
generators: Vec<Box<G>>,
|
||||
top_level_ctx: Arc<TopLevelContext>,
|
||||
llvm_options: &CodeGenLLVMOptions,
|
||||
f: Arc<WithCall>,
|
||||
f: &Arc<WithCall>,
|
||||
) -> (Arc<WorkerRegistry>, Vec<thread::JoinHandle<()>>) {
|
||||
let (sender, receiver) = unbounded();
|
||||
let task_count = Mutex::new(0);
|
||||
let wait_condvar = Condvar::new();
|
||||
|
||||
// init: 0 to be empty
|
||||
let mut static_value_store: StaticValueStore = Default::default();
|
||||
static_value_store.lookup.insert(Default::default(), 0);
|
||||
static_value_store.store.push(Default::default());
|
||||
let mut static_value_store = StaticValueStore::default();
|
||||
static_value_store.lookup.insert(Vec::default(), 0);
|
||||
static_value_store.store.push(HashMap::default());
|
||||
|
||||
let registry = Arc::new(WorkerRegistry {
|
||||
sender: Arc::new(sender),
|
||||
|
@ -225,19 +271,19 @@ impl WorkerRegistry {
|
|||
});
|
||||
|
||||
let mut handles = Vec::new();
|
||||
for mut generator in generators.into_iter() {
|
||||
for mut generator in generators {
|
||||
let registry = registry.clone();
|
||||
let registry2 = registry.clone();
|
||||
let f = f.clone();
|
||||
let handle = thread::spawn(move || {
|
||||
registry.worker_thread(generator.as_mut(), f);
|
||||
registry.worker_thread(generator.as_mut(), &f);
|
||||
});
|
||||
let handle = thread::spawn(move || {
|
||||
if let Err(e) = handle.join() {
|
||||
if let Some(e) = e.downcast_ref::<&'static str>() {
|
||||
eprintln!("Got an error: {}", e);
|
||||
eprintln!("Got an error: {e}");
|
||||
} else {
|
||||
eprintln!("Got an unknown error: {:?}", e);
|
||||
eprintln!("Got an unknown error: {e:?}");
|
||||
}
|
||||
registry2.panicked.store(true, Ordering::SeqCst);
|
||||
registry2.wait_condvar.notify_all();
|
||||
|
@ -273,17 +319,17 @@ impl WorkerRegistry {
|
|||
for handle in handles {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
if self.panicked.load(Ordering::SeqCst) {
|
||||
panic!("tasks panicked");
|
||||
}
|
||||
assert!(!self.panicked.load(Ordering::SeqCst), "tasks panicked");
|
||||
}
|
||||
|
||||
/// Adds a task to this [`WorkerRegistry`].
|
||||
pub fn add_task(&self, task: CodeGenTask) {
|
||||
*self.task_count.lock() += 1;
|
||||
self.sender.send(Some(task)).unwrap();
|
||||
}
|
||||
|
||||
fn worker_thread<G: CodeGenerator>(&self, generator: &mut G, f: Arc<WithCall>) {
|
||||
/// Function executed by worker thread for generating IR for each function.
|
||||
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());
|
||||
|
@ -316,9 +362,7 @@ impl WorkerRegistry {
|
|||
*self.task_count.lock() -= 1;
|
||||
self.wait_condvar.notify_all();
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
panic!("Codegen error: {}", errors.into_iter().sorted().join("\n----------\n"));
|
||||
}
|
||||
assert!(errors.is_empty(), "Codegen error: {}", errors.into_iter().sorted().join("\n----------\n"));
|
||||
|
||||
let result = module.verify();
|
||||
if let Err(err) = result {
|
||||
|
@ -327,9 +371,11 @@ impl WorkerRegistry {
|
|||
}
|
||||
|
||||
let pass_options = PassBuilderOptions::create();
|
||||
let target_machine = self.llvm_options.target.create_target_machine(
|
||||
self.llvm_options.opt_level
|
||||
).expect(format!("could not create target machine from properties {:?}", self.llvm_options.target).as_str());
|
||||
let target_machine = self
|
||||
.llvm_options
|
||||
.target
|
||||
.create_target_machine(self.llvm_options.opt_level)
|
||||
.unwrap_or_else(|| panic!("could not create target machine from properties {:?}", self.llvm_options.target));
|
||||
let passes = format!("default<O{}>", self.llvm_options.opt_level as u32);
|
||||
let result = module.run_passes(passes.as_str(), &target_machine, pass_options);
|
||||
if let Err(err) = result {
|
||||
|
@ -374,7 +420,7 @@ fn get_llvm_type<'ctx>(
|
|||
use TypeEnum::*;
|
||||
// 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(|| {
|
||||
type_cache.get(&unifier.get_representative(ty)).copied().unwrap_or_else(|| {
|
||||
let ty_enum = unifier.get_ty(ty);
|
||||
let result = match &*ty_enum {
|
||||
TObj { obj_id, fields, .. } => {
|
||||
|
@ -383,8 +429,8 @@ fn get_llvm_type<'ctx>(
|
|||
match (unifier.get_ty(ty).as_ref(), unifier.get_ty(primitives.option).as_ref())
|
||||
{
|
||||
(
|
||||
TypeEnum::TObj { obj_id, params, .. },
|
||||
TypeEnum::TObj { obj_id: opt_id, .. },
|
||||
TObj { obj_id, params, .. },
|
||||
TObj { obj_id: opt_id, .. },
|
||||
) if *obj_id == *opt_id => {
|
||||
return get_llvm_type(
|
||||
ctx,
|
||||
|
@ -409,32 +455,31 @@ fn get_llvm_type<'ctx>(
|
|||
&*definition.read()
|
||||
{
|
||||
let name = unifier.stringify(ty);
|
||||
match module.get_struct_type(&name) {
|
||||
Some(t) => t.ptr_type(AddressSpace::default()).into(),
|
||||
None => {
|
||||
let struct_type = ctx.opaque_struct_type(&name);
|
||||
type_cache.insert(
|
||||
unifier.get_representative(ty),
|
||||
struct_type.ptr_type(AddressSpace::default()).into()
|
||||
);
|
||||
let fields = fields_list
|
||||
.iter()
|
||||
.map(|f| {
|
||||
get_llvm_type(
|
||||
ctx,
|
||||
module,
|
||||
generator,
|
||||
unifier,
|
||||
top_level,
|
||||
type_cache,
|
||||
primitives,
|
||||
fields[&f.0].0,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
struct_type.set_body(&fields, false);
|
||||
if let Some(t) = module.get_struct_type(&name) {
|
||||
t.ptr_type(AddressSpace::default()).into()
|
||||
} else {
|
||||
let struct_type = ctx.opaque_struct_type(&name);
|
||||
type_cache.insert(
|
||||
unifier.get_representative(ty),
|
||||
struct_type.ptr_type(AddressSpace::default()).into()
|
||||
}
|
||||
);
|
||||
let fields = fields_list
|
||||
.iter()
|
||||
.map(|f| {
|
||||
get_llvm_type(
|
||||
ctx,
|
||||
module,
|
||||
generator,
|
||||
unifier,
|
||||
top_level,
|
||||
type_cache,
|
||||
primitives,
|
||||
fields[&f.0].0,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
struct_type.set_body(&fields, false);
|
||||
struct_type.ptr_type(AddressSpace::default()).into()
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
|
@ -472,12 +517,12 @@ fn get_llvm_type<'ctx>(
|
|||
})
|
||||
}
|
||||
|
||||
/// Retrieves the [LLVM type][BasicTypeEnum] corresponding to the [Type].
|
||||
/// Retrieves the [LLVM type][`BasicTypeEnum`] corresponding to the [`Type`].
|
||||
///
|
||||
/// This function is used mainly to obtain the ABI representation of `ty`, e.g. a `bool` is
|
||||
/// would be represented by an `i1`.
|
||||
///
|
||||
/// The difference between the in-memory representation (as returned by [get_llvm_type]) and the
|
||||
/// The difference between the in-memory representation (as returned by [`get_llvm_type`]) and the
|
||||
/// ABI representation is that the in-memory representation must be at least byte-sized and must
|
||||
/// be byte-aligned for the variable to be addressable in memory, whereas there is no such
|
||||
/// restriction for ABI representations.
|
||||
|
@ -500,17 +545,25 @@ fn get_llvm_abi_type<'ctx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn need_sret<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>) -> bool {
|
||||
fn need_sret_impl<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>, maybe_large: bool) -> bool {
|
||||
/// Whether `sret` is needed for a return value with type `ty`.
|
||||
///
|
||||
/// When returning a large data structure (e.g. structures that do not fit in 1-2 native words of
|
||||
/// the target processor) by value, a synthetic parameter with a pointer type will be passed in the
|
||||
/// slot of the first parameter to act as the location of which the return value is passed into.
|
||||
///
|
||||
/// See <https://releases.llvm.org/14.0.0/docs/LangRef.html#parameter-attributes> for more
|
||||
/// information.
|
||||
fn need_sret(ty: BasicTypeEnum) -> bool {
|
||||
fn need_sret_impl(ty: BasicTypeEnum, maybe_large: bool) -> bool {
|
||||
match ty {
|
||||
BasicTypeEnum::IntType(_) | BasicTypeEnum::PointerType(_) => false,
|
||||
BasicTypeEnum::FloatType(_) if maybe_large => false,
|
||||
BasicTypeEnum::StructType(ty) if maybe_large && ty.count_fields() <= 2 =>
|
||||
ty.get_field_types().iter().any(|ty| need_sret_impl(ctx, *ty, false)),
|
||||
ty.get_field_types().iter().any(|ty| need_sret_impl(*ty, false)),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
need_sret_impl(ctx, ty, true)
|
||||
need_sret_impl(ty, true)
|
||||
}
|
||||
|
||||
/// Implementation for generating LLVM IR for a function.
|
||||
|
@ -532,7 +585,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
unifier.top_level = Some(top_level_ctx.clone());
|
||||
|
||||
let mut cache = HashMap::new();
|
||||
for (a, b) in task.subst.iter() {
|
||||
for (a, b) in &task.subst {
|
||||
// this should be unification between variables and concrete types
|
||||
// and should not cause any problem...
|
||||
let b = task.store.to_unifier_type(&mut unifier, &primitives, *b, &mut cache);
|
||||
|
@ -546,7 +599,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
Err(err)
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// rebuild primitive store with unique representatives
|
||||
|
@ -589,22 +642,21 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
(primitives.range, context.i32_type().array_type(3).ptr_type(AddressSpace::default()).into()),
|
||||
(primitives.exception, {
|
||||
let name = "Exception";
|
||||
match module.get_struct_type(name) {
|
||||
Some(t) => t.ptr_type(AddressSpace::default()).as_basic_type_enum(),
|
||||
None => {
|
||||
let exception = context.opaque_struct_type("Exception");
|
||||
let int32 = context.i32_type().into();
|
||||
let int64 = context.i64_type().into();
|
||||
let str_ty = module.get_struct_type("str").unwrap().as_basic_type_enum();
|
||||
let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64];
|
||||
exception.set_body(&fields, false);
|
||||
exception.ptr_type(AddressSpace::default()).as_basic_type_enum()
|
||||
}
|
||||
if let Some(t) = module.get_struct_type(name) {
|
||||
t.ptr_type(AddressSpace::default()).as_basic_type_enum()
|
||||
} else {
|
||||
let exception = context.opaque_struct_type("Exception");
|
||||
let int32 = context.i32_type().into();
|
||||
let int64 = context.i64_type().into();
|
||||
let str_ty = module.get_struct_type("str").unwrap().as_basic_type_enum();
|
||||
let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64];
|
||||
exception.set_body(&fields, false);
|
||||
exception.ptr_type(AddressSpace::default()).as_basic_type_enum()
|
||||
}
|
||||
})
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.copied()
|
||||
.collect();
|
||||
// NOTE: special handling of option cannot use this type cache since it contains type var,
|
||||
// handled inside get_llvm_type instead
|
||||
|
@ -631,7 +683,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
Some(get_llvm_abi_type(context, &module, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, &primitives, ret))
|
||||
};
|
||||
|
||||
let has_sret = ret_type.map_or(false, |ty| need_sret(context, ty));
|
||||
let has_sret = ret_type.map_or(false, |ty| need_sret(ty));
|
||||
let mut params = args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
|
@ -680,7 +732,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
let body_bb = context.append_basic_block(fn_val, "body");
|
||||
|
||||
let mut var_assignment = HashMap::new();
|
||||
let offset = if has_sret { 1 } else { 0 };
|
||||
let offset = u32::from(has_sret);
|
||||
for (n, arg) in args.iter().enumerate() {
|
||||
let param = fn_val.get_nth_param((n as u32) + offset).unwrap();
|
||||
let local_type = get_llvm_type(
|
||||
|
@ -704,7 +756,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
let param_val = param.into_int_value();
|
||||
|
||||
if expected_ty.get_bit_width() == 8 && param_val.get_type().get_bit_width() == 1 {
|
||||
bool_to_i8(&builder, &context, param_val)
|
||||
bool_to_i8(&builder, context, param_val)
|
||||
} else {
|
||||
param_val
|
||||
}.into()
|
||||
|
@ -726,7 +778,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
let store = registry.static_value_store.lock();
|
||||
store.store[task.id].clone()
|
||||
};
|
||||
for (k, v) in static_values.into_iter() {
|
||||
for (k, v) in static_values {
|
||||
let (_, static_val, _) = var_assignment.get_mut(&args[k].name).unwrap();
|
||||
*static_val = Some(v);
|
||||
}
|
||||
|
@ -796,19 +848,19 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
return_buffer,
|
||||
unwind_target: None,
|
||||
outer_catch_clauses: None,
|
||||
const_strings: Default::default(),
|
||||
const_strings: HashMap::default(),
|
||||
registry,
|
||||
var_assignment,
|
||||
type_cache,
|
||||
primitives,
|
||||
init_bb,
|
||||
exception_val: Default::default(),
|
||||
exception_val: Option::default(),
|
||||
builder,
|
||||
module,
|
||||
unifier,
|
||||
static_value_store,
|
||||
need_sret: has_sret,
|
||||
current_loc: Default::default(),
|
||||
current_loc: Location::default(),
|
||||
debug_info: (dibuilder, compile_unit, func_scope.as_debug_info_scope()),
|
||||
};
|
||||
|
||||
|
@ -841,12 +893,12 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
|
||||
/// Generates LLVM IR for a function.
|
||||
///
|
||||
/// * `context` - The [LLVM Context][Context] used in generating the function body.
|
||||
/// * `generator` - The [CodeGenerator] for generating various program constructs.
|
||||
/// * `registry` - The [WorkerRegistry] responsible for monitoring this function generation task.
|
||||
/// * `builder` - The [Builder] used for generating LLVM IR.
|
||||
/// * `module` - The [Module] of which the generated LLVM function will be inserted into.
|
||||
/// * `task` - The [CodeGenTask] associated with this function generation task.
|
||||
/// * `context` - The [LLVM Context][`Context`] used in generating the function body.
|
||||
/// * `generator` - The [`CodeGenerator`] for generating various program constructs.
|
||||
/// * `registry` - The [`WorkerRegistry`] responsible for monitoring this function generation task.
|
||||
/// * `builder` - The [`Builder`] used for generating LLVM IR.
|
||||
/// * `module` - The [`Module`] of which the generated LLVM function will be inserted into.
|
||||
/// * `task` - The [`CodeGenTask`] associated with this function generation task.
|
||||
///
|
||||
pub fn gen_func<'ctx, G: CodeGenerator>(
|
||||
context: &'ctx Context,
|
||||
|
@ -864,15 +916,15 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
|
||||
/// Converts the value of a boolean-like value `bool_value` into an `i1`.
|
||||
fn bool_to_i1<'ctx>(builder: &Builder<'ctx>, bool_value: IntValue<'ctx>) -> IntValue<'ctx> {
|
||||
if bool_value.get_type().get_bit_width() != 1 {
|
||||
if bool_value.get_type().get_bit_width() == 1 {
|
||||
bool_value
|
||||
} else {
|
||||
builder.build_int_compare(
|
||||
IntPredicate::NE,
|
||||
bool_value,
|
||||
bool_value.get_type().const_zero(),
|
||||
"tobool"
|
||||
)
|
||||
} else {
|
||||
bool_value
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -913,9 +965,9 @@ fn bool_to_i8<'ctx>(
|
|||
/// let cmp = lo < hi;
|
||||
/// ```
|
||||
///
|
||||
/// Returns an `i1` [IntValue] representing the result of whether the `value` is in the range.
|
||||
fn gen_in_range_check<'ctx, 'a>(
|
||||
ctx: &CodeGenContext<'ctx, 'a>,
|
||||
/// Returns an `i1` [`IntValue`] representing the result of whether the `value` is in the range.
|
||||
fn gen_in_range_check<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
value: IntValue<'ctx>,
|
||||
stop: IntValue<'ctx>,
|
||||
step: IntValue<'ctx>,
|
||||
|
|
|
@ -24,9 +24,9 @@ use nac3parser::ast::{
|
|||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// See [CodeGenerator::gen_var_alloc].
|
||||
pub fn gen_var<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
/// See [`CodeGenerator::gen_var_alloc`].
|
||||
pub fn gen_var<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
name: Option<&str>,
|
||||
) -> Result<PointerValue<'ctx>, String> {
|
||||
|
@ -54,10 +54,10 @@ pub fn gen_var<'ctx, 'a>(
|
|||
Ok(ptr)
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_store_target].
|
||||
pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_store_target`].
|
||||
pub fn gen_store_target<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
pattern: &Expr<Option<Type>>,
|
||||
name: Option<&str>,
|
||||
) -> Result<Option<PointerValue<'ctx>>, String> {
|
||||
|
@ -84,9 +84,7 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
|||
} else {
|
||||
return Ok(None)
|
||||
};
|
||||
let ptr = if let BasicValueEnum::PointerValue(v) = val {
|
||||
v
|
||||
} else {
|
||||
let BasicValueEnum::PointerValue(ptr) = val else {
|
||||
unreachable!();
|
||||
};
|
||||
unsafe {
|
||||
|
@ -164,10 +162,10 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
|||
}))
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_assign].
|
||||
pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_assign`].
|
||||
pub fn gen_assign<'ctx, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
target: &Expr<Option<Type>>,
|
||||
value: ValueEnum<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
|
@ -212,14 +210,14 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
let Some(src_ind) = handle_slice_indices(&None, &None, &None, ctx, generator, value)? else {
|
||||
return Ok(())
|
||||
};
|
||||
list_slice_assignment(generator, ctx, ty, ls, (start, end, step), value, src_ind)
|
||||
list_slice_assignment(generator, ctx, ty, ls, (start, end, step), value, src_ind);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let name = if let ExprKind::Name { id, .. } = &target.node {
|
||||
format!("{}.addr", id.to_string())
|
||||
format!("{id}.addr")
|
||||
} else {
|
||||
String::from("target.addr")
|
||||
};
|
||||
|
@ -241,10 +239,10 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_for].
|
||||
pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_for`].
|
||||
pub fn gen_for<G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::For { iter, target, body, orelse, .. } = &stmt.node {
|
||||
|
@ -255,7 +253,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
let int32 = ctx.ctx.i32_type();
|
||||
let size_t = generator.get_size_type(ctx.ctx);
|
||||
let zero = int32.const_zero();
|
||||
let current = ctx.builder.get_insert_block().and_then(|bb| bb.get_parent()).unwrap();
|
||||
let current = ctx.builder.get_insert_block().and_then(BasicBlock::get_parent).unwrap();
|
||||
let body_bb = ctx.ctx.append_basic_block(current, "for.body");
|
||||
let cont_bb = ctx.ctx.append_basic_block(current, "for.end");
|
||||
// if there is no orelse, we just go to cont_bb
|
||||
|
@ -368,7 +366,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
generator.gen_block(ctx, body.iter())?;
|
||||
}
|
||||
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
for (k, (_, _, counter)) in &var_assignment {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
*static_val = None;
|
||||
|
@ -387,7 +385,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
for (k, (_, _, counter)) in &var_assignment {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
*static_val = None;
|
||||
|
@ -402,10 +400,10 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_while].
|
||||
pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_while`].
|
||||
pub fn gen_while<G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::While { test, body, orelse, .. } = &stmt.node {
|
||||
|
@ -441,7 +439,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
};
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
generator.gen_block(ctx, body.iter())?;
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
for (k, (_, _, counter)) in &var_assignment {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
*static_val = None;
|
||||
|
@ -457,7 +455,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
}
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
for (k, (_, _, counter)) in &var_assignment {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
*static_val = None;
|
||||
|
@ -471,10 +469,10 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_if].
|
||||
pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_if`].
|
||||
pub fn gen_if<G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
if let StmtKind::If { test, body, orelse, .. } = &stmt.node {
|
||||
|
@ -503,7 +501,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
};
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
generator.gen_block(ctx, body.iter())?;
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
for (k, (_, _, counter)) in &var_assignment {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
*static_val = None;
|
||||
|
@ -529,7 +527,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
if let Some(cont_bb) = cont_bb {
|
||||
ctx.builder.position_at_end(cont_bb);
|
||||
}
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
for (k, (_, _, counter)) in &var_assignment {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
*static_val = None;
|
||||
|
@ -541,8 +539,8 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn final_proxy<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
pub fn final_proxy<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
target: BasicBlock<'ctx>,
|
||||
block: BasicBlock<'ctx>,
|
||||
final_data: &mut (PointerValue, Vec<BasicBlock<'ctx>>, Vec<BasicBlock<'ctx>>),
|
||||
|
@ -560,9 +558,9 @@ pub fn final_proxy<'ctx, 'a>(
|
|||
|
||||
/// Inserts the declaration of the builtin function with the specified `symbol` name, and returns
|
||||
/// the function.
|
||||
pub fn get_builtins<'ctx, 'a>(
|
||||
pub fn get_builtins<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
symbol: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
ctx.module.get_function(symbol).unwrap_or_else(|| {
|
||||
|
@ -571,8 +569,7 @@ pub fn get_builtins<'ctx, 'a>(
|
|||
.ctx
|
||||
.void_type()
|
||||
.fn_type(&[ctx.get_llvm_type(generator, ctx.primitives.exception).into()], false),
|
||||
"__nac3_resume" => ctx.ctx.void_type().fn_type(&[], false),
|
||||
"__nac3_end_catch" => ctx.ctx.void_type().fn_type(&[], false),
|
||||
"__nac3_resume" | "__nac3_end_catch" => ctx.ctx.void_type().fn_type(&[], false),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let fun = ctx.module.add_function(symbol, ty, None);
|
||||
|
@ -586,8 +583,8 @@ pub fn get_builtins<'ctx, 'a>(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn exn_constructor<'ctx, 'a>(
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
pub fn exn_constructor<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
_fun: (&FunSignature, DefinitionId),
|
||||
mut args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
|
@ -613,20 +610,20 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
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 empty_string = ctx.gen_const(generator, &Constant::Str(String::new()), 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, ctx.primitives.str)?
|
||||
} else {
|
||||
let msg = if args.is_empty() {
|
||||
empty_string.unwrap()
|
||||
} else {
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?
|
||||
};
|
||||
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, ctx.primitives.int64)?
|
||||
} else {
|
||||
for i in &[6, 7, 8] {
|
||||
let value = if args.is_empty() {
|
||||
ctx.ctx.i64_type().const_zero().into()
|
||||
} else {
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?
|
||||
};
|
||||
let ptr = ctx.builder.build_in_bounds_gep(
|
||||
zelf,
|
||||
|
@ -636,7 +633,7 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
ctx.builder.build_store(ptr, value);
|
||||
}
|
||||
// set file, func to empty string
|
||||
for i in [1, 4].iter() {
|
||||
for i in &[1, 4] {
|
||||
let ptr = ctx.builder.build_in_bounds_gep(
|
||||
zelf,
|
||||
&[zero, int32.const_int(*i, false)],
|
||||
|
@ -645,7 +642,7 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
ctx.builder.build_store(ptr, empty_string.unwrap());
|
||||
}
|
||||
// set ints to zero
|
||||
for i in [2, 3].iter() {
|
||||
for i in &[2, 3] {
|
||||
let ptr = ctx.builder.build_in_bounds_gep(
|
||||
zelf,
|
||||
&[zero, int32.const_int(*i, false)],
|
||||
|
@ -661,9 +658,9 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
///
|
||||
/// * `exception` - The exception thrown by the `raise` statement.
|
||||
/// * `loc` - The location where the exception is raised from.
|
||||
pub fn gen_raise<'ctx, 'a>(
|
||||
pub fn gen_raise<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
exception: Option<&BasicValueEnum<'ctx>>,
|
||||
loc: Location,
|
||||
) {
|
||||
|
@ -768,7 +765,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
|
||||
let mut clauses = Vec::new();
|
||||
let mut found_catch_all = false;
|
||||
for handler_node in handlers.iter() {
|
||||
for handler_node in handlers {
|
||||
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler_node.node;
|
||||
// none or Exception
|
||||
if type_.is_none()
|
||||
|
@ -779,30 +776,30 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
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 obj_id = if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(type_.custom.unwrap()) {
|
||||
*obj_id
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let exception_name = format!("{}:{}", ctx.resolver.get_exception_id(obj_id.0), exn_name);
|
||||
let exn_id = ctx.resolver.get_string_id(&exception_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 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 obj_id = if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(type_.custom.unwrap()) {
|
||||
*obj_id
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let exception_name = format!("{}:{}", ctx.resolver.get_exception_id(obj_id.0), exn_name);
|
||||
let exn_id = ctx.resolver.get_string_id(&exception_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)
|
||||
all_clauses.extend_from_slice(&old_clauses.0);
|
||||
}
|
||||
}
|
||||
let old_clauses = ctx.outer_catch_clauses.replace((all_clauses, dispatcher, exn));
|
||||
|
@ -819,7 +816,9 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx.return_target = old_return;
|
||||
ctx.loop_target = old_loop_target.or(ctx.loop_target).take();
|
||||
|
||||
let old_unwind = if !finalbody.is_empty() {
|
||||
let old_unwind = if finalbody.is_empty() {
|
||||
None
|
||||
} else {
|
||||
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(
|
||||
|
@ -832,8 +831,6 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
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
|
||||
|
@ -886,7 +883,9 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
|
||||
let mut post_handlers = Vec::new();
|
||||
|
||||
let exnid = if !handlers.is_empty() {
|
||||
let exnid = if handlers.is_empty() {
|
||||
None
|
||||
} else {
|
||||
ctx.builder.position_at_end(dispatcher);
|
||||
unsafe {
|
||||
let zero = ctx.ctx.i32_type().const_zero();
|
||||
|
@ -897,8 +896,6 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
);
|
||||
Some(ctx.builder.build_load(exnid_ptr, "exnid"))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
for (handler_node, exn_type) in handlers.iter().zip(clauses.iter()) {
|
||||
|
@ -1011,7 +1008,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
let dest = ctx.builder.build_load(final_state, "final_dest");
|
||||
ctx.builder.build_indirect_branch(dest, &final_targets);
|
||||
}
|
||||
for block in final_paths.iter() {
|
||||
for block in &final_paths {
|
||||
if block.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(*block);
|
||||
ctx.builder.build_unconditional_branch(finalizer);
|
||||
|
@ -1034,10 +1031,10 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_with].
|
||||
pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_with`].
|
||||
pub fn gen_with<G: CodeGenerator>(
|
||||
_: &mut G,
|
||||
_: &mut CodeGenContext<'ctx, 'a>,
|
||||
_: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
// TODO: Implement with statement after finishing exceptions
|
||||
|
@ -1045,12 +1042,12 @@ pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
|
||||
/// Generates IR for a `return` statement.
|
||||
pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
|
||||
pub fn gen_return<G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
value: &Option<Box<Expr<Option<Type>>>>,
|
||||
) -> Result<(), String> {
|
||||
let func = ctx.builder.get_insert_block().and_then(|bb| bb.get_parent()).unwrap();
|
||||
let func = ctx.builder.get_insert_block().and_then(BasicBlock::get_parent).unwrap();
|
||||
let value = if let Some(v_expr) = value.as_ref() {
|
||||
if let Some(v) = generator.gen_expr(ctx, v_expr).transpose() {
|
||||
Some(
|
||||
|
@ -1091,15 +1088,15 @@ pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
});
|
||||
let value = value.as_ref().map(|v| v as &dyn BasicValue);
|
||||
ctx.builder.build_return(value.into());
|
||||
ctx.builder.build_return(value);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// See [CodeGenerator::gen_stmt].
|
||||
pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
||||
/// See [`CodeGenerator::gen_stmt`].
|
||||
pub fn gen_stmt<G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
ctx.current_loc = stmt.location;
|
||||
|
@ -1133,7 +1130,7 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|||
let Some(value) = generator.gen_expr(ctx, value)? else {
|
||||
return Ok(())
|
||||
};
|
||||
for target in targets.iter() {
|
||||
for target in targets {
|
||||
generator.gen_assign(ctx, target, value.clone())?;
|
||||
}
|
||||
}
|
||||
|
@ -1185,7 +1182,7 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|||
err_msg,
|
||||
[None, None, None],
|
||||
stmt.location,
|
||||
)
|
||||
);
|
||||
}
|
||||
_ => unimplemented!()
|
||||
};
|
||||
|
@ -1193,9 +1190,9 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
|
||||
/// Generates IR for a block statement contains `stmts`.
|
||||
pub fn gen_block<'ctx, 'a, 'b, G: CodeGenerator, I: Iterator<Item = &'b Stmt<Option<Type>>>>(
|
||||
pub fn gen_block<'a, G: CodeGenerator, I: Iterator<Item = &'a Stmt<Option<Type>>>>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'_, '_>,
|
||||
stmts: I,
|
||||
) -> Result<(), String> {
|
||||
for stmt in stmts {
|
||||
|
|
|
@ -63,12 +63,14 @@ impl SymbolResolver for Resolver {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||
self.id_to_def
|
||||
.read()
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("cannot find symbol `{}`", id))
|
||||
.ok_or_else(|| HashSet::from([
|
||||
format!("cannot find symbol `{}`", id),
|
||||
]))
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
|
@ -227,7 +229,7 @@ fn test_primitives() {
|
|||
threads,
|
||||
top_level,
|
||||
&llvm_options,
|
||||
f
|
||||
&f
|
||||
);
|
||||
registry.add_task(task);
|
||||
registry.wait_tasks_complete(handles);
|
||||
|
@ -417,7 +419,7 @@ fn test_simple_call() {
|
|||
threads,
|
||||
top_level,
|
||||
&llvm_options,
|
||||
f
|
||||
&f
|
||||
);
|
||||
registry.add_task(task);
|
||||
registry.wait_tasks_complete(handles);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use std::{collections::HashMap, fmt::Display};
|
||||
use std::{collections::HashMap, collections::HashSet, fmt::Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::typecheck::typedef::TypeEnum;
|
||||
|
@ -35,10 +35,10 @@ pub enum SymbolValue {
|
|||
}
|
||||
|
||||
impl SymbolValue {
|
||||
/// Creates a [SymbolValue] from a [Constant].
|
||||
/// Creates a [`SymbolValue`] from a [`Constant`].
|
||||
///
|
||||
/// * `constant` - The constant to create the value from.
|
||||
/// * `expected_ty` - The expected type of the [SymbolValue].
|
||||
/// * `expected_ty` - The expected type of the [`SymbolValue`].
|
||||
pub fn from_constant(
|
||||
constant: &Constant,
|
||||
expected_ty: Type,
|
||||
|
@ -50,39 +50,39 @@ impl SymbolValue {
|
|||
if unifier.unioned(expected_ty, primitives.option) {
|
||||
Ok(SymbolValue::OptionNone)
|
||||
} else {
|
||||
Err(format!("Expected {:?}, but got Option", expected_ty))
|
||||
Err(format!("Expected {expected_ty:?}, but got Option"))
|
||||
}
|
||||
}
|
||||
Constant::Bool(b) => {
|
||||
if unifier.unioned(expected_ty, primitives.bool) {
|
||||
Ok(SymbolValue::Bool(*b))
|
||||
} else {
|
||||
Err(format!("Expected {:?}, but got bool", expected_ty))
|
||||
Err(format!("Expected {expected_ty:?}, but got bool"))
|
||||
}
|
||||
}
|
||||
Constant::Str(s) => {
|
||||
if unifier.unioned(expected_ty, primitives.str) {
|
||||
Ok(SymbolValue::Str(s.to_string()))
|
||||
} else {
|
||||
Err(format!("Expected {:?}, but got str", expected_ty))
|
||||
Err(format!("Expected {expected_ty:?}, but got str"))
|
||||
}
|
||||
},
|
||||
Constant::Int(i) => {
|
||||
if unifier.unioned(expected_ty, primitives.int32) {
|
||||
i32::try_from(*i)
|
||||
.map(|val| SymbolValue::I32(val))
|
||||
.map(SymbolValue::I32)
|
||||
.map_err(|e| e.to_string())
|
||||
} else if unifier.unioned(expected_ty, primitives.int64) {
|
||||
i64::try_from(*i)
|
||||
.map(|val| SymbolValue::I64(val))
|
||||
.map(SymbolValue::I64)
|
||||
.map_err(|e| e.to_string())
|
||||
} else if unifier.unioned(expected_ty, primitives.uint32) {
|
||||
u32::try_from(*i)
|
||||
.map(|val| SymbolValue::U32(val))
|
||||
.map(SymbolValue::U32)
|
||||
.map_err(|e| e.to_string())
|
||||
} else if unifier.unioned(expected_ty, primitives.uint64) {
|
||||
u64::try_from(*i)
|
||||
.map(|val| SymbolValue::U64(val))
|
||||
.map(SymbolValue::U64)
|
||||
.map_err(|e| e.to_string())
|
||||
} else {
|
||||
Err(format!("Expected {}, but got int", unifier.stringify(expected_ty)))
|
||||
|
@ -96,7 +96,8 @@ impl SymbolValue {
|
|||
|
||||
assert_eq!(ty.len(), t.len());
|
||||
|
||||
let elems = t.into_iter()
|
||||
let elems = t
|
||||
.iter()
|
||||
.zip(ty)
|
||||
.map(|(constant, ty)| Self::from_constant(constant, *ty, primitives, unifier))
|
||||
.collect::<Result<Vec<SymbolValue>, _>>()?;
|
||||
|
@ -106,14 +107,14 @@ impl SymbolValue {
|
|||
if unifier.unioned(expected_ty, primitives.float) {
|
||||
Ok(SymbolValue::Double(*f))
|
||||
} else {
|
||||
Err(format!("Expected {:?}, but got float", expected_ty))
|
||||
Err(format!("Expected {expected_ty:?}, but got float"))
|
||||
}
|
||||
},
|
||||
_ => Err(format!("Unsupported value type {:?}", constant)),
|
||||
_ => Err(format!("Unsupported value type {constant:?}")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [Type] representing the data type of this value.
|
||||
/// Returns the [`Type`] representing the data type of this value.
|
||||
pub fn get_type(&self, primitives: &PrimitiveStore, unifier: &mut Unifier) -> Type {
|
||||
match self {
|
||||
SymbolValue::I32(_) => primitives.int32,
|
||||
|
@ -132,12 +133,11 @@ impl SymbolValue {
|
|||
ty: vs_tys,
|
||||
})
|
||||
}
|
||||
SymbolValue::OptionSome(_) => primitives.option,
|
||||
SymbolValue::OptionNone => primitives.option,
|
||||
SymbolValue::OptionSome(_) | SymbolValue::OptionNone => primitives.option,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [TypeAnnotation] representing the data type of this value.
|
||||
/// Returns the [`TypeAnnotation`] representing the data type of this value.
|
||||
pub fn get_type_annotation(&self, primitives: &PrimitiveStore, unifier: &mut Unifier) -> TypeAnnotation {
|
||||
match self {
|
||||
SymbolValue::Bool(..) => TypeAnnotation::Primitive(primitives.bool),
|
||||
|
@ -156,7 +156,7 @@ impl SymbolValue {
|
|||
}
|
||||
SymbolValue::OptionNone => TypeAnnotation::CustomClass {
|
||||
id: primitives.option.get_obj_id(unifier),
|
||||
params: Default::default(),
|
||||
params: Vec::default(),
|
||||
},
|
||||
SymbolValue::OptionSome(v) => {
|
||||
let ty = v.get_type_annotation(primitives, unifier);
|
||||
|
@ -168,7 +168,7 @@ impl SymbolValue {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the [TypeEnum] representing the data type of this value.
|
||||
/// Returns the [`TypeEnum`] representing the data type of this value.
|
||||
pub fn get_type_enum(&self, primitives: &PrimitiveStore, unifier: &mut Unifier) -> Rc<TypeEnum> {
|
||||
let ty = self.get_type(primitives, unifier);
|
||||
unifier.get_ty(ty)
|
||||
|
@ -178,12 +178,12 @@ impl SymbolValue {
|
|||
impl Display for SymbolValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SymbolValue::I32(i) => write!(f, "{}", i),
|
||||
SymbolValue::I64(i) => write!(f, "int64({})", i),
|
||||
SymbolValue::U32(i) => write!(f, "uint32({})", i),
|
||||
SymbolValue::U64(i) => write!(f, "uint64({})", i),
|
||||
SymbolValue::Str(s) => write!(f, "\"{}\"", s),
|
||||
SymbolValue::Double(d) => write!(f, "{}", d),
|
||||
SymbolValue::I32(i) => write!(f, "{i}"),
|
||||
SymbolValue::I64(i) => write!(f, "int64({i})"),
|
||||
SymbolValue::U32(i) => write!(f, "uint32({i})"),
|
||||
SymbolValue::U64(i) => write!(f, "uint64({i})"),
|
||||
SymbolValue::Str(s) => write!(f, "\"{s}\""),
|
||||
SymbolValue::Double(d) => write!(f, "{d}"),
|
||||
SymbolValue::Bool(b) => {
|
||||
if *b {
|
||||
write!(f, "True")
|
||||
|
@ -192,9 +192,9 @@ impl Display for SymbolValue {
|
|||
}
|
||||
}
|
||||
SymbolValue::Tuple(t) => {
|
||||
write!(f, "({})", t.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", "))
|
||||
write!(f, "({})", t.iter().map(|v| format!("{v}")).collect::<Vec<_>>().join(", "))
|
||||
}
|
||||
SymbolValue::OptionSome(v) => write!(f, "Some({})", v),
|
||||
SymbolValue::OptionSome(v) => write!(f, "Some({v})"),
|
||||
SymbolValue::OptionNone => write!(f, "none"),
|
||||
}
|
||||
}
|
||||
|
@ -204,25 +204,26 @@ pub trait StaticValue {
|
|||
/// Returns a unique identifier for this value.
|
||||
fn get_unique_identifier(&self) -> u64;
|
||||
|
||||
fn get_const_obj<'ctx, 'a>(
|
||||
/// Returns the constant object represented by this unique identifier.
|
||||
fn get_const_obj<'ctx>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> BasicValueEnum<'ctx>;
|
||||
|
||||
/// Converts this value to a LLVM [BasicValueEnum].
|
||||
fn to_basic_value_enum<'ctx, 'a>(
|
||||
/// Converts this value to a LLVM [`BasicValueEnum`].
|
||||
fn to_basic_value_enum<'ctx>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
expected_ty: Type,
|
||||
) -> Result<BasicValueEnum<'ctx>, String>;
|
||||
|
||||
/// Returns a field within this value.
|
||||
fn get_field<'ctx, 'a>(
|
||||
fn get_field<'ctx>(
|
||||
&self,
|
||||
name: StrRef,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> Option<ValueEnum<'ctx>>;
|
||||
|
||||
/// Returns a single element of this tuple.
|
||||
|
@ -231,7 +232,10 @@ pub trait StaticValue {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub enum ValueEnum<'ctx> {
|
||||
/// [ValueEnum] representing a static value.
|
||||
Static(Arc<dyn StaticValue + Send + Sync>),
|
||||
|
||||
/// [ValueEnum] representing a dynamic value.
|
||||
Dynamic(BasicValueEnum<'ctx>),
|
||||
}
|
||||
|
||||
|
@ -266,6 +270,8 @@ impl<'ctx> From<StructValue<'ctx>> for ValueEnum<'ctx> {
|
|||
}
|
||||
|
||||
impl<'ctx> ValueEnum<'ctx> {
|
||||
|
||||
/// Converts this [`ValueEnum`] to a [`BasicValueEnum`].
|
||||
pub fn to_basic_value_enum<'a>(
|
||||
self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
|
@ -280,7 +286,7 @@ impl<'ctx> ValueEnum<'ctx> {
|
|||
}
|
||||
|
||||
pub trait SymbolResolver {
|
||||
// get type of type variable identifier or top-level function type
|
||||
/// Get type of type variable identifier or top-level function type,
|
||||
fn get_symbol_type(
|
||||
&self,
|
||||
unifier: &mut Unifier,
|
||||
|
@ -289,16 +295,16 @@ pub trait SymbolResolver {
|
|||
str: StrRef,
|
||||
) -> Result<Type, String>;
|
||||
|
||||
// get the top-level definition of identifiers
|
||||
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, String>;
|
||||
/// Get the top-level definition of identifiers.
|
||||
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, HashSet<String>>;
|
||||
|
||||
fn get_symbol_value<'ctx, 'a>(
|
||||
fn get_symbol_value<'ctx>(
|
||||
&self,
|
||||
str: StrRef,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> Option<ValueEnum<'ctx>>;
|
||||
|
||||
fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option<SymbolValue>;
|
||||
fn get_default_param_value(&self, expr: &Expr) -> Option<SymbolValue>;
|
||||
fn get_string_id(&self, s: &str) -> i32;
|
||||
fn get_exception_id(&self, tyid: usize) -> usize;
|
||||
|
||||
|
@ -328,14 +334,14 @@ thread_local! {
|
|||
];
|
||||
}
|
||||
|
||||
// convert type annotation into type
|
||||
/// Converts a type annotation into a [Type].
|
||||
pub fn parse_type_annotation<T>(
|
||||
resolver: &dyn SymbolResolver,
|
||||
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
|
||||
unifier: &mut Unifier,
|
||||
primitives: &PrimitiveStore,
|
||||
expr: &Expr<T>,
|
||||
) -> Result<Type, String> {
|
||||
) -> Result<Type, HashSet<String>> {
|
||||
use nac3parser::ast::ExprKind::*;
|
||||
let ids = IDENTIFIER_ID.with(|ids| *ids);
|
||||
let int32_id = ids[0];
|
||||
|
@ -369,39 +375,44 @@ pub fn parse_type_annotation<T>(
|
|||
Ok(primitives.exception)
|
||||
} else {
|
||||
let obj_id = resolver.get_identifier_def(*id);
|
||||
match obj_id {
|
||||
Ok(obj_id) => {
|
||||
let def = top_level_defs[obj_id.0].read();
|
||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||
if !type_vars.is_empty() {
|
||||
return Err(format!(
|
||||
if let Ok(obj_id) = obj_id {
|
||||
let def = top_level_defs[obj_id.0].read();
|
||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||
if !type_vars.is_empty() {
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"Unexpected number of type parameters: expected {} but got 0",
|
||||
type_vars.len()
|
||||
));
|
||||
}
|
||||
let fields = chain(
|
||||
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
|
||||
methods.iter().map(|(k, v, _)| (*k, (*v, false))),
|
||||
)
|
||||
),
|
||||
]))
|
||||
}
|
||||
let fields = chain(
|
||||
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
|
||||
methods.iter().map(|(k, v, _)| (*k, (*v, false))),
|
||||
)
|
||||
.collect();
|
||||
Ok(unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id,
|
||||
fields,
|
||||
params: Default::default(),
|
||||
}))
|
||||
} else {
|
||||
Err(format!("Cannot use function name as type at {}", loc))
|
||||
}
|
||||
Ok(unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id,
|
||||
fields,
|
||||
params: HashMap::default(),
|
||||
}))
|
||||
} else {
|
||||
Err(HashSet::from([
|
||||
format!("Cannot use function name as type at {loc}"),
|
||||
]))
|
||||
}
|
||||
Err(_) => {
|
||||
let ty = resolver
|
||||
.get_symbol_type(unifier, top_level_defs, primitives, *id)
|
||||
.map_err(|e| format!("Unknown type annotation at {}: {}", loc, e))?;
|
||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
||||
Ok(ty)
|
||||
} else {
|
||||
Err(format!("Unknown type annotation {} at {}", id, loc))
|
||||
}
|
||||
} else {
|
||||
let ty = resolver
|
||||
.get_symbol_type(unifier, top_level_defs, primitives, *id)
|
||||
.map_err(|e| HashSet::from([
|
||||
format!("Unknown type annotation at {loc}: {e}"),
|
||||
]))?;
|
||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
||||
Ok(ty)
|
||||
} else {
|
||||
Err(HashSet::from([
|
||||
format!("Unknown type annotation {id} at {loc}"),
|
||||
]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -424,7 +435,9 @@ pub fn parse_type_annotation<T>(
|
|||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
|
||||
} else {
|
||||
Err("Expected multiple elements for tuple".into())
|
||||
Err(HashSet::from([
|
||||
"Expected multiple elements for tuple".into()
|
||||
]))
|
||||
}
|
||||
} else {
|
||||
let types = if let Tuple { elts, .. } = &slice.node {
|
||||
|
@ -441,11 +454,13 @@ pub fn parse_type_annotation<T>(
|
|||
let def = top_level_defs[obj_id.0].read();
|
||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||
if types.len() != type_vars.len() {
|
||||
return Err(format!(
|
||||
"Unexpected number of type parameters: expected {} but got {}",
|
||||
type_vars.len(),
|
||||
types.len()
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"Unexpected number of type parameters: expected {} but got {}",
|
||||
type_vars.len(),
|
||||
types.len()
|
||||
),
|
||||
]))
|
||||
}
|
||||
let mut subst = HashMap::new();
|
||||
for (var, ty) in izip!(type_vars.iter(), types.iter()) {
|
||||
|
@ -469,7 +484,9 @@ pub fn parse_type_annotation<T>(
|
|||
}));
|
||||
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }))
|
||||
} else {
|
||||
Err("Cannot use function name as type".into())
|
||||
Err(HashSet::from([
|
||||
"Cannot use function name as type".into(),
|
||||
]))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -480,10 +497,14 @@ pub fn parse_type_annotation<T>(
|
|||
if let Name { id, .. } = &value.node {
|
||||
subscript_name_handle(id, slice, unifier)
|
||||
} else {
|
||||
Err(format!("unsupported type expression at {}", expr.location))
|
||||
Err(HashSet::from([
|
||||
format!("unsupported type expression at {}", expr.location),
|
||||
]))
|
||||
}
|
||||
}
|
||||
_ => Err(format!("unsupported type expression at {}", expr.location)),
|
||||
_ => Err(HashSet::from([
|
||||
format!("unsupported type expression at {}", expr.location),
|
||||
])),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,7 +515,7 @@ impl dyn SymbolResolver + Send + Sync {
|
|||
unifier: &mut Unifier,
|
||||
primitives: &PrimitiveStore,
|
||||
expr: &Expr<T>,
|
||||
) -> Result<Type, String> {
|
||||
) -> Result<Type, HashSet<String>> {
|
||||
parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
|
||||
}
|
||||
|
||||
|
@ -513,7 +534,7 @@ impl dyn SymbolResolver + Send + Sync {
|
|||
unreachable!("expected class definition")
|
||||
}
|
||||
},
|
||||
&mut |id| format!("typevar{}", id),
|
||||
&mut |id| format!("typevar{id}"),
|
||||
&mut None,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ pub fn get_exn_constructor(
|
|||
FuncArg {
|
||||
name: "msg".into(),
|
||||
ty: string,
|
||||
default_value: Some(SymbolValue::Str("".into())),
|
||||
default_value: Some(SymbolValue::Str(String::new())),
|
||||
},
|
||||
FuncArg { name: "param0".into(), ty: int64, default_value: Some(SymbolValue::I64(0)) },
|
||||
FuncArg { name: "param1".into(), ty: int64, default_value: Some(SymbolValue::I64(0)) },
|
||||
|
@ -58,20 +58,20 @@ pub fn get_exn_constructor(
|
|||
let exn_type = unifier.add_ty(TypeEnum::TObj {
|
||||
obj_id: DefinitionId(class_id),
|
||||
fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(),
|
||||
params: Default::default(),
|
||||
params: HashMap::default(),
|
||||
});
|
||||
let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: exn_cons_args,
|
||||
ret: exn_type,
|
||||
vars: Default::default(),
|
||||
vars: HashMap::default(),
|
||||
}));
|
||||
let fun_def = TopLevelDef::Function {
|
||||
name: format!("{}.__init__", name),
|
||||
name: format!("{name}.__init__"),
|
||||
simple_name: "__init__".into(),
|
||||
signature,
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor)))),
|
||||
loc: None,
|
||||
|
@ -79,12 +79,12 @@ pub fn get_exn_constructor(
|
|||
let class_def = TopLevelDef::Class {
|
||||
name: name.into(),
|
||||
object_id: DefinitionId(class_id),
|
||||
type_vars: Default::default(),
|
||||
type_vars: Vec::default(),
|
||||
fields: exception_fields,
|
||||
methods: vec![("__init__".into(), signature, DefinitionId(cons_id))],
|
||||
ancestors: vec![
|
||||
TypeAnnotation::CustomClass { id: DefinitionId(class_id), params: Default::default() },
|
||||
TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() },
|
||||
TypeAnnotation::CustomClass { id: DefinitionId(class_id), params: Vec::default() },
|
||||
TypeAnnotation::CustomClass { id: DefinitionId(7), params: Vec::default() },
|
||||
],
|
||||
constructor: Some(signature),
|
||||
resolver: None,
|
||||
|
@ -93,7 +93,7 @@ pub fn get_exn_constructor(
|
|||
(fun_def, class_def, signature, exn_type)
|
||||
}
|
||||
|
||||
/// Creates a NumPy [TopLevelDef] function by code generation.
|
||||
/// Creates a NumPy [`TopLevelDef`] function by code generation.
|
||||
///
|
||||
/// * `name`: The name of the implemented NumPy function.
|
||||
/// * `ret_ty`: The return type of this function.
|
||||
|
@ -117,19 +117,19 @@ fn create_fn_by_codegen(
|
|||
ty: p.0,
|
||||
default_value: None,
|
||||
}).collect(),
|
||||
ret: ret_ty.clone(),
|
||||
ret: ret_ty,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(codegen_callback))),
|
||||
loc: None,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Creates a NumPy [TopLevelDef] function using an LLVM intrinsic.
|
||||
/// Creates a NumPy [`TopLevelDef`] function using an LLVM intrinsic.
|
||||
///
|
||||
/// * `name`: The name of the implemented NumPy function.
|
||||
/// * `ret_ty`: The return type of this function.
|
||||
|
@ -163,14 +163,14 @@ fn create_fn_by_intrinsic(
|
|||
let args_val = args_ty.iter().zip_eq(args.iter())
|
||||
.map(|(ty, arg)| {
|
||||
arg.1.clone()
|
||||
.to_basic_value_enum(ctx, generator, ty.clone())
|
||||
.to_basic_value_enum(ctx, generator, *ty)
|
||||
.unwrap()
|
||||
})
|
||||
.map_into::<BasicMetadataValueEnum>()
|
||||
.collect_vec();
|
||||
|
||||
let intrinsic_fn = ctx.module.get_function(intrinsic_fn).unwrap_or_else(|| {
|
||||
let ret_llvm_ty = ctx.get_llvm_abi_type(generator, ret_ty.clone());
|
||||
let ret_llvm_ty = ctx.get_llvm_abi_type(generator, ret_ty);
|
||||
let param_llvm_ty = param_tys.iter()
|
||||
.map(|p| ctx.get_llvm_abi_type(generator, *p))
|
||||
.map_into::<BasicMetadataTypeEnum>()
|
||||
|
@ -191,7 +191,7 @@ fn create_fn_by_intrinsic(
|
|||
)
|
||||
}
|
||||
|
||||
/// Creates a unary NumPy [TopLevelDef] function using an extern function (e.g. from `libc` or
|
||||
/// Creates a unary NumPy [`TopLevelDef`] function using an extern function (e.g. from `libc` or
|
||||
/// `libm`).
|
||||
///
|
||||
/// * `name`: The name of the implemented NumPy function.
|
||||
|
@ -229,14 +229,14 @@ fn create_fn_by_extern(
|
|||
let args_val = args_ty.iter().zip_eq(args.iter())
|
||||
.map(|(ty, arg)| {
|
||||
arg.1.clone()
|
||||
.to_basic_value_enum(ctx, generator, ty.clone())
|
||||
.to_basic_value_enum(ctx, generator, *ty)
|
||||
.unwrap()
|
||||
})
|
||||
.map_into::<BasicMetadataValueEnum>()
|
||||
.collect_vec();
|
||||
|
||||
let intrinsic_fn = ctx.module.get_function(extern_fn).unwrap_or_else(|| {
|
||||
let ret_llvm_ty = ctx.get_llvm_abi_type(generator, ret_ty.clone());
|
||||
let ret_llvm_ty = ctx.get_llvm_abi_type(generator, ret_ty);
|
||||
let param_llvm_ty = param_tys.iter()
|
||||
.map(|p| ctx.get_llvm_abi_type(generator, *p))
|
||||
.map_into::<BasicMetadataTypeEnum>()
|
||||
|
@ -363,9 +363,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
Arc::new(RwLock::new(TopLevelDef::Class {
|
||||
name: "Exception".into(),
|
||||
object_id: DefinitionId(7),
|
||||
type_vars: Default::default(),
|
||||
type_vars: Vec::default(),
|
||||
fields: exception_fields,
|
||||
methods: Default::default(),
|
||||
methods: Vec::default(),
|
||||
ancestors: vec![],
|
||||
constructor: None,
|
||||
resolver: None,
|
||||
|
@ -398,7 +398,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
],
|
||||
ancestors: vec![TypeAnnotation::CustomClass {
|
||||
id: DefinitionId(10),
|
||||
params: Default::default(),
|
||||
params: Vec::default(),
|
||||
}],
|
||||
constructor: None,
|
||||
resolver: None,
|
||||
|
@ -410,8 +410,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
simple_name: "is_some".into(),
|
||||
signature: is_some_ty.0,
|
||||
var_id: vec![option_ty_var_id],
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, obj, _, _, generator| {
|
||||
|
@ -435,8 +435,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
simple_name: "is_none".into(),
|
||||
signature: is_some_ty.0,
|
||||
var_id: vec![option_ty_var_id],
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, obj, _, _, generator| {
|
||||
|
@ -460,8 +460,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
simple_name: "unwrap".into(),
|
||||
signature: unwrap_ty.0,
|
||||
var_id: vec![option_ty_var_id],
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|_, _, _, _, _| {
|
||||
|
@ -478,9 +478,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: int32,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -553,9 +553,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: int64,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -624,9 +624,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: uint32,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -684,7 +684,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
"conv"
|
||||
);
|
||||
|
||||
val.into()
|
||||
val
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
|
@ -701,9 +701,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: uint64,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -760,7 +760,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
"conv"
|
||||
);
|
||||
|
||||
val.into()
|
||||
val
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
|
@ -777,9 +777,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: float,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -905,7 +905,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
Ok(Some(val.into()))
|
||||
Ok(Some(val))
|
||||
}),
|
||||
),
|
||||
Arc::new(RwLock::new(TopLevelDef::Function {
|
||||
|
@ -927,11 +927,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
},
|
||||
],
|
||||
ret: range,
|
||||
vars: Default::default(),
|
||||
vars: HashMap::default(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, _, args, generator| {
|
||||
|
@ -1013,11 +1013,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![FuncArg { name: "s".into(), ty: string, default_value: None }],
|
||||
ret: string,
|
||||
vars: Default::default(),
|
||||
vars: HashMap::default(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -1035,9 +1035,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: primitives.0.bool,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -1174,7 +1174,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
Ok(Some(val.into()))
|
||||
Ok(Some(val))
|
||||
}),
|
||||
),
|
||||
create_fn_by_codegen(
|
||||
|
@ -1261,7 +1261,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
Ok(Some(val.into()))
|
||||
Ok(Some(val))
|
||||
}),
|
||||
),
|
||||
Arc::new(RwLock::new({
|
||||
|
@ -1283,8 +1283,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
.collect(),
|
||||
})),
|
||||
var_id: vec![arg_ty.1],
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -1305,10 +1305,10 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
None,
|
||||
)
|
||||
.into_int_value();
|
||||
if len.get_type().get_bit_width() != 32 {
|
||||
Some(ctx.builder.build_int_truncate(len, int32, "len2i32").into())
|
||||
} else {
|
||||
if len.get_type().get_bit_width() == 32 {
|
||||
Some(len.into())
|
||||
} else {
|
||||
Some(ctx.builder.build_int_truncate(len, int32, "len2i32").into())
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -1327,9 +1327,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: num_ty.0,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -1389,9 +1389,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: num_ty.0,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -1448,9 +1448,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
ret: num_ty.0,
|
||||
vars: var_map.clone(),
|
||||
})),
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -1888,8 +1888,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
vars: HashMap::from([(option_ty_var_id, option_ty_var)]),
|
||||
})),
|
||||
var_id: vec![option_ty_var_id],
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, fun, args, generator| {
|
||||
|
@ -1904,7 +1904,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
})),
|
||||
];
|
||||
|
||||
let ast_list: Vec<Option<ast::Stmt<()>>> =
|
||||
let ast_list: Vec<Option<Stmt<()>>> =
|
||||
(0..top_level_def_list.len()).map(|_| None).collect();
|
||||
|
||||
izip!(top_level_def_list, ast_list).collect_vec()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -43,6 +43,7 @@ impl TopLevelDef {
|
|||
}
|
||||
|
||||
impl TopLevelComposer {
|
||||
#[must_use]
|
||||
pub fn make_primitives() -> (PrimitiveStore, Unifier) {
|
||||
let mut unifier = Unifier::new();
|
||||
let int32 = unifier.add_ty(TypeEnum::TObj {
|
||||
|
@ -134,22 +135,23 @@ impl TopLevelComposer {
|
|||
let primitives = PrimitiveStore {
|
||||
int32,
|
||||
int64,
|
||||
uint32,
|
||||
uint64,
|
||||
float,
|
||||
bool,
|
||||
none,
|
||||
range,
|
||||
str,
|
||||
exception,
|
||||
uint32,
|
||||
uint64,
|
||||
option,
|
||||
};
|
||||
crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier);
|
||||
(primitives, unifier)
|
||||
}
|
||||
|
||||
/// already include the definition_id of itself inside the ancestors vector
|
||||
/// when first registering, the type_vars, fields, methods, ancestors are invalid
|
||||
/// already include the `definition_id` of itself inside the ancestors vector
|
||||
/// when first registering, the `type_vars`, fields, methods, ancestors are invalid
|
||||
#[must_use]
|
||||
pub fn make_top_level_class_def(
|
||||
index: usize,
|
||||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||
|
@ -160,10 +162,10 @@ impl TopLevelComposer {
|
|||
TopLevelDef::Class {
|
||||
name,
|
||||
object_id: DefinitionId(index),
|
||||
type_vars: Default::default(),
|
||||
fields: Default::default(),
|
||||
methods: Default::default(),
|
||||
ancestors: Default::default(),
|
||||
type_vars: Vec::default(),
|
||||
fields: Vec::default(),
|
||||
methods: Vec::default(),
|
||||
ancestors: Vec::default(),
|
||||
constructor,
|
||||
resolver,
|
||||
loc,
|
||||
|
@ -171,6 +173,7 @@ impl TopLevelComposer {
|
|||
}
|
||||
|
||||
/// when first registering, the type is a invalid value
|
||||
#[must_use]
|
||||
pub fn make_top_level_function_def(
|
||||
name: String,
|
||||
simple_name: StrRef,
|
||||
|
@ -182,15 +185,16 @@ impl TopLevelComposer {
|
|||
name,
|
||||
simple_name,
|
||||
signature: ty,
|
||||
var_id: Default::default(),
|
||||
instance_to_symbol: Default::default(),
|
||||
instance_to_stmt: Default::default(),
|
||||
var_id: Vec::default(),
|
||||
instance_to_symbol: HashMap::default(),
|
||||
instance_to_stmt: HashMap::default(),
|
||||
resolver,
|
||||
codegen_callback: None,
|
||||
loc,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn make_class_method_name(mut class_name: String, method_name: &str) -> String {
|
||||
class_name.push('.');
|
||||
class_name.push_str(method_name);
|
||||
|
@ -200,13 +204,13 @@ impl TopLevelComposer {
|
|||
pub fn get_class_method_def_info(
|
||||
class_methods_def: &[(StrRef, Type, DefinitionId)],
|
||||
method_name: StrRef,
|
||||
) -> Result<(Type, DefinitionId), String> {
|
||||
) -> Result<(Type, DefinitionId), HashSet<String>> {
|
||||
for (name, ty, def_id) in class_methods_def {
|
||||
if name == &method_name {
|
||||
return Ok((*ty, *def_id));
|
||||
}
|
||||
}
|
||||
Err(format!("no method {} in the current class", method_name))
|
||||
Err(HashSet::from([format!("no method {method_name} in the current class")]))
|
||||
}
|
||||
|
||||
/// get all base class def id of a class, excluding itself. \
|
||||
|
@ -217,7 +221,7 @@ impl TopLevelComposer {
|
|||
pub fn get_all_ancestors_helper(
|
||||
child: &TypeAnnotation,
|
||||
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
||||
) -> Result<Vec<TypeAnnotation>, String> {
|
||||
) -> Result<Vec<TypeAnnotation>, HashSet<String>> {
|
||||
let mut result: Vec<TypeAnnotation> = Vec::new();
|
||||
let mut parent = Self::get_parent(child, temp_def_list);
|
||||
while let Some(p) = parent {
|
||||
|
@ -238,7 +242,7 @@ impl TopLevelComposer {
|
|||
if no_cycle {
|
||||
result.push(p);
|
||||
} else {
|
||||
return Err("cyclic inheritance detected".into());
|
||||
return Err(HashSet::from(["cyclic inheritance detected".into()]));
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
|
@ -257,22 +261,24 @@ impl TopLevelComposer {
|
|||
let child_def = temp_def_list.get(child_id.0).unwrap();
|
||||
let child_def = child_def.read();
|
||||
if let TopLevelDef::Class { ancestors, .. } = &*child_def {
|
||||
if !ancestors.is_empty() {
|
||||
Some(ancestors[0].clone())
|
||||
} else {
|
||||
if ancestors.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ancestors[0].clone())
|
||||
}
|
||||
} else {
|
||||
unreachable!("child must be top level class def")
|
||||
}
|
||||
}
|
||||
|
||||
/// get the var_id of a given TVar type
|
||||
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<u32, String> {
|
||||
/// get the `var_id` of a given `TVar` type
|
||||
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<u32, HashSet<String>> {
|
||||
if let TypeEnum::TVar { id, .. } = unifier.get_ty(var_ty).as_ref() {
|
||||
Ok(*id)
|
||||
} else {
|
||||
Err("not type var".to_string())
|
||||
Err(HashSet::from([
|
||||
"not type var".to_string(),
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,7 +340,7 @@ impl TopLevelComposer {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn get_all_assigned_field(stmts: &[ast::Stmt<()>]) -> Result<HashSet<StrRef>, String> {
|
||||
pub fn get_all_assigned_field(stmts: &[Stmt<()>]) -> Result<HashSet<StrRef>, HashSet<String>> {
|
||||
let mut result = HashSet::new();
|
||||
for s in stmts {
|
||||
match &s.node {
|
||||
|
@ -351,10 +357,12 @@ impl TopLevelComposer {
|
|||
}
|
||||
} =>
|
||||
{
|
||||
return Err(format!(
|
||||
"redundant type annotation for class fields at {}",
|
||||
s.location
|
||||
))
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"redundant type annotation for class fields at {}",
|
||||
s.location
|
||||
),
|
||||
]))
|
||||
}
|
||||
ast::StmtKind::Assign { targets, .. } => {
|
||||
for t in targets {
|
||||
|
@ -376,14 +384,14 @@ impl TopLevelComposer {
|
|||
ast::StmtKind::If { body, orelse, .. } => {
|
||||
let inited_for_sure = Self::get_all_assigned_field(body.as_slice())?
|
||||
.intersection(&Self::get_all_assigned_field(orelse.as_slice())?)
|
||||
.cloned()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>();
|
||||
result.extend(inited_for_sure);
|
||||
}
|
||||
ast::StmtKind::Try { body, orelse, finalbody, .. } => {
|
||||
let inited_for_sure = Self::get_all_assigned_field(body.as_slice())?
|
||||
.intersection(&Self::get_all_assigned_field(orelse.as_slice())?)
|
||||
.cloned()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>();
|
||||
result.extend(inited_for_sure);
|
||||
result.extend(Self::get_all_assigned_field(finalbody.as_slice())?);
|
||||
|
@ -391,9 +399,9 @@ impl TopLevelComposer {
|
|||
ast::StmtKind::With { body, .. } => {
|
||||
result.extend(Self::get_all_assigned_field(body.as_slice())?);
|
||||
}
|
||||
ast::StmtKind::Pass { .. } => {}
|
||||
ast::StmtKind::Assert { .. } => {}
|
||||
ast::StmtKind::Expr { .. } => {}
|
||||
ast::StmtKind::Pass { .. }
|
||||
| ast::StmtKind::Assert { .. }
|
||||
| ast::StmtKind::Expr { .. } => {}
|
||||
|
||||
_ => {
|
||||
unimplemented!()
|
||||
|
@ -406,7 +414,7 @@ impl TopLevelComposer {
|
|||
pub fn parse_parameter_default_value(
|
||||
default: &ast::Expr,
|
||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||
) -> Result<SymbolValue, String> {
|
||||
) -> Result<SymbolValue, HashSet<String>> {
|
||||
parse_parameter_default_value(default, resolver)
|
||||
}
|
||||
|
||||
|
@ -448,14 +456,14 @@ impl TopLevelComposer {
|
|||
}
|
||||
|
||||
let found = val.get_type_annotation(primitive, unifier);
|
||||
if !is_compatible(&found, ty, unifier, primitive) {
|
||||
if is_compatible(&found, ty, unifier, primitive) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"incompatible default parameter type, expect {}, found {}",
|
||||
ty.stringify(unifier),
|
||||
found.stringify(unifier),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,14 +471,14 @@ impl TopLevelComposer {
|
|||
pub fn parse_parameter_default_value(
|
||||
default: &ast::Expr,
|
||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||
) -> Result<SymbolValue, String> {
|
||||
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, String> {
|
||||
) -> Result<SymbolValue, HashSet<String>> {
|
||||
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, HashSet<String>> {
|
||||
match val {
|
||||
Constant::Int(v) => {
|
||||
if let Ok(v) = (*v).try_into() {
|
||||
Ok(SymbolValue::I32(v))
|
||||
} else {
|
||||
Err(format!("integer value out of range at {}", loc))
|
||||
Err(HashSet::from([format!("integer value out of range at {loc}")]))
|
||||
}
|
||||
}
|
||||
Constant::Float(v) => Ok(SymbolValue::Double(*v)),
|
||||
|
@ -478,10 +486,11 @@ pub fn parse_parameter_default_value(
|
|||
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
|
||||
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
|
||||
)),
|
||||
Constant::None => Err(format!(
|
||||
"`None` is not supported, use `none` for option type instead ({})",
|
||||
loc
|
||||
)),
|
||||
Constant::None => Err(HashSet::from([
|
||||
format!(
|
||||
"`None` is not supported, use `none` for option type instead ({loc})"
|
||||
),
|
||||
])),
|
||||
_ => unimplemented!("this constant is not supported at {}", loc),
|
||||
}
|
||||
}
|
||||
|
@ -494,37 +503,51 @@ pub fn parse_parameter_default_value(
|
|||
let v: Result<i64, _> = (*v).try_into();
|
||||
match v {
|
||||
Ok(v) => Ok(SymbolValue::I64(v)),
|
||||
_ => Err(format!("default param value out of range at {}", default.location)),
|
||||
_ => Err(HashSet::from([
|
||||
format!("default param value out of range at {}", default.location)
|
||||
])),
|
||||
}
|
||||
}
|
||||
_ => Err(format!("only allow constant integer here at {}", default.location))
|
||||
_ => Err(HashSet::from([
|
||||
format!("only allow constant integer here at {}", default.location),
|
||||
]))
|
||||
}
|
||||
ast::ExprKind::Name { id, .. } if *id == "uint32".into() => match &args[0].node {
|
||||
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
||||
let v: Result<u32, _> = (*v).try_into();
|
||||
match v {
|
||||
Ok(v) => Ok(SymbolValue::U32(v)),
|
||||
_ => Err(format!("default param value out of range at {}", default.location)),
|
||||
_ => Err(HashSet::from([
|
||||
format!("default param value out of range at {}", default.location),
|
||||
])),
|
||||
}
|
||||
}
|
||||
_ => Err(format!("only allow constant integer here at {}", default.location))
|
||||
_ => Err(HashSet::from([
|
||||
format!("only allow constant integer here at {}", default.location),
|
||||
]))
|
||||
}
|
||||
ast::ExprKind::Name { id, .. } if *id == "uint64".into() => match &args[0].node {
|
||||
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
||||
let v: Result<u64, _> = (*v).try_into();
|
||||
match v {
|
||||
Ok(v) => Ok(SymbolValue::U64(v)),
|
||||
_ => Err(format!("default param value out of range at {}", default.location)),
|
||||
_ => Err(HashSet::from([
|
||||
format!("default param value out of range at {}", default.location),
|
||||
])),
|
||||
}
|
||||
}
|
||||
_ => Err(format!("only allow constant integer here at {}", default.location))
|
||||
_ => Err(HashSet::from([
|
||||
format!("only allow constant integer here at {}", default.location),
|
||||
]))
|
||||
}
|
||||
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(
|
||||
SymbolValue::OptionSome(
|
||||
Box::new(parse_parameter_default_value(&args[0], resolver)?)
|
||||
)
|
||||
),
|
||||
_ => Err(format!("unsupported default parameter at {}", default.location)),
|
||||
_ => Err(HashSet::from([
|
||||
format!("unsupported default parameter at {}", default.location),
|
||||
])),
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts
|
||||
|
@ -535,17 +558,21 @@ pub fn parse_parameter_default_value(
|
|||
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
resolver.get_default_param_value(default).ok_or_else(
|
||||
|| format!(
|
||||
"`{}` cannot be used as a default parameter at {} \
|
||||
(not primitive type, option or tuple / not defined?)",
|
||||
id,
|
||||
default.location
|
||||
)
|
||||
|| HashSet::from([
|
||||
format!(
|
||||
"`{}` cannot be used as a default parameter at {} \
|
||||
(not primitive type, option or tuple / not defined?)",
|
||||
id,
|
||||
default.location
|
||||
),
|
||||
])
|
||||
)
|
||||
}
|
||||
_ => Err(format!(
|
||||
"unsupported default parameter (not primitive type, option or tuple) at {}",
|
||||
default.location
|
||||
))
|
||||
_ => Err(HashSet::from([
|
||||
format!(
|
||||
"unsupported default parameter (not primitive type, option or tuple) at {}",
|
||||
default.location
|
||||
),
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ use std::{
|
|||
collections::{HashMap, HashSet},
|
||||
fmt::Debug,
|
||||
iter::FromIterator,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
|
@ -49,13 +48,14 @@ pub struct GenCall {
|
|||
}
|
||||
|
||||
impl GenCall {
|
||||
#[must_use]
|
||||
pub fn new(fp: GenCallCallback) -> GenCall {
|
||||
GenCall { fp }
|
||||
}
|
||||
|
||||
pub fn run<'ctx, 'a>(
|
||||
pub fn run<'ctx>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
|
|
|
@ -36,7 +36,7 @@ struct Resolver(Arc<ResolverInternal>);
|
|||
impl SymbolResolver for Resolver {
|
||||
fn get_default_param_value(
|
||||
&self,
|
||||
_: &nac3parser::ast::Expr,
|
||||
_: &ast::Expr,
|
||||
) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -64,8 +64,9 @@ impl SymbolResolver for Resolver {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||
self.0.id_to_def.lock().get(&id).cloned()
|
||||
.ok_or_else(|| HashSet::from(["Unknown identifier".to_string()]))
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
|
@ -551,9 +552,9 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
|
|||
|
||||
if let Err(msg) = composer.start_analysis(false) {
|
||||
if print {
|
||||
println!("{}", msg);
|
||||
println!("{}", msg.iter().sorted().join("\n----------\n"));
|
||||
} else {
|
||||
assert_eq!(res[0], msg);
|
||||
assert_eq!(res[0], msg.iter().next().unwrap());
|
||||
}
|
||||
} else {
|
||||
// skip 5 to skip primitives
|
||||
|
@ -735,9 +736,9 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
|
|||
|
||||
if let Err(msg) = composer.start_analysis(true) {
|
||||
if print {
|
||||
println!("{}", msg);
|
||||
println!("{}", msg.iter().sorted().join("\n----------\n"));
|
||||
} else {
|
||||
assert_eq!(res[0], msg);
|
||||
assert_eq!(res[0], msg.iter().next().unwrap());
|
||||
}
|
||||
} else {
|
||||
// skip 5 to skip primitives
|
||||
|
|
|
@ -33,17 +33,16 @@ impl TypeAnnotation {
|
|||
match self {
|
||||
Primitive(ty) | TypeVar(ty) => unifier.stringify(*ty),
|
||||
CustomClass { id, params } => {
|
||||
let class_name = match unifier.top_level {
|
||||
Some(ref top) => {
|
||||
if let TopLevelDef::Class { name, .. } =
|
||||
&*top.definitions.read()[id.0].read()
|
||||
{
|
||||
(*name).into()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
let class_name = if let Some(ref top) = unifier.top_level {
|
||||
if let TopLevelDef::Class { name, .. } =
|
||||
&*top.definitions.read()[id.0].read()
|
||||
{
|
||||
(*name).into()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
None => format!("class_def_{}", id.0),
|
||||
} else {
|
||||
format!("class_def_{}", id.0)
|
||||
};
|
||||
format!(
|
||||
"{}{}",
|
||||
|
@ -51,9 +50,9 @@ impl TypeAnnotation {
|
|||
{
|
||||
let param_list = params.iter().map(|p| p.stringify(unifier)).collect_vec().join(", ");
|
||||
if param_list.is_empty() {
|
||||
"".into()
|
||||
String::new()
|
||||
} else {
|
||||
format!("[{}]", param_list)
|
||||
format!("[{param_list}]")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -68,12 +67,12 @@ impl TypeAnnotation {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parses an AST expression `expr` into a [TypeAnnotation].
|
||||
/// Parses an AST expression `expr` into a [`TypeAnnotation`].
|
||||
///
|
||||
/// * `locked` - A [HashMap] containing the IDs of known definitions, mapped to a [Vec] of all
|
||||
/// * `locked` - A [`HashMap`] containing the IDs of known definitions, mapped to a [`Vec`] of all
|
||||
/// generic variables associated with the definition.
|
||||
/// * `type_var` - The type variable associated with the type argument currently being parsed. Pass
|
||||
/// [None] when this function is invoked externally.
|
||||
/// [`None`] when this function is invoked externally.
|
||||
pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
|
||||
|
@ -83,7 +82,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
// the key stores the type_var of this topleveldef::class, we only need this field here
|
||||
locked: HashMap<DefinitionId, Vec<Type>>,
|
||||
type_var: Option<Type>,
|
||||
) -> Result<TypeAnnotation, String> {
|
||||
) -> Result<TypeAnnotation, HashSet<String>> {
|
||||
let name_handle = |id: &StrRef,
|
||||
unifier: &mut Unifier,
|
||||
locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||
|
@ -102,7 +101,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
} else if id == &"str".into() {
|
||||
Ok(TypeAnnotation::Primitive(primitives.str))
|
||||
} else if id == &"Exception".into() {
|
||||
Ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() })
|
||||
Ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Vec::default() })
|
||||
} else if let Ok(obj_id) = resolver.get_identifier_def(*id) {
|
||||
let type_vars = {
|
||||
let def_read = top_level_defs[obj_id.0].try_read();
|
||||
|
@ -110,10 +109,12 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
||||
type_vars.clone()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"function cannot be used as a type (at {})",
|
||||
expr.location
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"function cannot be used as a type (at {})",
|
||||
expr.location
|
||||
),
|
||||
]))
|
||||
}
|
||||
} else {
|
||||
locked.get(&obj_id).unwrap().clone()
|
||||
|
@ -121,11 +122,13 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
};
|
||||
// check param number here
|
||||
if !type_vars.is_empty() {
|
||||
return Err(format!(
|
||||
"expect {} type variable parameter but got 0 (at {})",
|
||||
type_vars.len(),
|
||||
expr.location,
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"expect {} type variable parameter but got 0 (at {})",
|
||||
type_vars.len(),
|
||||
expr.location,
|
||||
),
|
||||
]))
|
||||
}
|
||||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] })
|
||||
} else if let Ok(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) {
|
||||
|
@ -134,10 +137,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
unifier.unify(var, ty).unwrap();
|
||||
Ok(TypeAnnotation::TypeVar(ty))
|
||||
} else {
|
||||
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location))
|
||||
Err(HashSet::from([
|
||||
format!("`{}` is not a valid type annotation (at {})", id, expr.location),
|
||||
]))
|
||||
}
|
||||
} else {
|
||||
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location))
|
||||
Err(HashSet::from([
|
||||
format!("`{}` is not a valid type annotation (at {})", id, expr.location),
|
||||
]))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -146,9 +153,11 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
slice: &ast::Expr<T>,
|
||||
unifier: &mut Unifier,
|
||||
mut locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||
if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into(), "Option".into()].contains(id)
|
||||
if ["virtual".into(), "Generic".into(), "list".into(), "tuple".into(), "Option".into()].contains(id)
|
||||
{
|
||||
return Err(format!("keywords cannot be class name (at {})", expr.location));
|
||||
return Err(HashSet::from([
|
||||
format!("keywords cannot be class name (at {})", expr.location),
|
||||
]))
|
||||
}
|
||||
let obj_id = resolver.get_identifier_def(*id)?;
|
||||
let type_vars = {
|
||||
|
@ -171,12 +180,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
vec![slice]
|
||||
};
|
||||
if type_vars.len() != params_ast.len() {
|
||||
return Err(format!(
|
||||
"expect {} type parameters but got {} (at {})",
|
||||
type_vars.len(),
|
||||
params_ast.len(),
|
||||
params_ast[0].location,
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"expect {} type parameters but got {} (at {})",
|
||||
type_vars.len(),
|
||||
params_ast.len(),
|
||||
params_ast[0].location,
|
||||
),
|
||||
]))
|
||||
}
|
||||
let result = params_ast
|
||||
.iter()
|
||||
|
@ -202,11 +213,12 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
if no_type_var {
|
||||
result
|
||||
} else {
|
||||
return Err(format!(
|
||||
"application of type vars to generic class \
|
||||
is not currently supported (at {})",
|
||||
params_ast[0].location
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"application of type vars to generic class is not currently supported (at {})",
|
||||
params_ast[0].location
|
||||
),
|
||||
]))
|
||||
}
|
||||
};
|
||||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
|
||||
|
@ -312,7 +324,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
if let ast::ExprKind::Name { id, .. } = &value.node {
|
||||
class_name_handle(id, slice, unifier, locked)
|
||||
} else {
|
||||
Err(format!("unsupported expression type for class name (at {})", value.location))
|
||||
Err(HashSet::from([
|
||||
format!("unsupported expression type for class name (at {})", value.location)
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,14 +339,16 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
};
|
||||
let underlying_ty = underlying_ty[0];
|
||||
|
||||
let value = SymbolValue::from_constant(value, underlying_ty, primitives, unifier)?;
|
||||
let value = SymbolValue::from_constant(value, underlying_ty, primitives, unifier)
|
||||
.map_err(|err| HashSet::from([err]))?;
|
||||
|
||||
if matches!(value, SymbolValue::Str(_) | SymbolValue::Tuple(_) | SymbolValue::OptionSome(_)) {
|
||||
return Err(format!(
|
||||
"expression {} is not allowed for constant type annotation (at {})",
|
||||
value.to_string(),
|
||||
expr.location
|
||||
))
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"expression {value} is not allowed for constant type annotation (at {})",
|
||||
expr.location
|
||||
),
|
||||
]))
|
||||
}
|
||||
|
||||
Ok(TypeAnnotation::Constant {
|
||||
|
@ -341,7 +357,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
})
|
||||
}
|
||||
|
||||
_ => Err(format!("unsupported expression for type annotation (at {})", expr.location)),
|
||||
_ => Err(HashSet::from([
|
||||
format!("unsupported expression for type annotation (at {})", expr.location),
|
||||
])),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,38 +369,38 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
pub fn get_type_from_type_annotation_kinds(
|
||||
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
|
||||
unifier: &mut Unifier,
|
||||
primitives: &PrimitiveStore,
|
||||
ann: &TypeAnnotation,
|
||||
subst_list: &mut Option<Vec<Type>>
|
||||
) -> Result<Type, String> {
|
||||
) -> Result<Type, HashSet<String>> {
|
||||
match ann {
|
||||
TypeAnnotation::CustomClass { id: obj_id, params } => {
|
||||
let def_read = top_level_defs[obj_id.0].read();
|
||||
let class_def: &TopLevelDef = def_read.deref();
|
||||
let class_def: &TopLevelDef = &def_read;
|
||||
let TopLevelDef::Class { fields, methods, type_vars, .. } = class_def else {
|
||||
unreachable!("should be class def here")
|
||||
};
|
||||
|
||||
if type_vars.len() != params.len() {
|
||||
return Err(format!(
|
||||
"unexpected number of type parameters: expected {} but got {}",
|
||||
type_vars.len(),
|
||||
params.len()
|
||||
))
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"unexpected number of type parameters: expected {} but got {}",
|
||||
type_vars.len(),
|
||||
params.len()
|
||||
),
|
||||
]))
|
||||
}
|
||||
|
||||
let param_ty = params
|
||||
.iter()
|
||||
.map(|x| {
|
||||
get_type_from_type_annotation_kinds(
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
x,
|
||||
subst_list
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let param_ty = params
|
||||
.iter()
|
||||
.map(|x| {
|
||||
get_type_from_type_annotation_kinds(
|
||||
top_level_defs,
|
||||
unifier,
|
||||
x,
|
||||
subst_list
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let subst = {
|
||||
// check for compatible range
|
||||
|
@ -405,16 +423,18 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
if ok {
|
||||
result.insert(*id, p);
|
||||
} else {
|
||||
return Err(format!(
|
||||
"cannot apply type {} to type variable with id {:?}",
|
||||
unifier.internal_stringify(
|
||||
p,
|
||||
&mut |id| format!("class{}", id),
|
||||
&mut |id| format!("typevar{}", id),
|
||||
&mut None
|
||||
),
|
||||
*id
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"cannot apply type {} to type variable with id {:?}",
|
||||
unifier.internal_stringify(
|
||||
p,
|
||||
&mut |id| format!("class{id}"),
|
||||
&mut |id| format!("typevar{id}"),
|
||||
&mut None
|
||||
),
|
||||
*id
|
||||
)
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,11 +454,13 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
if ok {
|
||||
result.insert(*id, p);
|
||||
} else {
|
||||
return Err(format!(
|
||||
"cannot apply type {} to type variable {}",
|
||||
unifier.stringify(p),
|
||||
name.unwrap_or_else(|| format!("typevar{id}").into()),
|
||||
))
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"cannot apply type {} to type variable {}",
|
||||
unifier.stringify(p),
|
||||
name.unwrap_or_else(|| format!("typevar{id}").into()),
|
||||
),
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,7 +488,9 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
params: subst,
|
||||
});
|
||||
if need_subst {
|
||||
subst_list.as_mut().map(|wl| wl.push(ty));
|
||||
if let Some(wl) = subst_list.as_mut() {
|
||||
wl.push(ty);
|
||||
}
|
||||
}
|
||||
Ok(ty)
|
||||
}
|
||||
|
@ -487,7 +511,6 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
let ty = get_type_from_type_annotation_kinds(
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
ty.as_ref(),
|
||||
subst_list
|
||||
)?;
|
||||
|
@ -497,7 +520,6 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
let ty = get_type_from_type_annotation_kinds(
|
||||
top_level_defs,
|
||||
unifier,
|
||||
primitives,
|
||||
ty.as_ref(),
|
||||
subst_list
|
||||
)?;
|
||||
|
@ -507,7 +529,7 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
let tys = tys
|
||||
.iter()
|
||||
.map(|x| {
|
||||
get_type_from_type_annotation_kinds(top_level_defs, unifier, primitives, x, subst_list)
|
||||
get_type_from_type_annotation_kinds(top_level_defs, unifier, x, subst_list)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(unifier.add_ty(TypeEnum::TTuple { ty: tys }))
|
||||
|
@ -524,9 +546,10 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
/// considered to be type variables associated with the class \
|
||||
/// \
|
||||
/// But note that here we do not make a duplication of `T`, `V`, we directly
|
||||
/// use them as they are in the TopLevelDef::Class since those in the
|
||||
/// TopLevelDef::Class.type_vars will be substitute later when seeing applications/instantiations
|
||||
/// use them as they are in the [`TopLevelDef::Class`] since those in the
|
||||
/// `TopLevelDef::Class.type_vars` will be substitute later when seeing applications/instantiations
|
||||
/// the Type of their fields and methods will also be subst when application/instantiation
|
||||
#[must_use]
|
||||
pub fn make_self_type_annotation(type_vars: &[Type], object_id: DefinitionId) -> TypeAnnotation {
|
||||
TypeAnnotation::CustomClass {
|
||||
id: object_id,
|
||||
|
@ -537,21 +560,19 @@ pub fn make_self_type_annotation(type_vars: &[Type], object_id: DefinitionId) ->
|
|||
/// get all the occurences of type vars contained in a type annotation
|
||||
/// e.g. `A[int, B[T], V, virtual[C[G]]]` => [T, V, G]
|
||||
/// this function will not make a duplicate of type var
|
||||
#[must_use]
|
||||
pub fn get_type_var_contained_in_type_annotation(ann: &TypeAnnotation) -> Vec<TypeAnnotation> {
|
||||
let mut result: Vec<TypeAnnotation> = Vec::new();
|
||||
match ann {
|
||||
TypeAnnotation::TypeVar(..) => result.push(ann.clone()),
|
||||
TypeAnnotation::Virtual(ann) => {
|
||||
result.extend(get_type_var_contained_in_type_annotation(ann.as_ref()))
|
||||
TypeAnnotation::Virtual(ann) | TypeAnnotation::List(ann) => {
|
||||
result.extend(get_type_var_contained_in_type_annotation(ann.as_ref()));
|
||||
}
|
||||
TypeAnnotation::CustomClass { params, .. } => {
|
||||
for p in params {
|
||||
result.extend(get_type_var_contained_in_type_annotation(p));
|
||||
}
|
||||
}
|
||||
TypeAnnotation::List(ann) => {
|
||||
result.extend(get_type_var_contained_in_type_annotation(ann.as_ref()))
|
||||
}
|
||||
TypeAnnotation::Tuple(anns) => {
|
||||
for a in anns {
|
||||
result.extend(get_type_var_contained_in_type_annotation(a));
|
||||
|
@ -572,9 +593,9 @@ pub fn check_overload_type_annotation_compatible(
|
|||
(TypeAnnotation::Primitive(a), TypeAnnotation::Primitive(b)) => a == b,
|
||||
(TypeAnnotation::TypeVar(a), TypeAnnotation::TypeVar(b)) => {
|
||||
let a = unifier.get_ty(*a);
|
||||
let a = a.deref();
|
||||
let a = &*a;
|
||||
let b = unifier.get_ty(*b);
|
||||
let b = b.deref();
|
||||
let b = &*b;
|
||||
if let (
|
||||
TypeEnum::TVar { id: a, fields: None, .. },
|
||||
TypeEnum::TVar { id: b, fields: None, .. },
|
||||
|
|
|
@ -6,9 +6,11 @@ use nac3parser::ast::{self, Constant, Expr, ExprKind, Operator::{LShift, RShift}
|
|||
use std::{collections::HashSet, iter::once};
|
||||
|
||||
impl<'a> Inferencer<'a> {
|
||||
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), String> {
|
||||
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), HashSet<String>> {
|
||||
if matches!(expr.custom, Some(ty) if self.unifier.unioned(ty, self.primitives.none)) {
|
||||
Err(format!("Error at {}: cannot have value none", expr.location))
|
||||
Err(HashSet::from([
|
||||
format!("Error at {}: cannot have value none", expr.location),
|
||||
]))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -18,10 +20,11 @@ impl<'a> Inferencer<'a> {
|
|||
&mut self,
|
||||
pattern: &Expr<Option<Type>>,
|
||||
defined_identifiers: &mut HashSet<StrRef>,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), HashSet<String>> {
|
||||
match &pattern.node {
|
||||
ast::ExprKind::Name { id, .. } if id == &"none".into() =>
|
||||
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
|
||||
ExprKind::Name { id, .. } if id == &"none".into() => Err(HashSet::from([
|
||||
format!("cannot assign to a `none` (at {})", pattern.location),
|
||||
])),
|
||||
ExprKind::Name { id, .. } => {
|
||||
if !defined_identifiers.contains(id) {
|
||||
defined_identifiers.insert(*id);
|
||||
|
@ -30,7 +33,7 @@ impl<'a> Inferencer<'a> {
|
|||
Ok(())
|
||||
}
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
for elt in elts.iter() {
|
||||
for elt in elts {
|
||||
self.check_pattern(elt, defined_identifiers)?;
|
||||
self.should_have_value(elt)?;
|
||||
}
|
||||
|
@ -41,15 +44,19 @@ impl<'a> Inferencer<'a> {
|
|||
self.should_have_value(value)?;
|
||||
self.check_expr(slice, defined_identifiers)?;
|
||||
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap()) {
|
||||
return Err(format!(
|
||||
"Error at {}: cannot assign to tuple element",
|
||||
value.location
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"Error at {}: cannot assign to tuple element",
|
||||
value.location
|
||||
),
|
||||
]))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ExprKind::Constant { .. } => {
|
||||
Err(format!("cannot assign to a constant (at {})", pattern.location))
|
||||
Err(HashSet::from([
|
||||
format!("cannot assign to a constant (at {})", pattern.location),
|
||||
]))
|
||||
}
|
||||
_ => self.check_expr(pattern, defined_identifiers),
|
||||
}
|
||||
|
@ -59,15 +66,17 @@ impl<'a> Inferencer<'a> {
|
|||
&mut self,
|
||||
expr: &Expr<Option<Type>>,
|
||||
defined_identifiers: &mut HashSet<StrRef>,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), HashSet<String>> {
|
||||
// there are some cases where the custom field is None
|
||||
if let Some(ty) = &expr.custom {
|
||||
if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) && !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
|
||||
return Err(format!(
|
||||
"expected concrete type at {} but got {}",
|
||||
expr.location,
|
||||
self.unifier.get_ty(*ty).get_type_name()
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"expected concrete type at {} but got {}",
|
||||
expr.location,
|
||||
self.unifier.get_ty(*ty).get_type_name()
|
||||
)
|
||||
]))
|
||||
}
|
||||
}
|
||||
match &expr.node {
|
||||
|
@ -87,10 +96,12 @@ impl<'a> Inferencer<'a> {
|
|||
self.defined_identifiers.insert(*id);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!(
|
||||
"type error at identifier `{}` ({}) at {}",
|
||||
id, e, expr.location
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"type error at identifier `{}` ({}) at {}",
|
||||
id, e, expr.location
|
||||
)
|
||||
]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +109,7 @@ impl<'a> Inferencer<'a> {
|
|||
ExprKind::List { elts, .. }
|
||||
| ExprKind::Tuple { elts, .. }
|
||||
| ExprKind::BoolOp { values: elts, .. } => {
|
||||
for elt in elts.iter() {
|
||||
for elt in elts {
|
||||
self.check_expr(elt, defined_identifiers)?;
|
||||
self.should_have_value(elt)?;
|
||||
}
|
||||
|
@ -116,16 +127,17 @@ impl<'a> Inferencer<'a> {
|
|||
// Check whether a bitwise shift has a negative RHS constant value
|
||||
if *op == LShift || *op == RShift {
|
||||
if let ExprKind::Constant { value, .. } = &right.node {
|
||||
let rhs_val = match value {
|
||||
Constant::Int(v) => v,
|
||||
_ => unreachable!(),
|
||||
let Constant::Int(rhs_val) = value else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if *rhs_val < 0 {
|
||||
return Err(format!(
|
||||
"shift count is negative at {}",
|
||||
right.location
|
||||
));
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"shift count is negative at {}",
|
||||
right.location
|
||||
),
|
||||
]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +170,7 @@ impl<'a> Inferencer<'a> {
|
|||
}
|
||||
ExprKind::Lambda { args, body } => {
|
||||
let mut defined_identifiers = defined_identifiers.clone();
|
||||
for arg in args.args.iter() {
|
||||
for arg in &args.args {
|
||||
// TODO: should we check the types here?
|
||||
if !defined_identifiers.contains(&arg.node.arg) {
|
||||
defined_identifiers.insert(arg.node.arg);
|
||||
|
@ -201,19 +213,19 @@ impl<'a> Inferencer<'a> {
|
|||
&mut self,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
defined_identifiers: &mut HashSet<StrRef>,
|
||||
) -> Result<bool, String> {
|
||||
) -> Result<bool, HashSet<String>> {
|
||||
match &stmt.node {
|
||||
StmtKind::For { target, iter, body, orelse, .. } => {
|
||||
self.check_expr(iter, defined_identifiers)?;
|
||||
self.should_have_value(iter)?;
|
||||
let mut local_defined_identifiers = defined_identifiers.clone();
|
||||
for stmt in orelse.iter() {
|
||||
for stmt in orelse {
|
||||
self.check_stmt(stmt, &mut local_defined_identifiers)?;
|
||||
}
|
||||
let mut local_defined_identifiers = defined_identifiers.clone();
|
||||
self.check_pattern(target, &mut local_defined_identifiers)?;
|
||||
self.should_have_value(target)?;
|
||||
for stmt in body.iter() {
|
||||
for stmt in body {
|
||||
self.check_stmt(stmt, &mut local_defined_identifiers)?;
|
||||
}
|
||||
Ok(false)
|
||||
|
@ -226,7 +238,7 @@ impl<'a> Inferencer<'a> {
|
|||
let body_returned = self.check_block(body, &mut body_identifiers)?;
|
||||
let orelse_returned = self.check_block(orelse, &mut orelse_identifiers)?;
|
||||
|
||||
for ident in body_identifiers.iter() {
|
||||
for ident in &body_identifiers {
|
||||
if !defined_identifiers.contains(ident) && orelse_identifiers.contains(ident) {
|
||||
defined_identifiers.insert(*ident);
|
||||
}
|
||||
|
@ -243,7 +255,7 @@ impl<'a> Inferencer<'a> {
|
|||
}
|
||||
StmtKind::With { items, body, .. } => {
|
||||
let mut new_defined_identifiers = defined_identifiers.clone();
|
||||
for item in items.iter() {
|
||||
for item in items {
|
||||
self.check_expr(&item.context_expr, defined_identifiers)?;
|
||||
if let Some(var) = item.optional_vars.as_ref() {
|
||||
self.check_pattern(var, &mut new_defined_identifiers)?;
|
||||
|
@ -255,7 +267,7 @@ impl<'a> Inferencer<'a> {
|
|||
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() {
|
||||
for handler in handlers {
|
||||
let mut defined_identifiers = defined_identifiers.clone();
|
||||
let ast::ExcepthandlerKind::ExceptHandler { name, body, .. } = &handler.node;
|
||||
if let Some(name) = name {
|
||||
|
@ -308,11 +320,11 @@ impl<'a> Inferencer<'a> {
|
|||
&mut self,
|
||||
block: &[Stmt<Option<Type>>],
|
||||
defined_identifiers: &mut HashSet<StrRef>,
|
||||
) -> Result<bool, String> {
|
||||
) -> Result<bool, HashSet<String>> {
|
||||
let mut ret = false;
|
||||
for stmt in block {
|
||||
if ret {
|
||||
eprintln!("warning: dead code at {:?}\n", stmt.location)
|
||||
eprintln!("warning: dead code at {:?}\n", stmt.location);
|
||||
}
|
||||
if self.check_stmt(stmt, defined_identifiers)? {
|
||||
ret = true;
|
||||
|
|
|
@ -2,11 +2,12 @@ use crate::typecheck::{
|
|||
type_inferencer::*,
|
||||
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
|
||||
};
|
||||
use nac3parser::ast::{self, StrRef};
|
||||
use nac3parser::ast::StrRef;
|
||||
use nac3parser::ast::{Cmpop, Operator, Unaryop};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[must_use]
|
||||
pub fn binop_name(op: &Operator) -> &'static str {
|
||||
match op {
|
||||
Operator::Add => "__add__",
|
||||
|
@ -25,6 +26,7 @@ pub fn binop_name(op: &Operator) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn binop_assign_name(op: &Operator) -> &'static str {
|
||||
match op {
|
||||
Operator::Add => "__iadd__",
|
||||
|
@ -43,6 +45,7 @@ pub fn binop_assign_name(op: &Operator) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn unaryop_name(op: &Unaryop) -> &'static str {
|
||||
match op {
|
||||
Unaryop::UAdd => "__pos__",
|
||||
|
@ -52,6 +55,7 @@ pub fn unaryop_name(op: &Unaryop) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn comparison_name(op: &Cmpop) -> Option<&'static str> {
|
||||
match op {
|
||||
Cmpop::Lt => Some("__lt__"),
|
||||
|
@ -87,7 +91,7 @@ pub fn impl_binop(
|
|||
ty: Type,
|
||||
other_ty: &[Type],
|
||||
ret_ty: Type,
|
||||
ops: &[ast::Operator],
|
||||
ops: &[Operator],
|
||||
) {
|
||||
with_fields(unifier, ty, |unifier, fields| {
|
||||
let (other_ty, other_var_id) = if other_ty.len() == 1 {
|
||||
|
@ -137,7 +141,7 @@ pub fn impl_binop(
|
|||
});
|
||||
}
|
||||
|
||||
pub fn impl_unaryop(unifier: &mut Unifier, ty: Type, ret_ty: Type, ops: &[ast::Unaryop]) {
|
||||
pub fn impl_unaryop(unifier: &mut Unifier, ty: Type, ret_ty: Type, ops: &[Unaryop]) {
|
||||
with_fields(unifier, ty, |unifier, fields| {
|
||||
for op in ops {
|
||||
fields.insert(
|
||||
|
@ -160,7 +164,7 @@ pub fn impl_cmpop(
|
|||
store: &PrimitiveStore,
|
||||
ty: Type,
|
||||
other_ty: Type,
|
||||
ops: &[ast::Cmpop],
|
||||
ops: &[Cmpop],
|
||||
) {
|
||||
with_fields(unifier, ty, |unifier, fields| {
|
||||
for op in ops {
|
||||
|
@ -183,7 +187,7 @@ pub fn impl_cmpop(
|
|||
});
|
||||
}
|
||||
|
||||
/// Add, Sub, Mult
|
||||
/// `Add`, `Sub`, `Mult`
|
||||
pub fn impl_basic_arithmetic(
|
||||
unifier: &mut Unifier,
|
||||
store: &PrimitiveStore,
|
||||
|
@ -197,11 +201,11 @@ pub fn impl_basic_arithmetic(
|
|||
ty,
|
||||
other_ty,
|
||||
ret_ty,
|
||||
&[ast::Operator::Add, ast::Operator::Sub, ast::Operator::Mult],
|
||||
)
|
||||
&[Operator::Add, Operator::Sub, Operator::Mult],
|
||||
);
|
||||
}
|
||||
|
||||
/// Pow
|
||||
/// `Pow`
|
||||
pub fn impl_pow(
|
||||
unifier: &mut Unifier,
|
||||
store: &PrimitiveStore,
|
||||
|
@ -209,10 +213,10 @@ pub fn impl_pow(
|
|||
other_ty: &[Type],
|
||||
ret_ty: Type,
|
||||
) {
|
||||
impl_binop(unifier, store, ty, other_ty, ret_ty, &[ast::Operator::Pow])
|
||||
impl_binop(unifier, store, ty, other_ty, ret_ty, &[Operator::Pow]);
|
||||
}
|
||||
|
||||
/// BitOr, BitXor, BitAnd
|
||||
/// `BitOr`, `BitXor`, `BitAnd`
|
||||
pub fn impl_bitwise_arithmetic(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
|
||||
impl_binop(
|
||||
unifier,
|
||||
|
@ -220,21 +224,21 @@ pub fn impl_bitwise_arithmetic(unifier: &mut Unifier, store: &PrimitiveStore, ty
|
|||
ty,
|
||||
&[ty],
|
||||
ty,
|
||||
&[ast::Operator::BitAnd, ast::Operator::BitOr, ast::Operator::BitXor],
|
||||
)
|
||||
&[Operator::BitAnd, Operator::BitOr, Operator::BitXor],
|
||||
);
|
||||
}
|
||||
|
||||
/// LShift, RShift
|
||||
/// `LShift`, `RShift`
|
||||
pub fn impl_bitwise_shift(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
|
||||
impl_binop(unifier, store, ty, &[store.int32, store.uint32], ty, &[ast::Operator::LShift, ast::Operator::RShift]);
|
||||
impl_binop(unifier, store, ty, &[store.int32, store.uint32], ty, &[Operator::LShift, Operator::RShift]);
|
||||
}
|
||||
|
||||
/// Div
|
||||
/// `Div`
|
||||
pub fn impl_div(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type, other_ty: &[Type]) {
|
||||
impl_binop(unifier, store, ty, other_ty, store.float, &[ast::Operator::Div])
|
||||
impl_binop(unifier, store, ty, other_ty, store.float, &[Operator::Div]);
|
||||
}
|
||||
|
||||
/// FloorDiv
|
||||
/// `FloorDiv`
|
||||
pub fn impl_floordiv(
|
||||
unifier: &mut Unifier,
|
||||
store: &PrimitiveStore,
|
||||
|
@ -242,10 +246,10 @@ pub fn impl_floordiv(
|
|||
other_ty: &[Type],
|
||||
ret_ty: Type,
|
||||
) {
|
||||
impl_binop(unifier, store, ty, other_ty, ret_ty, &[ast::Operator::FloorDiv])
|
||||
impl_binop(unifier, store, ty, other_ty, ret_ty, &[Operator::FloorDiv]);
|
||||
}
|
||||
|
||||
/// Mod
|
||||
/// `Mod`
|
||||
pub fn impl_mod(
|
||||
unifier: &mut Unifier,
|
||||
store: &PrimitiveStore,
|
||||
|
@ -253,38 +257,38 @@ pub fn impl_mod(
|
|||
other_ty: &[Type],
|
||||
ret_ty: Type,
|
||||
) {
|
||||
impl_binop(unifier, store, ty, other_ty, ret_ty, &[ast::Operator::Mod])
|
||||
impl_binop(unifier, store, ty, other_ty, ret_ty, &[Operator::Mod]);
|
||||
}
|
||||
|
||||
/// UAdd, USub
|
||||
/// `UAdd`, `USub`
|
||||
pub fn impl_sign(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type) {
|
||||
impl_unaryop(unifier, ty, ty, &[ast::Unaryop::UAdd, ast::Unaryop::USub])
|
||||
impl_unaryop(unifier, ty, ty, &[Unaryop::UAdd, Unaryop::USub]);
|
||||
}
|
||||
|
||||
/// Invert
|
||||
/// `Invert`
|
||||
pub fn impl_invert(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type) {
|
||||
impl_unaryop(unifier, ty, ty, &[ast::Unaryop::Invert])
|
||||
impl_unaryop(unifier, ty, ty, &[Unaryop::Invert]);
|
||||
}
|
||||
|
||||
/// Not
|
||||
/// `Not`
|
||||
pub fn impl_not(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
|
||||
impl_unaryop(unifier, ty, store.bool, &[ast::Unaryop::Not])
|
||||
impl_unaryop(unifier, ty, store.bool, &[Unaryop::Not]);
|
||||
}
|
||||
|
||||
/// Lt, LtE, Gt, GtE
|
||||
/// `Lt`, `LtE`, `Gt`, `GtE`
|
||||
pub fn impl_comparison(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type, other_ty: Type) {
|
||||
impl_cmpop(
|
||||
unifier,
|
||||
store,
|
||||
ty,
|
||||
other_ty,
|
||||
&[ast::Cmpop::Lt, ast::Cmpop::Gt, ast::Cmpop::LtE, ast::Cmpop::GtE],
|
||||
)
|
||||
&[Cmpop::Lt, Cmpop::Gt, Cmpop::LtE, Cmpop::GtE],
|
||||
);
|
||||
}
|
||||
|
||||
/// Eq, NotEq
|
||||
/// `Eq`, `NotEq`
|
||||
pub fn impl_eq(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
|
||||
impl_cmpop(unifier, store, ty, ty, &[ast::Cmpop::Eq, ast::Cmpop::NotEq])
|
||||
impl_cmpop(unifier, store, ty, ty, &[Cmpop::Eq, Cmpop::NotEq]);
|
||||
}
|
||||
|
||||
pub fn set_primitives_magic_methods(store: &PrimitiveStore, unifier: &mut Unifier) {
|
||||
|
|
|
@ -43,15 +43,18 @@ pub struct TypeError {
|
|||
}
|
||||
|
||||
impl TypeError {
|
||||
#[must_use]
|
||||
pub fn new(kind: TypeErrorKind, loc: Option<Location>) -> TypeError {
|
||||
TypeError { kind, loc }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn at(mut self, loc: Option<Location>) -> TypeError {
|
||||
self.loc = self.loc.or(loc);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_display(self, unifier: &Unifier) -> DisplayTypeError {
|
||||
DisplayTypeError { err: self, unifier }
|
||||
}
|
||||
|
@ -64,8 +67,8 @@ pub struct DisplayTypeError<'a> {
|
|||
|
||||
fn loc_to_str(loc: Option<Location>) -> String {
|
||||
match loc {
|
||||
Some(loc) => format!("(in {})", loc),
|
||||
None => "".to_string(),
|
||||
Some(loc) => format!("(in {loc})"),
|
||||
None => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,21 +78,20 @@ impl<'a> Display for DisplayTypeError<'a> {
|
|||
let mut notes = Some(HashMap::new());
|
||||
match &self.err.kind {
|
||||
TooManyArguments { expected, got } => {
|
||||
write!(f, "Too many arguments. Expected {} but got {}", expected, got)
|
||||
write!(f, "Too many arguments. Expected {expected} but got {got}")
|
||||
}
|
||||
MissingArgs(args) => {
|
||||
write!(f, "Missing arguments: {}", args)
|
||||
write!(f, "Missing arguments: {args}")
|
||||
}
|
||||
UnknownArgName(name) => {
|
||||
write!(f, "Unknown argument name: {}", name)
|
||||
write!(f, "Unknown argument name: {name}")
|
||||
}
|
||||
IncorrectArgType { name, expected, got } => {
|
||||
let expected = self.unifier.stringify_with_notes(*expected, &mut notes);
|
||||
let got = self.unifier.stringify_with_notes(*got, &mut notes);
|
||||
write!(
|
||||
f,
|
||||
"Incorrect argument type for {}. Expected {}, but got {}",
|
||||
name, expected, got
|
||||
"Incorrect argument type for {name}. Expected {expected}, but got {got}"
|
||||
)
|
||||
}
|
||||
FieldUnificationError { field, types, loc } => {
|
||||
|
@ -126,7 +128,7 @@ impl<'a> Display for DisplayTypeError<'a> {
|
|||
);
|
||||
if let Some(loc) = loc {
|
||||
result?;
|
||||
write!(f, " (in {})", loc)?;
|
||||
write!(f, " (in {loc})")?;
|
||||
return Ok(());
|
||||
}
|
||||
result
|
||||
|
@ -136,12 +138,12 @@ impl<'a> Display for DisplayTypeError<'a> {
|
|||
{
|
||||
let t1 = self.unifier.stringify_with_notes(*t1, &mut notes);
|
||||
let t2 = self.unifier.stringify_with_notes(*t2, &mut notes);
|
||||
write!(f, "Tuple length mismatch: got {} and {}", t1, t2)
|
||||
write!(f, "Tuple length mismatch: got {t1} and {t2}")
|
||||
}
|
||||
_ => {
|
||||
let t1 = self.unifier.stringify_with_notes(*t1, &mut notes);
|
||||
let t2 = self.unifier.stringify_with_notes(*t2, &mut notes);
|
||||
write!(f, "Incompatible types: {} and {}", t1, t2)
|
||||
write!(f, "Incompatible types: {t1} and {t2}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,18 +152,17 @@ impl<'a> Display for DisplayTypeError<'a> {
|
|||
write!(f, "Cannot assign to an element of a tuple")
|
||||
} else {
|
||||
let t = self.unifier.stringify_with_notes(*t, &mut notes);
|
||||
write!(f, "Cannot assign to field {} of {}, which is immutable", name, t)
|
||||
write!(f, "Cannot assign to field {name} of {t}, which is immutable")
|
||||
}
|
||||
}
|
||||
NoSuchField(name, t) => {
|
||||
let t = self.unifier.stringify_with_notes(*t, &mut notes);
|
||||
write!(f, "`{}::{}` field/method does not exist", t, name)
|
||||
write!(f, "`{t}::{name}` field/method does not exist")
|
||||
}
|
||||
TupleIndexOutOfBounds { index, len } => {
|
||||
write!(
|
||||
f,
|
||||
"Tuple index out of bounds. Got {} but tuple has only {} elements",
|
||||
index, len
|
||||
"Tuple index out of bounds. Got {index} but tuple has only {len} elements"
|
||||
)
|
||||
}
|
||||
RequiresTypeAnn => {
|
||||
|
@ -172,13 +173,13 @@ impl<'a> Display for DisplayTypeError<'a> {
|
|||
}
|
||||
}?;
|
||||
if let Some(loc) = self.err.loc {
|
||||
write!(f, " at {}", loc)?;
|
||||
write!(f, " at {loc}")?;
|
||||
}
|
||||
let notes = notes.unwrap();
|
||||
if !notes.is_empty() {
|
||||
write!(f, "\n\nNotes:")?;
|
||||
for line in notes.values() {
|
||||
write!(f, "\n {}", line)?;
|
||||
write!(f, "\n {line}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -62,23 +62,23 @@ pub struct Inferencer<'a> {
|
|||
}
|
||||
|
||||
struct NaiveFolder();
|
||||
impl fold::Fold<()> for NaiveFolder {
|
||||
impl Fold<()> for NaiveFolder {
|
||||
type TargetU = Option<Type>;
|
||||
type Error = String;
|
||||
fn map_user(&mut self, _: ()) -> Result<Self::TargetU, Self::Error> {
|
||||
type Error = HashSet<String>;
|
||||
fn map_user(&mut self, (): ()) -> Result<Self::TargetU, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn report_error<T>(msg: &str, location: Location) -> Result<T, String> {
|
||||
Err(format!("{} at {}", msg, location))
|
||||
fn report_error<T>(msg: &str, location: Location) -> Result<T, HashSet<String>> {
|
||||
Err(HashSet::from([format!("{msg} at {location}")]))
|
||||
}
|
||||
|
||||
impl<'a> fold::Fold<()> for Inferencer<'a> {
|
||||
impl<'a> Fold<()> for Inferencer<'a> {
|
||||
type TargetU = Option<Type>;
|
||||
type Error = String;
|
||||
type Error = HashSet<String>;
|
||||
|
||||
fn map_user(&mut self, _: ()) -> Result<Self::TargetU, Self::Error> {
|
||||
fn map_user(&mut self, (): ()) -> Result<Self::TargetU, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
{
|
||||
let top_level_defs = self.top_level.definitions.read();
|
||||
let mut naive_folder = NaiveFolder();
|
||||
for handler in handlers.into_iter() {
|
||||
for handler in handlers {
|
||||
let ast::ExcepthandlerKind::ExceptHandler { type_, name, body } =
|
||||
handler.node;
|
||||
let type_ = if let Some(type_) = type_ {
|
||||
|
@ -159,9 +159,9 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
}
|
||||
if let Some(old_typ) = self.variable_mapping.insert(name, typ) {
|
||||
let loc = handler.location;
|
||||
self.unifier.unify(old_typ, typ).map_err(|e| {
|
||||
e.at(Some(loc)).to_display(self.unifier).to_string()
|
||||
})?;
|
||||
self.unifier.unify(old_typ, typ).map_err(|e| HashSet::from([
|
||||
e.at(Some(loc)).to_display(self.unifier).to_string(),
|
||||
]))?;
|
||||
}
|
||||
}
|
||||
let mut type_ = naive_folder.fold_expr(*type_)?;
|
||||
|
@ -226,65 +226,65 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
}
|
||||
}
|
||||
ast::StmtKind::Assign { ref mut targets, ref config_comment, .. } => {
|
||||
for target in targets.iter_mut() {
|
||||
for target in &mut *targets {
|
||||
if let ExprKind::Attribute { ctx, .. } = &mut target.node {
|
||||
*ctx = ExprContext::Store;
|
||||
}
|
||||
}
|
||||
if targets.iter().all(|t| matches!(t.node, ast::ExprKind::Name { .. })) {
|
||||
if let ast::StmtKind::Assign { targets, value, .. } = node.node {
|
||||
let value = self.fold_expr(*value)?;
|
||||
let value_ty = value.custom.unwrap();
|
||||
let targets: Result<Vec<_>, _> = targets
|
||||
.into_iter()
|
||||
.map(|target| {
|
||||
if let ast::ExprKind::Name { id, ctx } = target.node {
|
||||
self.defined_identifiers.insert(id);
|
||||
let target_ty = if let Some(ty) = self.variable_mapping.get(&id)
|
||||
{
|
||||
*ty
|
||||
} else {
|
||||
let unifier = &mut self.unifier;
|
||||
self.function_data
|
||||
.resolver
|
||||
.get_symbol_type(
|
||||
unifier,
|
||||
&self.top_level.definitions.read(),
|
||||
self.primitives,
|
||||
id,
|
||||
)
|
||||
.unwrap_or_else(|_| {
|
||||
self.variable_mapping.insert(id, value_ty);
|
||||
value_ty
|
||||
})
|
||||
};
|
||||
let location = target.location;
|
||||
self.unifier.unify(value_ty, target_ty).map(|_| Located {
|
||||
location,
|
||||
node: ast::ExprKind::Name { id, ctx },
|
||||
custom: Some(target_ty),
|
||||
})
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let loc = node.location;
|
||||
let targets = targets
|
||||
.map_err(|e| e.at(Some(loc)).to_display(self.unifier).to_string())?;
|
||||
return Ok(Located {
|
||||
location: node.location,
|
||||
node: ast::StmtKind::Assign {
|
||||
targets,
|
||||
value: Box::new(value),
|
||||
type_comment: None,
|
||||
config_comment: config_comment.clone(),
|
||||
},
|
||||
custom: None,
|
||||
});
|
||||
} else {
|
||||
if targets.iter().all(|t| matches!(t.node, ExprKind::Name { .. })) {
|
||||
let ast::StmtKind::Assign { targets, value, .. } = node.node else {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
let value = self.fold_expr(*value)?;
|
||||
let value_ty = value.custom.unwrap();
|
||||
let targets: Result<Vec<_>, _> = targets
|
||||
.into_iter()
|
||||
.map(|target| {
|
||||
if let ExprKind::Name { id, ctx } = target.node {
|
||||
self.defined_identifiers.insert(id);
|
||||
let target_ty = if let Some(ty) = self.variable_mapping.get(&id)
|
||||
{
|
||||
*ty
|
||||
} else {
|
||||
let unifier: &mut Unifier = self.unifier;
|
||||
self.function_data
|
||||
.resolver
|
||||
.get_symbol_type(
|
||||
unifier,
|
||||
&self.top_level.definitions.read(),
|
||||
self.primitives,
|
||||
id,
|
||||
)
|
||||
.unwrap_or_else(|_| {
|
||||
self.variable_mapping.insert(id, value_ty);
|
||||
value_ty
|
||||
})
|
||||
};
|
||||
let location = target.location;
|
||||
self.unifier.unify(value_ty, target_ty).map(|()| Located {
|
||||
location,
|
||||
node: ExprKind::Name { id, ctx },
|
||||
custom: Some(target_ty),
|
||||
})
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let loc = node.location;
|
||||
let targets = targets
|
||||
.map_err(|e| HashSet::from([e.at(Some(loc)).to_display(self.unifier).to_string()]))?;
|
||||
return Ok(Located {
|
||||
location: node.location,
|
||||
node: ast::StmtKind::Assign {
|
||||
targets,
|
||||
value: Box::new(value),
|
||||
type_comment: None,
|
||||
config_comment: config_comment.clone(),
|
||||
},
|
||||
custom: None,
|
||||
});
|
||||
}
|
||||
for target in targets {
|
||||
self.infer_pattern(target)?;
|
||||
|
@ -292,7 +292,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
fold::fold_stmt(self, node)?
|
||||
}
|
||||
ast::StmtKind::With { ref items, .. } => {
|
||||
for item in items.iter() {
|
||||
for item in items {
|
||||
if let Some(var) = &item.optional_vars {
|
||||
self.infer_pattern(var)?;
|
||||
}
|
||||
|
@ -302,20 +302,21 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
_ => fold::fold_stmt(self, node)?,
|
||||
};
|
||||
match &stmt.node {
|
||||
ast::StmtKind::For { .. } => {}
|
||||
ast::StmtKind::Try { .. } => {}
|
||||
ast::StmtKind::AnnAssign { .. }
|
||||
| ast::StmtKind::Break { .. }
|
||||
| ast::StmtKind::Continue { .. }
|
||||
| ast::StmtKind::Expr { .. }
|
||||
| ast::StmtKind::For { .. }
|
||||
| ast::StmtKind::Pass { .. }
|
||||
| ast::StmtKind::Try { .. } => {}
|
||||
ast::StmtKind::If { test, .. } | ast::StmtKind::While { test, .. } => {
|
||||
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
|
||||
}
|
||||
ast::StmtKind::Assign { targets, value, .. } => {
|
||||
for target in targets.iter() {
|
||||
for target in targets {
|
||||
self.unify(target.custom.unwrap(), value.custom.unwrap(), &target.location)?;
|
||||
}
|
||||
}
|
||||
ast::StmtKind::AnnAssign { .. } | ast::StmtKind::Expr { .. } => {}
|
||||
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);
|
||||
|
@ -334,13 +335,13 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
}
|
||||
}
|
||||
ast::StmtKind::With { items, .. } => {
|
||||
for item in items.iter() {
|
||||
for item in items {
|
||||
let ty = item.context_expr.custom.unwrap();
|
||||
// if we can simply unify without creating new types...
|
||||
let mut fast_path = false;
|
||||
if let TypeEnum::TObj { fields, .. } = &*self.unifier.get_ty(ty) {
|
||||
fast_path = true;
|
||||
if let Some(enter) = fields.get(&"__enter__".into()).cloned() {
|
||||
if let Some(enter) = fields.get(&"__enter__".into()).copied() {
|
||||
if let TypeEnum::TFunc(signature) = &*self.unifier.get_ty(enter.0) {
|
||||
if !signature.args.is_empty() {
|
||||
return report_error(
|
||||
|
@ -368,7 +369,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
stmt.location,
|
||||
);
|
||||
}
|
||||
if let Some(exit) = fields.get(&"__exit__".into()).cloned() {
|
||||
if let Some(exit) = fields.get(&"__exit__".into()).copied() {
|
||||
if let TypeEnum::TFunc(signature) = &*self.unifier.get_ty(exit.0) {
|
||||
if !signature.args.is_empty() {
|
||||
return report_error(
|
||||
|
@ -393,13 +394,13 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
|| self.unifier.get_dummy_var().0,
|
||||
|var| var.custom.unwrap(),
|
||||
),
|
||||
vars: Default::default(),
|
||||
vars: HashMap::default(),
|
||||
});
|
||||
let enter = self.unifier.add_ty(enter);
|
||||
let exit = TypeEnum::TFunc(FunSignature {
|
||||
args: vec![],
|
||||
ret: self.unifier.get_dummy_var().0,
|
||||
vars: Default::default(),
|
||||
vars: HashMap::default(),
|
||||
});
|
||||
let exit = self.unifier.add_ty(exit);
|
||||
let mut fields = HashMap::new();
|
||||
|
@ -440,22 +441,22 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
|
||||
fn fold_expr(&mut self, node: ast::Expr<()>) -> Result<ast::Expr<Self::TargetU>, Self::Error> {
|
||||
let expr = match node.node {
|
||||
ast::ExprKind::Call { func, args, keywords } => {
|
||||
ExprKind::Call { func, args, keywords } => {
|
||||
return self.fold_call(node.location, *func, args, keywords);
|
||||
}
|
||||
ast::ExprKind::Lambda { args, body } => {
|
||||
ExprKind::Lambda { args, body } => {
|
||||
return self.fold_lambda(node.location, *args, *body);
|
||||
}
|
||||
ast::ExprKind::ListComp { elt, generators } => {
|
||||
ExprKind::ListComp { elt, generators } => {
|
||||
return self.fold_listcomp(node.location, *elt, generators);
|
||||
}
|
||||
_ => fold::fold_expr(self, node)?,
|
||||
};
|
||||
let custom = match &expr.node {
|
||||
ast::ExprKind::Constant { value, .. } => {
|
||||
ExprKind::Constant { value, .. } => {
|
||||
Some(self.infer_constant(value, &expr.location)?)
|
||||
}
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
ExprKind::Name { id, .. } => {
|
||||
// the name `none` is special since it may have different types
|
||||
if id == &"none".into() {
|
||||
if let TypeEnum::TObj { params, .. } =
|
||||
|
@ -489,7 +490,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
}
|
||||
Err(e) => {
|
||||
return report_error(
|
||||
&format!("type error at identifier `{}` ({})", id, e),
|
||||
&format!("type error at identifier `{id}` ({e})"),
|
||||
expr.location,
|
||||
);
|
||||
}
|
||||
|
@ -498,51 +499,53 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
Some(self.infer_identifier(*id)?)
|
||||
}
|
||||
}
|
||||
ast::ExprKind::List { elts, .. } => Some(self.infer_list(elts)?),
|
||||
ast::ExprKind::Tuple { elts, .. } => Some(self.infer_tuple(elts)?),
|
||||
ast::ExprKind::Attribute { value, attr, ctx } => {
|
||||
ExprKind::List { elts, .. } => Some(self.infer_list(elts)?),
|
||||
ExprKind::Tuple { elts, .. } => Some(self.infer_tuple(elts)?),
|
||||
ExprKind::Attribute { value, attr, ctx } => {
|
||||
Some(self.infer_attribute(value, *attr, ctx)?)
|
||||
}
|
||||
ast::ExprKind::BoolOp { values, .. } => Some(self.infer_bool_ops(values)?),
|
||||
ast::ExprKind::BinOp { left, op, right } => {
|
||||
ExprKind::BoolOp { values, .. } => Some(self.infer_bool_ops(values)?),
|
||||
ExprKind::BinOp { left, op, right } => {
|
||||
Some(self.infer_bin_ops(expr.location, left, op, right, false)?)
|
||||
}
|
||||
ast::ExprKind::UnaryOp { op, operand } => Some(self.infer_unary_ops(op, operand)?),
|
||||
ast::ExprKind::Compare { left, ops, comparators } => {
|
||||
ExprKind::UnaryOp { op, operand } => Some(self.infer_unary_ops(op, operand)?),
|
||||
ExprKind::Compare { left, ops, comparators } => {
|
||||
Some(self.infer_compare(left, ops, comparators)?)
|
||||
}
|
||||
ast::ExprKind::Subscript { value, slice, ctx, .. } => {
|
||||
ExprKind::Subscript { value, slice, ctx, .. } => {
|
||||
Some(self.infer_subscript(value.as_ref(), slice.as_ref(), ctx)?)
|
||||
}
|
||||
ast::ExprKind::IfExp { test, body, orelse } => {
|
||||
ExprKind::IfExp { test, body, orelse } => {
|
||||
Some(self.infer_if_expr(test, body.as_ref(), orelse.as_ref())?)
|
||||
}
|
||||
ast::ExprKind::ListComp { .. }
|
||||
| ast::ExprKind::Lambda { .. }
|
||||
| ast::ExprKind::Call { .. } => expr.custom, // already computed
|
||||
ast::ExprKind::Slice { .. } => None, // we don't need it for slice
|
||||
ExprKind::ListComp { .. }
|
||||
| ExprKind::Lambda { .. }
|
||||
| ExprKind::Call { .. } => expr.custom, // already computed
|
||||
ExprKind::Slice { .. } => None, // we don't need it for slice
|
||||
_ => return report_error("not supported", expr.location),
|
||||
};
|
||||
Ok(ast::Expr { custom, location: expr.location, node: expr.node })
|
||||
}
|
||||
}
|
||||
|
||||
type InferenceResult = Result<Type, String>;
|
||||
type InferenceResult = Result<Type, HashSet<String>>;
|
||||
|
||||
impl<'a> Inferencer<'a> {
|
||||
/// Constrain a <: b
|
||||
/// Currently implemented as unification
|
||||
fn constrain(&mut self, a: Type, b: Type, location: &Location) -> Result<(), String> {
|
||||
fn constrain(&mut self, a: Type, b: Type, location: &Location) -> Result<(), HashSet<String>> {
|
||||
self.unify(a, b, location)
|
||||
}
|
||||
|
||||
fn unify(&mut self, a: Type, b: Type, location: &Location) -> Result<(), String> {
|
||||
fn unify(&mut self, a: Type, b: Type, location: &Location) -> Result<(), HashSet<String>> {
|
||||
self.unifier
|
||||
.unify(a, b)
|
||||
.map_err(|e| e.at(Some(*location)).to_display(self.unifier).to_string())
|
||||
.map_err(|e| HashSet::from([
|
||||
e.at(Some(*location)).to_display(self.unifier).to_string(),
|
||||
]))
|
||||
}
|
||||
|
||||
fn infer_pattern(&mut self, pattern: &ast::Expr<()>) -> Result<(), String> {
|
||||
fn infer_pattern(&mut self, pattern: &ast::Expr<()>) -> Result<(), HashSet<String>> {
|
||||
match &pattern.node {
|
||||
ExprKind::Name { id, .. } => {
|
||||
if !self.defined_identifiers.contains(id) {
|
||||
|
@ -551,7 +554,7 @@ impl<'a> Inferencer<'a> {
|
|||
Ok(())
|
||||
}
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
for elt in elts.iter() {
|
||||
for elt in elts {
|
||||
self.infer_pattern(elt)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -591,9 +594,9 @@ impl<'a> Inferencer<'a> {
|
|||
.map(|v| v.name)
|
||||
.rev()
|
||||
.collect();
|
||||
self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| {
|
||||
e.at(Some(location)).to_display(self.unifier).to_string()
|
||||
})?;
|
||||
self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| HashSet::from([
|
||||
e.at(Some(location)).to_display(self.unifier).to_string(),
|
||||
]))?;
|
||||
return Ok(sign.ret);
|
||||
}
|
||||
}
|
||||
|
@ -622,7 +625,7 @@ impl<'a> Inferencer<'a> {
|
|||
location: Location,
|
||||
args: Arguments,
|
||||
body: ast::Expr<()>,
|
||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
||||
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||
if !args.posonlyargs.is_empty()
|
||||
|| args.vararg.is_some()
|
||||
|| !args.kwonlyargs.is_empty()
|
||||
|
@ -637,7 +640,7 @@ impl<'a> Inferencer<'a> {
|
|||
}
|
||||
|
||||
let mut defined_identifiers = self.defined_identifiers.clone();
|
||||
for arg in args.args.iter() {
|
||||
for arg in &args.args {
|
||||
let name = &arg.node.arg;
|
||||
if !defined_identifiers.contains(name) {
|
||||
defined_identifiers.insert(*name);
|
||||
|
@ -649,7 +652,7 @@ impl<'a> Inferencer<'a> {
|
|||
.map(|v| (v.node.arg, self.unifier.get_fresh_var(Some(v.node.arg), Some(v.location)).0))
|
||||
.collect();
|
||||
let mut variable_mapping = self.variable_mapping.clone();
|
||||
variable_mapping.extend(fn_args.iter().cloned());
|
||||
variable_mapping.extend(fn_args.iter().copied());
|
||||
let ret = self.unifier.get_dummy_var().0;
|
||||
|
||||
let mut new_context = Inferencer {
|
||||
|
@ -670,7 +673,7 @@ impl<'a> Inferencer<'a> {
|
|||
.map(|(k, ty)| FuncArg { name: *k, ty: *ty, default_value: None })
|
||||
.collect(),
|
||||
ret,
|
||||
vars: Default::default(),
|
||||
vars: HashMap::default(),
|
||||
};
|
||||
let body = new_context.fold_expr(body)?;
|
||||
new_context.unify(fun.ret, body.custom.unwrap(), &location)?;
|
||||
|
@ -691,7 +694,7 @@ impl<'a> Inferencer<'a> {
|
|||
location: Location,
|
||||
elt: ast::Expr<()>,
|
||||
mut generators: Vec<Comprehension>,
|
||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
||||
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||
if generators.len() != 1 {
|
||||
return report_error(
|
||||
"Only 1 generator statement for list comprehension is supported",
|
||||
|
@ -739,7 +742,7 @@ impl<'a> Inferencer<'a> {
|
|||
// iter should be a list of targets...
|
||||
// actually it should be an iterator of targets, but we don't have iter type for now
|
||||
// if conditions should be bool
|
||||
for v in ifs.iter() {
|
||||
for v in &ifs {
|
||||
new_context.unify(v.custom.unwrap(), new_context.primitives.bool, &v.location)?;
|
||||
}
|
||||
|
||||
|
@ -748,7 +751,7 @@ impl<'a> Inferencer<'a> {
|
|||
custom: Some(new_context.unifier.add_ty(TypeEnum::TList { ty: elt.custom.unwrap() })),
|
||||
node: ExprKind::ListComp {
|
||||
elt: Box::new(elt),
|
||||
generators: vec![ast::Comprehension {
|
||||
generators: vec![Comprehension {
|
||||
target: Box::new(target),
|
||||
iter: Box::new(iter),
|
||||
ifs,
|
||||
|
@ -764,7 +767,7 @@ impl<'a> Inferencer<'a> {
|
|||
func: ast::Expr<()>,
|
||||
mut args: Vec<ast::Expr<()>>,
|
||||
keywords: Vec<Located<ast::KeywordData>>,
|
||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
||||
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||
let func =
|
||||
if let Located { location: func_location, custom, node: ExprKind::Name { id, ctx } } =
|
||||
func
|
||||
|
@ -812,17 +815,17 @@ impl<'a> Inferencer<'a> {
|
|||
{
|
||||
let custom = Some(self.primitives.int64);
|
||||
let v: Result<i64, _> = (*val).try_into();
|
||||
if v.is_ok() {
|
||||
return Ok(Located {
|
||||
return if v.is_ok() {
|
||||
Ok(Located {
|
||||
location: args[0].location,
|
||||
custom,
|
||||
node: ExprKind::Constant {
|
||||
value: ast::Constant::Int(*val),
|
||||
kind: kind.clone(),
|
||||
},
|
||||
});
|
||||
})
|
||||
} else {
|
||||
return report_error("Integer out of bound", args[0].location)
|
||||
report_error("Integer out of bound", args[0].location)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -832,17 +835,17 @@ impl<'a> Inferencer<'a> {
|
|||
{
|
||||
let custom = Some(self.primitives.uint32);
|
||||
let v: Result<u32, _> = (*val).try_into();
|
||||
if v.is_ok() {
|
||||
return Ok(Located {
|
||||
return if v.is_ok() {
|
||||
Ok(Located {
|
||||
location: args[0].location,
|
||||
custom,
|
||||
node: ExprKind::Constant {
|
||||
value: ast::Constant::Int(*val),
|
||||
kind: kind.clone(),
|
||||
},
|
||||
});
|
||||
})
|
||||
} else {
|
||||
return report_error("Integer out of bound", args[0].location)
|
||||
report_error("Integer out of bound", args[0].location)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -852,17 +855,17 @@ impl<'a> Inferencer<'a> {
|
|||
{
|
||||
let custom = Some(self.primitives.uint64);
|
||||
let v: Result<u64, _> = (*val).try_into();
|
||||
if v.is_ok() {
|
||||
return Ok(Located {
|
||||
return if v.is_ok() {
|
||||
Ok(Located {
|
||||
location: args[0].location,
|
||||
custom,
|
||||
node: ExprKind::Constant {
|
||||
value: ast::Constant::Int(*val),
|
||||
kind: kind.clone(),
|
||||
},
|
||||
});
|
||||
})
|
||||
} else {
|
||||
return report_error("Integer out of bound", args[0].location)
|
||||
report_error("Integer out of bound", args[0].location)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -898,7 +901,9 @@ impl<'a> Inferencer<'a> {
|
|||
.collect();
|
||||
self.unifier
|
||||
.unify_call(&call, func.custom.unwrap(), sign, &required)
|
||||
.map_err(|e| e.at(Some(location)).to_display(self.unifier).to_string())?;
|
||||
.map_err(|e| HashSet::from([
|
||||
e.at(Some(location)).to_display(self.unifier).to_string(),
|
||||
]))?;
|
||||
return Ok(Located {
|
||||
location,
|
||||
custom: Some(sign.ret),
|
||||
|
@ -926,12 +931,12 @@ impl<'a> Inferencer<'a> {
|
|||
}
|
||||
|
||||
fn infer_identifier(&mut self, id: StrRef) -> InferenceResult {
|
||||
if let Some(ty) = self.variable_mapping.get(&id) {
|
||||
Ok(*ty)
|
||||
Ok(if let Some(ty) = self.variable_mapping.get(&id) {
|
||||
*ty
|
||||
} else {
|
||||
let variable_mapping = &mut self.variable_mapping;
|
||||
let unifier = &mut self.unifier;
|
||||
Ok(self
|
||||
let unifier: &mut Unifier = self.unifier;
|
||||
self
|
||||
.function_data
|
||||
.resolver
|
||||
.get_symbol_type(unifier, &self.top_level.definitions.read(), self.primitives, id)
|
||||
|
@ -939,8 +944,8 @@ impl<'a> Inferencer<'a> {
|
|||
let ty = unifier.get_dummy_var().0;
|
||||
variable_mapping.insert(id, ty);
|
||||
ty
|
||||
}))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn infer_constant(&mut self, constant: &ast::Constant, loc: &Location) -> InferenceResult {
|
||||
|
@ -971,7 +976,7 @@ impl<'a> Inferencer<'a> {
|
|||
|
||||
fn infer_list(&mut self, elts: &[ast::Expr<Option<Type>>]) -> InferenceResult {
|
||||
let ty = self.unifier.get_dummy_var().0;
|
||||
for t in elts.iter() {
|
||||
for t in elts {
|
||||
self.unify(ty, t.custom.unwrap(), &t.location)?;
|
||||
}
|
||||
Ok(self.unifier.add_ty(TypeEnum::TList { ty }))
|
||||
|
@ -992,14 +997,13 @@ impl<'a> Inferencer<'a> {
|
|||
if let TypeEnum::TObj { fields, .. } = &*self.unifier.get_ty(ty) {
|
||||
// just a fast path
|
||||
match (fields.get(&attr), ctx == &ExprContext::Store) {
|
||||
(Some((ty, true)), _) => Ok(*ty),
|
||||
(Some((ty, false)), false) => Ok(*ty),
|
||||
(Some((ty, true)), _) | (Some((ty, false)), false) => Ok(*ty),
|
||||
(Some((_, false)), true) => {
|
||||
report_error(&format!("Field `{}` is immutable", attr), value.location)
|
||||
report_error(&format!("Field `{attr}` is immutable"), value.location)
|
||||
}
|
||||
(None, _) => {
|
||||
let t = self.unifier.stringify(ty);
|
||||
report_error(&format!("`{}::{}` field/method does not exist", t, attr), value.location)
|
||||
report_error(&format!("`{t}::{attr}` field/method does not exist"), value.location)
|
||||
},
|
||||
}
|
||||
} else {
|
||||
|
@ -1073,8 +1077,11 @@ impl<'a> Inferencer<'a> {
|
|||
) -> InferenceResult {
|
||||
let boolean = self.primitives.bool;
|
||||
for (a, b, c) in izip!(once(left).chain(comparators), comparators, ops) {
|
||||
let method =
|
||||
comparison_name(c).ok_or_else(|| "unsupported comparator".to_string())?.into();
|
||||
let method = comparison_name(c)
|
||||
.ok_or_else(|| HashSet::from([
|
||||
"unsupported comparator".to_string()
|
||||
]))?
|
||||
.into();
|
||||
self.build_method_call(
|
||||
a.location,
|
||||
method,
|
||||
|
@ -1094,7 +1101,7 @@ impl<'a> Inferencer<'a> {
|
|||
) -> InferenceResult {
|
||||
let ty = self.unifier.get_dummy_var().0;
|
||||
match &slice.node {
|
||||
ast::ExprKind::Slice { lower, upper, step } => {
|
||||
ExprKind::Slice { lower, upper, step } => {
|
||||
for v in [lower.as_ref(), upper.as_ref(), step.as_ref()].iter().flatten() {
|
||||
self.constrain(v.custom.unwrap(), self.primitives.int32, &v.location)?;
|
||||
}
|
||||
|
@ -1102,10 +1109,10 @@ impl<'a> Inferencer<'a> {
|
|||
self.constrain(value.custom.unwrap(), list, &value.location)?;
|
||||
Ok(list)
|
||||
}
|
||||
ast::ExprKind::Constant { value: ast::Constant::Int(val), .. } => {
|
||||
ExprKind::Constant { value: ast::Constant::Int(val), .. } => {
|
||||
// the index is a constant, so value can be a sequence.
|
||||
let ind: Option<i32> = (*val).try_into().ok();
|
||||
let ind = ind.ok_or_else(|| "Index must be int32".to_string())?;
|
||||
let ind = ind.ok_or_else(|| HashSet::from(["Index must be int32".to_string()]))?;
|
||||
let map = once((
|
||||
ind.into(),
|
||||
RecordField::new(ty, ctx == &ExprContext::Store, Some(value.location)),
|
||||
|
|
|
@ -20,7 +20,7 @@ struct Resolver {
|
|||
impl SymbolResolver for Resolver {
|
||||
fn get_default_param_value(
|
||||
&self,
|
||||
_: &nac3parser::ast::Expr,
|
||||
_: &ast::Expr,
|
||||
) -> Option<crate::symbol_resolver::SymbolValue> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -43,8 +43,9 @@ impl SymbolResolver for Resolver {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
self.id_to_def.get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||
self.id_to_def.get(&id).cloned()
|
||||
.ok_or_else(|| HashSet::from(["Unknown identifier".to_string()]))
|
||||
}
|
||||
|
||||
fn get_string_id(&self, _: &str) -> i32 {
|
||||
|
@ -62,7 +63,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, nac3parser::ast::Location)>,
|
||||
pub virtual_checks: Vec<(Type, Type, Location)>,
|
||||
pub calls: HashMap<CodeLocation, CallId>,
|
||||
pub top_level: TopLevelContext,
|
||||
}
|
||||
|
|
|
@ -97,8 +97,8 @@ impl From<i32> for RecordKey {
|
|||
impl Display for RecordKey {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
RecordKey::Str(s) => write!(f, "{}", s),
|
||||
RecordKey::Int(i) => write!(f, "{}", i),
|
||||
RecordKey::Str(s) => write!(f, "{s}"),
|
||||
RecordKey::Int(i) => write!(f, "{i}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +111,7 @@ pub struct RecordField {
|
|||
}
|
||||
|
||||
impl RecordField {
|
||||
#[must_use]
|
||||
pub fn new(ty: Type, mutable: bool, loc: Option<Location>) -> RecordField {
|
||||
RecordField { ty, mutable, loc }
|
||||
}
|
||||
|
@ -185,6 +186,7 @@ pub enum TypeEnum {
|
|||
}
|
||||
|
||||
impl TypeEnum {
|
||||
#[must_use]
|
||||
pub fn get_type_name(&self) -> &'static str {
|
||||
match self {
|
||||
TypeEnum::TRigidVar { .. } => "TRigidVar",
|
||||
|
@ -220,6 +222,7 @@ impl Default for Unifier {
|
|||
|
||||
impl Unifier {
|
||||
/// Get an empty unifier
|
||||
#[must_use]
|
||||
pub fn new() -> Unifier {
|
||||
Unifier {
|
||||
unification_table: UnificationTable::new(),
|
||||
|
@ -252,6 +255,7 @@ impl Unifier {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_shared_unifier(&self) -> SharedUnifier {
|
||||
Arc::new(Mutex::new((
|
||||
self.unification_table.get_send(),
|
||||
|
@ -261,7 +265,7 @@ impl Unifier {
|
|||
}
|
||||
|
||||
/// Register a type to the unifier.
|
||||
/// Returns a key in the unification_table.
|
||||
/// Returns a key in the `unification_table`.
|
||||
pub fn add_ty(&mut self, a: TypeEnum) -> Type {
|
||||
self.unification_table.new_key(Rc::new(a))
|
||||
}
|
||||
|
@ -294,6 +298,7 @@ impl Unifier {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_call_signature_immutable(&self, id: CallId) -> Option<FunSignature> {
|
||||
let fun = self.calls.get(id.0).unwrap().fun.borrow().unwrap();
|
||||
if let TypeEnum::TFunc(sign) = &*self.get_ty_immutable(fun) {
|
||||
|
@ -307,11 +312,12 @@ impl Unifier {
|
|||
self.unification_table.get_representative(ty)
|
||||
}
|
||||
|
||||
/// Get the TypeEnum of a type.
|
||||
/// Get the `TypeEnum` of a type.
|
||||
pub fn get_ty(&mut self, a: Type) -> Rc<TypeEnum> {
|
||||
self.unification_table.probe_value(a).clone()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_ty_immutable(&self, a: Type) -> Rc<TypeEnum> {
|
||||
self.unification_table.probe_value_immutable(a).clone()
|
||||
}
|
||||
|
@ -394,8 +400,7 @@ impl Unifier {
|
|||
Some(
|
||||
range
|
||||
.iter()
|
||||
.map(|ty| self.get_instantiations(*ty).unwrap_or_else(|| vec![*ty]))
|
||||
.flatten()
|
||||
.flat_map(|ty| self.get_instantiations(*ty).unwrap_or_else(|| vec![*ty]))
|
||||
.collect_vec(),
|
||||
)
|
||||
}
|
||||
|
@ -436,7 +441,7 @@ impl Unifier {
|
|||
.map(|params| {
|
||||
self.subst(
|
||||
ty,
|
||||
&zip(keys.iter().cloned(), params.iter().cloned()).collect(),
|
||||
&zip(keys.iter().copied(), params.iter().copied()).collect(),
|
||||
)
|
||||
.unwrap_or(ty)
|
||||
})
|
||||
|
@ -454,7 +459,7 @@ impl Unifier {
|
|||
TRigidVar { .. } | TConstant { .. } => true,
|
||||
TVar { .. } => allowed_typevars.iter().any(|b| self.unification_table.unioned(a, *b)),
|
||||
TCall { .. } => false,
|
||||
TList { ty } => self.is_concrete(*ty, allowed_typevars),
|
||||
TList { ty } | TVirtual { ty } => self.is_concrete(*ty, allowed_typevars),
|
||||
TTuple { ty } => ty.iter().all(|ty| self.is_concrete(*ty, allowed_typevars)),
|
||||
TObj { params: vars, .. } => {
|
||||
vars.values().all(|ty| self.is_concrete(*ty, allowed_typevars))
|
||||
|
@ -462,7 +467,6 @@ impl Unifier {
|
|||
// functions are instantiated for each call sites, so the function type can contain
|
||||
// type variables.
|
||||
TFunc { .. } => true,
|
||||
TVirtual { ty } => self.is_concrete(*ty, allowed_typevars),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,11 +496,11 @@ impl Unifier {
|
|||
}
|
||||
|
||||
let Call { posargs, kwargs, ret, fun, loc } = call;
|
||||
let instantiated = self.instantiate_fun(b, &*signature);
|
||||
let instantiated = self.instantiate_fun(b, signature);
|
||||
let r = self.get_ty(instantiated);
|
||||
let r = r.as_ref();
|
||||
let signature;
|
||||
if let TypeEnum::TFunc(s) = &*r {
|
||||
if let TypeEnum::TFunc(s) = r {
|
||||
signature = s;
|
||||
} else {
|
||||
unreachable!();
|
||||
|
@ -523,7 +527,7 @@ impl Unifier {
|
|||
TypeError::new(TypeErrorKind::IncorrectArgType { name, expected, got: *t }, *loc)
|
||||
})?;
|
||||
}
|
||||
for (k, t) in kwargs.iter() {
|
||||
for (k, t) in kwargs {
|
||||
if let Some(i) = required.iter().position(|v| v == k) {
|
||||
required.remove(i);
|
||||
}
|
||||
|
@ -610,7 +614,7 @@ impl Unifier {
|
|||
}
|
||||
(Some(fields1), Some(fields2)) => {
|
||||
let mut new_fields: Mapping<_, _> = fields2.clone();
|
||||
for (key, val1) in fields1.iter() {
|
||||
for (key, val1) in fields1 {
|
||||
if let Some(val2) = fields2.get(key) {
|
||||
self.unify_impl(val1.ty, val2.ty, false).map_err(|_| {
|
||||
TypeError::new(
|
||||
|
@ -639,9 +643,9 @@ impl Unifier {
|
|||
};
|
||||
let intersection = self
|
||||
.get_intersection(a, b)
|
||||
.map_err(|_| TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None))?
|
||||
.map_err(|()| TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None))?
|
||||
.unwrap();
|
||||
let range = if let TypeEnum::TVar { range, .. } = &*self.get_ty(intersection) {
|
||||
let range = if let TVar { range, .. } = &*self.get_ty(intersection) {
|
||||
range.clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
|
@ -649,7 +653,7 @@ impl Unifier {
|
|||
self.unification_table.unify(a, b);
|
||||
self.unification_table.set_value(
|
||||
a,
|
||||
Rc::new(TypeEnum::TVar {
|
||||
Rc::new(TVar {
|
||||
id: name1.map_or(*id2, |_| *id),
|
||||
fields: new_fields,
|
||||
range,
|
||||
|
@ -678,7 +682,7 @@ impl Unifier {
|
|||
}
|
||||
(TVar { fields: Some(fields), range, is_const_generic: false, .. }, TTuple { ty }) => {
|
||||
let len = ty.len() as i32;
|
||||
for (k, v) in fields.iter() {
|
||||
for (k, v) in fields {
|
||||
match *k {
|
||||
RecordKey::Int(i) => {
|
||||
if v.mutable {
|
||||
|
@ -707,10 +711,10 @@ impl Unifier {
|
|||
self.set_a_to_b(a, x);
|
||||
}
|
||||
(TVar { fields: Some(fields), range, is_const_generic: false, .. }, TList { ty }) => {
|
||||
for (k, v) in fields.iter() {
|
||||
for (k, v) in fields {
|
||||
match *k {
|
||||
RecordKey::Int(_) => {
|
||||
self.unify_impl(v.ty, *ty, false).map_err(|e| e.at(v.loc))?
|
||||
self.unify_impl(v.ty, *ty, false).map_err(|e| e.at(v.loc))?;
|
||||
}
|
||||
RecordKey::Str(_) => {
|
||||
return Err(TypeError::new(TypeErrorKind::NoSuchField(*k, b), v.loc))
|
||||
|
@ -742,7 +746,6 @@ impl Unifier {
|
|||
|
||||
(TConstant { value: val1, ty: ty1, .. }, TConstant { value: val2, ty: ty2, .. }) => {
|
||||
if val1 != val2 {
|
||||
eprintln!("VALUE MISMATCH: lhs={val1:?} rhs={val2:?} eq={}", val1 == val2);
|
||||
return self.incompatible_types(a, b)
|
||||
}
|
||||
self.unify_impl(*ty1, *ty2, false)?;
|
||||
|
@ -768,7 +771,7 @@ impl Unifier {
|
|||
self.set_a_to_b(a, b);
|
||||
}
|
||||
(TVar { fields: Some(map), range, .. }, TObj { fields, .. }) => {
|
||||
for (k, field) in map.iter() {
|
||||
for (k, field) in map {
|
||||
match *k {
|
||||
RecordKey::Str(s) => {
|
||||
let (ty, mutable) = fields.get(&s).copied().ok_or_else(|| {
|
||||
|
@ -800,7 +803,7 @@ impl Unifier {
|
|||
(TVar { fields: Some(map), range, .. }, TVirtual { ty }) => {
|
||||
let ty = self.get_ty(*ty);
|
||||
if let TObj { fields, .. } = ty.as_ref() {
|
||||
for (k, field) in map.iter() {
|
||||
for (k, field) in map {
|
||||
match *k {
|
||||
RecordKey::Str(s) => {
|
||||
let (ty, _) = fields.get(&s).copied().ok_or_else(|| {
|
||||
|
@ -867,7 +870,7 @@ impl Unifier {
|
|||
(TCall(calls1), TCall(calls2)) => {
|
||||
// we do not unify individual calls, instead we defer until the unification wtih a
|
||||
// function definition.
|
||||
let calls = calls1.iter().chain(calls2.iter()).cloned().collect();
|
||||
let calls = calls1.iter().chain(calls2.iter()).copied().collect();
|
||||
self.set_a_to_b(a, b);
|
||||
self.unification_table.set_value(b, Rc::new(TCall(calls)));
|
||||
}
|
||||
|
@ -880,7 +883,7 @@ impl Unifier {
|
|||
.rev()
|
||||
.collect();
|
||||
// we unify every calls to the function signature.
|
||||
for c in calls.iter() {
|
||||
for c in calls {
|
||||
let call = self.calls[c.0].clone();
|
||||
self.unify_call(&call, b, signature, &required)?;
|
||||
}
|
||||
|
@ -913,9 +916,9 @@ impl Unifier {
|
|||
_ => {
|
||||
if swapped {
|
||||
return self.incompatible_types(a, b);
|
||||
} else {
|
||||
self.unify_impl(b, a, true)?;
|
||||
}
|
||||
|
||||
self.unify_impl(b, a, true)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -935,7 +938,7 @@ impl Unifier {
|
|||
ty,
|
||||
&mut |id| {
|
||||
top_level.as_ref().map_or_else(
|
||||
|| format!("{}", id),
|
||||
|| format!("{id}"),
|
||||
|top_level| {
|
||||
if let TopLevelDef::Class { name, .. } =
|
||||
&*top_level.definitions.read()[id].read()
|
||||
|
@ -947,7 +950,7 @@ impl Unifier {
|
|||
},
|
||||
)
|
||||
},
|
||||
&mut |id| format!("typevar{}", id),
|
||||
&mut |id| format!("typevar{id}"),
|
||||
notes,
|
||||
)
|
||||
}
|
||||
|
@ -990,7 +993,7 @@ impl Unifier {
|
|||
if !range.is_empty() && notes.is_some() && !notes.as_ref().unwrap().contains_key(id)
|
||||
{
|
||||
// just in case if there is any cyclic dependency
|
||||
notes.as_mut().unwrap().insert(*id, "".into());
|
||||
notes.as_mut().unwrap().insert(*id, String::new());
|
||||
let body = format!(
|
||||
"{} ∈ {{{}}}",
|
||||
n,
|
||||
|
@ -1023,15 +1026,15 @@ impl Unifier {
|
|||
}
|
||||
TypeEnum::TObj { obj_id, params, .. } => {
|
||||
let name = obj_to_name(obj_id.0);
|
||||
if !params.is_empty() {
|
||||
if params.is_empty() {
|
||||
name
|
||||
} else {
|
||||
let params = params
|
||||
.iter()
|
||||
.map(|(_, v)| self.internal_stringify(*v, obj_to_name, var_to_name, notes));
|
||||
// sort to preserve order
|
||||
let mut params = params.sorted();
|
||||
format!("{}[{}]", name, params.join(", "))
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}
|
||||
TypeEnum::TCall { .. } => "call".to_owned(),
|
||||
|
@ -1057,7 +1060,7 @@ impl Unifier {
|
|||
})
|
||||
.join(", ");
|
||||
let ret = self.internal_stringify(signature.ret, obj_to_name, var_to_name, notes);
|
||||
format!("fn[[{}], {}]", params, ret)
|
||||
format!("fn[[{params}], {ret}]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1067,7 +1070,7 @@ impl Unifier {
|
|||
let table = &mut self.unification_table;
|
||||
let ty_b = table.probe_value(b).clone();
|
||||
table.unify(a, b);
|
||||
table.set_value(a, ty_b)
|
||||
table.set_value(a, ty_b);
|
||||
}
|
||||
|
||||
fn incompatible_types(&mut self, a: Type, b: Type) -> Result<(), TypeError> {
|
||||
|
@ -1080,7 +1083,7 @@ impl Unifier {
|
|||
fn instantiate_fun(&mut self, ty: Type, fun: &FunSignature) -> Type {
|
||||
let mut instantiated = true;
|
||||
let mut vars = Vec::new();
|
||||
for (k, v) in fun.vars.iter() {
|
||||
for (k, v) in &fun.vars {
|
||||
if let TypeEnum::TVar { id, name, loc, range, .. } =
|
||||
self.unification_table.probe_value(*v).as_ref()
|
||||
{
|
||||
|
@ -1135,7 +1138,7 @@ impl Unifier {
|
|||
// should be safe to not implement the substitution for those variants.
|
||||
match &*ty {
|
||||
TypeEnum::TRigidVar { .. } => None,
|
||||
TypeEnum::TVar { id, .. } => mapping.get(id).cloned(),
|
||||
TypeEnum::TVar { id, .. } => mapping.get(id).copied(),
|
||||
TypeEnum::TTuple { ty } => {
|
||||
let mut new_ty = Cow::from(ty);
|
||||
for (i, t) in ty.iter().enumerate() {
|
||||
|
@ -1197,7 +1200,7 @@ impl Unifier {
|
|||
}
|
||||
if new_params.is_some() || new_ret.is_some() || matches!(new_args, Cow::Owned(..)) {
|
||||
let params = new_params.unwrap_or_else(|| params.clone());
|
||||
let ret = new_ret.unwrap_or_else(|| *ret);
|
||||
let ret = new_ret.unwrap_or(*ret);
|
||||
let args = new_args.into_owned();
|
||||
Some(self.add_ty(TypeEnum::TFunc(FunSignature { args, ret, vars: params })))
|
||||
} else {
|
||||
|
@ -1217,10 +1220,10 @@ impl Unifier {
|
|||
cache: &mut HashMap<Type, Option<Type>>,
|
||||
) -> Option<Mapping<K>>
|
||||
where
|
||||
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
|
||||
K: std::hash::Hash + Eq + Clone,
|
||||
{
|
||||
let mut map2 = None;
|
||||
for (k, v) in map.iter() {
|
||||
for (k, v) in map {
|
||||
if let Some(v1) = self.subst_impl(*v, mapping, cache) {
|
||||
if map2.is_none() {
|
||||
map2 = Some(map.clone());
|
||||
|
@ -1238,10 +1241,10 @@ impl Unifier {
|
|||
cache: &mut HashMap<Type, Option<Type>>,
|
||||
) -> Option<Mapping<K, (Type, bool)>>
|
||||
where
|
||||
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
|
||||
K: std::hash::Hash + Eq + Clone,
|
||||
{
|
||||
let mut map2 = None;
|
||||
for (k, (v, mutability)) in map.iter() {
|
||||
for (k, (v, mutability)) in map {
|
||||
if let Some(v1) = self.subst_impl(*v, mapping, cache) {
|
||||
if map2.is_none() {
|
||||
map2 = Some(map.clone());
|
||||
|
@ -1297,7 +1300,7 @@ impl Unifier {
|
|||
if range.is_empty() {
|
||||
Ok(Some(a))
|
||||
} else {
|
||||
for v in range.iter() {
|
||||
for v in range {
|
||||
let result = self.get_intersection(a, *v);
|
||||
if let Ok(result) = result {
|
||||
return Ok(result.or(Some(a)));
|
||||
|
@ -1313,7 +1316,7 @@ impl Unifier {
|
|||
.try_collect()?;
|
||||
if ty.iter().any(Option::is_some) {
|
||||
Ok(Some(self.add_ty(TTuple {
|
||||
ty: zip(ty.into_iter(), ty1.iter()).map(|(a, b)| a.unwrap_or(*b)).collect(),
|
||||
ty: zip(ty, ty1.iter()).map(|(a, b)| a.unwrap_or(*b)).collect(),
|
||||
})))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
@ -1339,7 +1342,7 @@ impl Unifier {
|
|||
if range.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
for t in range.iter() {
|
||||
for t in range {
|
||||
let result = self.get_intersection(*t, b);
|
||||
if let Ok(result) = result {
|
||||
return Ok(result);
|
||||
|
|
|
@ -47,7 +47,7 @@ impl Unifier {
|
|||
|
||||
fn map_eq<K>(&mut self, map1: &Mapping<K>, map2: &Mapping<K>) -> bool
|
||||
where
|
||||
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
|
||||
K: std::hash::Hash + Eq + Clone,
|
||||
{
|
||||
if map1.len() != map2.len() {
|
||||
return false;
|
||||
|
@ -62,7 +62,7 @@ impl Unifier {
|
|||
|
||||
fn map_eq2<K>(&mut self, map1: &Mapping<K, RecordField>, map2: &Mapping<K, RecordField>) -> bool
|
||||
where
|
||||
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
|
||||
K: std::hash::Hash + Eq + Clone,
|
||||
{
|
||||
if map1.len() != map2.len() {
|
||||
return false;
|
||||
|
@ -139,7 +139,7 @@ impl TestEnvironment {
|
|||
match &typ[..end] {
|
||||
"tuple" => {
|
||||
let mut s = &typ[end..];
|
||||
assert!(&s[0..1] == "[");
|
||||
assert_eq!(&s[0..1], "[");
|
||||
let mut ty = Vec::new();
|
||||
while &s[0..1] != "]" {
|
||||
let result = self.internal_parse(&s[1..], mapping);
|
||||
|
@ -149,14 +149,14 @@ impl TestEnvironment {
|
|||
(self.unifier.add_ty(TypeEnum::TTuple { ty }), &s[1..])
|
||||
}
|
||||
"list" => {
|
||||
assert!(&typ[end..end + 1] == "[");
|
||||
assert_eq!(&typ[end..end + 1], "[");
|
||||
let (ty, s) = self.internal_parse(&typ[end + 1..], mapping);
|
||||
assert!(&s[0..1] == "]");
|
||||
assert_eq!(&s[0..1], "]");
|
||||
(self.unifier.add_ty(TypeEnum::TList { ty }), &s[1..])
|
||||
}
|
||||
"Record" => {
|
||||
let mut s = &typ[end..];
|
||||
assert!(&s[0..1] == "[");
|
||||
assert_eq!(&s[0..1], "[");
|
||||
let mut fields = HashMap::new();
|
||||
while &s[0..1] != "]" {
|
||||
let eq = s.find('=').unwrap();
|
||||
|
@ -176,7 +176,7 @@ impl TestEnvironment {
|
|||
let te = self.unifier.get_ty(ty);
|
||||
if let TypeEnum::TObj { params, .. } = &*te.as_ref() {
|
||||
if !params.is_empty() {
|
||||
assert!(&s[0..1] == "[");
|
||||
assert_eq!(&s[0..1], "[");
|
||||
let mut p = Vec::new();
|
||||
while &s[0..1] != "]" {
|
||||
let result = self.internal_parse(&s[1..], mapping);
|
||||
|
|
|
@ -41,9 +41,9 @@ impl<'a> DwarfReader<'a> {
|
|||
/// offsets previously applied to the other instance.
|
||||
pub fn from_reader(other: &DwarfReader<'a>, reset_offset: bool) -> DwarfReader<'a> {
|
||||
if reset_offset {
|
||||
DwarfReader::new(&other.base_slice, other.base_virt_addr)
|
||||
DwarfReader::new(other.base_slice, other.base_virt_addr)
|
||||
} else {
|
||||
DwarfReader::new(&other.slice, other.virt_addr)
|
||||
DwarfReader::new(other.slice, other.virt_addr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +267,7 @@ impl<'a> CFI_Record<'a> {
|
|||
0xFFFFFFFF => unimplemented!(),
|
||||
|
||||
_ => {
|
||||
let mut fde_reader = DwarfReader::from_reader(&cie_reader, false);
|
||||
let mut fde_reader = DwarfReader::from_reader(cie_reader, false);
|
||||
fde_reader.offset(length);
|
||||
fde_reader
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ impl<'a> CFI_Record<'a> {
|
|||
// Skip code/data alignment factors & return address register along the way as well
|
||||
// We only tackle the case where 'z' and 'R' are part of the augmentation string, otherwise
|
||||
// we cannot get the addresses to make .eh_frame_hdr
|
||||
let mut aug_data_reader = DwarfReader::from_reader(&cie_reader, false);
|
||||
let mut aug_data_reader = DwarfReader::from_reader(cie_reader, false);
|
||||
let mut aug_str_len = 0;
|
||||
loop {
|
||||
if aug_data_reader.read_u8() == b'\0' {
|
||||
|
|
|
@ -10,7 +10,7 @@ pub const EI_MAG2: usize = 2;
|
|||
pub const ELFMAG2: u8 = b'L';
|
||||
pub const EI_MAG3: usize = 3;
|
||||
pub const ELFMAG3: u8 = b'F';
|
||||
pub const ELFMAG: &'static [u8; 5usize] = b"\x7fELF\x00";
|
||||
pub const ELFMAG: &[u8; 5usize] = b"\x7fELF\x00";
|
||||
pub const SELFMAG: usize = 4;
|
||||
pub const EI_CLASS: usize = 4;
|
||||
pub const ELFCLASSNONE: u8 = 0;
|
||||
|
@ -428,8 +428,8 @@ pub const VER_NDX_ELIMINATE: usize = 65281;
|
|||
pub const VER_NEED_NONE: usize = 0;
|
||||
pub const VER_NEED_CURRENT: usize = 1;
|
||||
pub const VER_NEED_NUM: usize = 2;
|
||||
pub const ELF_NOTE_SOLARIS: &'static [u8; 13usize] = b"SUNW Solaris\x00";
|
||||
pub const ELF_NOTE_GNU: &'static [u8; 4usize] = b"GNU\x00";
|
||||
pub const ELF_NOTE_SOLARIS: &[u8; 13usize] = b"SUNW Solaris\x00";
|
||||
pub const ELF_NOTE_GNU: &[u8; 4usize] = b"GNU\x00";
|
||||
pub const ELF_NOTE_PAGESIZE_HINT: usize = 1;
|
||||
pub const NT_GNU_ABI_TAG: usize = 1;
|
||||
pub const ELF_NOTE_ABI: usize = 1;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use dwarf::*;
|
||||
use elf::*;
|
||||
use std::collections::HashMap;
|
||||
use std::{convert, mem, ptr, slice, str};
|
||||
use std::{mem, ptr, slice, str};
|
||||
|
||||
extern crate byteorder;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
@ -21,7 +21,7 @@ pub enum Error {
|
|||
Lookup(&'static str),
|
||||
}
|
||||
|
||||
impl convert::From<&'static str> for Error {
|
||||
impl From<&'static str> for Error {
|
||||
fn from(desc: &'static str) -> Error {
|
||||
Error::Parsing(desc)
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Result<T, ()> {
|
|||
if data.len() < offset + mem::size_of::<T>() {
|
||||
Err(())
|
||||
} else {
|
||||
let ptr = data.as_ptr().wrapping_offset(offset as isize) as *const T;
|
||||
let ptr = data.as_ptr().wrapping_add(offset) as *const T;
|
||||
Ok(unsafe { ptr::read_unaligned(ptr) })
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ pub fn get_ref_slice<T: Copy>(data: &[u8], offset: usize, len: usize) -> Result<
|
|||
if data.len() < offset + mem::size_of::<T>() * len {
|
||||
Err(())
|
||||
} else {
|
||||
let ptr = data.as_ptr().wrapping_offset(offset as isize) as *const T;
|
||||
let ptr = data.as_ptr().wrapping_add(offset) as *const T;
|
||||
Ok(unsafe { slice::from_raw_parts(ptr, len) })
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ impl<'a> Linker<'a> {
|
|||
}
|
||||
|
||||
fn load_section(&mut self, shdr: &Elf32_Shdr, sh_name_str: &'a str, data: Vec<u8>) -> usize {
|
||||
let mut elf_shdr = shdr.clone();
|
||||
let mut elf_shdr = *shdr;
|
||||
|
||||
// Maintain alignment requirement specified in sh_addralign
|
||||
let align = shdr.sh_addralign;
|
||||
|
@ -207,7 +207,7 @@ impl<'a> Linker<'a> {
|
|||
STN_UNDEF => None,
|
||||
sym_index => Some(
|
||||
self.symtab
|
||||
.get(sym_index as usize)
|
||||
.get(sym_index)
|
||||
.ok_or("symbol out of bounds of symbol table")?,
|
||||
),
|
||||
};
|
||||
|
@ -240,7 +240,7 @@ impl<'a> Linker<'a> {
|
|||
let get_target_section_index = || -> Result<usize, Error> {
|
||||
self.section_map
|
||||
.get(&(target_section as usize))
|
||||
.map(|&index| index)
|
||||
.copied()
|
||||
.ok_or(Error::Parsing("Cannot find section with matching sh_index"))
|
||||
};
|
||||
|
||||
|
@ -314,13 +314,9 @@ impl<'a> Linker<'a> {
|
|||
|
||||
R_RISCV_PCREL_LO12_I => {
|
||||
let expected_offset = sym_option.map_or(0, |sym| sym.st_value);
|
||||
let indirect_reloc = if let Some(reloc) =
|
||||
relocs.iter().find(|reloc| reloc.offset() == expected_offset)
|
||||
{
|
||||
reloc
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let indirect_reloc = relocs
|
||||
.iter()
|
||||
.find(|reloc| reloc.offset() == expected_offset)?;
|
||||
Some(RelocInfo {
|
||||
defined_val: {
|
||||
let indirect_sym =
|
||||
|
@ -603,23 +599,24 @@ impl<'a> Linker<'a> {
|
|||
// Section table for the .elf paired with the section name
|
||||
// To be formalized incrementally
|
||||
// Very hashmap-like structure, but the order matters, so it is a vector
|
||||
let mut elf_shdrs = Vec::new();
|
||||
elf_shdrs.push(SectionRecord {
|
||||
shdr: Elf32_Shdr {
|
||||
sh_name: 0,
|
||||
sh_type: 0,
|
||||
sh_flags: 0,
|
||||
sh_addr: 0,
|
||||
sh_offset: 0,
|
||||
sh_size: 0,
|
||||
sh_link: 0,
|
||||
sh_info: 0,
|
||||
sh_addralign: 0,
|
||||
sh_entsize: 0,
|
||||
let elf_shdrs = vec![
|
||||
SectionRecord {
|
||||
shdr: Elf32_Shdr {
|
||||
sh_name: 0,
|
||||
sh_type: 0,
|
||||
sh_flags: 0,
|
||||
sh_addr: 0,
|
||||
sh_offset: 0,
|
||||
sh_size: 0,
|
||||
sh_link: 0,
|
||||
sh_info: 0,
|
||||
sh_addralign: 0,
|
||||
sh_entsize: 0,
|
||||
},
|
||||
name: "",
|
||||
data: vec![0; 0],
|
||||
},
|
||||
name: "",
|
||||
data: vec![0; 0],
|
||||
});
|
||||
];
|
||||
let elf_sh_data_off = mem::size_of::<Elf32_Ehdr>() + mem::size_of::<Elf32_Phdr>() * 5;
|
||||
|
||||
// Image of the linked dynamic library, to be formalized incrementally
|
||||
|
@ -659,8 +656,8 @@ impl<'a> Linker<'a> {
|
|||
linker.load_section(
|
||||
&text_shdr,
|
||||
".text",
|
||||
(&data[text_shdr.sh_offset as usize
|
||||
..text_shdr.sh_offset as usize + text_shdr.sh_size as usize])
|
||||
data[text_shdr.sh_offset as usize
|
||||
..text_shdr.sh_offset as usize + text_shdr.sh_size as usize]
|
||||
.to_vec(),
|
||||
);
|
||||
linker.section_map.insert(text_shdr_index, 1);
|
||||
|
@ -678,8 +675,8 @@ impl<'a> Linker<'a> {
|
|||
let loaded_index = linker.load_section(
|
||||
&arm_exidx_shdr,
|
||||
".ARM.exidx",
|
||||
(&data[arm_exidx_shdr.sh_offset as usize
|
||||
..arm_exidx_shdr.sh_offset as usize + arm_exidx_shdr.sh_size as usize])
|
||||
data[arm_exidx_shdr.sh_offset as usize
|
||||
..arm_exidx_shdr.sh_offset as usize + arm_exidx_shdr.sh_size as usize]
|
||||
.to_vec(),
|
||||
);
|
||||
linker.section_map.insert(arm_exidx_shdr_index, loaded_index);
|
||||
|
@ -698,7 +695,7 @@ impl<'a> Linker<'a> {
|
|||
let elf_shdrs_index = linker.load_section(
|
||||
shdr,
|
||||
str::from_utf8(section_name).unwrap(),
|
||||
(&data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize]).to_vec(),
|
||||
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize].to_vec(),
|
||||
);
|
||||
linker.section_map.insert(i, elf_shdrs_index);
|
||||
}
|
||||
|
@ -918,7 +915,7 @@ impl<'a> Linker<'a> {
|
|||
dynsym_names.push((0, 0));
|
||||
|
||||
for rela_dyn_sym_index in rela_dyn_sym_indices {
|
||||
let mut sym = linker.symtab[rela_dyn_sym_index as usize].clone();
|
||||
let mut sym = linker.symtab[rela_dyn_sym_index as usize];
|
||||
let sym_name = name_starting_at_slice(strtab, sym.st_name as usize)
|
||||
.map_err(|_| "cannot read symbol name from the original .strtab")?;
|
||||
let dynstr_start_index = dynstr.len();
|
||||
|
@ -928,7 +925,7 @@ impl<'a> Linker<'a> {
|
|||
let elf_shdr_index = linker
|
||||
.section_map
|
||||
.get(&(sym.st_shndx as usize))
|
||||
.map(|&index| index)
|
||||
.copied()
|
||||
.ok_or(Error::Parsing("Cannot find section with matching sh_index"))?;
|
||||
let elf_shdr_offset = linker.elf_shdrs[elf_shdr_index].shdr.sh_offset;
|
||||
sym.st_value += elf_shdr_offset;
|
||||
|
@ -955,7 +952,7 @@ impl<'a> Linker<'a> {
|
|||
let modinit_shdr_index = linker
|
||||
.section_map
|
||||
.get(&(modinit_sym.st_shndx as usize))
|
||||
.map(|&index| index)
|
||||
.copied()
|
||||
.ok_or(Error::Parsing("Cannot find section with matching sh_index"))?;
|
||||
let modinit_shdr = linker.elf_shdrs[modinit_shdr_index].shdr;
|
||||
|
||||
|
@ -1013,9 +1010,8 @@ impl<'a> Linker<'a> {
|
|||
let mut hash_bucket: Vec<u32> = vec![0; dynsym.len()];
|
||||
let mut hash_chain: Vec<u32> = vec![0; dynsym.len()];
|
||||
|
||||
for sym_index in 1..dynsym.len() {
|
||||
let (str_start, str_end) = dynsym_names[sym_index];
|
||||
let hash = elf_hash(&dynstr[str_start..str_end]);
|
||||
for (sym_index, (str_start, str_end)) in dynsym_names.iter().enumerate().take(dynsym.len()).skip(1) {
|
||||
let hash = elf_hash(&dynstr[*str_start..*str_end]);
|
||||
let mut hash_index = hash as usize % hash_bucket.len();
|
||||
|
||||
if hash_bucket[hash_index] == 0 {
|
||||
|
@ -1104,7 +1100,7 @@ impl<'a> Linker<'a> {
|
|||
let elf_shdrs_index = linker.load_section(
|
||||
shdr,
|
||||
str::from_utf8(section_name).unwrap(),
|
||||
(&data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize])
|
||||
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize]
|
||||
.to_vec(),
|
||||
);
|
||||
linker.section_map.insert(i, elf_shdrs_index);
|
||||
|
@ -1208,7 +1204,7 @@ impl<'a> Linker<'a> {
|
|||
let elf_shdrs_index = linker.load_section(
|
||||
shdr,
|
||||
section_name,
|
||||
(&data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize])
|
||||
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize]
|
||||
.to_vec(),
|
||||
);
|
||||
linker.section_map.insert(i, elf_shdrs_index);
|
||||
|
@ -1262,7 +1258,7 @@ impl<'a> Linker<'a> {
|
|||
let bss_elf_index = linker.load_section(
|
||||
shdr,
|
||||
section_name,
|
||||
(&data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize])
|
||||
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize]
|
||||
.to_vec(),
|
||||
);
|
||||
linker.section_map.insert(bss_section_index, bss_elf_index);
|
||||
|
@ -1463,7 +1459,7 @@ impl<'a> Linker<'a> {
|
|||
// Update the EHDR
|
||||
let ehdr_ptr = linker.image.as_mut_ptr() as *mut Elf32_Ehdr;
|
||||
unsafe {
|
||||
(*ehdr_ptr) = Elf32_Ehdr {
|
||||
*ehdr_ptr = Elf32_Ehdr {
|
||||
e_ident: ehdr.e_ident,
|
||||
e_type: ET_DYN,
|
||||
e_machine: ehdr.e_machine,
|
||||
|
|
|
@ -486,8 +486,8 @@ where
|
|||
}
|
||||
}
|
||||
match p {
|
||||
0xD800..=0xDFFF => Ok(std::char::REPLACEMENT_CHARACTER),
|
||||
_ => std::char::from_u32(p).ok_or(unicode_error),
|
||||
0xD800..=0xDFFF => Ok(char::REPLACEMENT_CHARACTER),
|
||||
_ => char::from_u32(p).ok_or(unicode_error),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use nac3core::{
|
|||
use nac3parser::ast::{self, StrRef};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub struct ResolverInternal {
|
||||
pub id_to_type: Mutex<HashMap<StrRef, Type>>,
|
||||
|
@ -50,19 +51,20 @@ impl SymbolResolver for Resolver {
|
|||
_: &PrimitiveStore,
|
||||
str: StrRef,
|
||||
) -> Result<Type, String> {
|
||||
self.0.id_to_type.lock().get(&str).cloned().ok_or(format!("cannot get type of {}", str))
|
||||
self.0.id_to_type.lock().get(&str).copied().ok_or(format!("cannot get type of {str}"))
|
||||
}
|
||||
|
||||
fn get_symbol_value<'ctx, 'a>(
|
||||
fn get_symbol_value<'ctx>(
|
||||
&self,
|
||||
_: StrRef,
|
||||
_: &mut CodeGenContext<'ctx, 'a>,
|
||||
_: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> Option<ValueEnum<'ctx>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
||||
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Undefined identifier".to_string())
|
||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||
self.0.id_to_def.lock().get(&id).copied()
|
||||
.ok_or_else(|| HashSet::from(["Undefined identifier".to_string()]))
|
||||
}
|
||||
|
||||
fn get_string_id(&self, s: &str) -> i32 {
|
||||
|
|
|
@ -8,6 +8,7 @@ use inkwell::{
|
|||
};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use std::{collections::HashMap, fs, path::Path, sync::Arc};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use nac3core::{
|
||||
codegen::{
|
||||
|
@ -31,6 +32,7 @@ use nac3parser::{
|
|||
|
||||
mod basic_symbol_resolver;
|
||||
use basic_symbol_resolver::*;
|
||||
use nac3core::toplevel::composer::ComposerConfig;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
@ -76,43 +78,48 @@ fn handle_typevar_definition(
|
|||
def_list: &[Arc<RwLock<TopLevelDef>>],
|
||||
unifier: &mut Unifier,
|
||||
primitives: &PrimitiveStore,
|
||||
) -> Result<Type, String> {
|
||||
) -> Result<Type, HashSet<String>> {
|
||||
let ExprKind::Call { func, args, .. } = &var.node else {
|
||||
return Err(format!(
|
||||
"expression {:?} cannot be handled as a generic parameter in global scope",
|
||||
var
|
||||
))
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
||||
),
|
||||
]))
|
||||
};
|
||||
|
||||
match &func.node {
|
||||
ExprKind::Name { id, .. } if id == &"TypeVar".into() => {
|
||||
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
|
||||
return Err(format!("Expected string constant for first parameter of `TypeVar`, got {:?}", &args[0].node))
|
||||
return Err(HashSet::from([
|
||||
format!("Expected string constant for first parameter of `TypeVar`, got {:?}", &args[0].node),
|
||||
]))
|
||||
};
|
||||
let generic_name: StrRef = ty_name.to_string().into();
|
||||
|
||||
let constraints = args
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|x| -> Result<Type, String> {
|
||||
.map(|x| -> Result<Type, HashSet<String>> {
|
||||
let ty = parse_ast_to_type_annotation_kinds(
|
||||
resolver,
|
||||
def_list,
|
||||
unifier,
|
||||
primitives,
|
||||
x,
|
||||
Default::default(),
|
||||
HashMap::default(),
|
||||
None,
|
||||
)?;
|
||||
get_type_from_type_annotation_kinds(
|
||||
def_list, unifier, primitives, &ty, &mut None
|
||||
def_list, unifier, &ty, &mut None
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let loc = func.location;
|
||||
|
||||
if constraints.len() == 1 {
|
||||
return Err(format!("A single constraint is not allowed (at {})", loc))
|
||||
return Err(HashSet::from([
|
||||
format!("A single constraint is not allowed (at {loc})"),
|
||||
]))
|
||||
}
|
||||
|
||||
Ok(unifier.get_fresh_var_with_range(&constraints, Some(generic_name), Some(loc)).0)
|
||||
|
@ -120,14 +127,18 @@ fn handle_typevar_definition(
|
|||
|
||||
ExprKind::Name { id, .. } if id == &"ConstGeneric".into() => {
|
||||
if args.len() != 2 {
|
||||
return Err(format!("Expected 2 arguments for `ConstGeneric`, got {}", args.len()))
|
||||
return Err(HashSet::from([
|
||||
format!("Expected 2 arguments for `ConstGeneric`, got {}", args.len()),
|
||||
]))
|
||||
}
|
||||
|
||||
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
|
||||
return Err(format!(
|
||||
"Expected string constant for first parameter of `ConstGeneric`, got {:?}",
|
||||
&args[0].node
|
||||
))
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"Expected string constant for first parameter of `ConstGeneric`, got {:?}",
|
||||
&args[0].node
|
||||
),
|
||||
]))
|
||||
};
|
||||
let generic_name: StrRef = ty_name.to_string().into();
|
||||
|
||||
|
@ -137,21 +148,22 @@ fn handle_typevar_definition(
|
|||
unifier,
|
||||
primitives,
|
||||
&args[1],
|
||||
Default::default(),
|
||||
HashMap::default(),
|
||||
None,
|
||||
)?;
|
||||
let constraint = get_type_from_type_annotation_kinds(
|
||||
def_list, unifier, primitives, &ty, &mut None
|
||||
def_list, unifier, &ty, &mut None
|
||||
)?;
|
||||
let loc = func.location;
|
||||
|
||||
Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).0)
|
||||
}
|
||||
|
||||
_ => Err(format!(
|
||||
"expression {:?} cannot be handled as a generic parameter in global scope",
|
||||
var
|
||||
))
|
||||
_ => Err(HashSet::from([
|
||||
format!(
|
||||
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
||||
),
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,14 +220,7 @@ fn handle_assignment_pattern(
|
|||
} else {
|
||||
match &value.node {
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
if elts.len() != targets.len() {
|
||||
Err(format!(
|
||||
"number of elements to unpack does not match (expect {}, found {}) at {}",
|
||||
targets.len(),
|
||||
elts.len(),
|
||||
value.location
|
||||
))
|
||||
} else {
|
||||
if elts.len() == targets.len() {
|
||||
for (tar, val) in targets.iter().zip(elts) {
|
||||
handle_assignment_pattern(
|
||||
std::slice::from_ref(tar),
|
||||
|
@ -228,6 +233,13 @@ fn handle_assignment_pattern(
|
|||
)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"number of elements to unpack does not match (expect {}, found {}) at {}",
|
||||
targets.len(),
|
||||
elts.len(),
|
||||
value.location
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(format!(
|
||||
|
@ -268,7 +280,7 @@ fn main() {
|
|||
}
|
||||
} else {
|
||||
if threads != 1 {
|
||||
println!("Warning: Number of threads specified in command-line but multithreading is disabled in LLVM at build time! Defaulting to single-threaded compilation")
|
||||
println!("Warning: Number of threads specified in command-line but multithreading is disabled in LLVM at build time! Defaulting to single-threaded compilation");
|
||||
}
|
||||
1
|
||||
};
|
||||
|
@ -283,28 +295,28 @@ fn main() {
|
|||
let program = match fs::read_to_string(file_name.clone()) {
|
||||
Ok(program) => program,
|
||||
Err(err) => {
|
||||
println!("Cannot open input file: {}", err);
|
||||
println!("Cannot open input file: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let primitive: PrimitiveStore = TopLevelComposer::make_primitives().0;
|
||||
let (mut composer, builtins_def, builtins_ty) =
|
||||
TopLevelComposer::new(vec![], Default::default());
|
||||
TopLevelComposer::new(vec![], ComposerConfig::default());
|
||||
|
||||
let internal_resolver: Arc<ResolverInternal> = ResolverInternal {
|
||||
id_to_type: builtins_ty.into(),
|
||||
id_to_def: builtins_def.into(),
|
||||
class_names: Default::default(),
|
||||
module_globals: Default::default(),
|
||||
str_store: Default::default(),
|
||||
class_names: Mutex::default(),
|
||||
module_globals: Mutex::default(),
|
||||
str_store: Mutex::default(),
|
||||
}.into();
|
||||
let resolver =
|
||||
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||
|
||||
let parser_result = parser::parse_program(&program, file_name.into()).unwrap();
|
||||
|
||||
for stmt in parser_result.into_iter() {
|
||||
for stmt in parser_result {
|
||||
match &stmt.node {
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
let def_list = composer.extract_def_list();
|
||||
|
@ -319,7 +331,7 @@ fn main() {
|
|||
unifier,
|
||||
primitives,
|
||||
) {
|
||||
eprintln!("{}", err);
|
||||
eprintln!("{err}");
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
@ -328,7 +340,7 @@ fn main() {
|
|||
if module == &Some("__future__".into()) && names.len() == 1 && names[0].name == "annotations".into() => (),
|
||||
_ => {
|
||||
let (name, def_id, ty) =
|
||||
composer.register_top_level(stmt, Some(resolver.clone()), "__main__".into(), true).unwrap();
|
||||
composer.register_top_level(stmt, Some(resolver.clone()), "__main__", true).unwrap();
|
||||
internal_resolver.add_id_def(name, def_id);
|
||||
if let Some(ty) = ty {
|
||||
internal_resolver.add_id_type(name, ty);
|
||||
|
@ -355,7 +367,7 @@ fn main() {
|
|||
.0]
|
||||
.write();
|
||||
if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } = &mut *instance {
|
||||
instance_to_symbol.insert("".to_string(), "run".to_string());
|
||||
instance_to_symbol.insert(String::new(), "run".to_string());
|
||||
instance_to_stmt[""].clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
|
@ -374,7 +386,7 @@ fn main() {
|
|||
};
|
||||
|
||||
let task = CodeGenTask {
|
||||
subst: Default::default(),
|
||||
subst: Vec::default(),
|
||||
symbol_name: "run".to_string(),
|
||||
body: instance.body,
|
||||
signature,
|
||||
|
@ -385,7 +397,7 @@ fn main() {
|
|||
id: 0,
|
||||
};
|
||||
|
||||
let membuffers: Arc<Mutex<Vec<Vec<u8>>>> = Default::default();
|
||||
let membuffers: Arc<Mutex<Vec<Vec<u8>>>> = Arc::default();
|
||||
let membuffer = membuffers.clone();
|
||||
|
||||
let f = Arc::new(WithCall::new(Box::new(move |module| {
|
||||
|
@ -394,9 +406,9 @@ fn main() {
|
|||
membuffer.lock().push(buffer);
|
||||
})));
|
||||
let threads = (0..threads)
|
||||
.map(|i| Box::new(DefaultCodeGenerator::new(format!("module{}", i), 64)))
|
||||
.map(|i| Box::new(DefaultCodeGenerator::new(format!("module{i}"), 64)))
|
||||
.collect();
|
||||
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, &llvm_options, f);
|
||||
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, &llvm_options, &f);
|
||||
registry.add_task(task);
|
||||
registry.wait_tasks_complete(handles);
|
||||
|
||||
|
@ -415,7 +427,7 @@ fn main() {
|
|||
.unwrap();
|
||||
|
||||
if emit_llvm {
|
||||
other.write_bitcode_to_path(Path::new(&format!("module{}.bc", idx)));
|
||||
other.write_bitcode_to_path(Path::new(&format!("module{idx}.bc")));
|
||||
}
|
||||
|
||||
main.link_in_module(other).unwrap();
|
||||
|
|
Loading…
Reference in New Issue