Compare commits
9 Commits
893a355bfb
...
a19f1065e3
Author | SHA1 | Date |
---|---|---|
|
a19f1065e3 | |
|
5bf05c6a69 | |
|
32746c37be | |
|
1d6291b9ba | |
|
16655959f2 | |
|
beee3e1f7e | |
|
d4c109b6ef | |
|
5ffd06dd61 | |
|
95d0c3c93c |
|
@ -502,9 +502,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
@ -641,6 +641,7 @@ name = "nac3artiq"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"inkwell",
|
"inkwell",
|
||||||
|
"itertools 0.12.0",
|
||||||
"nac3core",
|
"nac3core",
|
||||||
"nac3ld",
|
"nac3ld",
|
||||||
"nac3parser",
|
"nac3parser",
|
||||||
|
@ -667,7 +668,7 @@ dependencies = [
|
||||||
"indoc",
|
"indoc",
|
||||||
"inkwell",
|
"inkwell",
|
||||||
"insta",
|
"insta",
|
||||||
"itertools 0.11.0",
|
"itertools 0.12.0",
|
||||||
"nac3parser",
|
"nac3parser",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
|
|
@ -94,8 +94,8 @@
|
||||||
(pkgs.fetchFromGitHub {
|
(pkgs.fetchFromGitHub {
|
||||||
owner = "m-labs";
|
owner = "m-labs";
|
||||||
repo = "artiq";
|
repo = "artiq";
|
||||||
rev = "4c189f8c0576111733bb6ff934035c080c8ccc58";
|
rev = "8b4572f9cad34ac0c2b6f6bba9382e7b59b2f93b";
|
||||||
sha256 = "sha256-gYGzmfaIoftKFDwn8AybUenYtIpux+tHGMu51WgwA8A=";
|
sha256 = "sha256-O/0sUSxxXU1AL9cmT9qdzCkzdOKREBNftz22/8ouQcc=";
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
|
|
|
@ -9,6 +9,7 @@ name = "nac3artiq"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
itertools = "0.12"
|
||||||
pyo3 = { version = "0.20", features = ["extension-module"] }
|
pyo3 = { version = "0.20", features = ["extension-module"] }
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
tempfile = "3.8"
|
tempfile = "3.8"
|
||||||
|
|
|
@ -86,7 +86,13 @@ def ceil64(x):
|
||||||
import device_db
|
import device_db
|
||||||
core_arguments = device_db.device_db["core"]["arguments"]
|
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
|
allow_registration = True
|
||||||
# Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side.
|
# Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side.
|
||||||
registered_functions = set()
|
registered_functions = set()
|
||||||
|
|
|
@ -215,148 +215,148 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
||||||
ctx: &mut CodeGenContext<'_, '_>,
|
ctx: &mut CodeGenContext<'_, '_>,
|
||||||
stmt: &Stmt<Option<Type>>,
|
stmt: &Stmt<Option<Type>>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if let StmtKind::With { items, body, .. } = &stmt.node {
|
let StmtKind::With { items, body, .. } = &stmt.node else {
|
||||||
if items.len() == 1 && items[0].optional_vars.is_none() {
|
unreachable!()
|
||||||
let item = &items[0];
|
};
|
||||||
|
|
||||||
// Behavior of parallel and sequential:
|
if items.len() == 1 && items[0].optional_vars.is_none() {
|
||||||
// Each function call (indirectly, can be inside a sequential block) within a parallel
|
let item = &items[0];
|
||||||
// block will update the end variable to the maximum now_mu in the block.
|
|
||||||
// Each function call directly inside a parallel block will reset the timeline after
|
|
||||||
// execution. A parallel block within a sequential block (or not within any block) will
|
|
||||||
// set the timeline to the max now_mu within the block (and the outer max now_mu will also
|
|
||||||
// be updated).
|
|
||||||
//
|
|
||||||
// Implementation: We track the start and end separately.
|
|
||||||
// - If there is a start variable, it indicates that we are directly inside a
|
|
||||||
// parallel block and we have to reset the timeline after every function call.
|
|
||||||
// - If there is a end variable, it indicates that we are (indirectly) inside a
|
|
||||||
// parallel block, and we should update the max end value.
|
|
||||||
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
|
|
||||||
if id == &"parallel".into() || id == &"legacy_parallel".into() {
|
|
||||||
let old_start = self.start.take();
|
|
||||||
let old_end = self.end.take();
|
|
||||||
let old_parallel_mode = self.parallel_mode;
|
|
||||||
|
|
||||||
let now = if let Some(old_start) = &old_start {
|
// Behavior of parallel and sequential:
|
||||||
self.gen_expr(ctx, old_start)?
|
// Each function call (indirectly, can be inside a sequential block) within a parallel
|
||||||
.unwrap()
|
// block will update the end variable to the maximum now_mu in the block.
|
||||||
.to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
|
// Each function call directly inside a parallel block will reset the timeline after
|
||||||
} else {
|
// execution. A parallel block within a sequential block (or not within any block) will
|
||||||
self.timeline.emit_now_mu(ctx)
|
// set the timeline to the max now_mu within the block (and the outer max now_mu will also
|
||||||
};
|
// be updated).
|
||||||
|
//
|
||||||
|
// Implementation: We track the start and end separately.
|
||||||
|
// - If there is a start variable, it indicates that we are directly inside a
|
||||||
|
// parallel block and we have to reset the timeline after every function call.
|
||||||
|
// - If there is a end variable, it indicates that we are (indirectly) inside a
|
||||||
|
// parallel block, and we should update the max end value.
|
||||||
|
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
|
||||||
|
if id == &"parallel".into() || id == &"legacy_parallel".into() {
|
||||||
|
let old_start = self.start.take();
|
||||||
|
let old_end = self.end.take();
|
||||||
|
let old_parallel_mode = self.parallel_mode;
|
||||||
|
|
||||||
// Emulate variable allocation, as we need to use the CodeGenContext
|
let now = if let Some(old_start) = &old_start {
|
||||||
// HashMap to store our variable due to lifetime limitation
|
self.gen_expr(ctx, old_start)?
|
||||||
// Note: we should be able to store variables directly if generic
|
|
||||||
// associative type is used by limiting the lifetime of CodeGenerator to
|
|
||||||
// the LLVM Context.
|
|
||||||
// The name is guaranteed to be unique as users cannot use this as variable
|
|
||||||
// name.
|
|
||||||
self.start = old_start.clone().map_or_else(
|
|
||||||
|| {
|
|
||||||
let start = format!("with-{}-start", self.name_counter).into();
|
|
||||||
let start_expr = Located {
|
|
||||||
// location does not matter at this point
|
|
||||||
location: stmt.location,
|
|
||||||
node: ExprKind::Name { id: start, ctx: name_ctx.clone() },
|
|
||||||
custom: Some(ctx.primitives.int64),
|
|
||||||
};
|
|
||||||
let start = self
|
|
||||||
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
|
|
||||||
.unwrap();
|
|
||||||
ctx.builder.build_store(start, now);
|
|
||||||
Ok(Some(start_expr)) as Result<_, String>
|
|
||||||
},
|
|
||||||
|v| Ok(Some(v)),
|
|
||||||
)?;
|
|
||||||
let end = format!("with-{}-end", self.name_counter).into();
|
|
||||||
let end_expr = Located {
|
|
||||||
// location does not matter at this point
|
|
||||||
location: stmt.location,
|
|
||||||
node: ExprKind::Name { id: end, ctx: name_ctx.clone() },
|
|
||||||
custom: Some(ctx.primitives.int64),
|
|
||||||
};
|
|
||||||
let end = self
|
|
||||||
.gen_store_target(ctx, &end_expr, Some("end.addr"))?
|
|
||||||
.unwrap();
|
|
||||||
ctx.builder.build_store(end, now);
|
|
||||||
self.end = Some(end_expr);
|
|
||||||
self.name_counter += 1;
|
|
||||||
self.parallel_mode = match id.to_string().as_str() {
|
|
||||||
"parallel" => ParallelMode::Deep,
|
|
||||||
"legacy_parallel" => ParallelMode::Legacy,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.gen_block(ctx, body.iter())?;
|
|
||||||
|
|
||||||
let current = ctx.builder.get_insert_block().unwrap();
|
|
||||||
|
|
||||||
// if the current block is terminated, move before the terminator
|
|
||||||
// we want to set the timeline before reaching the terminator
|
|
||||||
// TODO: This may be unsound if there are multiple exit paths in the
|
|
||||||
// block... e.g.
|
|
||||||
// if ...:
|
|
||||||
// return
|
|
||||||
// Perhaps we can fix this by using actual with block?
|
|
||||||
let reset_position = if let Some(terminator) = current.get_terminator() {
|
|
||||||
ctx.builder.position_before(&terminator);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
// set duration
|
|
||||||
let end_expr = self.end.take().unwrap();
|
|
||||||
let end_val = self
|
|
||||||
.gen_expr(ctx, &end_expr)?
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_basic_value_enum(ctx, self, end_expr.custom.unwrap())?;
|
.to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
|
||||||
|
} else {
|
||||||
|
self.timeline.emit_now_mu(ctx)
|
||||||
|
};
|
||||||
|
|
||||||
// inside a sequential block
|
// Emulate variable allocation, as we need to use the CodeGenContext
|
||||||
if old_start.is_none() {
|
// HashMap to store our variable due to lifetime limitation
|
||||||
self.timeline.emit_at_mu(ctx, end_val);
|
// Note: we should be able to store variables directly if generic
|
||||||
}
|
// associative type is used by limiting the lifetime of CodeGenerator to
|
||||||
|
// the LLVM Context.
|
||||||
|
// The name is guaranteed to be unique as users cannot use this as variable
|
||||||
|
// name.
|
||||||
|
self.start = old_start.clone().map_or_else(
|
||||||
|
|| {
|
||||||
|
let start = format!("with-{}-start", self.name_counter).into();
|
||||||
|
let start_expr = Located {
|
||||||
|
// location does not matter at this point
|
||||||
|
location: stmt.location,
|
||||||
|
node: ExprKind::Name { id: start, ctx: name_ctx.clone() },
|
||||||
|
custom: Some(ctx.primitives.int64),
|
||||||
|
};
|
||||||
|
let start = self
|
||||||
|
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
|
||||||
|
.unwrap();
|
||||||
|
ctx.builder.build_store(start, now);
|
||||||
|
Ok(Some(start_expr)) as Result<_, String>
|
||||||
|
},
|
||||||
|
|v| Ok(Some(v)),
|
||||||
|
)?;
|
||||||
|
let end = format!("with-{}-end", self.name_counter).into();
|
||||||
|
let end_expr = Located {
|
||||||
|
// location does not matter at this point
|
||||||
|
location: stmt.location,
|
||||||
|
node: ExprKind::Name { id: end, ctx: name_ctx.clone() },
|
||||||
|
custom: Some(ctx.primitives.int64),
|
||||||
|
};
|
||||||
|
let end = self
|
||||||
|
.gen_store_target(ctx, &end_expr, Some("end.addr"))?
|
||||||
|
.unwrap();
|
||||||
|
ctx.builder.build_store(end, now);
|
||||||
|
self.end = Some(end_expr);
|
||||||
|
self.name_counter += 1;
|
||||||
|
self.parallel_mode = match id.to_string().as_str() {
|
||||||
|
"parallel" => ParallelMode::Deep,
|
||||||
|
"legacy_parallel" => ParallelMode::Legacy,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
// inside a parallel block, should update the outer max now_mu
|
self.gen_block(ctx, body.iter())?;
|
||||||
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
|
|
||||||
|
|
||||||
self.parallel_mode = old_parallel_mode;
|
let current = ctx.builder.get_insert_block().unwrap();
|
||||||
self.end = old_end;
|
|
||||||
self.start = old_start;
|
|
||||||
|
|
||||||
if reset_position {
|
// if the current block is terminated, move before the terminator
|
||||||
ctx.builder.position_at_end(current);
|
// we want to set the timeline before reaching the terminator
|
||||||
}
|
// TODO: This may be unsound if there are multiple exit paths in the
|
||||||
|
// block... e.g.
|
||||||
|
// if ...:
|
||||||
|
// return
|
||||||
|
// Perhaps we can fix this by using actual with block?
|
||||||
|
let reset_position = if let Some(terminator) = current.get_terminator() {
|
||||||
|
ctx.builder.position_before(&terminator);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
return Ok(());
|
// set duration
|
||||||
} else if id == &"sequential".into() {
|
let end_expr = self.end.take().unwrap();
|
||||||
// For deep parallel, temporarily take away start to avoid function calls in
|
let end_val = self
|
||||||
// the block from resetting the timeline.
|
.gen_expr(ctx, &end_expr)?
|
||||||
// This does not affect legacy parallel, as the timeline will be reset after
|
.unwrap()
|
||||||
// this block finishes execution.
|
.to_basic_value_enum(ctx, self, end_expr.custom.unwrap())?;
|
||||||
let start = self.start.take();
|
|
||||||
self.gen_block(ctx, body.iter())?;
|
|
||||||
self.start = start;
|
|
||||||
|
|
||||||
// Reset the timeline when we are exiting the sequential block
|
// inside a sequential block
|
||||||
// Legacy parallel does not need this, since it will be reset after codegen
|
if old_start.is_none() {
|
||||||
// for this statement is completed
|
self.timeline.emit_at_mu(ctx, end_val);
|
||||||
if self.parallel_mode == ParallelMode::Deep {
|
|
||||||
self.timeline_reset_start(ctx)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inside a parallel block, should update the outer max now_mu
|
||||||
|
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
|
||||||
|
|
||||||
|
self.parallel_mode = old_parallel_mode;
|
||||||
|
self.end = old_end;
|
||||||
|
self.start = old_start;
|
||||||
|
|
||||||
|
if reset_position {
|
||||||
|
ctx.builder.position_at_end(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
} else if id == &"sequential".into() {
|
||||||
|
// For deep parallel, temporarily take away start to avoid function calls in
|
||||||
|
// the block from resetting the timeline.
|
||||||
|
// This does not affect legacy parallel, as the timeline will be reset after
|
||||||
|
// this block finishes execution.
|
||||||
|
let start = self.start.take();
|
||||||
|
self.gen_block(ctx, body.iter())?;
|
||||||
|
self.start = start;
|
||||||
|
|
||||||
|
// Reset the timeline when we are exiting the sequential block
|
||||||
|
// Legacy parallel does not need this, since it will be reset after codegen
|
||||||
|
// for this statement is completed
|
||||||
|
if self.parallel_mode == ParallelMode::Deep {
|
||||||
|
self.timeline_reset_start(ctx)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// not parallel/sequential
|
|
||||||
gen_with(self, ctx, stmt)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// not parallel/sequential
|
||||||
|
gen_with(self, ctx, stmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ use inkwell::{
|
||||||
targets::*,
|
targets::*,
|
||||||
OptimizationLevel,
|
OptimizationLevel,
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use nac3core::codegen::{CodeGenLLVMOptions, CodeGenTargetMachineOptions, gen_func_impl};
|
use nac3core::codegen::{CodeGenLLVMOptions, CodeGenTargetMachineOptions, gen_func_impl};
|
||||||
use nac3core::toplevel::builtins::get_exn_constructor;
|
use nac3core::toplevel::builtins::get_exn_constructor;
|
||||||
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
|
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
|
||||||
|
@ -75,7 +76,7 @@ pub struct PrimitivePythonId {
|
||||||
list: u64,
|
list: u64,
|
||||||
tuple: u64,
|
tuple: u64,
|
||||||
typevar: u64,
|
typevar: u64,
|
||||||
const_generic_dummy: u64,
|
const_generic_marker: u64,
|
||||||
none: u64,
|
none: u64,
|
||||||
exception: u64,
|
exception: u64,
|
||||||
generic_alias: (u64, u64),
|
generic_alias: (u64, u64),
|
||||||
|
@ -470,7 +471,7 @@ impl Nac3 {
|
||||||
|
|
||||||
if let Err(e) = composer.start_analysis(true) {
|
if let Err(e) = composer.start_analysis(true) {
|
||||||
// report error of __modinit__ separately
|
// report error of __modinit__ separately
|
||||||
return if e.contains("<nac3_synthesized_modinit>") {
|
return if e.iter().any(|err| err.contains("<nac3_synthesized_modinit>")) {
|
||||||
let msg = Self::report_modinit(
|
let msg = Self::report_modinit(
|
||||||
&arg_names,
|
&arg_names,
|
||||||
method_name,
|
method_name,
|
||||||
|
@ -481,12 +482,15 @@ impl Nac3 {
|
||||||
);
|
);
|
||||||
Err(CompileError::new_err(format!(
|
Err(CompileError::new_err(format!(
|
||||||
"compilation failed\n----------\n{}",
|
"compilation failed\n----------\n{}",
|
||||||
msg.unwrap_or(e)
|
msg.unwrap_or(e.iter().sorted().join("\n----------\n"))
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Err(CompileError::new_err(format!(
|
Err(CompileError::new_err(
|
||||||
"compilation failed\n----------\n{e}"
|
format!(
|
||||||
)))
|
"compilation failed\n----------\n{}",
|
||||||
|
e.iter().sorted().join("\n----------\n"),
|
||||||
|
),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let top_level = Arc::new(composer.make_top_level_context());
|
let top_level = Arc::new(composer.make_top_level_context());
|
||||||
|
@ -529,14 +533,13 @@ impl Nac3 {
|
||||||
let instance = {
|
let instance = {
|
||||||
let defs = top_level.definitions.read();
|
let defs = top_level.definitions.read();
|
||||||
let mut definition = defs[def_id.0].write();
|
let mut definition = defs[def_id.0].write();
|
||||||
if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } =
|
let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } =
|
||||||
&mut *definition
|
&mut *definition else {
|
||||||
{
|
|
||||||
instance_to_symbol.insert(String::new(), "__modinit__".into());
|
|
||||||
instance_to_stmt[""].clone()
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
instance_to_symbol.insert(String::new(), "__modinit__".into());
|
||||||
|
instance_to_stmt[""].clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let task = CodeGenTask {
|
let task = CodeGenTask {
|
||||||
|
@ -776,7 +779,7 @@ fn add_exceptions(
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl Nac3 {
|
impl Nac3 {
|
||||||
#[new]
|
#[new]
|
||||||
fn new(isa: &str, py: Python) -> PyResult<Self> {
|
fn new(isa: &str, artiq_builtins: &PyDict, py: Python) -> PyResult<Self> {
|
||||||
let isa = match isa {
|
let isa = match isa {
|
||||||
"host" => Isa::Host,
|
"host" => Isa::Host,
|
||||||
"rv32g" => Isa::RiscV32G,
|
"rv32g" => Isa::RiscV32G,
|
||||||
|
@ -842,44 +845,18 @@ impl Nac3 {
|
||||||
let typing_mod = PyModule::import(py, "typing").unwrap();
|
let typing_mod = PyModule::import(py, "typing").unwrap();
|
||||||
let types_mod = PyModule::import(py, "types").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(),))
|
let get_attr_id = |obj: &PyModule, attr| id_fn.call1((obj.getattr(attr).unwrap(),))
|
||||||
.unwrap().extract().unwrap();
|
.unwrap().extract().unwrap();
|
||||||
let primitive_ids = PrimitivePythonId {
|
let primitive_ids = PrimitivePythonId {
|
||||||
virtual_id: get_id(
|
virtual_id: get_id(artiq_builtins.get_item("virtual").ok().flatten().unwrap()),
|
||||||
builtins_mod
|
|
||||||
.getattr("globals")
|
|
||||||
.unwrap()
|
|
||||||
.call0()
|
|
||||||
.unwrap()
|
|
||||||
.get_item("virtual")
|
|
||||||
.unwrap(
|
|
||||||
)),
|
|
||||||
generic_alias: (
|
generic_alias: (
|
||||||
get_attr_id(typing_mod, "_GenericAlias"),
|
get_attr_id(typing_mod, "_GenericAlias"),
|
||||||
get_attr_id(types_mod, "GenericAlias"),
|
get_attr_id(types_mod, "GenericAlias"),
|
||||||
),
|
),
|
||||||
none: id_fn
|
none: get_id(artiq_builtins.get_item("none").ok().flatten().unwrap()),
|
||||||
.call1((builtins_mod
|
|
||||||
.getattr("globals")
|
|
||||||
.unwrap()
|
|
||||||
.call0()
|
|
||||||
.unwrap()
|
|
||||||
.get_item("none")
|
|
||||||
.unwrap(),))
|
|
||||||
.unwrap()
|
|
||||||
.extract()
|
|
||||||
.unwrap(),
|
|
||||||
typevar: get_attr_id(typing_mod, "TypeVar"),
|
typevar: get_attr_id(typing_mod, "TypeVar"),
|
||||||
const_generic_dummy: id_fn
|
const_generic_marker: get_id(artiq_builtins.get_item("_ConstGenericMarker").ok().flatten().unwrap()),
|
||||||
.call1((
|
|
||||||
builtins_mod.getattr("globals")
|
|
||||||
.and_then(|v| v.call0())
|
|
||||||
.and_then(|v| v.get_item("_ConstGenericMarker"))
|
|
||||||
.unwrap(),
|
|
||||||
))
|
|
||||||
.and_then(PyAny::extract)
|
|
||||||
.unwrap(),
|
|
||||||
int: get_attr_id(builtins_mod, "int"),
|
int: get_attr_id(builtins_mod, "int"),
|
||||||
int32: get_attr_id(numpy_mod, "int32"),
|
int32: get_attr_id(numpy_mod, "int32"),
|
||||||
int64: get_attr_id(numpy_mod, "int64"),
|
int64: get_attr_id(numpy_mod, "int64"),
|
||||||
|
@ -891,17 +868,7 @@ impl Nac3 {
|
||||||
list: get_attr_id(builtins_mod, "list"),
|
list: get_attr_id(builtins_mod, "list"),
|
||||||
tuple: get_attr_id(builtins_mod, "tuple"),
|
tuple: get_attr_id(builtins_mod, "tuple"),
|
||||||
exception: get_attr_id(builtins_mod, "Exception"),
|
exception: get_attr_id(builtins_mod, "Exception"),
|
||||||
option: id_fn
|
option: get_id(artiq_builtins.get_item("Option").ok().flatten().unwrap()),
|
||||||
.call1((builtins_mod
|
|
||||||
.getattr("globals")
|
|
||||||
.unwrap()
|
|
||||||
.call0()
|
|
||||||
.unwrap()
|
|
||||||
.get_item("Option")
|
|
||||||
.unwrap(),))
|
|
||||||
.unwrap()
|
|
||||||
.extract()
|
|
||||||
.unwrap(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
|
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
|
||||||
|
|
|
@ -15,7 +15,7 @@ use pyo3::{
|
||||||
PyAny, PyObject, PyResult, Python,
|
PyAny, PyObject, PyResult, Python,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, Ordering::Relaxed}
|
atomic::{AtomicBool, Ordering::Relaxed}
|
||||||
|
@ -311,37 +311,37 @@ impl InnerResolver {
|
||||||
unreachable!("none cannot be typeid")
|
unreachable!("none cannot be typeid")
|
||||||
} else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).copied() {
|
} else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).copied() {
|
||||||
let def = defs[def_id.0].read();
|
let def = defs[def_id.0].read();
|
||||||
if let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def {
|
let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def else {
|
||||||
// do not handle type var param and concrete check here, and no subst
|
|
||||||
Ok(Ok({
|
|
||||||
let ty = TypeEnum::TObj {
|
|
||||||
obj_id: *object_id,
|
|
||||||
params: type_vars
|
|
||||||
.iter()
|
|
||||||
.map(|x| {
|
|
||||||
if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*x) {
|
|
||||||
(*id, *x)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
fields: {
|
|
||||||
let mut res = methods
|
|
||||||
.iter()
|
|
||||||
.map(|(iden, ty, _)| (*iden, (*ty, false)))
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
res.extend(fields.clone().into_iter().map(|x| (x.0, (x.1, x.2))));
|
|
||||||
res
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// here also false, later instantiation use python object to check compatible
|
|
||||||
(unifier.add_ty(ty), false)
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
// only object is supported, functions are not supported
|
// only object is supported, functions are not supported
|
||||||
unreachable!("function type is not supported, should not be queried")
|
unreachable!("function type is not supported, should not be queried")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// do not handle type var param and concrete check here, and no subst
|
||||||
|
Ok(Ok({
|
||||||
|
let ty = TypeEnum::TObj {
|
||||||
|
obj_id: *object_id,
|
||||||
|
params: type_vars
|
||||||
|
.iter()
|
||||||
|
.map(|x| {
|
||||||
|
let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*x) else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
(*id, *x)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
fields: {
|
||||||
|
let mut res = methods
|
||||||
|
.iter()
|
||||||
|
.map(|(iden, ty, _)| (*iden, (*ty, false)))
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
res.extend(fields.clone().into_iter().map(|x| (x.0, (x.1, x.2))));
|
||||||
|
res
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// here also false, later instantiation use python object to check compatible
|
||||||
|
(unifier.add_ty(ty), false)
|
||||||
|
}))
|
||||||
} else if ty_ty_id == self.primitive_ids.typevar {
|
} else if ty_ty_id == self.primitive_ids.typevar {
|
||||||
let name: &str = pyty.getattr("__name__").unwrap().extract().unwrap();
|
let name: &str = pyty.getattr("__name__").unwrap().extract().unwrap();
|
||||||
let (constraint_types, is_const_generic) = {
|
let (constraint_types, is_const_generic) = {
|
||||||
|
@ -353,7 +353,7 @@ impl InnerResolver {
|
||||||
for i in 0usize.. {
|
for i in 0usize.. {
|
||||||
if let Ok(constr) = constraints.get_item(i) {
|
if let Ok(constr) = constraints.get_item(i) {
|
||||||
let constr_id: u64 = self.helper.id_fn.call1(py, (constr,))?.extract(py)?;
|
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;
|
is_const_generic = true;
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -652,23 +652,23 @@ impl InnerResolver {
|
||||||
// if is `none`
|
// if is `none`
|
||||||
let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
|
let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
|
||||||
if zelf_id == self.primitive_ids.none {
|
if zelf_id == self.primitive_ids.none {
|
||||||
if let TypeEnum::TObj { params, .. } =
|
let ty_enum = unifier.get_ty_immutable(primitives.option);
|
||||||
unifier.get_ty_immutable(primitives.option).as_ref()
|
let TypeEnum::TObj { params, .. } = ty_enum.as_ref() else {
|
||||||
{
|
unreachable!("must be tobj")
|
||||||
let var_map = params
|
};
|
||||||
.iter()
|
|
||||||
.map(|(id_var, ty)| {
|
let var_map = params
|
||||||
if let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) {
|
.iter()
|
||||||
assert_eq!(*id, *id_var);
|
.map(|(id_var, ty)| {
|
||||||
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) else {
|
||||||
} else {
|
unreachable!()
|
||||||
unreachable!()
|
};
|
||||||
}
|
|
||||||
})
|
assert_eq!(*id, *id_var);
|
||||||
.collect::<HashMap<_, _>>();
|
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
||||||
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()))
|
})
|
||||||
}
|
.collect::<HashMap<_, _>>();
|
||||||
unreachable!("must be tobj")
|
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
let ty = match self.get_obj_type(py, field_data, unifier, defs, primitives)? {
|
let ty = match self.get_obj_type(py, field_data, unifier, defs, primitives)? {
|
||||||
|
@ -688,14 +688,13 @@ impl InnerResolver {
|
||||||
let var_map = params
|
let var_map = params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id_var, ty)| {
|
.map(|(id_var, ty)| {
|
||||||
if let TypeEnum::TVar { id, range, name, loc, .. } =
|
let TypeEnum::TVar { id, range, name, loc, .. } =
|
||||||
&*unifier.get_ty(*ty)
|
&*unifier.get_ty(*ty) else {
|
||||||
{
|
|
||||||
assert_eq!(*id, *id_var);
|
|
||||||
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
assert_eq!(*id, *id_var);
|
||||||
|
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
||||||
})
|
})
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
let mut instantiate_obj = || {
|
let mut instantiate_obj = || {
|
||||||
|
@ -900,28 +899,29 @@ impl InnerResolver {
|
||||||
|
|
||||||
Ok(Some(global.as_pointer_value().into()))
|
Ok(Some(global.as_pointer_value().into()))
|
||||||
} else if ty_id == self.primitive_ids.tuple {
|
} else if ty_id == self.primitive_ids.tuple {
|
||||||
if let TypeEnum::TTuple { ty } = ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
|
let expected_ty_enum = ctx.unifier.get_ty_immutable(expected_ty);
|
||||||
let tup_tys = ty.iter();
|
let TypeEnum::TTuple { ty } = expected_ty_enum.as_ref() else {
|
||||||
let elements: &PyTuple = obj.downcast()?;
|
unreachable!()
|
||||||
assert_eq!(elements.len(), tup_tys.len());
|
};
|
||||||
let val: Result<Option<Vec<_>>, _> =
|
|
||||||
elements
|
let tup_tys = ty.iter();
|
||||||
.iter()
|
let elements: &PyTuple = obj.downcast()?;
|
||||||
.enumerate()
|
assert_eq!(elements.len(), tup_tys.len());
|
||||||
.zip(tup_tys)
|
let val: Result<Option<Vec<_>>, _> =
|
||||||
.map(|((i, elem), ty)| self
|
elements
|
||||||
.get_obj_value(py, elem, ctx, generator, *ty).map_err(|e|
|
.iter()
|
||||||
super::CompileError::new_err(
|
.enumerate()
|
||||||
format!("Error getting element {i}: {e}")
|
.zip(tup_tys)
|
||||||
)
|
.map(|((i, elem), ty)| self
|
||||||
|
.get_obj_value(py, elem, ctx, generator, *ty).map_err(|e|
|
||||||
|
super::CompileError::new_err(
|
||||||
|
format!("Error getting element {i}: {e}")
|
||||||
)
|
)
|
||||||
).collect();
|
)
|
||||||
let val = val?.unwrap();
|
).collect();
|
||||||
let val = ctx.ctx.const_struct(&val, false);
|
let val = val?.unwrap();
|
||||||
Ok(Some(val.into()))
|
let val = ctx.ctx.const_struct(&val, false);
|
||||||
} else {
|
Ok(Some(val.into()))
|
||||||
unreachable!("must expect tuple type")
|
|
||||||
}
|
|
||||||
} else if ty_id == self.primitive_ids.option {
|
} else if ty_id == self.primitive_ids.option {
|
||||||
let option_val_ty = match ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
|
let option_val_ty = match ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
|
||||||
TypeEnum::TObj { obj_id, params, .. }
|
TypeEnum::TObj { obj_id, params, .. }
|
||||||
|
@ -993,27 +993,25 @@ impl InnerResolver {
|
||||||
// should be classes
|
// should be classes
|
||||||
let definition =
|
let definition =
|
||||||
top_level_defs.get(self.pyid_to_def.read().get(&ty_id).unwrap().0).unwrap().read();
|
top_level_defs.get(self.pyid_to_def.read().get(&ty_id).unwrap().0).unwrap().read();
|
||||||
if let TopLevelDef::Class { fields, .. } = &*definition {
|
let TopLevelDef::Class { fields, .. } = &*definition else { unreachable!() };
|
||||||
let values: Result<Option<Vec<_>>, _> = fields
|
|
||||||
.iter()
|
let values: Result<Option<Vec<_>>, _> = fields
|
||||||
.map(|(name, ty, _)| {
|
.iter()
|
||||||
self.get_obj_value(py, obj.getattr(name.to_string().as_str())?, ctx, generator, *ty)
|
.map(|(name, ty, _)| {
|
||||||
.map_err(|e| super::CompileError::new_err(format!("Error getting field {name}: {e}")))
|
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}")))
|
||||||
.collect();
|
})
|
||||||
let values = values?;
|
.collect();
|
||||||
if let Some(values) = values {
|
let values = values?;
|
||||||
let val = ty.const_named_struct(&values);
|
if let Some(values) = values {
|
||||||
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
|
let val = ty.const_named_struct(&values);
|
||||||
ctx.module.add_global(ty, Some(AddressSpace::default()), &id_str)
|
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
|
||||||
});
|
ctx.module.add_global(ty, Some(AddressSpace::default()), &id_str)
|
||||||
global.set_initializer(&val);
|
});
|
||||||
Ok(Some(global.as_pointer_value().into()))
|
global.set_initializer(&val);
|
||||||
} else {
|
Ok(Some(global.as_pointer_value().into()))
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1065,27 +1063,26 @@ impl InnerResolver {
|
||||||
|
|
||||||
impl SymbolResolver for Resolver {
|
impl SymbolResolver for Resolver {
|
||||||
fn get_default_param_value(&self, expr: &ast::Expr) -> Option<SymbolValue> {
|
fn get_default_param_value(&self, expr: &ast::Expr) -> Option<SymbolValue> {
|
||||||
match &expr.node {
|
let ast::ExprKind::Name { id, .. } = &expr.node else {
|
||||||
ast::ExprKind::Name { id, .. } => {
|
|
||||||
Python::with_gil(|py| -> PyResult<Option<SymbolValue>> {
|
unreachable!("only for resolving names")
|
||||||
let obj: &PyAny = self.0.module.extract(py)?;
|
};
|
||||||
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
|
|
||||||
let mut sym_value = None;
|
Python::with_gil(|py| -> PyResult<Option<SymbolValue>> {
|
||||||
for (key, val) in members {
|
let obj: &PyAny = self.0.module.extract(py)?;
|
||||||
let key: &str = key.extract()?;
|
let members: &PyDict = obj.getattr("__dict__").unwrap().downcast().unwrap();
|
||||||
if key == id.to_string() {
|
let mut sym_value = None;
|
||||||
if let Ok(Ok(v)) = self.0.get_default_param_obj_value(py, val) {
|
for (key, val) in members {
|
||||||
sym_value = Some(v);
|
let key: &str = key.extract()?;
|
||||||
}
|
if key == id.to_string() {
|
||||||
break;
|
if let Ok(Ok(v)) = self.0.get_default_param_obj_value(py, val) {
|
||||||
}
|
sym_value = Some(v);
|
||||||
}
|
}
|
||||||
Ok(sym_value)
|
break;
|
||||||
})
|
}
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
_ => unreachable!("only for resolving names"),
|
Ok(sym_value)
|
||||||
}
|
}).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_symbol_type(
|
fn get_symbol_type(
|
||||||
|
@ -1172,17 +1169,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();
|
let id_to_def = self.0.id_to_def.read();
|
||||||
id_to_def.get(&id).copied().ok_or_else(String::new)
|
id_to_def.get(&id).copied().ok_or_else(String::new)
|
||||||
}
|
}
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
let py_id =
|
let py_id = self.0.name_to_pyid.get(&id)
|
||||||
self.0.name_to_pyid.get(&id).ok_or(format!("Undefined identifier `{id}`"))?;
|
.ok_or_else(|| HashSet::from([
|
||||||
let result = self.0.pyid_to_def.read().get(py_id).copied().ok_or(format!(
|
format!("Undefined identifier `{id}`"),
|
||||||
"`{id}` is not registered with NAC3 (@nac3 decorator missing?)"
|
]))?;
|
||||||
))?;
|
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);
|
self.0.id_to_def.write().insert(id, result);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
})
|
})
|
||||||
|
|
|
@ -29,29 +29,29 @@ impl TimeFns for NowPinningTimeFns64 {
|
||||||
let now_hiptr =
|
let now_hiptr =
|
||||||
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr");
|
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr");
|
||||||
|
|
||||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr else {
|
||||||
let now_loptr = unsafe {
|
unreachable!()
|
||||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if let (BasicValueEnum::IntValue(now_hi), BasicValueEnum::IntValue(now_lo)) = (
|
let now_loptr = unsafe {
|
||||||
ctx.builder.build_load(now_hiptr, "now.hi"),
|
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
|
||||||
ctx.builder.build_load(now_loptr, "now.lo"),
|
};
|
||||||
) {
|
|
||||||
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "");
|
let (BasicValueEnum::IntValue(now_hi), BasicValueEnum::IntValue(now_lo)) = (
|
||||||
let shifted_hi = ctx.builder.build_left_shift(
|
ctx.builder.build_load(now_hiptr, "now.hi"),
|
||||||
zext_hi,
|
ctx.builder.build_load(now_loptr, "now.lo"),
|
||||||
i64_type.const_int(32, false),
|
) else {
|
||||||
"",
|
unreachable!()
|
||||||
);
|
};
|
||||||
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "");
|
|
||||||
ctx.builder.build_or(shifted_hi, zext_lo, "now_mu").into()
|
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "");
|
||||||
} else {
|
let shifted_hi = ctx.builder.build_left_shift(
|
||||||
unreachable!();
|
zext_hi,
|
||||||
}
|
i64_type.const_int(32, false),
|
||||||
} else {
|
"",
|
||||||
unreachable!();
|
);
|
||||||
}
|
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "");
|
||||||
|
ctx.builder.build_or(shifted_hi, zext_lo, "now_mu").into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
|
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
|
||||||
|
@ -59,41 +59,41 @@ impl TimeFns for NowPinningTimeFns64 {
|
||||||
let i64_type = ctx.ctx.i64_type();
|
let i64_type = ctx.ctx.i64_type();
|
||||||
|
|
||||||
let i64_32 = i64_type.const_int(32, false);
|
let i64_32 = i64_type.const_int(32, false);
|
||||||
if let BasicValueEnum::IntValue(time) = t {
|
let BasicValueEnum::IntValue(time) = t else {
|
||||||
let time_hi = ctx.builder.build_int_truncate(
|
unreachable!()
|
||||||
ctx.builder.build_right_shift(time, i64_32, false, "time.hi"),
|
};
|
||||||
i32_type,
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
|
|
||||||
let now = ctx
|
|
||||||
.module
|
|
||||||
.get_global("now")
|
|
||||||
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
|
||||||
let now_hiptr = ctx.builder.build_bitcast(
|
|
||||||
now,
|
|
||||||
i32_type.ptr_type(AddressSpace::default()),
|
|
||||||
"now.hi.addr",
|
|
||||||
);
|
|
||||||
|
|
||||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
let time_hi = ctx.builder.build_int_truncate(
|
||||||
let now_loptr = unsafe {
|
ctx.builder.build_right_shift(time, i64_32, false, "time.hi"),
|
||||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
|
i32_type,
|
||||||
};
|
"",
|
||||||
ctx.builder
|
);
|
||||||
.build_store(now_hiptr, time_hi)
|
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
|
||||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
let now = ctx
|
||||||
.unwrap();
|
.module
|
||||||
ctx.builder
|
.get_global("now")
|
||||||
.build_store(now_loptr, time_lo)
|
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
||||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
let now_hiptr = ctx.builder.build_bitcast(
|
||||||
.unwrap();
|
now,
|
||||||
} else {
|
i32_type.ptr_type(AddressSpace::default()),
|
||||||
unreachable!();
|
"now.hi.addr",
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
unreachable!();
|
let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr else {
|
||||||
}
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let now_loptr = unsafe {
|
||||||
|
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
|
||||||
|
};
|
||||||
|
ctx.builder
|
||||||
|
.build_store(now_hiptr, time_hi)
|
||||||
|
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||||
|
.unwrap();
|
||||||
|
ctx.builder
|
||||||
|
.build_store(now_loptr, time_lo)
|
||||||
|
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_delay_mu<'ctx>(
|
fn emit_delay_mu<'ctx>(
|
||||||
|
@ -110,56 +110,56 @@ impl TimeFns for NowPinningTimeFns64 {
|
||||||
let now_hiptr =
|
let now_hiptr =
|
||||||
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr");
|
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr");
|
||||||
|
|
||||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr else {
|
||||||
let now_loptr = unsafe {
|
unreachable!()
|
||||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
|
|
||||||
};
|
|
||||||
|
|
||||||
if let (
|
|
||||||
BasicValueEnum::IntValue(now_hi),
|
|
||||||
BasicValueEnum::IntValue(now_lo),
|
|
||||||
BasicValueEnum::IntValue(dt),
|
|
||||||
) = (
|
|
||||||
ctx.builder.build_load(now_hiptr, "now.hi"),
|
|
||||||
ctx.builder.build_load(now_loptr, "now.lo"),
|
|
||||||
dt,
|
|
||||||
) {
|
|
||||||
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "");
|
|
||||||
let shifted_hi = ctx.builder.build_left_shift(
|
|
||||||
zext_hi,
|
|
||||||
i64_type.const_int(32, false),
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "");
|
|
||||||
let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now");
|
|
||||||
|
|
||||||
let time = ctx.builder.build_int_add(now_val, dt, "time");
|
|
||||||
let time_hi = ctx.builder.build_int_truncate(
|
|
||||||
ctx.builder.build_right_shift(
|
|
||||||
time,
|
|
||||||
i64_type.const_int(32, false),
|
|
||||||
false,
|
|
||||||
"",
|
|
||||||
),
|
|
||||||
i32_type,
|
|
||||||
"time.hi",
|
|
||||||
);
|
|
||||||
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
|
|
||||||
|
|
||||||
ctx.builder
|
|
||||||
.build_store(now_hiptr, time_hi)
|
|
||||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
|
||||||
.unwrap();
|
|
||||||
ctx.builder
|
|
||||||
.build_store(now_loptr, time_lo)
|
|
||||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let now_loptr = unsafe {
|
||||||
|
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
|
||||||
|
};
|
||||||
|
|
||||||
|
let (
|
||||||
|
BasicValueEnum::IntValue(now_hi),
|
||||||
|
BasicValueEnum::IntValue(now_lo),
|
||||||
|
BasicValueEnum::IntValue(dt),
|
||||||
|
) = (
|
||||||
|
ctx.builder.build_load(now_hiptr, "now.hi"),
|
||||||
|
ctx.builder.build_load(now_loptr, "now.lo"),
|
||||||
|
dt,
|
||||||
|
) else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "");
|
||||||
|
let shifted_hi = ctx.builder.build_left_shift(
|
||||||
|
zext_hi,
|
||||||
|
i64_type.const_int(32, false),
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "");
|
||||||
|
let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now");
|
||||||
|
|
||||||
|
let time = ctx.builder.build_int_add(now_val, dt, "time");
|
||||||
|
let time_hi = ctx.builder.build_int_truncate(
|
||||||
|
ctx.builder.build_right_shift(
|
||||||
|
time,
|
||||||
|
i64_type.const_int(32, false),
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
i32_type,
|
||||||
|
"time.hi",
|
||||||
|
);
|
||||||
|
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
|
||||||
|
|
||||||
|
ctx.builder
|
||||||
|
.build_store(now_hiptr, time_hi)
|
||||||
|
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||||
|
.unwrap();
|
||||||
|
ctx.builder
|
||||||
|
.build_store(now_loptr, time_lo)
|
||||||
|
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,14 +176,14 @@ impl TimeFns for NowPinningTimeFns {
|
||||||
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
||||||
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now");
|
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now");
|
||||||
|
|
||||||
if let BasicValueEnum::IntValue(now_raw) = now_raw {
|
let BasicValueEnum::IntValue(now_raw) = now_raw else {
|
||||||
let i64_32 = i64_type.const_int(32, false);
|
unreachable!()
|
||||||
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo");
|
};
|
||||||
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi");
|
|
||||||
ctx.builder.build_or(now_lo, now_hi, "now_mu").into()
|
let i64_32 = i64_type.const_int(32, false);
|
||||||
} else {
|
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo");
|
||||||
unreachable!();
|
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi");
|
||||||
}
|
ctx.builder.build_or(now_lo, now_hi, "now_mu").into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
|
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
|
||||||
|
@ -191,41 +191,41 @@ impl TimeFns for NowPinningTimeFns {
|
||||||
let i64_type = ctx.ctx.i64_type();
|
let i64_type = ctx.ctx.i64_type();
|
||||||
let i64_32 = i64_type.const_int(32, false);
|
let i64_32 = i64_type.const_int(32, false);
|
||||||
|
|
||||||
if let BasicValueEnum::IntValue(time) = t {
|
let BasicValueEnum::IntValue(time) = t else {
|
||||||
let time_hi = ctx.builder.build_int_truncate(
|
unreachable!()
|
||||||
ctx.builder.build_right_shift(time, i64_32, false, ""),
|
};
|
||||||
i32_type,
|
|
||||||
"time.hi",
|
|
||||||
);
|
|
||||||
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
|
|
||||||
let now = ctx
|
|
||||||
.module
|
|
||||||
.get_global("now")
|
|
||||||
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
|
||||||
let now_hiptr = ctx.builder.build_bitcast(
|
|
||||||
now,
|
|
||||||
i32_type.ptr_type(AddressSpace::default()),
|
|
||||||
"now.hi.addr",
|
|
||||||
);
|
|
||||||
|
|
||||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
let time_hi = ctx.builder.build_int_truncate(
|
||||||
let now_loptr = unsafe {
|
ctx.builder.build_right_shift(time, i64_32, false, ""),
|
||||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
|
i32_type,
|
||||||
};
|
"time.hi",
|
||||||
ctx.builder
|
);
|
||||||
.build_store(now_hiptr, time_hi)
|
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
|
||||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
let now = ctx
|
||||||
.unwrap();
|
.module
|
||||||
ctx.builder
|
.get_global("now")
|
||||||
.build_store(now_loptr, time_lo)
|
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
||||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
let now_hiptr = ctx.builder.build_bitcast(
|
||||||
.unwrap();
|
now,
|
||||||
} else {
|
i32_type.ptr_type(AddressSpace::default()),
|
||||||
unreachable!();
|
"now.hi.addr",
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
unreachable!();
|
let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr else {
|
||||||
}
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let now_loptr = unsafe {
|
||||||
|
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
|
||||||
|
};
|
||||||
|
ctx.builder
|
||||||
|
.build_store(now_hiptr, time_hi)
|
||||||
|
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||||
|
.unwrap();
|
||||||
|
ctx.builder
|
||||||
|
.build_store(now_loptr, time_lo)
|
||||||
|
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_delay_mu<'ctx>(
|
fn emit_delay_mu<'ctx>(
|
||||||
|
@ -242,41 +242,41 @@ impl TimeFns for NowPinningTimeFns {
|
||||||
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
|
||||||
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "");
|
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "");
|
||||||
|
|
||||||
if let (BasicValueEnum::IntValue(now_raw), BasicValueEnum::IntValue(dt)) = (now_raw, dt) {
|
let (BasicValueEnum::IntValue(now_raw), BasicValueEnum::IntValue(dt)) = (now_raw, dt) else {
|
||||||
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo");
|
unreachable!()
|
||||||
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi");
|
};
|
||||||
let now_val = ctx.builder.build_or(now_lo, now_hi, "now_val");
|
|
||||||
let time = ctx.builder.build_int_add(now_val, dt, "time");
|
|
||||||
let time_hi = ctx.builder.build_int_truncate(
|
|
||||||
ctx.builder.build_right_shift(time, i64_32, false, "time.hi"),
|
|
||||||
i32_type,
|
|
||||||
"now_trunc",
|
|
||||||
);
|
|
||||||
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
|
|
||||||
let now_hiptr = ctx.builder.build_bitcast(
|
|
||||||
now,
|
|
||||||
i32_type.ptr_type(AddressSpace::default()),
|
|
||||||
"now.hi.addr",
|
|
||||||
);
|
|
||||||
|
|
||||||
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
|
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo");
|
||||||
let now_loptr = unsafe {
|
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi");
|
||||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
|
let now_val = ctx.builder.build_or(now_lo, now_hi, "now_val");
|
||||||
};
|
let time = ctx.builder.build_int_add(now_val, dt, "time");
|
||||||
ctx.builder
|
let time_hi = ctx.builder.build_int_truncate(
|
||||||
.build_store(now_hiptr, time_hi)
|
ctx.builder.build_right_shift(time, i64_32, false, "time.hi"),
|
||||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
i32_type,
|
||||||
.unwrap();
|
"now_trunc",
|
||||||
ctx.builder
|
);
|
||||||
.build_store(now_loptr, time_lo)
|
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
|
||||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
let now_hiptr = ctx.builder.build_bitcast(
|
||||||
.unwrap();
|
now,
|
||||||
} else {
|
i32_type.ptr_type(AddressSpace::default()),
|
||||||
unreachable!();
|
"now.hi.addr",
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
unreachable!();
|
let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr else {
|
||||||
}
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let now_loptr = unsafe {
|
||||||
|
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
|
||||||
|
};
|
||||||
|
ctx.builder
|
||||||
|
.build_store(now_hiptr, time_hi)
|
||||||
|
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||||
|
.unwrap();
|
||||||
|
ctx.builder
|
||||||
|
.build_store(now_loptr, time_lo)
|
||||||
|
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//! Datatypes to support source location information.
|
//! Datatypes to support source location information.
|
||||||
|
use std::cmp::Ordering;
|
||||||
use crate::ast_gen::StrRef;
|
use crate::ast_gen::StrRef;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct FileName(pub StrRef);
|
pub struct FileName(pub StrRef);
|
||||||
impl Default for FileName {
|
impl Default for FileName {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -17,7 +18,7 @@ impl From<String> for FileName {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A location somewhere in the sourcecode.
|
/// A location somewhere in the sourcecode.
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
pub row: usize,
|
pub row: usize,
|
||||||
pub column: 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 {
|
impl Location {
|
||||||
pub fn visualize<'a>(
|
pub fn visualize<'a>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -5,7 +5,7 @@ authors = ["M-Labs"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
itertools = "0.11"
|
itertools = "0.12"
|
||||||
crossbeam = "0.8"
|
crossbeam = "0.8"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
rayon = "1.5"
|
rayon = "1.5"
|
||||||
|
|
|
@ -60,6 +60,10 @@ pub enum ConcreteTypeEnum {
|
||||||
ret: ConcreteType,
|
ret: ConcreteType,
|
||||||
vars: HashMap<u32, ConcreteType>,
|
vars: HashMap<u32, ConcreteType>,
|
||||||
},
|
},
|
||||||
|
TConstant {
|
||||||
|
value: SymbolValue,
|
||||||
|
ty: ConcreteType,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConcreteTypeStore {
|
impl ConcreteTypeStore {
|
||||||
|
@ -198,7 +202,11 @@ impl ConcreteTypeStore {
|
||||||
TypeEnum::TFunc(signature) => {
|
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() {
|
let index = if let Some(ConcreteType(index)) = cache.get(&ty).unwrap() {
|
||||||
self.store[*index] = result;
|
self.store[*index] = result;
|
||||||
|
@ -285,6 +293,11 @@ impl ConcreteTypeStore {
|
||||||
.map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache)))
|
.map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache)))
|
||||||
.collect::<HashMap<_, _>>(),
|
.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);
|
let result = unifier.add_ty(result);
|
||||||
if let Some(ty) = cache.get(&cty).unwrap() {
|
if let Some(ty) = cache.get(&cty).unwrap() {
|
||||||
|
|
|
@ -39,11 +39,10 @@ pub fn get_subst_key(
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut vars = obj
|
let mut vars = obj
|
||||||
.map(|ty| {
|
.map(|ty| {
|
||||||
if let TypeEnum::TObj { params, .. } = &*unifier.get_ty(ty) {
|
let TypeEnum::TObj { params, .. } = &*unifier.get_ty(ty) else {
|
||||||
params.clone()
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
params.clone()
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
vars.extend(fun_vars.iter());
|
vars.extend(fun_vars.iter());
|
||||||
|
@ -224,7 +223,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||||
{
|
{
|
||||||
self.ctx.i64_type()
|
self.ctx.i64_type()
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!()
|
||||||
};
|
};
|
||||||
Some(ty.const_int(*val as u64, false).into())
|
Some(ty.const_int(*val as u64, false).into())
|
||||||
}
|
}
|
||||||
|
@ -599,28 +598,27 @@ pub fn gen_constructor<'ctx, 'a, G: CodeGenerator>(
|
||||||
def: &TopLevelDef,
|
def: &TopLevelDef,
|
||||||
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||||
match def {
|
let TopLevelDef::Class { methods, .. } = def else {
|
||||||
TopLevelDef::Class { methods, .. } => {
|
unreachable!()
|
||||||
// TODO: what about other fields that require alloca?
|
};
|
||||||
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();
|
// TODO: what about other fields that require alloca?
|
||||||
let zelf_ty: BasicTypeEnum = ty.get_element_type().try_into().unwrap();
|
let fun_id = methods.iter().find(|method| method.0 == "__init__".into()).map(|method| method.2);
|
||||||
let zelf: BasicValueEnum<'ctx> = ctx.builder.build_alloca(zelf_ty, "alloca").into();
|
let ty = ctx.get_llvm_type(generator, signature.ret).into_pointer_type();
|
||||||
// call `__init__` if there is one
|
let zelf_ty: BasicTypeEnum = ty.get_element_type().try_into().unwrap();
|
||||||
if let Some(fun_id) = fun_id {
|
let zelf: BasicValueEnum<'ctx> = ctx.builder.build_alloca(zelf_ty, "alloca").into();
|
||||||
let mut sign = signature.clone();
|
// call `__init__` if there is one
|
||||||
sign.ret = ctx.primitives.none;
|
if let Some(fun_id) = fun_id {
|
||||||
generator.gen_call(
|
let mut sign = signature.clone();
|
||||||
ctx,
|
sign.ret = ctx.primitives.none;
|
||||||
Some((signature.ret, zelf.into())),
|
generator.gen_call(
|
||||||
(&sign, fun_id),
|
ctx,
|
||||||
params,
|
Some((signature.ret, zelf.into())),
|
||||||
)?;
|
(&sign, fun_id),
|
||||||
}
|
params,
|
||||||
Ok(zelf)
|
)?;
|
||||||
}
|
|
||||||
TopLevelDef::Function { .. } => unreachable!(),
|
|
||||||
}
|
}
|
||||||
|
Ok(zelf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`CodeGenerator::gen_func_instance`].
|
/// See [`CodeGenerator::gen_func_instance`].
|
||||||
|
@ -630,74 +628,71 @@ pub fn gen_func_instance<'ctx>(
|
||||||
fun: (&FunSignature, &mut TopLevelDef, String),
|
fun: (&FunSignature, &mut TopLevelDef, String),
|
||||||
id: usize,
|
id: usize,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
if let (
|
let (
|
||||||
sign,
|
sign,
|
||||||
TopLevelDef::Function {
|
TopLevelDef::Function {
|
||||||
name, instance_to_symbol, instance_to_stmt, var_id, resolver, ..
|
name, instance_to_symbol, instance_to_stmt, var_id, resolver, ..
|
||||||
},
|
},
|
||||||
key,
|
key,
|
||||||
) = fun
|
) = fun else { unreachable!() };
|
||||||
{
|
|
||||||
if let Some(sym) = instance_to_symbol.get(&key) {
|
|
||||||
return Ok(sym.clone());
|
|
||||||
}
|
|
||||||
let symbol = format!("{}.{}", name, instance_to_symbol.len());
|
|
||||||
instance_to_symbol.insert(key, symbol.clone());
|
|
||||||
let mut filter = var_id.clone();
|
|
||||||
if let Some((obj_ty, _)) = &obj {
|
|
||||||
if let TypeEnum::TObj { params, .. } = &*ctx.unifier.get_ty(*obj_ty) {
|
|
||||||
filter.extend(params.keys());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), sign, Some(&filter));
|
|
||||||
let instance = instance_to_stmt.get(&key).unwrap();
|
|
||||||
|
|
||||||
let mut store = ConcreteTypeStore::new();
|
if let Some(sym) = instance_to_symbol.get(&key) {
|
||||||
let mut cache = HashMap::new();
|
return Ok(sym.clone());
|
||||||
|
|
||||||
let subst = sign
|
|
||||||
.vars
|
|
||||||
.iter()
|
|
||||||
.map(|(id, ty)| {
|
|
||||||
(
|
|
||||||
*instance.subst.get(id).unwrap(),
|
|
||||||
store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, *ty, &mut cache),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut signature =
|
|
||||||
store.from_signature(&mut ctx.unifier, &ctx.primitives, sign, &mut cache);
|
|
||||||
|
|
||||||
if let Some(obj) = &obj {
|
|
||||||
let zelf =
|
|
||||||
store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, obj.0, &mut cache);
|
|
||||||
if let ConcreteTypeEnum::TFunc { args, .. } = &mut signature {
|
|
||||||
args.insert(
|
|
||||||
0,
|
|
||||||
ConcreteFuncArg { name: "self".into(), ty: zelf, default_value: None },
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let signature = store.add_cty(signature);
|
|
||||||
|
|
||||||
ctx.registry.add_task(CodeGenTask {
|
|
||||||
symbol_name: symbol.clone(),
|
|
||||||
body: instance.body.clone(),
|
|
||||||
resolver: resolver.as_ref().unwrap().clone(),
|
|
||||||
calls: instance.calls.clone(),
|
|
||||||
subst,
|
|
||||||
signature,
|
|
||||||
store,
|
|
||||||
unifier_index: instance.unifier_id,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
Ok(symbol)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
|
let symbol = format!("{}.{}", name, instance_to_symbol.len());
|
||||||
|
instance_to_symbol.insert(key, symbol.clone());
|
||||||
|
let mut filter = var_id.clone();
|
||||||
|
if let Some((obj_ty, _)) = &obj {
|
||||||
|
if let TypeEnum::TObj { params, .. } = &*ctx.unifier.get_ty(*obj_ty) {
|
||||||
|
filter.extend(params.keys());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), sign, Some(&filter));
|
||||||
|
let instance = instance_to_stmt.get(&key).unwrap();
|
||||||
|
|
||||||
|
let mut store = ConcreteTypeStore::new();
|
||||||
|
let mut cache = HashMap::new();
|
||||||
|
|
||||||
|
let subst = sign
|
||||||
|
.vars
|
||||||
|
.iter()
|
||||||
|
.map(|(id, ty)| {
|
||||||
|
(
|
||||||
|
*instance.subst.get(id).unwrap(),
|
||||||
|
store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, *ty, &mut cache),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut signature =
|
||||||
|
store.from_signature(&mut ctx.unifier, &ctx.primitives, sign, &mut cache);
|
||||||
|
|
||||||
|
if let Some(obj) = &obj {
|
||||||
|
let zelf =
|
||||||
|
store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, obj.0, &mut cache);
|
||||||
|
let ConcreteTypeEnum::TFunc { args, .. } = &mut signature else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
args.insert(
|
||||||
|
0,
|
||||||
|
ConcreteFuncArg { name: "self".into(), ty: zelf, default_value: None },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let signature = store.add_cty(signature);
|
||||||
|
|
||||||
|
ctx.registry.add_task(CodeGenTask {
|
||||||
|
symbol_name: symbol.clone(),
|
||||||
|
body: instance.body.clone(),
|
||||||
|
resolver: resolver.as_ref().unwrap().clone(),
|
||||||
|
calls: instance.calls.clone(),
|
||||||
|
subst,
|
||||||
|
signature,
|
||||||
|
store,
|
||||||
|
unifier_index: instance.unifier_id,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
Ok(symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`CodeGenerator::gen_call`].
|
/// See [`CodeGenerator::gen_call`].
|
||||||
|
@ -946,172 +941,172 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
expr: &Expr<Option<Type>>,
|
expr: &Expr<Option<Type>>,
|
||||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||||
if let ExprKind::ListComp { elt, generators } = &expr.node {
|
let ExprKind::ListComp { elt, generators } = &expr.node else {
|
||||||
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
let init_bb = ctx.ctx.append_basic_block(current, "listcomp.init");
|
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||||
let test_bb = ctx.ctx.append_basic_block(current, "listcomp.test");
|
|
||||||
let body_bb = ctx.ctx.append_basic_block(current, "listcomp.body");
|
|
||||||
let cont_bb = ctx.ctx.append_basic_block(current, "listcomp.cont");
|
|
||||||
|
|
||||||
ctx.builder.build_unconditional_branch(init_bb);
|
let init_bb = ctx.ctx.append_basic_block(current, "listcomp.init");
|
||||||
|
let test_bb = ctx.ctx.append_basic_block(current, "listcomp.test");
|
||||||
|
let body_bb = ctx.ctx.append_basic_block(current, "listcomp.body");
|
||||||
|
let cont_bb = ctx.ctx.append_basic_block(current, "listcomp.cont");
|
||||||
|
|
||||||
ctx.builder.position_at_end(init_bb);
|
ctx.builder.build_unconditional_branch(init_bb);
|
||||||
|
|
||||||
let Comprehension { target, iter, ifs, .. } = &generators[0];
|
ctx.builder.position_at_end(init_bb);
|
||||||
let iter_val = if let Some(v) = generator.gen_expr(ctx, iter)? {
|
|
||||||
v.to_basic_value_enum(ctx, generator, iter.custom.unwrap())?
|
|
||||||
} else {
|
|
||||||
for bb in [test_bb, body_bb, cont_bb] {
|
|
||||||
ctx.builder.position_at_end(bb);
|
|
||||||
ctx.builder.build_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(None)
|
let Comprehension { target, iter, ifs, .. } = &generators[0];
|
||||||
};
|
let iter_val = if let Some(v) = generator.gen_expr(ctx, iter)? {
|
||||||
let int32 = ctx.ctx.i32_type();
|
v.to_basic_value_enum(ctx, generator, iter.custom.unwrap())?
|
||||||
let size_t = generator.get_size_type(ctx.ctx);
|
} else {
|
||||||
let zero_size_t = size_t.const_zero();
|
for bb in [test_bb, body_bb, cont_bb] {
|
||||||
let zero_32 = int32.const_zero();
|
ctx.builder.position_at_end(bb);
|
||||||
|
ctx.builder.build_unreachable();
|
||||||
let index = generator.gen_var_alloc(ctx, size_t.into(), Some("index.addr"))?;
|
|
||||||
ctx.builder.build_store(index, zero_size_t);
|
|
||||||
|
|
||||||
let elem_ty = ctx.get_llvm_type(generator, elt.custom.unwrap());
|
|
||||||
let is_range = ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range);
|
|
||||||
let list;
|
|
||||||
let list_content;
|
|
||||||
|
|
||||||
if is_range {
|
|
||||||
let iter_val = iter_val.into_pointer_value();
|
|
||||||
let (start, stop, step) = destructure_range(ctx, iter_val);
|
|
||||||
let diff = ctx.builder.build_int_sub(stop, start, "diff");
|
|
||||||
// add 1 to the length as the value is rounded to zero
|
|
||||||
// the length may be 1 more than the actual length if the division is exact, but the
|
|
||||||
// length is a upper bound only anyway so it does not matter.
|
|
||||||
let length = ctx.builder.build_int_signed_div(diff, step, "div");
|
|
||||||
let length = ctx.builder.build_int_add(length, int32.const_int(1, false), "add1");
|
|
||||||
// in case length is non-positive
|
|
||||||
let is_valid =
|
|
||||||
ctx.builder.build_int_compare(IntPredicate::SGT, length, zero_32, "check");
|
|
||||||
|
|
||||||
let list_alloc_size = ctx.builder.build_select(
|
|
||||||
is_valid,
|
|
||||||
ctx.builder.build_int_z_extend_or_bit_cast(length, size_t, "z_ext_len"),
|
|
||||||
zero_size_t,
|
|
||||||
"listcomp.alloc_size"
|
|
||||||
);
|
|
||||||
list = allocate_list(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
elem_ty,
|
|
||||||
list_alloc_size.into_int_value(),
|
|
||||||
Some("listcomp.addr")
|
|
||||||
);
|
|
||||||
list_content = ctx.build_gep_and_load(list, &[zero_size_t, zero_32], Some("listcomp.data.addr"))
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap();
|
|
||||||
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
|
|
||||||
|
|
||||||
ctx.builder.build_conditional_branch(
|
|
||||||
gen_in_range_check(ctx, start, stop, step),
|
|
||||||
test_bb,
|
|
||||||
cont_bb,
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.builder.position_at_end(test_bb);
|
|
||||||
// add and test
|
|
||||||
let tmp = ctx.builder.build_int_add(
|
|
||||||
ctx.builder.build_load(i, "i").into_int_value(),
|
|
||||||
step,
|
|
||||||
"start_loop",
|
|
||||||
);
|
|
||||||
ctx.builder.build_store(i, tmp);
|
|
||||||
ctx.builder.build_conditional_branch(
|
|
||||||
gen_in_range_check(ctx, tmp, stop, step),
|
|
||||||
body_bb,
|
|
||||||
cont_bb,
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.builder.position_at_end(body_bb);
|
|
||||||
} else {
|
|
||||||
let length = ctx
|
|
||||||
.build_gep_and_load(
|
|
||||||
iter_val.into_pointer_value(),
|
|
||||||
&[zero_size_t, int32.const_int(1, false)],
|
|
||||||
Some("length"),
|
|
||||||
)
|
|
||||||
.into_int_value();
|
|
||||||
list = allocate_list(generator, ctx, elem_ty, length, Some("listcomp"));
|
|
||||||
list_content =
|
|
||||||
ctx.build_gep_and_load(list, &[zero_size_t, zero_32], Some("list_content")).into_pointer_value();
|
|
||||||
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
|
||||||
// counter = -1
|
|
||||||
ctx.builder.build_store(counter, size_t.const_int(u64::MAX, true));
|
|
||||||
ctx.builder.build_unconditional_branch(test_bb);
|
|
||||||
|
|
||||||
ctx.builder.position_at_end(test_bb);
|
|
||||||
let tmp = ctx.builder.build_load(counter, "i").into_int_value();
|
|
||||||
let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc");
|
|
||||||
ctx.builder.build_store(counter, tmp);
|
|
||||||
let cmp = ctx.builder.build_int_compare(IntPredicate::SLT, tmp, length, "cmp");
|
|
||||||
ctx.builder.build_conditional_branch(cmp, body_bb, cont_bb);
|
|
||||||
|
|
||||||
ctx.builder.position_at_end(body_bb);
|
|
||||||
let arr_ptr = ctx
|
|
||||||
.build_gep_and_load(iter_val.into_pointer_value(), &[zero_size_t, zero_32], Some("arr.addr"))
|
|
||||||
.into_pointer_value();
|
|
||||||
let val = ctx.build_gep_and_load(arr_ptr, &[tmp], Some("val"));
|
|
||||||
generator.gen_assign(ctx, target, val.into())?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emits the content of `cont_bb`
|
return Ok(None)
|
||||||
let emit_cont_bb = |ctx: &CodeGenContext| {
|
};
|
||||||
ctx.builder.position_at_end(cont_bb);
|
let int32 = ctx.ctx.i32_type();
|
||||||
let len_ptr = unsafe {
|
let size_t = generator.get_size_type(ctx.ctx);
|
||||||
ctx.builder.build_gep(list, &[zero_size_t, int32.const_int(1, false)], "length")
|
let zero_size_t = size_t.const_zero();
|
||||||
};
|
let zero_32 = int32.const_zero();
|
||||||
ctx.builder.build_store(len_ptr, ctx.builder.build_load(index, "index"));
|
|
||||||
|
let index = generator.gen_var_alloc(ctx, size_t.into(), Some("index.addr"))?;
|
||||||
|
ctx.builder.build_store(index, zero_size_t);
|
||||||
|
|
||||||
|
let elem_ty = ctx.get_llvm_type(generator, elt.custom.unwrap());
|
||||||
|
let is_range = ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range);
|
||||||
|
let list;
|
||||||
|
let list_content;
|
||||||
|
|
||||||
|
if is_range {
|
||||||
|
let iter_val = iter_val.into_pointer_value();
|
||||||
|
let (start, stop, step) = destructure_range(ctx, iter_val);
|
||||||
|
let diff = ctx.builder.build_int_sub(stop, start, "diff");
|
||||||
|
// add 1 to the length as the value is rounded to zero
|
||||||
|
// the length may be 1 more than the actual length if the division is exact, but the
|
||||||
|
// length is a upper bound only anyway so it does not matter.
|
||||||
|
let length = ctx.builder.build_int_signed_div(diff, step, "div");
|
||||||
|
let length = ctx.builder.build_int_add(length, int32.const_int(1, false), "add1");
|
||||||
|
// in case length is non-positive
|
||||||
|
let is_valid =
|
||||||
|
ctx.builder.build_int_compare(IntPredicate::SGT, length, zero_32, "check");
|
||||||
|
|
||||||
|
let list_alloc_size = ctx.builder.build_select(
|
||||||
|
is_valid,
|
||||||
|
ctx.builder.build_int_z_extend_or_bit_cast(length, size_t, "z_ext_len"),
|
||||||
|
zero_size_t,
|
||||||
|
"listcomp.alloc_size"
|
||||||
|
);
|
||||||
|
list = allocate_list(
|
||||||
|
generator,
|
||||||
|
ctx,
|
||||||
|
elem_ty,
|
||||||
|
list_alloc_size.into_int_value(),
|
||||||
|
Some("listcomp.addr")
|
||||||
|
);
|
||||||
|
list_content = ctx.build_gep_and_load(list, &[zero_size_t, zero_32], Some("listcomp.data.addr"))
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap();
|
||||||
|
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
|
||||||
|
|
||||||
|
ctx.builder.build_conditional_branch(
|
||||||
|
gen_in_range_check(ctx, start, stop, step),
|
||||||
|
test_bb,
|
||||||
|
cont_bb,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(test_bb);
|
||||||
|
// add and test
|
||||||
|
let tmp = ctx.builder.build_int_add(
|
||||||
|
ctx.builder.build_load(i, "i").into_int_value(),
|
||||||
|
step,
|
||||||
|
"start_loop",
|
||||||
|
);
|
||||||
|
ctx.builder.build_store(i, tmp);
|
||||||
|
ctx.builder.build_conditional_branch(
|
||||||
|
gen_in_range_check(ctx, tmp, stop, step),
|
||||||
|
body_bb,
|
||||||
|
cont_bb,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(body_bb);
|
||||||
|
} else {
|
||||||
|
let length = ctx
|
||||||
|
.build_gep_and_load(
|
||||||
|
iter_val.into_pointer_value(),
|
||||||
|
&[zero_size_t, int32.const_int(1, false)],
|
||||||
|
Some("length"),
|
||||||
|
)
|
||||||
|
.into_int_value();
|
||||||
|
list = allocate_list(generator, ctx, elem_ty, length, Some("listcomp"));
|
||||||
|
list_content =
|
||||||
|
ctx.build_gep_and_load(list, &[zero_size_t, zero_32], Some("list_content")).into_pointer_value();
|
||||||
|
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
||||||
|
// counter = -1
|
||||||
|
ctx.builder.build_store(counter, size_t.const_int(u64::MAX, true));
|
||||||
|
ctx.builder.build_unconditional_branch(test_bb);
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(test_bb);
|
||||||
|
let tmp = ctx.builder.build_load(counter, "i").into_int_value();
|
||||||
|
let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc");
|
||||||
|
ctx.builder.build_store(counter, tmp);
|
||||||
|
let cmp = ctx.builder.build_int_compare(IntPredicate::SLT, tmp, length, "cmp");
|
||||||
|
ctx.builder.build_conditional_branch(cmp, body_bb, cont_bb);
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(body_bb);
|
||||||
|
let arr_ptr = ctx
|
||||||
|
.build_gep_and_load(iter_val.into_pointer_value(), &[zero_size_t, zero_32], Some("arr.addr"))
|
||||||
|
.into_pointer_value();
|
||||||
|
let val = ctx.build_gep_and_load(arr_ptr, &[tmp], Some("val"));
|
||||||
|
generator.gen_assign(ctx, target, val.into())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emits the content of `cont_bb`
|
||||||
|
let emit_cont_bb = |ctx: &CodeGenContext| {
|
||||||
|
ctx.builder.position_at_end(cont_bb);
|
||||||
|
let len_ptr = unsafe {
|
||||||
|
ctx.builder.build_gep(list, &[zero_size_t, int32.const_int(1, false)], "length")
|
||||||
};
|
};
|
||||||
|
ctx.builder.build_store(len_ptr, ctx.builder.build_load(index, "index"));
|
||||||
|
};
|
||||||
|
|
||||||
for cond in ifs {
|
for cond in ifs {
|
||||||
let result = if let Some(v) = generator.gen_expr(ctx, cond)? {
|
let result = if let Some(v) = generator.gen_expr(ctx, cond)? {
|
||||||
v.to_basic_value_enum(ctx, generator, cond.custom.unwrap())?.into_int_value()
|
v.to_basic_value_enum(ctx, generator, cond.custom.unwrap())?.into_int_value()
|
||||||
} else {
|
} else {
|
||||||
// Bail if the predicate is an ellipsis - Emit cont_bb contents in case the
|
// Bail if the predicate is an ellipsis - Emit cont_bb contents in case the
|
||||||
// no element matches the predicate
|
// no element matches the predicate
|
||||||
emit_cont_bb(ctx);
|
|
||||||
|
|
||||||
return Ok(None)
|
|
||||||
};
|
|
||||||
let result = generator.bool_to_i1(ctx, result);
|
|
||||||
let succ = ctx.ctx.append_basic_block(current, "then");
|
|
||||||
ctx.builder.build_conditional_branch(result, succ, test_bb);
|
|
||||||
|
|
||||||
ctx.builder.position_at_end(succ);
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(elem) = generator.gen_expr(ctx, elt)? else {
|
|
||||||
// Similarly, bail if the generator expression is an ellipsis, but keep cont_bb contents
|
|
||||||
emit_cont_bb(ctx);
|
emit_cont_bb(ctx);
|
||||||
|
|
||||||
return Ok(None)
|
return Ok(None)
|
||||||
};
|
};
|
||||||
let i = ctx.builder.build_load(index, "i").into_int_value();
|
let result = generator.bool_to_i1(ctx, result);
|
||||||
let elem_ptr = unsafe { ctx.builder.build_gep(list_content, &[i], "elem_ptr") };
|
let succ = ctx.ctx.append_basic_block(current, "then");
|
||||||
let val = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
|
ctx.builder.build_conditional_branch(result, succ, test_bb);
|
||||||
ctx.builder.build_store(elem_ptr, val);
|
|
||||||
ctx.builder
|
|
||||||
.build_store(index, ctx.builder.build_int_add(i, size_t.const_int(1, false), "inc"));
|
|
||||||
ctx.builder.build_unconditional_branch(test_bb);
|
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(succ);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(elem) = generator.gen_expr(ctx, elt)? else {
|
||||||
|
// Similarly, bail if the generator expression is an ellipsis, but keep cont_bb contents
|
||||||
emit_cont_bb(ctx);
|
emit_cont_bb(ctx);
|
||||||
|
|
||||||
Ok(Some(list.into()))
|
return Ok(None)
|
||||||
} else {
|
};
|
||||||
unreachable!()
|
let i = ctx.builder.build_load(index, "i").into_int_value();
|
||||||
}
|
let elem_ptr = unsafe { ctx.builder.build_gep(list_content, &[i], "elem_ptr") };
|
||||||
|
let val = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
|
||||||
|
ctx.builder.build_store(elem_ptr, val);
|
||||||
|
ctx.builder
|
||||||
|
.build_store(index, ctx.builder.build_int_add(i, size_t.const_int(1, false), "inc"));
|
||||||
|
ctx.builder.build_unconditional_branch(test_bb);
|
||||||
|
|
||||||
|
emit_cont_bb(ctx);
|
||||||
|
|
||||||
|
Ok(Some(list.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates LLVM IR for a [binary operator expression][expr].
|
/// Generates LLVM IR for a [binary operator expression][expr].
|
||||||
|
@ -1170,9 +1165,11 @@ pub fn gen_binop_expr<'ctx, G: CodeGenerator>(
|
||||||
.unwrap_left();
|
.unwrap_left();
|
||||||
Ok(Some(res.into()))
|
Ok(Some(res.into()))
|
||||||
} else {
|
} else {
|
||||||
let (op_name, id) = if let TypeEnum::TObj { fields, obj_id, .. } =
|
let left_ty_enum = ctx.unifier.get_ty_immutable(left.custom.unwrap());
|
||||||
ctx.unifier.get_ty_immutable(left.custom.unwrap()).as_ref()
|
let TypeEnum::TObj { fields, obj_id, .. } = left_ty_enum.as_ref() else {
|
||||||
{
|
unreachable!("must be tobj")
|
||||||
|
};
|
||||||
|
let (op_name, id) = {
|
||||||
let (binop_name, binop_assign_name) = (
|
let (binop_name, binop_assign_name) = (
|
||||||
binop_name(op).into(),
|
binop_name(op).into(),
|
||||||
binop_assign_name(op).into()
|
binop_assign_name(op).into()
|
||||||
|
@ -1183,34 +1180,33 @@ pub fn gen_binop_expr<'ctx, G: CodeGenerator>(
|
||||||
} else {
|
} else {
|
||||||
(binop_name, *obj_id)
|
(binop_name, *obj_id)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
unreachable!("must be tobj")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let signature = match ctx.calls.get(&loc.into()) {
|
let signature = match ctx.calls.get(&loc.into()) {
|
||||||
Some(call) => ctx.unifier.get_call_signature(*call).unwrap(),
|
Some(call) => ctx.unifier.get_call_signature(*call).unwrap(),
|
||||||
None => {
|
None => {
|
||||||
if let TypeEnum::TObj { fields, .. } =
|
let left_enum_ty = ctx.unifier.get_ty_immutable(left.custom.unwrap());
|
||||||
ctx.unifier.get_ty_immutable(left.custom.unwrap()).as_ref()
|
let TypeEnum::TObj { fields, .. } = left_enum_ty.as_ref() else {
|
||||||
{
|
|
||||||
let fn_ty = fields.get(&op_name).unwrap().0;
|
|
||||||
if let TypeEnum::TFunc(sig) = ctx.unifier.get_ty_immutable(fn_ty).as_ref() {
|
|
||||||
sig.clone()
|
|
||||||
} else {
|
|
||||||
unreachable!("must be func sig")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!("must be tobj")
|
unreachable!("must be tobj")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let fn_ty = fields.get(&op_name).unwrap().0;
|
||||||
|
let fn_ty_enum = ctx.unifier.get_ty_immutable(fn_ty);
|
||||||
|
let TypeEnum::TFunc(sig) = fn_ty_enum.as_ref() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
sig.clone()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let fun_id = {
|
let fun_id = {
|
||||||
let defs = ctx.top_level.definitions.read();
|
let defs = ctx.top_level.definitions.read();
|
||||||
let obj_def = defs.get(id.0).unwrap().read();
|
let obj_def = defs.get(id.0).unwrap().read();
|
||||||
if let TopLevelDef::Class { methods, .. } = &*obj_def {
|
let TopLevelDef::Class { methods, .. } = &*obj_def else {
|
||||||
methods.iter().find(|method| method.0 == op_name).unwrap().2
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
methods.iter().find(|method| method.0 == op_name).unwrap().2
|
||||||
};
|
};
|
||||||
generator
|
generator
|
||||||
.gen_call(
|
.gen_call(
|
||||||
|
@ -1290,11 +1286,11 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let ty = if elements.is_empty() {
|
let ty = if elements.is_empty() {
|
||||||
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(expr.custom.unwrap()) {
|
let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(expr.custom.unwrap()) else {
|
||||||
ctx.get_llvm_type(generator, *ty)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
ctx.get_llvm_type(generator, *ty)
|
||||||
} else {
|
} else {
|
||||||
elements[0].get_type()
|
elements[0].get_type()
|
||||||
};
|
};
|
||||||
|
@ -1636,11 +1632,11 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
ctx.unifier.get_call_signature(*call).unwrap()
|
ctx.unifier.get_call_signature(*call).unwrap()
|
||||||
} else {
|
} else {
|
||||||
let ty = func.custom.unwrap();
|
let ty = func.custom.unwrap();
|
||||||
if let TypeEnum::TFunc(sign) = &*ctx.unifier.get_ty(ty) {
|
let TypeEnum::TFunc(sign) = &*ctx.unifier.get_ty(ty) else {
|
||||||
sign.clone()
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
sign.clone()
|
||||||
};
|
};
|
||||||
let func = func.as_ref();
|
let func = func.as_ref();
|
||||||
match &func.node {
|
match &func.node {
|
||||||
|
@ -1649,7 +1645,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
let fun = ctx
|
let fun = ctx
|
||||||
.resolver
|
.resolver
|
||||||
.get_identifier_def(*id)
|
.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
|
return Ok(generator
|
||||||
.gen_call(ctx, None, (&signature, fun), params)?
|
.gen_call(ctx, None, (&signature, fun), params)?
|
||||||
.map(Into::into));
|
.map(Into::into));
|
||||||
|
@ -1669,11 +1665,11 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
let fun_id = {
|
let fun_id = {
|
||||||
let defs = ctx.top_level.definitions.read();
|
let defs = ctx.top_level.definitions.read();
|
||||||
let obj_def = defs.get(id.0).unwrap().read();
|
let obj_def = defs.get(id.0).unwrap().read();
|
||||||
if let TopLevelDef::Class { methods, .. } = &*obj_def {
|
let TopLevelDef::Class { methods, .. } = &*obj_def else {
|
||||||
methods.iter().find(|method| method.0 == *attr).unwrap().2
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
methods.iter().find(|method| method.0 == *attr).unwrap().2
|
||||||
};
|
};
|
||||||
// directly generate code for option.unwrap
|
// directly generate code for option.unwrap
|
||||||
// since it needs to return static value to optimize for kernel invariant
|
// since it needs to return static value to optimize for kernel invariant
|
||||||
|
@ -1755,125 +1751,127 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Subscript { value, slice, .. } => {
|
ExprKind::Subscript { value, slice, .. } => {
|
||||||
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
match &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||||
let v = if let Some(v) = generator.gen_expr(ctx, value)? {
|
TypeEnum::TList { ty } => {
|
||||||
v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?.into_pointer_value()
|
let v = if let Some(v) = generator.gen_expr(ctx, value)? {
|
||||||
} else {
|
v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?.into_pointer_value()
|
||||||
return Ok(None)
|
|
||||||
};
|
|
||||||
let ty = ctx.get_llvm_type(generator, *ty);
|
|
||||||
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero], Some("arr.addr"))
|
|
||||||
.into_pointer_value();
|
|
||||||
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
|
||||||
let one = int32.const_int(1, false);
|
|
||||||
let Some((start, end, step)) =
|
|
||||||
handle_slice_indices(lower, upper, step, ctx, generator, v)? else {
|
|
||||||
return Ok(None)
|
|
||||||
};
|
|
||||||
let length = calculate_len_for_slice_range(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
start,
|
|
||||||
ctx.builder
|
|
||||||
.build_select(
|
|
||||||
ctx.builder.build_int_compare(
|
|
||||||
IntPredicate::SLT,
|
|
||||||
step,
|
|
||||||
zero,
|
|
||||||
"is_neg",
|
|
||||||
),
|
|
||||||
ctx.builder.build_int_sub(end, one, "e_min_one"),
|
|
||||||
ctx.builder.build_int_add(end, one, "e_add_one"),
|
|
||||||
"final_e",
|
|
||||||
)
|
|
||||||
.into_int_value(),
|
|
||||||
step,
|
|
||||||
);
|
|
||||||
let res_array_ret = allocate_list(generator, ctx, ty, length, Some("ret"));
|
|
||||||
let Some(res_ind) =
|
|
||||||
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)? else {
|
|
||||||
return Ok(None)
|
|
||||||
};
|
|
||||||
list_slice_assignment(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
ty,
|
|
||||||
res_array_ret,
|
|
||||||
res_ind,
|
|
||||||
v,
|
|
||||||
(start, end, step),
|
|
||||||
);
|
|
||||||
res_array_ret.into()
|
|
||||||
} else {
|
|
||||||
let len = ctx
|
|
||||||
.build_gep_and_load(v, &[zero, int32.const_int(1, false)], Some("len"))
|
|
||||||
.into_int_value();
|
|
||||||
let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? {
|
|
||||||
v.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?.into_int_value()
|
|
||||||
} else {
|
} else {
|
||||||
return Ok(None)
|
return Ok(None)
|
||||||
};
|
};
|
||||||
let raw_index = ctx.builder.build_int_s_extend(
|
let ty = ctx.get_llvm_type(generator, *ty);
|
||||||
raw_index,
|
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero], Some("arr.addr"))
|
||||||
generator.get_size_type(ctx.ctx),
|
.into_pointer_value();
|
||||||
"sext",
|
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
||||||
);
|
let one = int32.const_int(1, false);
|
||||||
// handle negative index
|
let Some((start, end, step)) =
|
||||||
let is_negative = ctx.builder.build_int_compare(
|
handle_slice_indices(lower, upper, step, ctx, generator, v)? else {
|
||||||
IntPredicate::SLT,
|
return Ok(None)
|
||||||
raw_index,
|
};
|
||||||
generator.get_size_type(ctx.ctx).const_zero(),
|
let length = calculate_len_for_slice_range(
|
||||||
"is_neg",
|
generator,
|
||||||
);
|
ctx,
|
||||||
let adjusted = ctx.builder.build_int_add(raw_index, len, "adjusted");
|
start,
|
||||||
let index = ctx
|
ctx.builder
|
||||||
.builder
|
.build_select(
|
||||||
.build_select(is_negative, adjusted, raw_index, "index")
|
ctx.builder.build_int_compare(
|
||||||
.into_int_value();
|
IntPredicate::SLT,
|
||||||
// unsigned less than is enough, because negative index after adjustment is
|
step,
|
||||||
// bigger than the length (for unsigned cmp)
|
zero,
|
||||||
let bound_check = ctx.builder.build_int_compare(
|
"is_neg",
|
||||||
IntPredicate::ULT,
|
),
|
||||||
index,
|
ctx.builder.build_int_sub(end, one, "e_min_one"),
|
||||||
len,
|
ctx.builder.build_int_add(end, one, "e_add_one"),
|
||||||
"inbound",
|
"final_e",
|
||||||
);
|
)
|
||||||
ctx.make_assert(
|
.into_int_value(),
|
||||||
generator,
|
step,
|
||||||
bound_check,
|
);
|
||||||
"0:IndexError",
|
let res_array_ret = allocate_list(generator, ctx, ty, length, Some("ret"));
|
||||||
"index {0} out of bounds 0:{1}",
|
let Some(res_ind) =
|
||||||
[Some(raw_index), Some(len), None],
|
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)? else {
|
||||||
expr.location,
|
return Ok(None)
|
||||||
);
|
};
|
||||||
ctx.build_gep_and_load(arr_ptr, &[index], None).into()
|
list_slice_assignment(
|
||||||
}
|
generator,
|
||||||
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
ctx,
|
||||||
let index: u32 =
|
ty,
|
||||||
if let ExprKind::Constant { value: Constant::Int(v), .. } = &slice.node {
|
res_array_ret,
|
||||||
(*v).try_into().unwrap()
|
res_ind,
|
||||||
|
v,
|
||||||
|
(start, end, step),
|
||||||
|
);
|
||||||
|
res_array_ret.into()
|
||||||
} else {
|
} else {
|
||||||
unreachable!("tuple subscript must be const int after type check");
|
let len = ctx
|
||||||
};
|
.build_gep_and_load(v, &[zero, int32.const_int(1, false)], Some("len"))
|
||||||
match generator.gen_expr(ctx, value)? {
|
.into_int_value();
|
||||||
Some(ValueEnum::Dynamic(v)) => {
|
let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? {
|
||||||
let v = v.into_struct_value();
|
v.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?.into_int_value()
|
||||||
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap().into()
|
|
||||||
}
|
|
||||||
Some(ValueEnum::Static(v)) => {
|
|
||||||
if let Some(v) = v.get_tuple_element(index) {
|
|
||||||
v
|
|
||||||
} else {
|
} else {
|
||||||
let tup = v
|
return Ok(None)
|
||||||
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
};
|
||||||
.into_struct_value();
|
let raw_index = ctx.builder.build_int_s_extend(
|
||||||
ctx.builder.build_extract_value(tup, index, "tup_elem").unwrap().into()
|
raw_index,
|
||||||
}
|
generator.get_size_type(ctx.ctx),
|
||||||
|
"sext",
|
||||||
|
);
|
||||||
|
// handle negative index
|
||||||
|
let is_negative = ctx.builder.build_int_compare(
|
||||||
|
IntPredicate::SLT,
|
||||||
|
raw_index,
|
||||||
|
generator.get_size_type(ctx.ctx).const_zero(),
|
||||||
|
"is_neg",
|
||||||
|
);
|
||||||
|
let adjusted = ctx.builder.build_int_add(raw_index, len, "adjusted");
|
||||||
|
let index = ctx
|
||||||
|
.builder
|
||||||
|
.build_select(is_negative, adjusted, raw_index, "index")
|
||||||
|
.into_int_value();
|
||||||
|
// unsigned less than is enough, because negative index after adjustment is
|
||||||
|
// bigger than the length (for unsigned cmp)
|
||||||
|
let bound_check = ctx.builder.build_int_compare(
|
||||||
|
IntPredicate::ULT,
|
||||||
|
index,
|
||||||
|
len,
|
||||||
|
"inbound",
|
||||||
|
);
|
||||||
|
ctx.make_assert(
|
||||||
|
generator,
|
||||||
|
bound_check,
|
||||||
|
"0:IndexError",
|
||||||
|
"index {0} out of bounds 0:{1}",
|
||||||
|
[Some(raw_index), Some(len), None],
|
||||||
|
expr.location,
|
||||||
|
);
|
||||||
|
ctx.build_gep_and_load(arr_ptr, &[index], None).into()
|
||||||
}
|
}
|
||||||
None => return Ok(None),
|
|
||||||
}
|
}
|
||||||
} else {
|
TypeEnum::TTuple { .. } => {
|
||||||
unreachable!("should not be other subscriptable types after type check");
|
let index: u32 =
|
||||||
|
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");
|
||||||
|
};
|
||||||
|
match generator.gen_expr(ctx, value)? {
|
||||||
|
Some(ValueEnum::Dynamic(v)) => {
|
||||||
|
let v = v.into_struct_value();
|
||||||
|
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap().into()
|
||||||
|
}
|
||||||
|
Some(ValueEnum::Static(v)) => {
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("should not be other subscriptable types after type check"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprKind::ListComp { .. } => {
|
ExprKind::ListComp { .. } => {
|
||||||
|
|
|
@ -451,40 +451,38 @@ fn get_llvm_type<'ctx>(
|
||||||
// a struct with fields in the order of declaration
|
// a struct with fields in the order of declaration
|
||||||
let top_level_defs = top_level.definitions.read();
|
let top_level_defs = top_level.definitions.read();
|
||||||
let definition = top_level_defs.get(obj_id.0).unwrap();
|
let definition = top_level_defs.get(obj_id.0).unwrap();
|
||||||
let ty = if let TopLevelDef::Class { fields: fields_list, .. } =
|
let TopLevelDef::Class { fields: fields_list, .. } = &*definition.read() else {
|
||||||
&*definition.read()
|
|
||||||
{
|
|
||||||
let name = unifier.stringify(ty);
|
|
||||||
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!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
return ty;
|
|
||||||
|
let name = unifier.stringify(ty);
|
||||||
|
let ty = 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()
|
||||||
|
};
|
||||||
|
return ty
|
||||||
}
|
}
|
||||||
TTuple { ty } => {
|
TTuple { ty } => {
|
||||||
// a struct with fields in the order present in the tuple
|
// a struct with fields in the order present in the tuple
|
||||||
|
@ -661,22 +659,21 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
||||||
// NOTE: special handling of option cannot use this type cache since it contains type var,
|
// NOTE: special handling of option cannot use this type cache since it contains type var,
|
||||||
// handled inside get_llvm_type instead
|
// handled inside get_llvm_type instead
|
||||||
|
|
||||||
let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } =
|
let ConcreteTypeEnum::TFunc { args, ret, .. } =
|
||||||
task.store.get(task.signature)
|
task.store.get(task.signature) else {
|
||||||
{
|
|
||||||
(
|
|
||||||
args.iter()
|
|
||||||
.map(|arg| FuncArg {
|
|
||||||
name: arg.name,
|
|
||||||
ty: task.store.to_unifier_type(&mut unifier, &primitives, arg.ty, &mut cache),
|
|
||||||
default_value: arg.default_value.clone(),
|
|
||||||
})
|
|
||||||
.collect_vec(),
|
|
||||||
task.store.to_unifier_type(&mut unifier, &primitives, *ret, &mut cache),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (args, ret) = (
|
||||||
|
args.iter()
|
||||||
|
.map(|arg| FuncArg {
|
||||||
|
name: arg.name,
|
||||||
|
ty: task.store.to_unifier_type(&mut unifier, &primitives, arg.ty, &mut cache),
|
||||||
|
default_value: arg.default_value.clone(),
|
||||||
|
})
|
||||||
|
.collect_vec(),
|
||||||
|
task.store.to_unifier_type(&mut unifier, &primitives, *ret, &mut cache),
|
||||||
|
);
|
||||||
let ret_type = if unifier.unioned(ret, primitives.none) {
|
let ret_type = if unifier.unioned(ret, primitives.none) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -63,12 +63,14 @@ impl SymbolResolver for Resolver {
|
||||||
unimplemented!()
|
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
|
self.id_to_def
|
||||||
.read()
|
.read()
|
||||||
.get(&id)
|
.get(&id)
|
||||||
.cloned()
|
.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 {
|
fn get_string_id(&self, _: &str) -> i32 {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{collections::HashMap, fmt::Display};
|
use std::{collections::HashMap, collections::HashSet, fmt::Display};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::typecheck::typedef::TypeEnum;
|
use crate::typecheck::typedef::TypeEnum;
|
||||||
|
@ -296,7 +296,7 @@ pub trait SymbolResolver {
|
||||||
) -> Result<Type, String>;
|
) -> Result<Type, String>;
|
||||||
|
|
||||||
/// Get the top-level definition of identifiers.
|
/// Get the top-level definition of identifiers.
|
||||||
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, String>;
|
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, HashSet<String>>;
|
||||||
|
|
||||||
fn get_symbol_value<'ctx>(
|
fn get_symbol_value<'ctx>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -341,7 +341,7 @@ pub fn parse_type_annotation<T>(
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
primitives: &PrimitiveStore,
|
primitives: &PrimitiveStore,
|
||||||
expr: &Expr<T>,
|
expr: &Expr<T>,
|
||||||
) -> Result<Type, String> {
|
) -> Result<Type, HashSet<String>> {
|
||||||
use nac3parser::ast::ExprKind::*;
|
use nac3parser::ast::ExprKind::*;
|
||||||
let ids = IDENTIFIER_ID.with(|ids| *ids);
|
let ids = IDENTIFIER_ID.with(|ids| *ids);
|
||||||
let int32_id = ids[0];
|
let int32_id = ids[0];
|
||||||
|
@ -379,10 +379,12 @@ pub fn parse_type_annotation<T>(
|
||||||
let def = top_level_defs[obj_id.0].read();
|
let def = top_level_defs[obj_id.0].read();
|
||||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||||
if !type_vars.is_empty() {
|
if !type_vars.is_empty() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Unexpected number of type parameters: expected {} but got 0",
|
format!(
|
||||||
type_vars.len()
|
"Unexpected number of type parameters: expected {} but got 0",
|
||||||
));
|
type_vars.len()
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
let fields = chain(
|
let fields = chain(
|
||||||
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
|
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
|
||||||
|
@ -395,16 +397,22 @@ pub fn parse_type_annotation<T>(
|
||||||
params: HashMap::default(),
|
params: HashMap::default(),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Cannot use function name as type at {loc}"))
|
Err(HashSet::from([
|
||||||
|
format!("Cannot use function name as type at {loc}"),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ty = resolver
|
let ty = resolver
|
||||||
.get_symbol_type(unifier, top_level_defs, primitives, *id)
|
.get_symbol_type(unifier, top_level_defs, primitives, *id)
|
||||||
.map_err(|e| format!("Unknown type annotation at {loc}: {e}"))?;
|
.map_err(|e| HashSet::from([
|
||||||
|
format!("Unknown type annotation at {loc}: {e}"),
|
||||||
|
]))?;
|
||||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Unknown type annotation {id} at {loc}"))
|
Err(HashSet::from([
|
||||||
|
format!("Unknown type annotation {id} at {loc}"),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,7 +435,9 @@ pub fn parse_type_annotation<T>(
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
|
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
|
||||||
} else {
|
} else {
|
||||||
Err("Expected multiple elements for tuple".into())
|
Err(HashSet::from([
|
||||||
|
"Expected multiple elements for tuple".into()
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let types = if let Tuple { elts, .. } = &slice.node {
|
let types = if let Tuple { elts, .. } = &slice.node {
|
||||||
|
@ -444,11 +454,13 @@ pub fn parse_type_annotation<T>(
|
||||||
let def = top_level_defs[obj_id.0].read();
|
let def = top_level_defs[obj_id.0].read();
|
||||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||||
if types.len() != type_vars.len() {
|
if types.len() != type_vars.len() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Unexpected number of type parameters: expected {} but got {}",
|
format!(
|
||||||
type_vars.len(),
|
"Unexpected number of type parameters: expected {} but got {}",
|
||||||
types.len()
|
type_vars.len(),
|
||||||
));
|
types.len()
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
let mut subst = HashMap::new();
|
let mut subst = HashMap::new();
|
||||||
for (var, ty) in izip!(type_vars.iter(), types.iter()) {
|
for (var, ty) in izip!(type_vars.iter(), types.iter()) {
|
||||||
|
@ -472,7 +484,9 @@ pub fn parse_type_annotation<T>(
|
||||||
}));
|
}));
|
||||||
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }))
|
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }))
|
||||||
} else {
|
} else {
|
||||||
Err("Cannot use function name as type".into())
|
Err(HashSet::from([
|
||||||
|
"Cannot use function name as type".into(),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -483,10 +497,14 @@ pub fn parse_type_annotation<T>(
|
||||||
if let Name { id, .. } = &value.node {
|
if let Name { id, .. } = &value.node {
|
||||||
subscript_name_handle(id, slice, unifier)
|
subscript_name_handle(id, slice, unifier)
|
||||||
} else {
|
} 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),
|
||||||
|
])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,7 +515,7 @@ impl dyn SymbolResolver + Send + Sync {
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
primitives: &PrimitiveStore,
|
primitives: &PrimitiveStore,
|
||||||
expr: &Expr<T>,
|
expr: &Expr<T>,
|
||||||
) -> Result<Type, String> {
|
) -> Result<Type, HashSet<String>> {
|
||||||
parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
|
parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,11 +528,11 @@ impl dyn SymbolResolver + Send + Sync {
|
||||||
unifier.internal_stringify(
|
unifier.internal_stringify(
|
||||||
ty,
|
ty,
|
||||||
&mut |id| {
|
&mut |id| {
|
||||||
if let TopLevelDef::Class { name, .. } = &*top_level_defs[id].read() {
|
let TopLevelDef::Class { name, .. } = &*top_level_defs[id].read() else {
|
||||||
name.to_string()
|
|
||||||
} else {
|
|
||||||
unreachable!("expected class definition")
|
unreachable!("expected class definition")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
name.to_string()
|
||||||
},
|
},
|
||||||
&mut |id| format!("typevar{id}"),
|
&mut |id| format!("typevar{id}"),
|
||||||
&mut None,
|
&mut None,
|
||||||
|
|
|
@ -421,11 +421,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
||||||
generator,
|
generator,
|
||||||
expect_ty,
|
expect_ty,
|
||||||
)?;
|
)?;
|
||||||
if let BasicValueEnum::PointerValue(ptr) = obj_val {
|
let BasicValueEnum::PointerValue(ptr) = obj_val else {
|
||||||
Ok(Some(ctx.builder.build_is_not_null(ptr, "is_some").into()))
|
|
||||||
} else {
|
|
||||||
unreachable!("option must be ptr")
|
unreachable!("option must be ptr")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Ok(Some(ctx.builder.build_is_not_null(ptr, "is_some").into()))
|
||||||
},
|
},
|
||||||
)))),
|
)))),
|
||||||
loc: None,
|
loc: None,
|
||||||
|
@ -446,11 +446,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
||||||
generator,
|
generator,
|
||||||
expect_ty,
|
expect_ty,
|
||||||
)?;
|
)?;
|
||||||
if let BasicValueEnum::PointerValue(ptr) = obj_val {
|
let BasicValueEnum::PointerValue(ptr) = obj_val else {
|
||||||
Ok(Some(ctx.builder.build_is_null(ptr, "is_none").into()))
|
|
||||||
} else {
|
|
||||||
unreachable!("option must be ptr")
|
unreachable!("option must be ptr")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Ok(Some(ctx.builder.build_is_null(ptr, "is_none").into()))
|
||||||
},
|
},
|
||||||
)))),
|
)))),
|
||||||
loc: None,
|
loc: None,
|
||||||
|
@ -686,7 +686,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
||||||
|
|
||||||
val
|
val
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!()
|
||||||
};
|
};
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
},
|
},
|
||||||
|
@ -762,7 +762,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
||||||
|
|
||||||
val
|
val
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!()
|
||||||
};
|
};
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
},
|
},
|
||||||
|
@ -1361,7 +1361,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
||||||
} else if is_type(m_ty, n_ty) && is_type(n_ty, float) {
|
} else if is_type(m_ty, n_ty) && is_type(n_ty, float) {
|
||||||
("llvm.minnum.f64", llvm_f64)
|
("llvm.minnum.f64", llvm_f64)
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!()
|
||||||
};
|
};
|
||||||
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
|
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
|
||||||
let fn_type = arg_ty.fn_type(&[arg_ty.into(), arg_ty.into()], false);
|
let fn_type = arg_ty.fn_type(&[arg_ty.into(), arg_ty.into()], false);
|
||||||
|
@ -1423,7 +1423,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
||||||
} else if is_type(m_ty, n_ty) && is_type(n_ty, float) {
|
} else if is_type(m_ty, n_ty) && is_type(n_ty, float) {
|
||||||
("llvm.maxnum.f64", llvm_f64)
|
("llvm.maxnum.f64", llvm_f64)
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!()
|
||||||
};
|
};
|
||||||
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
|
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
|
||||||
let fn_type = arg_ty.fn_type(&[arg_ty.into(), arg_ty.into()], false);
|
let fn_type = arg_ty.fn_type(&[arg_ty.into(), arg_ty.into()], false);
|
||||||
|
@ -1480,7 +1480,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
||||||
is_float = true;
|
is_float = true;
|
||||||
("llvm.fabs.f64", llvm_f64)
|
("llvm.fabs.f64", llvm_f64)
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!()
|
||||||
};
|
};
|
||||||
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
|
let intrinsic = ctx.module.get_function(fun_name).unwrap_or_else(|| {
|
||||||
let fn_type = if is_float {
|
let fn_type = if is_float {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -204,13 +204,13 @@ impl TopLevelComposer {
|
||||||
pub fn get_class_method_def_info(
|
pub fn get_class_method_def_info(
|
||||||
class_methods_def: &[(StrRef, Type, DefinitionId)],
|
class_methods_def: &[(StrRef, Type, DefinitionId)],
|
||||||
method_name: StrRef,
|
method_name: StrRef,
|
||||||
) -> Result<(Type, DefinitionId), String> {
|
) -> Result<(Type, DefinitionId), HashSet<String>> {
|
||||||
for (name, ty, def_id) in class_methods_def {
|
for (name, ty, def_id) in class_methods_def {
|
||||||
if name == &method_name {
|
if name == &method_name {
|
||||||
return Ok((*ty, *def_id));
|
return Ok((*ty, *def_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(format!("no method {method_name} in the current class"))
|
Err(HashSet::from([format!("no method {method_name} in the current class")]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get all base class def id of a class, excluding itself. \
|
/// get all base class def id of a class, excluding itself. \
|
||||||
|
@ -221,7 +221,7 @@ impl TopLevelComposer {
|
||||||
pub fn get_all_ancestors_helper(
|
pub fn get_all_ancestors_helper(
|
||||||
child: &TypeAnnotation,
|
child: &TypeAnnotation,
|
||||||
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
||||||
) -> Result<Vec<TypeAnnotation>, String> {
|
) -> Result<Vec<TypeAnnotation>, HashSet<String>> {
|
||||||
let mut result: Vec<TypeAnnotation> = Vec::new();
|
let mut result: Vec<TypeAnnotation> = Vec::new();
|
||||||
let mut parent = Self::get_parent(child, temp_def_list);
|
let mut parent = Self::get_parent(child, temp_def_list);
|
||||||
while let Some(p) = parent {
|
while let Some(p) = parent {
|
||||||
|
@ -233,16 +233,16 @@ impl TopLevelComposer {
|
||||||
};
|
};
|
||||||
// check cycle
|
// check cycle
|
||||||
let no_cycle = result.iter().all(|x| {
|
let no_cycle = result.iter().all(|x| {
|
||||||
if let TypeAnnotation::CustomClass { id, .. } = x {
|
let TypeAnnotation::CustomClass { id, .. } = x else {
|
||||||
id.0 != p_id.0
|
|
||||||
} else {
|
|
||||||
unreachable!("must be class kind annotation")
|
unreachable!("must be class kind annotation")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
id.0 != p_id.0
|
||||||
});
|
});
|
||||||
if no_cycle {
|
if no_cycle {
|
||||||
result.push(p);
|
result.push(p);
|
||||||
} else {
|
} else {
|
||||||
return Err("cyclic inheritance detected".into());
|
return Err(HashSet::from(["cyclic inheritance detected".into()]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@ -260,23 +260,25 @@ impl TopLevelComposer {
|
||||||
};
|
};
|
||||||
let child_def = temp_def_list.get(child_id.0).unwrap();
|
let child_def = temp_def_list.get(child_id.0).unwrap();
|
||||||
let child_def = child_def.read();
|
let child_def = child_def.read();
|
||||||
if let TopLevelDef::Class { ancestors, .. } = &*child_def {
|
let TopLevelDef::Class { ancestors, .. } = &*child_def else {
|
||||||
if ancestors.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(ancestors[0].clone())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!("child must be top level class def")
|
unreachable!("child must be top level class def")
|
||||||
|
};
|
||||||
|
|
||||||
|
if ancestors.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ancestors[0].clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the `var_id` of a given `TVar` type
|
/// get the `var_id` of a given `TVar` type
|
||||||
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<u32, String> {
|
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() {
|
if let TypeEnum::TVar { id, .. } = unifier.get_ty(var_ty).as_ref() {
|
||||||
Ok(*id)
|
Ok(*id)
|
||||||
} else {
|
} else {
|
||||||
Err("not type var".to_string())
|
Err(HashSet::from([
|
||||||
|
"not type var".to_string(),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,39 +292,38 @@ impl TopLevelComposer {
|
||||||
let this = this.as_ref();
|
let this = this.as_ref();
|
||||||
let other = unifier.get_ty(other);
|
let other = unifier.get_ty(other);
|
||||||
let other = other.as_ref();
|
let other = other.as_ref();
|
||||||
if let (
|
let (
|
||||||
TypeEnum::TFunc(FunSignature { args: this_args, ret: this_ret, .. }),
|
TypeEnum::TFunc(FunSignature { args: this_args, ret: this_ret, .. }),
|
||||||
TypeEnum::TFunc(FunSignature { args: other_args, ret: other_ret, .. }),
|
TypeEnum::TFunc(FunSignature { args: other_args, ret: other_ret, .. }),
|
||||||
) = (this, other)
|
) = (this, other) else {
|
||||||
{
|
|
||||||
// check args
|
|
||||||
let args_ok = this_args
|
|
||||||
.iter()
|
|
||||||
.map(|FuncArg { name, ty, .. }| (name, type_var_to_concrete_def.get(ty).unwrap()))
|
|
||||||
.zip(other_args.iter().map(|FuncArg { name, ty, .. }| {
|
|
||||||
(name, type_var_to_concrete_def.get(ty).unwrap())
|
|
||||||
}))
|
|
||||||
.all(|(this, other)| {
|
|
||||||
if this.0 == &"self".into() && this.0 == other.0 {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
this.0 == other.0
|
|
||||||
&& check_overload_type_annotation_compatible(this.1, other.1, unifier)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// check rets
|
|
||||||
let ret_ok = check_overload_type_annotation_compatible(
|
|
||||||
type_var_to_concrete_def.get(this_ret).unwrap(),
|
|
||||||
type_var_to_concrete_def.get(other_ret).unwrap(),
|
|
||||||
unifier,
|
|
||||||
);
|
|
||||||
|
|
||||||
// return
|
|
||||||
args_ok && ret_ok
|
|
||||||
} else {
|
|
||||||
unreachable!("this function must be called with function type")
|
unreachable!("this function must be called with function type")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// check args
|
||||||
|
let args_ok = this_args
|
||||||
|
.iter()
|
||||||
|
.map(|FuncArg { name, ty, .. }| (name, type_var_to_concrete_def.get(ty).unwrap()))
|
||||||
|
.zip(other_args.iter().map(|FuncArg { name, ty, .. }| {
|
||||||
|
(name, type_var_to_concrete_def.get(ty).unwrap())
|
||||||
|
}))
|
||||||
|
.all(|(this, other)| {
|
||||||
|
if this.0 == &"self".into() && this.0 == other.0 {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
this.0 == other.0
|
||||||
|
&& check_overload_type_annotation_compatible(this.1, other.1, unifier)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// check rets
|
||||||
|
let ret_ok = check_overload_type_annotation_compatible(
|
||||||
|
type_var_to_concrete_def.get(this_ret).unwrap(),
|
||||||
|
type_var_to_concrete_def.get(other_ret).unwrap(),
|
||||||
|
unifier,
|
||||||
|
);
|
||||||
|
|
||||||
|
// return
|
||||||
|
args_ok && ret_ok
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_overload_field_type(
|
pub fn check_overload_field_type(
|
||||||
|
@ -338,7 +339,7 @@ impl TopLevelComposer {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_assigned_field(stmts: &[Stmt<()>]) -> Result<HashSet<StrRef>, String> {
|
pub fn get_all_assigned_field(stmts: &[Stmt<()>]) -> Result<HashSet<StrRef>, HashSet<String>> {
|
||||||
let mut result = HashSet::new();
|
let mut result = HashSet::new();
|
||||||
for s in stmts {
|
for s in stmts {
|
||||||
match &s.node {
|
match &s.node {
|
||||||
|
@ -355,10 +356,12 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
} =>
|
} =>
|
||||||
{
|
{
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"redundant type annotation for class fields at {}",
|
format!(
|
||||||
s.location
|
"redundant type annotation for class fields at {}",
|
||||||
))
|
s.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
ast::StmtKind::Assign { targets, .. } => {
|
ast::StmtKind::Assign { targets, .. } => {
|
||||||
for t in targets {
|
for t in targets {
|
||||||
|
@ -410,7 +413,7 @@ impl TopLevelComposer {
|
||||||
pub fn parse_parameter_default_value(
|
pub fn parse_parameter_default_value(
|
||||||
default: &ast::Expr,
|
default: &ast::Expr,
|
||||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||||
) -> Result<SymbolValue, String> {
|
) -> Result<SymbolValue, HashSet<String>> {
|
||||||
parse_parameter_default_value(default, resolver)
|
parse_parameter_default_value(default, resolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,14 +470,14 @@ impl TopLevelComposer {
|
||||||
pub fn parse_parameter_default_value(
|
pub fn parse_parameter_default_value(
|
||||||
default: &ast::Expr,
|
default: &ast::Expr,
|
||||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||||
) -> Result<SymbolValue, String> {
|
) -> Result<SymbolValue, HashSet<String>> {
|
||||||
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, String> {
|
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, HashSet<String>> {
|
||||||
match val {
|
match val {
|
||||||
Constant::Int(v) => {
|
Constant::Int(v) => {
|
||||||
if let Ok(v) = (*v).try_into() {
|
if let Ok(v) = (*v).try_into() {
|
||||||
Ok(SymbolValue::I32(v))
|
Ok(SymbolValue::I32(v))
|
||||||
} else {
|
} 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)),
|
Constant::Float(v) => Ok(SymbolValue::Double(*v)),
|
||||||
|
@ -482,9 +485,11 @@ pub fn parse_parameter_default_value(
|
||||||
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
|
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
|
||||||
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
|
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
|
||||||
)),
|
)),
|
||||||
Constant::None => Err(format!(
|
Constant::None => Err(HashSet::from([
|
||||||
"`None` is not supported, use `none` for option type instead ({loc})"
|
format!(
|
||||||
)),
|
"`None` is not supported, use `none` for option type instead ({loc})"
|
||||||
|
),
|
||||||
|
])),
|
||||||
_ => unimplemented!("this constant is not supported at {}", loc),
|
_ => unimplemented!("this constant is not supported at {}", loc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,37 +502,51 @@ pub fn parse_parameter_default_value(
|
||||||
let v: Result<i64, _> = (*v).try_into();
|
let v: Result<i64, _> = (*v).try_into();
|
||||||
match v {
|
match v {
|
||||||
Ok(v) => Ok(SymbolValue::I64(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::Name { id, .. } if *id == "uint32".into() => match &args[0].node {
|
||||||
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
||||||
let v: Result<u32, _> = (*v).try_into();
|
let v: Result<u32, _> = (*v).try_into();
|
||||||
match v {
|
match v {
|
||||||
Ok(v) => Ok(SymbolValue::U32(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::Name { id, .. } if *id == "uint64".into() => match &args[0].node {
|
||||||
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
||||||
let v: Result<u64, _> = (*v).try_into();
|
let v: Result<u64, _> = (*v).try_into();
|
||||||
match v {
|
match v {
|
||||||
Ok(v) => Ok(SymbolValue::U64(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(
|
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(
|
||||||
SymbolValue::OptionSome(
|
SymbolValue::OptionSome(
|
||||||
Box::new(parse_parameter_default_value(&args[0], resolver)?)
|
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
|
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts
|
||||||
|
@ -538,17 +557,21 @@ pub fn parse_parameter_default_value(
|
||||||
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
|
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
|
||||||
ast::ExprKind::Name { id, .. } => {
|
ast::ExprKind::Name { id, .. } => {
|
||||||
resolver.get_default_param_value(default).ok_or_else(
|
resolver.get_default_param_value(default).ok_or_else(
|
||||||
|| format!(
|
|| HashSet::from([
|
||||||
"`{}` cannot be used as a default parameter at {} \
|
format!(
|
||||||
(not primitive type, option or tuple / not defined?)",
|
"`{}` cannot be used as a default parameter at {} \
|
||||||
id,
|
(not primitive type, option or tuple / not defined?)",
|
||||||
default.location
|
id,
|
||||||
)
|
default.location
|
||||||
|
),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => Err(format!(
|
_ => Err(HashSet::from([
|
||||||
"unsupported default parameter (not primitive type, option or tuple) at {}",
|
format!(
|
||||||
default.location
|
"unsupported default parameter (not primitive type, option or tuple) at {}",
|
||||||
))
|
default.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,9 @@ impl SymbolResolver for Resolver {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||||
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Unknown identifier".to_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 {
|
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 let Err(msg) = composer.start_analysis(false) {
|
||||||
if print {
|
if print {
|
||||||
println!("{}", msg);
|
println!("{}", msg.iter().sorted().join("\n----------\n"));
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(res[0], msg);
|
assert_eq!(res[0], msg.iter().next().unwrap());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// skip 5 to skip primitives
|
// 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 let Err(msg) = composer.start_analysis(true) {
|
||||||
if print {
|
if print {
|
||||||
println!("{}", msg);
|
println!("{}", msg.iter().sorted().join("\n----------\n"));
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(res[0], msg);
|
assert_eq!(res[0], msg.iter().next().unwrap());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// skip 5 to skip primitives
|
// skip 5 to skip primitives
|
||||||
|
|
|
@ -82,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
|
// the key stores the type_var of this topleveldef::class, we only need this field here
|
||||||
locked: HashMap<DefinitionId, Vec<Type>>,
|
locked: HashMap<DefinitionId, Vec<Type>>,
|
||||||
type_var: Option<Type>,
|
type_var: Option<Type>,
|
||||||
) -> Result<TypeAnnotation, String> {
|
) -> Result<TypeAnnotation, HashSet<String>> {
|
||||||
let name_handle = |id: &StrRef,
|
let name_handle = |id: &StrRef,
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
locked: HashMap<DefinitionId, Vec<Type>>| {
|
locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||||
|
@ -109,10 +109,12 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
||||||
type_vars.clone()
|
type_vars.clone()
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"function cannot be used as a type (at {})",
|
format!(
|
||||||
expr.location
|
"function cannot be used as a type (at {})",
|
||||||
));
|
expr.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
locked.get(&obj_id).unwrap().clone()
|
locked.get(&obj_id).unwrap().clone()
|
||||||
|
@ -120,11 +122,13 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
};
|
};
|
||||||
// check param number here
|
// check param number here
|
||||||
if !type_vars.is_empty() {
|
if !type_vars.is_empty() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expect {} type variable parameter but got 0 (at {})",
|
format!(
|
||||||
type_vars.len(),
|
"expect {} type variable parameter but got 0 (at {})",
|
||||||
expr.location,
|
type_vars.len(),
|
||||||
));
|
expr.location,
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] })
|
Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] })
|
||||||
} else if let Ok(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) {
|
} else if let Ok(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) {
|
||||||
|
@ -133,10 +137,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
unifier.unify(var, ty).unwrap();
|
unifier.unify(var, ty).unwrap();
|
||||||
Ok(TypeAnnotation::TypeVar(ty))
|
Ok(TypeAnnotation::TypeVar(ty))
|
||||||
} else {
|
} 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 {
|
} 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),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -147,17 +155,19 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
mut locked: HashMap<DefinitionId, Vec<Type>>| {
|
mut locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||||
if ["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 obj_id = resolver.get_identifier_def(*id)?;
|
||||||
let type_vars = {
|
let type_vars = {
|
||||||
let def_read = top_level_defs[obj_id.0].try_read();
|
let def_read = top_level_defs[obj_id.0].try_read();
|
||||||
if let Some(def_read) = def_read {
|
if let Some(def_read) = def_read {
|
||||||
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
let TopLevelDef::Class { type_vars, .. } = &*def_read else {
|
||||||
type_vars.clone()
|
|
||||||
} else {
|
|
||||||
unreachable!("must be class here")
|
unreachable!("must be class here")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
type_vars.clone()
|
||||||
} else {
|
} else {
|
||||||
locked.get(&obj_id).unwrap().clone()
|
locked.get(&obj_id).unwrap().clone()
|
||||||
}
|
}
|
||||||
|
@ -170,12 +180,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
vec![slice]
|
vec![slice]
|
||||||
};
|
};
|
||||||
if type_vars.len() != params_ast.len() {
|
if type_vars.len() != params_ast.len() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expect {} type parameters but got {} (at {})",
|
format!(
|
||||||
type_vars.len(),
|
"expect {} type parameters but got {} (at {})",
|
||||||
params_ast.len(),
|
type_vars.len(),
|
||||||
params_ast[0].location,
|
params_ast.len(),
|
||||||
));
|
params_ast[0].location,
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
let result = params_ast
|
let result = params_ast
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -201,11 +213,12 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
if no_type_var {
|
if no_type_var {
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"application of type vars to generic class \
|
format!(
|
||||||
is not currently supported (at {})",
|
"application of type vars to generic class is not currently supported (at {})",
|
||||||
params_ast[0].location
|
params_ast[0].location
|
||||||
));
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
|
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
|
||||||
|
@ -311,7 +324,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
if let ast::ExprKind::Name { id, .. } = &value.node {
|
if let ast::ExprKind::Name { id, .. } = &value.node {
|
||||||
class_name_handle(id, slice, unifier, locked)
|
class_name_handle(id, slice, unifier, locked)
|
||||||
} else {
|
} 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)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,13 +339,16 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
};
|
};
|
||||||
let underlying_ty = underlying_ty[0];
|
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(_)) {
|
if matches!(value, SymbolValue::Str(_) | SymbolValue::Tuple(_) | SymbolValue::OptionSome(_)) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expression {value} is not allowed for constant type annotation (at {})",
|
format!(
|
||||||
expr.location
|
"expression {value} is not allowed for constant type annotation (at {})",
|
||||||
))
|
expr.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(TypeAnnotation::Constant {
|
Ok(TypeAnnotation::Constant {
|
||||||
|
@ -339,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,7 +371,7 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
ann: &TypeAnnotation,
|
ann: &TypeAnnotation,
|
||||||
subst_list: &mut Option<Vec<Type>>
|
subst_list: &mut Option<Vec<Type>>
|
||||||
) -> Result<Type, String> {
|
) -> Result<Type, HashSet<String>> {
|
||||||
match ann {
|
match ann {
|
||||||
TypeAnnotation::CustomClass { id: obj_id, params } => {
|
TypeAnnotation::CustomClass { id: obj_id, params } => {
|
||||||
let def_read = top_level_defs[obj_id.0].read();
|
let def_read = top_level_defs[obj_id.0].read();
|
||||||
|
@ -361,11 +381,13 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
};
|
};
|
||||||
|
|
||||||
if type_vars.len() != params.len() {
|
if type_vars.len() != params.len() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"unexpected number of type parameters: expected {} but got {}",
|
format!(
|
||||||
type_vars.len(),
|
"unexpected number of type parameters: expected {} but got {}",
|
||||||
params.len()
|
type_vars.len(),
|
||||||
))
|
params.len()
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
let param_ty = params
|
let param_ty = params
|
||||||
|
@ -401,16 +423,18 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
if ok {
|
if ok {
|
||||||
result.insert(*id, p);
|
result.insert(*id, p);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"cannot apply type {} to type variable with id {:?}",
|
format!(
|
||||||
unifier.internal_stringify(
|
"cannot apply type {} to type variable with id {:?}",
|
||||||
p,
|
unifier.internal_stringify(
|
||||||
&mut |id| format!("class{id}"),
|
p,
|
||||||
&mut |id| format!("typevar{id}"),
|
&mut |id| format!("class{id}"),
|
||||||
&mut None
|
&mut |id| format!("typevar{id}"),
|
||||||
),
|
&mut None
|
||||||
*id
|
),
|
||||||
));
|
*id
|
||||||
|
)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,11 +454,13 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
if ok {
|
if ok {
|
||||||
result.insert(*id, p);
|
result.insert(*id, p);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"cannot apply type {} to type variable {}",
|
format!(
|
||||||
unifier.stringify(p),
|
"cannot apply type {} to type variable {}",
|
||||||
name.unwrap_or_else(|| format!("typevar{id}").into()),
|
unifier.stringify(p),
|
||||||
))
|
name.unwrap_or_else(|| format!("typevar{id}").into()),
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,13 +497,11 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) => Ok(*ty),
|
TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) => Ok(*ty),
|
||||||
TypeAnnotation::Constant { ty, value, .. } => {
|
TypeAnnotation::Constant { ty, value, .. } => {
|
||||||
let ty_enum = unifier.get_ty(*ty);
|
let ty_enum = unifier.get_ty(*ty);
|
||||||
let (ty, loc) = match &*ty_enum {
|
let TypeEnum::TVar { range: ntv_underlying_ty, loc, is_const_generic: true, .. } = &*ty_enum else {
|
||||||
TypeEnum::TVar { range: ntv_underlying_ty, loc, is_const_generic: true, .. } => {
|
unreachable!("{} ({})", unifier.stringify(*ty), ty_enum.get_type_name());
|
||||||
(ntv_underlying_ty[0], loc)
|
|
||||||
}
|
|
||||||
_ => unreachable!("{} ({})", unifier.stringify(*ty), ty_enum.get_type_name()),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let ty = ntv_underlying_ty[0];
|
||||||
let var = unifier.get_fresh_constant(value.clone(), ty, *loc);
|
let var = unifier.get_fresh_constant(value.clone(), ty, *loc);
|
||||||
Ok(var)
|
Ok(var)
|
||||||
}
|
}
|
||||||
|
@ -570,15 +594,14 @@ pub fn check_overload_type_annotation_compatible(
|
||||||
let a = &*a;
|
let a = &*a;
|
||||||
let b = unifier.get_ty(*b);
|
let b = unifier.get_ty(*b);
|
||||||
let b = &*b;
|
let b = &*b;
|
||||||
if let (
|
let (
|
||||||
TypeEnum::TVar { id: a, fields: None, .. },
|
TypeEnum::TVar { id: a, fields: None, .. },
|
||||||
TypeEnum::TVar { id: b, fields: None, .. },
|
TypeEnum::TVar { id: b, fields: None, .. },
|
||||||
) = (a, b)
|
) = (a, b) else {
|
||||||
{
|
|
||||||
a == b
|
|
||||||
} else {
|
|
||||||
unreachable!("must be type var")
|
unreachable!("must be type var")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
a == b
|
||||||
}
|
}
|
||||||
(TypeAnnotation::Virtual(a), TypeAnnotation::Virtual(b))
|
(TypeAnnotation::Virtual(a), TypeAnnotation::Virtual(b))
|
||||||
| (TypeAnnotation::List(a), TypeAnnotation::List(b)) => {
|
| (TypeAnnotation::List(a), TypeAnnotation::List(b)) => {
|
||||||
|
|
|
@ -6,9 +6,11 @@ use nac3parser::ast::{self, Constant, Expr, ExprKind, Operator::{LShift, RShift}
|
||||||
use std::{collections::HashSet, iter::once};
|
use std::{collections::HashSet, iter::once};
|
||||||
|
|
||||||
impl<'a> Inferencer<'a> {
|
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)) {
|
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 {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -18,10 +20,11 @@ impl<'a> Inferencer<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: &Expr<Option<Type>>,
|
pattern: &Expr<Option<Type>>,
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), HashSet<String>> {
|
||||||
match &pattern.node {
|
match &pattern.node {
|
||||||
ExprKind::Name { id, .. } if id == &"none".into() =>
|
ExprKind::Name { id, .. } if id == &"none".into() => Err(HashSet::from([
|
||||||
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
|
format!("cannot assign to a `none` (at {})", pattern.location),
|
||||||
|
])),
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
if !defined_identifiers.contains(id) {
|
if !defined_identifiers.contains(id) {
|
||||||
defined_identifiers.insert(*id);
|
defined_identifiers.insert(*id);
|
||||||
|
@ -41,15 +44,19 @@ impl<'a> Inferencer<'a> {
|
||||||
self.should_have_value(value)?;
|
self.should_have_value(value)?;
|
||||||
self.check_expr(slice, defined_identifiers)?;
|
self.check_expr(slice, defined_identifiers)?;
|
||||||
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap()) {
|
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap()) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Error at {}: cannot assign to tuple element",
|
format!(
|
||||||
value.location
|
"Error at {}: cannot assign to tuple element",
|
||||||
));
|
value.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ExprKind::Constant { .. } => {
|
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),
|
_ => self.check_expr(pattern, defined_identifiers),
|
||||||
}
|
}
|
||||||
|
@ -59,15 +66,17 @@ impl<'a> Inferencer<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
expr: &Expr<Option<Type>>,
|
expr: &Expr<Option<Type>>,
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), HashSet<String>> {
|
||||||
// there are some cases where the custom field is None
|
// there are some cases where the custom field is None
|
||||||
if let Some(ty) = &expr.custom {
|
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) {
|
if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) && !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expected concrete type at {} but got {}",
|
format!(
|
||||||
expr.location,
|
"expected concrete type at {} but got {}",
|
||||||
self.unifier.get_ty(*ty).get_type_name()
|
expr.location,
|
||||||
));
|
self.unifier.get_ty(*ty).get_type_name()
|
||||||
|
)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
|
@ -87,10 +96,12 @@ impl<'a> Inferencer<'a> {
|
||||||
self.defined_identifiers.insert(*id);
|
self.defined_identifiers.insert(*id);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"type error at identifier `{}` ({}) at {}",
|
format!(
|
||||||
id, e, expr.location
|
"type error at identifier `{}` ({}) at {}",
|
||||||
));
|
id, e, expr.location
|
||||||
|
)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,10 +132,12 @@ impl<'a> Inferencer<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if *rhs_val < 0 {
|
if *rhs_val < 0 {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"shift count is negative at {}",
|
format!(
|
||||||
right.location
|
"shift count is negative at {}",
|
||||||
));
|
right.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +213,7 @@ impl<'a> Inferencer<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
stmt: &Stmt<Option<Type>>,
|
stmt: &Stmt<Option<Type>>,
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, HashSet<String>> {
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
StmtKind::For { target, iter, body, orelse, .. } => {
|
StmtKind::For { target, iter, body, orelse, .. } => {
|
||||||
self.check_expr(iter, defined_identifiers)?;
|
self.check_expr(iter, defined_identifiers)?;
|
||||||
|
@ -307,11 +320,11 @@ impl<'a> Inferencer<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
block: &[Stmt<Option<Type>>],
|
block: &[Stmt<Option<Type>>],
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, HashSet<String>> {
|
||||||
let mut ret = false;
|
let mut ret = false;
|
||||||
for stmt in block {
|
for stmt in block {
|
||||||
if ret {
|
if ret {
|
||||||
println!("warning: dead code at {:?}\n", stmt.location);
|
eprintln!("warning: dead code at {:?}\n", stmt.location);
|
||||||
}
|
}
|
||||||
if self.check_stmt(stmt, defined_identifiers)? {
|
if self.check_stmt(stmt, defined_identifiers)? {
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
|
@ -64,19 +64,19 @@ pub struct Inferencer<'a> {
|
||||||
struct NaiveFolder();
|
struct NaiveFolder();
|
||||||
impl Fold<()> for NaiveFolder {
|
impl Fold<()> for NaiveFolder {
|
||||||
type TargetU = Option<Type>;
|
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)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_error<T>(msg: &str, location: Location) -> Result<T, String> {
|
fn report_error<T>(msg: &str, location: Location) -> Result<T, HashSet<String>> {
|
||||||
Err(format!("{msg} at {location}"))
|
Err(HashSet::from([format!("{msg} at {location}")]))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Fold<()> for Inferencer<'a> {
|
impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
type TargetU = Option<Type>;
|
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)
|
Ok(None)
|
||||||
|
@ -159,9 +159,9 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
}
|
}
|
||||||
if let Some(old_typ) = self.variable_mapping.insert(name, typ) {
|
if let Some(old_typ) = self.variable_mapping.insert(name, typ) {
|
||||||
let loc = handler.location;
|
let loc = handler.location;
|
||||||
self.unifier.unify(old_typ, typ).map_err(|e| {
|
self.unifier.unify(old_typ, typ).map_err(|e| HashSet::from([
|
||||||
e.at(Some(loc)).to_display(self.unifier).to_string()
|
e.at(Some(loc)).to_display(self.unifier).to_string(),
|
||||||
})?;
|
]))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut type_ = naive_folder.fold_expr(*type_)?;
|
let mut type_ = naive_folder.fold_expr(*type_)?;
|
||||||
|
@ -241,40 +241,40 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
let targets: Result<Vec<_>, _> = targets
|
let targets: Result<Vec<_>, _> = targets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|target| {
|
.map(|target| {
|
||||||
if let ExprKind::Name { id, ctx } = target.node {
|
let ExprKind::Name { id, ctx } = target.node else {
|
||||||
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!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
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),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let loc = node.location;
|
let loc = node.location;
|
||||||
let targets = targets
|
let targets = targets
|
||||||
.map_err(|e| e.at(Some(loc)).to_display(self.unifier).to_string())?;
|
.map_err(|e| HashSet::from([e.at(Some(loc)).to_display(self.unifier).to_string()]))?;
|
||||||
return Ok(Located {
|
return Ok(Located {
|
||||||
location: node.location,
|
location: node.location,
|
||||||
node: ast::StmtKind::Assign {
|
node: ast::StmtKind::Assign {
|
||||||
|
@ -465,12 +465,12 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
let var_map = params
|
let var_map = params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id_var, ty)| {
|
.map(|(id_var, ty)| {
|
||||||
if let TypeEnum::TVar { id, range, name, loc, .. } = &*self.unifier.get_ty(*ty) {
|
let TypeEnum::TVar { id, range, name, loc, .. } = &*self.unifier.get_ty(*ty) else {
|
||||||
assert_eq!(*id, *id_var);
|
|
||||||
(*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
assert_eq!(*id, *id_var);
|
||||||
|
(*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
||||||
})
|
})
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
Some(self.unifier.subst(self.primitives.option, &var_map).unwrap())
|
Some(self.unifier.subst(self.primitives.option, &var_map).unwrap())
|
||||||
|
@ -528,22 +528,24 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type InferenceResult = Result<Type, String>;
|
type InferenceResult = Result<Type, HashSet<String>>;
|
||||||
|
|
||||||
impl<'a> Inferencer<'a> {
|
impl<'a> Inferencer<'a> {
|
||||||
/// Constrain a <: b
|
/// Constrain a <: b
|
||||||
/// Currently implemented as unification
|
/// 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)
|
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
|
self.unifier
|
||||||
.unify(a, b)
|
.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 {
|
match &pattern.node {
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
if !self.defined_identifiers.contains(id) {
|
if !self.defined_identifiers.contains(id) {
|
||||||
|
@ -592,9 +594,9 @@ impl<'a> Inferencer<'a> {
|
||||||
.map(|v| v.name)
|
.map(|v| v.name)
|
||||||
.rev()
|
.rev()
|
||||||
.collect();
|
.collect();
|
||||||
self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| {
|
self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| HashSet::from([
|
||||||
e.at(Some(location)).to_display(self.unifier).to_string()
|
e.at(Some(location)).to_display(self.unifier).to_string(),
|
||||||
})?;
|
]))?;
|
||||||
return Ok(sign.ret);
|
return Ok(sign.ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,7 +625,7 @@ impl<'a> Inferencer<'a> {
|
||||||
location: Location,
|
location: Location,
|
||||||
args: Arguments,
|
args: Arguments,
|
||||||
body: ast::Expr<()>,
|
body: ast::Expr<()>,
|
||||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||||
if !args.posonlyargs.is_empty()
|
if !args.posonlyargs.is_empty()
|
||||||
|| args.vararg.is_some()
|
|| args.vararg.is_some()
|
||||||
|| !args.kwonlyargs.is_empty()
|
|| !args.kwonlyargs.is_empty()
|
||||||
|
@ -692,7 +694,7 @@ impl<'a> Inferencer<'a> {
|
||||||
location: Location,
|
location: Location,
|
||||||
elt: ast::Expr<()>,
|
elt: ast::Expr<()>,
|
||||||
mut generators: Vec<Comprehension>,
|
mut generators: Vec<Comprehension>,
|
||||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||||
if generators.len() != 1 {
|
if generators.len() != 1 {
|
||||||
return report_error(
|
return report_error(
|
||||||
"Only 1 generator statement for list comprehension is supported",
|
"Only 1 generator statement for list comprehension is supported",
|
||||||
|
@ -765,7 +767,7 @@ impl<'a> Inferencer<'a> {
|
||||||
func: ast::Expr<()>,
|
func: ast::Expr<()>,
|
||||||
mut args: Vec<ast::Expr<()>>,
|
mut args: Vec<ast::Expr<()>>,
|
||||||
keywords: Vec<Located<ast::KeywordData>>,
|
keywords: Vec<Located<ast::KeywordData>>,
|
||||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||||
let func =
|
let func =
|
||||||
if let Located { location: func_location, custom, node: ExprKind::Name { id, ctx } } =
|
if let Located { location: func_location, custom, node: ExprKind::Name { id, ctx } } =
|
||||||
func
|
func
|
||||||
|
@ -899,7 +901,9 @@ impl<'a> Inferencer<'a> {
|
||||||
.collect();
|
.collect();
|
||||||
self.unifier
|
self.unifier
|
||||||
.unify_call(&call, func.custom.unwrap(), sign, &required)
|
.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 {
|
return Ok(Located {
|
||||||
location,
|
location,
|
||||||
custom: Some(sign.ret),
|
custom: Some(sign.ret),
|
||||||
|
@ -1073,8 +1077,11 @@ impl<'a> Inferencer<'a> {
|
||||||
) -> InferenceResult {
|
) -> InferenceResult {
|
||||||
let boolean = self.primitives.bool;
|
let boolean = self.primitives.bool;
|
||||||
for (a, b, c) in izip!(once(left).chain(comparators), comparators, ops) {
|
for (a, b, c) in izip!(once(left).chain(comparators), comparators, ops) {
|
||||||
let method =
|
let method = comparison_name(c)
|
||||||
comparison_name(c).ok_or_else(|| "unsupported comparator".to_string())?.into();
|
.ok_or_else(|| HashSet::from([
|
||||||
|
"unsupported comparator".to_string()
|
||||||
|
]))?
|
||||||
|
.into();
|
||||||
self.build_method_call(
|
self.build_method_call(
|
||||||
a.location,
|
a.location,
|
||||||
method,
|
method,
|
||||||
|
@ -1105,7 +1112,7 @@ impl<'a> Inferencer<'a> {
|
||||||
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.
|
// the index is a constant, so value can be a sequence.
|
||||||
let ind: Option<i32> = (*val).try_into().ok();
|
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((
|
let map = once((
|
||||||
ind.into(),
|
ind.into(),
|
||||||
RecordField::new(ty, ctx == &ExprContext::Store, Some(value.location)),
|
RecordField::new(ty, ctx == &ExprContext::Store, Some(value.location)),
|
||||||
|
|
|
@ -43,8 +43,9 @@ impl SymbolResolver for Resolver {
|
||||||
unimplemented!()
|
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.get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
|
self.id_to_def.get(&id).cloned()
|
||||||
|
.ok_or_else(|| HashSet::from(["Unknown identifier".to_string()]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_string_id(&self, _: &str) -> i32 {
|
fn get_string_id(&self, _: &str) -> i32 {
|
||||||
|
|
|
@ -499,12 +499,9 @@ impl Unifier {
|
||||||
let instantiated = self.instantiate_fun(b, signature);
|
let instantiated = self.instantiate_fun(b, signature);
|
||||||
let r = self.get_ty(instantiated);
|
let r = self.get_ty(instantiated);
|
||||||
let r = r.as_ref();
|
let r = r.as_ref();
|
||||||
let signature;
|
let TypeEnum::TFunc(signature) = r else {
|
||||||
if let TypeEnum::TFunc(s) = r {
|
unreachable!()
|
||||||
signature = s;
|
};
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
// we check to make sure that all required arguments (those without default
|
// we check to make sure that all required arguments (those without default
|
||||||
// arguments) are provided, and do not provide the same argument twice.
|
// arguments) are provided, and do not provide the same argument twice.
|
||||||
let mut required = required.to_vec();
|
let mut required = required.to_vec();
|
||||||
|
@ -746,7 +743,6 @@ impl Unifier {
|
||||||
|
|
||||||
(TConstant { value: val1, ty: ty1, .. }, TConstant { value: val2, ty: ty2, .. }) => {
|
(TConstant { value: val1, ty: ty1, .. }, TConstant { value: val2, ty: ty2, .. }) => {
|
||||||
if val1 != val2 {
|
if val1 != val2 {
|
||||||
eprintln!("VALUE MISMATCH: lhs={val1:?} rhs={val2:?} eq={}", val1 == val2);
|
|
||||||
return self.incompatible_types(a, b)
|
return self.incompatible_types(a, b)
|
||||||
}
|
}
|
||||||
self.unify_impl(*ty1, *ty2, false)?;
|
self.unify_impl(*ty1, *ty2, false)?;
|
||||||
|
@ -941,13 +937,12 @@ impl Unifier {
|
||||||
top_level.as_ref().map_or_else(
|
top_level.as_ref().map_or_else(
|
||||||
|| format!("{id}"),
|
|| format!("{id}"),
|
||||||
|top_level| {
|
|top_level| {
|
||||||
if let TopLevelDef::Class { name, .. } =
|
let top_level_def = &top_level.definitions.read()[id];
|
||||||
&*top_level.definitions.read()[id].read()
|
let TopLevelDef::Class { name, .. } = &*top_level_def.read() else {
|
||||||
{
|
|
||||||
name.to_string()
|
|
||||||
} else {
|
|
||||||
unreachable!("expected class definition")
|
unreachable!("expected class definition")
|
||||||
}
|
};
|
||||||
|
|
||||||
|
name.to_string()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -339,23 +339,21 @@ fn test_recursive_subst() {
|
||||||
let int = *env.type_mapping.get("int").unwrap();
|
let int = *env.type_mapping.get("int").unwrap();
|
||||||
let foo_id = *env.type_mapping.get("Foo").unwrap();
|
let foo_id = *env.type_mapping.get("Foo").unwrap();
|
||||||
let foo_ty = env.unifier.get_ty(foo_id);
|
let foo_ty = env.unifier.get_ty(foo_id);
|
||||||
let mapping: HashMap<_, _>;
|
|
||||||
with_fields(&mut env.unifier, foo_id, |_unifier, fields| {
|
with_fields(&mut env.unifier, foo_id, |_unifier, fields| {
|
||||||
fields.insert("rec".into(), (foo_id, true));
|
fields.insert("rec".into(), (foo_id, true));
|
||||||
});
|
});
|
||||||
if let TypeEnum::TObj { params, .. } = &*foo_ty {
|
let TypeEnum::TObj { params, .. } = &*foo_ty else {
|
||||||
mapping = params.iter().map(|(id, _)| (*id, int)).collect();
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
let mapping = params.iter().map(|(id, _)| (*id, int)).collect();
|
||||||
let instantiated = env.unifier.subst(foo_id, &mapping).unwrap();
|
let instantiated = env.unifier.subst(foo_id, &mapping).unwrap();
|
||||||
let instantiated_ty = env.unifier.get_ty(instantiated);
|
let instantiated_ty = env.unifier.get_ty(instantiated);
|
||||||
if let TypeEnum::TObj { fields, .. } = &*instantiated_ty {
|
|
||||||
assert!(env.unifier.unioned(fields.get(&"a".into()).unwrap().0, int));
|
let TypeEnum::TObj { fields, .. } = &*instantiated_ty else {
|
||||||
assert!(env.unifier.unioned(fields.get(&"rec".into()).unwrap().0, instantiated));
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
assert!(env.unifier.unioned(fields.get(&"a".into()).unwrap().0, int));
|
||||||
|
assert!(env.unifier.unioned(fields.get(&"rec".into()).unwrap().0, instantiated));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -10,6 +10,7 @@ use nac3core::{
|
||||||
use nac3parser::ast::{self, StrRef};
|
use nac3parser::ast::{self, StrRef};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub struct ResolverInternal {
|
pub struct ResolverInternal {
|
||||||
pub id_to_type: Mutex<HashMap<StrRef, Type>>,
|
pub id_to_type: Mutex<HashMap<StrRef, Type>>,
|
||||||
|
@ -61,8 +62,9 @@ impl SymbolResolver for Resolver {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||||
self.0.id_to_def.lock().get(&id).copied().ok_or_else(|| "Undefined identifier".to_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 {
|
fn get_string_id(&self, s: &str) -> i32 {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use inkwell::{
|
||||||
};
|
};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use std::{collections::HashMap, fs, path::Path, sync::Arc};
|
use std::{collections::HashMap, fs, path::Path, sync::Arc};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use nac3core::{
|
use nac3core::{
|
||||||
codegen::{
|
codegen::{
|
||||||
|
@ -74,24 +75,28 @@ fn handle_typevar_definition(
|
||||||
def_list: &[Arc<RwLock<TopLevelDef>>],
|
def_list: &[Arc<RwLock<TopLevelDef>>],
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
primitives: &PrimitiveStore,
|
primitives: &PrimitiveStore,
|
||||||
) -> Result<Type, String> {
|
) -> Result<Type, HashSet<String>> {
|
||||||
let ExprKind::Call { func, args, .. } = &var.node else {
|
let ExprKind::Call { func, args, .. } = &var.node else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
format!(
|
||||||
))
|
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
||||||
|
),
|
||||||
|
]))
|
||||||
};
|
};
|
||||||
|
|
||||||
match &func.node {
|
match &func.node {
|
||||||
ExprKind::Name { id, .. } if id == &"TypeVar".into() => {
|
ExprKind::Name { id, .. } if id == &"TypeVar".into() => {
|
||||||
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
|
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 generic_name: StrRef = ty_name.to_string().into();
|
||||||
|
|
||||||
let constraints = args
|
let constraints = args
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|x| -> Result<Type, String> {
|
.map(|x| -> Result<Type, HashSet<String>> {
|
||||||
let ty = parse_ast_to_type_annotation_kinds(
|
let ty = parse_ast_to_type_annotation_kinds(
|
||||||
resolver,
|
resolver,
|
||||||
def_list,
|
def_list,
|
||||||
|
@ -109,7 +114,9 @@ fn handle_typevar_definition(
|
||||||
let loc = func.location;
|
let loc = func.location;
|
||||||
|
|
||||||
if constraints.len() == 1 {
|
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)
|
Ok(unifier.get_fresh_var_with_range(&constraints, Some(generic_name), Some(loc)).0)
|
||||||
|
@ -117,14 +124,18 @@ fn handle_typevar_definition(
|
||||||
|
|
||||||
ExprKind::Name { id, .. } if id == &"ConstGeneric".into() => {
|
ExprKind::Name { id, .. } if id == &"ConstGeneric".into() => {
|
||||||
if args.len() != 2 {
|
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 {
|
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Expected string constant for first parameter of `ConstGeneric`, got {:?}",
|
format!(
|
||||||
&args[0].node
|
"Expected string constant for first parameter of `ConstGeneric`, got {:?}",
|
||||||
))
|
&args[0].node
|
||||||
|
),
|
||||||
|
]))
|
||||||
};
|
};
|
||||||
let generic_name: StrRef = ty_name.to_string().into();
|
let generic_name: StrRef = ty_name.to_string().into();
|
||||||
|
|
||||||
|
@ -145,9 +156,11 @@ fn handle_typevar_definition(
|
||||||
Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).0)
|
Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).0)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Err(format!(
|
_ => Err(HashSet::from([
|
||||||
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
format!(
|
||||||
))
|
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,12 +363,11 @@ fn main() {
|
||||||
.unwrap_or_else(|_| panic!("cannot find run() entry point"))
|
.unwrap_or_else(|_| panic!("cannot find run() entry point"))
|
||||||
.0]
|
.0]
|
||||||
.write();
|
.write();
|
||||||
if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } = &mut *instance {
|
let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } = &mut *instance else {
|
||||||
instance_to_symbol.insert(String::new(), "run".to_string());
|
|
||||||
instance_to_stmt[""].clone()
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
};
|
||||||
|
instance_to_symbol.insert(String::new(), "run".to_string());
|
||||||
|
instance_to_stmt[""].clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let llvm_options = CodeGenLLVMOptions {
|
let llvm_options = CodeGenLLVMOptions {
|
||||||
|
|
Loading…
Reference in New Issue