Compare commits
7 Commits
misc/impl-
...
master
Author | SHA1 | Date |
---|---|---|
David Mak | 01edd5af67 | |
occheung | 015714eee1 | |
occheung | 71dec251e3 | |
occheung | fce61f7b8c | |
abdul124 | babc081dbd | |
abdul124 | 5337dbe23b | |
abdul124 | f862c01412 |
|
@ -30,9 +30,8 @@ use tempfile::{self, TempDir};
|
||||||
|
|
||||||
use nac3core::{
|
use nac3core::{
|
||||||
codegen::{
|
codegen::{
|
||||||
concrete_type::ConcreteTypeStore, gen_func_impl, irrt::load_irrt,
|
concrete_type::ConcreteTypeStore, gen_func_impl, irrt::load_irrt, CodeGenLLVMOptions,
|
||||||
tracert::TraceRuntimeConfig, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask,
|
CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, WithCall, WorkerRegistry,
|
||||||
CodeGenerator, WithCall, WorkerRegistry,
|
|
||||||
},
|
},
|
||||||
inkwell::{
|
inkwell::{
|
||||||
context::Context,
|
context::Context,
|
||||||
|
@ -717,13 +716,8 @@ impl Nac3 {
|
||||||
let membuffer = membuffers.clone();
|
let membuffer = membuffers.clone();
|
||||||
let mut has_return = false;
|
let mut has_return = false;
|
||||||
py.allow_threads(|| {
|
py.allow_threads(|| {
|
||||||
let (registry, handles) = WorkerRegistry::create_workers(
|
let (registry, handles) =
|
||||||
threads,
|
WorkerRegistry::create_workers(threads, top_level.clone(), &self.llvm_options, &f);
|
||||||
top_level.clone(),
|
|
||||||
&self.llvm_options,
|
|
||||||
&TraceRuntimeConfig::default(),
|
|
||||||
&f,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut generator = ArtiqCodeGenerator::new("main".to_string(), size_t, self.time_fns);
|
let mut generator = ArtiqCodeGenerator::new("main".to_string(), size_t, self.time_fns);
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
|
|
|
@ -8,7 +8,6 @@ edition = "2021"
|
||||||
default = ["derive"]
|
default = ["derive"]
|
||||||
derive = ["dep:nac3core_derive"]
|
derive = ["dep:nac3core_derive"]
|
||||||
no-escape-analysis = []
|
no-escape-analysis = []
|
||||||
tracing = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
itertools = "0.13"
|
itertools = "0.13"
|
||||||
|
@ -32,5 +31,4 @@ indoc = "2.0"
|
||||||
insta = "=1.11.0"
|
insta = "=1.11.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
itertools = "0.13"
|
|
||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
fs::File,
|
fs::File,
|
||||||
|
@ -7,28 +6,14 @@ use std::{
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
struct IRRTCompilation<'a> {
|
fn main() {
|
||||||
pub file: &'a str,
|
|
||||||
pub gcc_options: Vec<&'a str>,
|
|
||||||
pub cargo_instructions: Vec<&'a str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts the extension-less filename from a [`Path`].
|
|
||||||
fn path_to_extless_filename(path: &Path) -> &str {
|
|
||||||
path.file_name().map(Path::new).and_then(Path::file_stem).and_then(OsStr::to_str).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compiles a source C file into LLVM bitcode.
|
|
||||||
fn compile_file_to_ir(compile_opts: &IRRTCompilation) {
|
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let out_path = Path::new(&out_dir);
|
let out_dir = Path::new(&out_dir);
|
||||||
let irrt_dir = Path::new("irrt");
|
let irrt_dir = Path::new("irrt");
|
||||||
|
|
||||||
let path = Path::new(compile_opts.file);
|
let irrt_cpp_path = irrt_dir.join("irrt.cpp");
|
||||||
let filename_without_ext = path_to_extless_filename(path);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HACK: Sadly, clang doesn't let us emit generic LLVM bitcode.
|
* HACK: Sadly, clang doesn't let us emit generic LLVM bitcode.
|
||||||
|
@ -50,22 +35,22 @@ fn compile_file_to_ir(compile_opts: &IRRTCompilation) {
|
||||||
"-",
|
"-",
|
||||||
"-I",
|
"-I",
|
||||||
irrt_dir.to_str().unwrap(),
|
irrt_dir.to_str().unwrap(),
|
||||||
|
irrt_cpp_path.to_str().unwrap(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Apply custom flags from IRRTCompilation
|
|
||||||
flags.extend_from_slice(&compile_opts.gcc_options);
|
|
||||||
|
|
||||||
match env::var("PROFILE").as_deref() {
|
match env::var("PROFILE").as_deref() {
|
||||||
Ok("debug") => flags.extend_from_slice(&["-O0", "-DIRRT_DEBUG_ASSERT"]),
|
Ok("debug") => {
|
||||||
Ok("release") => flags.push("-O3"),
|
flags.push("-O0");
|
||||||
|
flags.push("-DIRRT_DEBUG_ASSERT");
|
||||||
|
}
|
||||||
|
Ok("release") => {
|
||||||
|
flags.push("-O3");
|
||||||
|
}
|
||||||
flavor => panic!("Unknown or missing build flavor {flavor:?}"),
|
flavor => panic!("Unknown or missing build flavor {flavor:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
flags.push(path.to_str().unwrap());
|
// Tell Cargo to rerun if any file under `irrt_dir` (recursive) changes
|
||||||
|
println!("cargo:rerun-if-changed={}", irrt_dir.to_str().unwrap());
|
||||||
// Tell Cargo to rerun if the main IRRT source is changed
|
|
||||||
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
|
|
||||||
compile_opts.cargo_instructions.iter().for_each(|inst| println!("cargo::{inst}"));
|
|
||||||
|
|
||||||
// Compile IRRT and capture the LLVM IR output
|
// Compile IRRT and capture the LLVM IR output
|
||||||
let output = Command::new("clang-irrt")
|
let output = Command::new("clang-irrt")
|
||||||
|
@ -76,7 +61,8 @@ fn compile_file_to_ir(compile_opts: &IRRTCompilation) {
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let output = std::str::from_utf8(&output.stdout).unwrap();
|
// https://github.com/rust-lang/regex/issues/244
|
||||||
|
let output = std::str::from_utf8(&output.stdout).unwrap().replace("\r\n", "\n");
|
||||||
let mut filtered_output = String::with_capacity(output.len());
|
let mut filtered_output = String::with_capacity(output.len());
|
||||||
|
|
||||||
// Filter out irrelevant IR
|
// Filter out irrelevant IR
|
||||||
|
@ -90,7 +76,7 @@ fn compile_file_to_ir(compile_opts: &IRRTCompilation) {
|
||||||
r"(?ms:^define.*?\}$)|(?m:^declare.*?$)|(?m:^%.+?=\s*type\s*\{.+?\}$)|(?m:^@.+?=.+$)",
|
r"(?ms:^define.*?\}$)|(?m:^declare.*?$)|(?m:^%.+?=\s*type\s*\{.+?\}$)|(?m:^@.+?=.+$)",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
for f in regex_filter.captures_iter(output) {
|
for f in regex_filter.captures_iter(&output) {
|
||||||
assert_eq!(f.len(), 1);
|
assert_eq!(f.len(), 1);
|
||||||
filtered_output.push_str(&f[0]);
|
filtered_output.push_str(&f[0]);
|
||||||
filtered_output.push('\n');
|
filtered_output.push('\n');
|
||||||
|
@ -104,47 +90,20 @@ fn compile_file_to_ir(compile_opts: &IRRTCompilation) {
|
||||||
// Doing `DEBUG_DUMP_IRRT=1 cargo build -p nac3core` dumps the LLVM IR generated
|
// Doing `DEBUG_DUMP_IRRT=1 cargo build -p nac3core` dumps the LLVM IR generated
|
||||||
const DEBUG_DUMP_IRRT: &str = "DEBUG_DUMP_IRRT";
|
const DEBUG_DUMP_IRRT: &str = "DEBUG_DUMP_IRRT";
|
||||||
println!("cargo:rerun-if-env-changed={DEBUG_DUMP_IRRT}");
|
println!("cargo:rerun-if-env-changed={DEBUG_DUMP_IRRT}");
|
||||||
if env::var("DEBUG_DUMP_IRRT").is_ok() {
|
if env::var(DEBUG_DUMP_IRRT).is_ok() {
|
||||||
let mut file = File::create(out_path.join(format!("{filename_without_ext}.ll"))).unwrap();
|
let mut file = File::create(out_dir.join("irrt.ll")).unwrap();
|
||||||
file.write_all(output.as_bytes()).unwrap();
|
file.write_all(output.as_bytes()).unwrap();
|
||||||
|
|
||||||
let mut file =
|
let mut file = File::create(out_dir.join("irrt-filtered.ll")).unwrap();
|
||||||
File::create(out_path.join(format!("{filename_without_ext}-filtered.ll"))).unwrap();
|
|
||||||
file.write_all(filtered_output.as_bytes()).unwrap();
|
file.write_all(filtered_output.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut llvm_as = Command::new("llvm-as-irrt")
|
let mut llvm_as = Command::new("llvm-as-irrt")
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.arg("-o")
|
.arg("-o")
|
||||||
.arg(out_path.join(format!("{filename_without_ext}.bc")))
|
.arg(out_dir.join("irrt.bc"))
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
llvm_as.stdin.as_mut().unwrap().write_all(filtered_output.as_bytes()).unwrap();
|
llvm_as.stdin.as_mut().unwrap().write_all(filtered_output.as_bytes()).unwrap();
|
||||||
assert!(llvm_as.wait().unwrap().success());
|
assert!(llvm_as.wait().unwrap().success());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let irrt_compilations: &[IRRTCompilation] = &[
|
|
||||||
IRRTCompilation {
|
|
||||||
file: "irrt/irrt.cpp",
|
|
||||||
gcc_options: Vec::default(),
|
|
||||||
cargo_instructions: vec!["rerun-if-changed=irrt/irrt"],
|
|
||||||
},
|
|
||||||
IRRTCompilation {
|
|
||||||
file: "irrt/tracert.cpp",
|
|
||||||
gcc_options: Vec::default(),
|
|
||||||
cargo_instructions: Vec::default(),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
assert!(irrt_compilations
|
|
||||||
.iter()
|
|
||||||
.map(|comp| comp.file)
|
|
||||||
.map(Path::new)
|
|
||||||
.map(path_to_extless_filename)
|
|
||||||
.all_unique());
|
|
||||||
|
|
||||||
for path in irrt_compilations {
|
|
||||||
compile_file_to_ir(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
extern "C" {
|
|
||||||
// stdio.h
|
|
||||||
int printf(const char *format, ...);
|
|
||||||
} // extern "C"
|
|
|
@ -1,11 +1,8 @@
|
||||||
use std::iter::once;
|
|
||||||
|
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
attributes::{Attribute, AttributeLoc},
|
attributes::{Attribute, AttributeLoc},
|
||||||
values::{BasicValue, BasicValueEnum, CallSiteValue, FloatValue, IntValue},
|
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue},
|
||||||
AddressSpace,
|
|
||||||
};
|
};
|
||||||
use itertools::{Either, Itertools};
|
use itertools::Either;
|
||||||
|
|
||||||
use super::CodeGenContext;
|
use super::CodeGenContext;
|
||||||
|
|
||||||
|
@ -194,49 +191,3 @@ generate_linalg_extern_fn!(call_np_linalg_det, "np_linalg_det", 2);
|
||||||
generate_linalg_extern_fn!(call_sp_linalg_lu, "sp_linalg_lu", 3);
|
generate_linalg_extern_fn!(call_sp_linalg_lu, "sp_linalg_lu", 3);
|
||||||
generate_linalg_extern_fn!(call_sp_linalg_schur, "sp_linalg_schur", 3);
|
generate_linalg_extern_fn!(call_sp_linalg_schur, "sp_linalg_schur", 3);
|
||||||
generate_linalg_extern_fn!(call_sp_linalg_hessenberg, "sp_linalg_hessenberg", 3);
|
generate_linalg_extern_fn!(call_sp_linalg_hessenberg, "sp_linalg_hessenberg", 3);
|
||||||
|
|
||||||
/// Invokes the `printf` function.
|
|
||||||
pub fn call_printf<'ctx>(
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
format: &str,
|
|
||||||
args: &[BasicValueEnum<'ctx>],
|
|
||||||
) -> IntValue<'ctx> {
|
|
||||||
const FN_NAME: &str = "printf";
|
|
||||||
|
|
||||||
let llvm_i8 = ctx.ctx.i8_type();
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
|
|
||||||
|
|
||||||
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
|
|
||||||
let fn_type = llvm_i32.fn_type(&[llvm_pi8.into()], true);
|
|
||||||
let func = ctx.module.add_function(FN_NAME, fn_type, None);
|
|
||||||
for attr in ["nofree", "nounwind"] {
|
|
||||||
func.add_attribute(
|
|
||||||
AttributeLoc::Function,
|
|
||||||
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
func
|
|
||||||
});
|
|
||||||
|
|
||||||
let pformat = ctx
|
|
||||||
.builder
|
|
||||||
.build_global_string_ptr(&format!("{format}\0"), "")
|
|
||||||
.map(|v| v.as_basic_value_enum())
|
|
||||||
.map(BasicValueEnum::into_pointer_value)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let fn_args = once(&pformat.as_basic_value_enum())
|
|
||||||
.chain(args)
|
|
||||||
.copied()
|
|
||||||
.map(BasicValueEnum::into)
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
ctx.builder
|
|
||||||
.build_call(extern_fn, fn_args.as_slice(), "")
|
|
||||||
.map(CallSiteValue::try_as_basic_value)
|
|
||||||
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
|
||||||
.map(Either::unwrap_left)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ use crate::{
|
||||||
};
|
};
|
||||||
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
|
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
|
||||||
pub use generator::{CodeGenerator, DefaultCodeGenerator};
|
pub use generator::{CodeGenerator, DefaultCodeGenerator};
|
||||||
use tracert::{TraceRuntimeConfig, TraceRuntimeState};
|
|
||||||
use types::{ListType, NDArrayType, ProxyType, RangeType};
|
use types::{ListType, NDArrayType, ProxyType, RangeType};
|
||||||
|
|
||||||
pub mod builtin_fns;
|
pub mod builtin_fns;
|
||||||
|
@ -50,7 +49,6 @@ pub mod irrt;
|
||||||
pub mod llvm_intrinsics;
|
pub mod llvm_intrinsics;
|
||||||
pub mod numpy;
|
pub mod numpy;
|
||||||
pub mod stmt;
|
pub mod stmt;
|
||||||
pub mod tracert;
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod values;
|
pub mod values;
|
||||||
|
|
||||||
|
@ -222,8 +220,6 @@ pub struct CodeGenContext<'ctx, 'a> {
|
||||||
/// See [`need_sret`].
|
/// See [`need_sret`].
|
||||||
pub need_sret: bool,
|
pub need_sret: bool,
|
||||||
|
|
||||||
pub tracert_state: Option<TraceRuntimeState>,
|
|
||||||
|
|
||||||
/// The current source location.
|
/// The current source location.
|
||||||
pub current_loc: Location,
|
pub current_loc: Location,
|
||||||
}
|
}
|
||||||
|
@ -271,8 +267,6 @@ pub struct WorkerRegistry {
|
||||||
|
|
||||||
/// LLVM-related options for code generation.
|
/// LLVM-related options for code generation.
|
||||||
pub llvm_options: CodeGenLLVMOptions,
|
pub llvm_options: CodeGenLLVMOptions,
|
||||||
|
|
||||||
tracert_config: TraceRuntimeConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkerRegistry {
|
impl WorkerRegistry {
|
||||||
|
@ -282,7 +276,6 @@ impl WorkerRegistry {
|
||||||
generators: Vec<Box<G>>,
|
generators: Vec<Box<G>>,
|
||||||
top_level_ctx: Arc<TopLevelContext>,
|
top_level_ctx: Arc<TopLevelContext>,
|
||||||
llvm_options: &CodeGenLLVMOptions,
|
llvm_options: &CodeGenLLVMOptions,
|
||||||
tracert_config: &TraceRuntimeConfig,
|
|
||||||
f: &Arc<WithCall>,
|
f: &Arc<WithCall>,
|
||||||
) -> (Arc<WorkerRegistry>, Vec<thread::JoinHandle<()>>) {
|
) -> (Arc<WorkerRegistry>, Vec<thread::JoinHandle<()>>) {
|
||||||
let (sender, receiver) = unbounded();
|
let (sender, receiver) = unbounded();
|
||||||
|
@ -304,7 +297,6 @@ impl WorkerRegistry {
|
||||||
wait_condvar,
|
wait_condvar,
|
||||||
top_level_ctx,
|
top_level_ctx,
|
||||||
llvm_options: llvm_options.clone(),
|
llvm_options: llvm_options.clone(),
|
||||||
tracert_config: tracert_config.clone(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut handles = Vec::new();
|
let mut handles = Vec::new();
|
||||||
|
@ -988,13 +980,6 @@ pub fn gen_func_impl<
|
||||||
unifier,
|
unifier,
|
||||||
static_value_store,
|
static_value_store,
|
||||||
need_sret: has_sret,
|
need_sret: has_sret,
|
||||||
tracert_state: if cfg!(feature = "tracing")
|
|
||||||
|| registry.tracert_config.enabled_tags.is_empty()
|
|
||||||
{
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(TraceRuntimeState::create(registry.tracert_config.clone()))
|
|
||||||
},
|
|
||||||
current_loc: Location::default(),
|
current_loc: Location::default(),
|
||||||
debug_info: (dibuilder, compile_unit, func_scope.as_debug_info_scope()),
|
debug_info: (dibuilder, compile_unit, func_scope.as_debug_info_scope()),
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,6 @@ use parking_lot::RwLock;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
concrete_type::ConcreteTypeStore,
|
concrete_type::ConcreteTypeStore,
|
||||||
tracert::TraceRuntimeConfig,
|
|
||||||
types::{ListType, NDArrayType, ProxyType, RangeType},
|
types::{ListType, NDArrayType, ProxyType, RangeType},
|
||||||
CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator,
|
CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator,
|
||||||
DefaultCodeGenerator, WithCall, WorkerRegistry,
|
DefaultCodeGenerator, WithCall, WorkerRegistry,
|
||||||
|
@ -246,13 +245,7 @@ fn test_primitives() {
|
||||||
opt_level: OptimizationLevel::Default,
|
opt_level: OptimizationLevel::Default,
|
||||||
target: CodeGenTargetMachineOptions::from_host_triple(),
|
target: CodeGenTargetMachineOptions::from_host_triple(),
|
||||||
};
|
};
|
||||||
let (registry, handles) = WorkerRegistry::create_workers(
|
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, &llvm_options, &f);
|
||||||
threads,
|
|
||||||
top_level,
|
|
||||||
&llvm_options,
|
|
||||||
&TraceRuntimeConfig::default(),
|
|
||||||
&f,
|
|
||||||
);
|
|
||||||
registry.add_task(task);
|
registry.add_task(task);
|
||||||
registry.wait_tasks_complete(handles);
|
registry.wait_tasks_complete(handles);
|
||||||
}
|
}
|
||||||
|
@ -445,13 +438,7 @@ fn test_simple_call() {
|
||||||
opt_level: OptimizationLevel::Default,
|
opt_level: OptimizationLevel::Default,
|
||||||
target: CodeGenTargetMachineOptions::from_host_triple(),
|
target: CodeGenTargetMachineOptions::from_host_triple(),
|
||||||
};
|
};
|
||||||
let (registry, handles) = WorkerRegistry::create_workers(
|
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, &llvm_options, &f);
|
||||||
threads,
|
|
||||||
top_level,
|
|
||||||
&llvm_options,
|
|
||||||
&TraceRuntimeConfig::default(),
|
|
||||||
&f,
|
|
||||||
);
|
|
||||||
registry.add_task(task);
|
registry.add_task(task);
|
||||||
registry.wait_tasks_complete(handles);
|
registry.wait_tasks_complete(handles);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
use std::panic::Location;
|
|
||||||
|
|
||||||
use inkwell::context::Context;
|
|
||||||
use inkwell::memory_buffer::MemoryBuffer;
|
|
||||||
use inkwell::module::Module;
|
|
||||||
use inkwell::values::BasicValueEnum;
|
|
||||||
use inkwell::AtomicOrdering;
|
|
||||||
|
|
||||||
use crate::codegen::{extern_fns, CodeGenContext};
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Eq, PartialEq)]
|
|
||||||
pub struct TraceRuntimeConfig {
|
|
||||||
pub enabled_tags: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, Clone, PartialEq)]
|
|
||||||
pub struct TraceRuntimeState {
|
|
||||||
config: TraceRuntimeConfig,
|
|
||||||
indent: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TraceRuntimeState {
|
|
||||||
#[must_use]
|
|
||||||
pub fn create(config: TraceRuntimeConfig) -> TraceRuntimeState {
|
|
||||||
TraceRuntimeState { config, indent: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn load_tracert<'ctx>(ctx: &'ctx Context, config: &TraceRuntimeConfig) -> Option<Module<'ctx>> {
|
|
||||||
if cfg!(feature = "tracing") && !config.enabled_tags.is_empty() {
|
|
||||||
let bitcode_buf = MemoryBuffer::create_from_memory_range(
|
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/tracert.bc")),
|
|
||||||
"tracert_bitcode_buffer",
|
|
||||||
);
|
|
||||||
let module = Module::parse_bitcode_from_buffer(&bitcode_buf, ctx).unwrap();
|
|
||||||
|
|
||||||
return Some(module);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Might need to redesign how trace logging should be done
|
|
||||||
|
|
||||||
pub fn trace_log<'ctx>(
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
tag: &'static str,
|
|
||||||
format: &'static str,
|
|
||||||
args: &[BasicValueEnum<'ctx>],
|
|
||||||
) {
|
|
||||||
if ctx.tracert_state.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add indentation
|
|
||||||
let str = format!("[TRACING] {tag} - {format}\n\0");
|
|
||||||
extern_fns::call_printf(ctx, &str, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
pub fn trace_log_with_location<'ctx>(
|
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
|
||||||
tag: &'static str,
|
|
||||||
format: &str,
|
|
||||||
args: &[BasicValueEnum<'ctx>],
|
|
||||||
) {
|
|
||||||
if ctx.tracert_state.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add indentation
|
|
||||||
let caller_loc = Location::caller();
|
|
||||||
let str = format!(
|
|
||||||
"[TRACING] {}:{}:{}: {tag} - {format}\n\0",
|
|
||||||
caller_loc.file(),
|
|
||||||
caller_loc.line(),
|
|
||||||
caller_loc.column()
|
|
||||||
);
|
|
||||||
extern_fns::call_printf(ctx, &str, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_push_level(ctx: &mut CodeGenContext<'_, '_>) {
|
|
||||||
let Some(tracert_state) = &mut ctx.tracert_state else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
debug_assert!(tracert_state.indent < usize::MAX);
|
|
||||||
if tracert_state.indent < usize::MAX {
|
|
||||||
tracert_state.indent = tracert_state.indent.saturating_add(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_pop_level(ctx: &mut CodeGenContext<'_, '_>) {
|
|
||||||
let Some(tracert_state) = &mut ctx.tracert_state else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
debug_assert!(tracert_state.indent > 0);
|
|
||||||
if tracert_state.indent > 0 {
|
|
||||||
tracert_state.indent = tracert_state.indent.saturating_sub(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mfence(ctx: &mut CodeGenContext<'_, '_>) {
|
|
||||||
if ctx.tracert_state.is_some() {
|
|
||||||
ctx.builder.build_fence(AtomicOrdering::SequentiallyConsistent, 0, "").unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
use nac3parser::ast::{fold::Fold, ExprKind, Ident};
|
use nac3parser::ast::{fold::Fold, ExprKind, Ident};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -463,9 +464,9 @@ impl TopLevelComposer {
|
||||||
Ok((name, DefinitionId(self.definition_ast_list.len() - 1), Some(ty_to_be_unified)))
|
Ok((name, DefinitionId(self.definition_ast_list.len() - 1), Some(ty_to_be_unified)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Analyze the AST and modify the corresponding `TopLevelDef`
|
||||||
pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> {
|
pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> {
|
||||||
self.analyze_top_level_class_type_var()?;
|
self.analyze_top_level_class_definition()?;
|
||||||
self.analyze_top_level_class_bases()?;
|
|
||||||
self.analyze_top_level_class_fields_methods()?;
|
self.analyze_top_level_class_fields_methods()?;
|
||||||
self.analyze_top_level_function()?;
|
self.analyze_top_level_function()?;
|
||||||
if inference {
|
if inference {
|
||||||
|
@ -475,386 +476,129 @@ impl TopLevelComposer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// step 1, analyze the type vars associated with top level class
|
/// step 1, analyze the top level class definitions
|
||||||
fn analyze_top_level_class_type_var(&mut self) -> Result<(), HashSet<String>> {
|
///
|
||||||
|
/// Checks for class type variables and ancestors adding them to the `TopLevelDef` list
|
||||||
|
fn analyze_top_level_class_definition(&mut self) -> Result<(), HashSet<String>> {
|
||||||
let def_list = &self.definition_ast_list;
|
let def_list = &self.definition_ast_list;
|
||||||
let temp_def_list = self.extract_def_list();
|
|
||||||
let unifier = self.unifier.borrow_mut();
|
let unifier = self.unifier.borrow_mut();
|
||||||
let primitives_store = &self.primitives_ty;
|
let primitives_store = &self.primitives_ty;
|
||||||
|
let mut errors = HashSet::new();
|
||||||
|
|
||||||
let mut analyze = |class_def: &Arc<RwLock<TopLevelDef>>, class_ast: &Option<Stmt>| {
|
// Initially only copy the definitions of buitin classes and functions
|
||||||
// only deal with class def here
|
// class definitions are added in the same order as they appear in the program
|
||||||
let mut class_def = class_def.write();
|
let mut temp_def_list: Vec<Arc<RwLock<TopLevelDef>>> =
|
||||||
let (class_bases_ast, class_def_type_vars, class_resolver) = {
|
def_list.iter().take(self.builtin_num).map(|f| f.0.clone()).collect_vec();
|
||||||
if let TopLevelDef::Class { type_vars, resolver, .. } = &mut *class_def {
|
|
||||||
let Some(ast::Located { node: ast::StmtKind::ClassDef { bases, .. }, .. }) =
|
|
||||||
class_ast
|
|
||||||
else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
(bases, type_vars, resolver)
|
// Check for class generic variables and ancestors
|
||||||
} else {
|
for (class_def, class_ast) in def_list.iter().skip(self.builtin_num) {
|
||||||
return Ok(());
|
if class_ast.is_some() && matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
|
||||||
}
|
// Add class type variables and direct parents to the `TopLevelDef`
|
||||||
};
|
if let Err(e) = Self::analyze_class_bases(
|
||||||
let class_resolver = class_resolver.as_ref().unwrap();
|
class_def,
|
||||||
let class_resolver = &**class_resolver;
|
class_ast,
|
||||||
|
|
||||||
let mut is_generic = false;
|
|
||||||
for b in class_bases_ast {
|
|
||||||
match &b.node {
|
|
||||||
// analyze typevars bounded to the class,
|
|
||||||
// only support things like `class A(Generic[T, V])`,
|
|
||||||
// things like `class A(Generic[T, V, ImportedModule.T])` is not supported
|
|
||||||
// i.e. only simple names are allowed in the subscript
|
|
||||||
// should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params
|
|
||||||
ExprKind::Subscript { value, slice, .. }
|
|
||||||
if {
|
|
||||||
matches!(
|
|
||||||
&value.node,
|
|
||||||
ast::ExprKind::Name { id, .. } if id == &"Generic".into()
|
|
||||||
)
|
|
||||||
} =>
|
|
||||||
{
|
|
||||||
if is_generic {
|
|
||||||
return Err(HashSet::from([format!(
|
|
||||||
"only single Generic[...] is allowed (at {})",
|
|
||||||
b.location
|
|
||||||
)]));
|
|
||||||
}
|
|
||||||
is_generic = true;
|
|
||||||
|
|
||||||
let type_var_list: Vec<&Expr<()>>;
|
|
||||||
// if `class A(Generic[T, V, G])`
|
|
||||||
if let ExprKind::Tuple { elts, .. } = &slice.node {
|
|
||||||
type_var_list = elts.iter().collect_vec();
|
|
||||||
// `class A(Generic[T])`
|
|
||||||
} else {
|
|
||||||
type_var_list = vec![&**slice];
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the type vars
|
|
||||||
let type_vars = type_var_list
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| {
|
|
||||||
class_resolver.parse_type_annotation(
|
|
||||||
&temp_def_list,
|
&temp_def_list,
|
||||||
unifier,
|
unifier,
|
||||||
primitives_store,
|
primitives_store,
|
||||||
e,
|
) {
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
// check if all are unique type vars
|
|
||||||
let all_unique_type_var = {
|
|
||||||
let mut occurred_type_var_id: HashSet<TypeVarId> = HashSet::new();
|
|
||||||
type_vars.iter().all(|x| {
|
|
||||||
let ty = unifier.get_ty(*x);
|
|
||||||
if let TypeEnum::TVar { id, .. } = ty.as_ref() {
|
|
||||||
occurred_type_var_id.insert(*id)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
if !all_unique_type_var {
|
|
||||||
return Err(HashSet::from([format!(
|
|
||||||
"duplicate type variable occurs (at {})",
|
|
||||||
slice.location
|
|
||||||
)]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to TopLevelDef
|
|
||||||
class_def_type_vars.extend(type_vars);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if others, do nothing in this function
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut errors = HashSet::new();
|
|
||||||
for (class_def, class_ast) in def_list.iter().skip(self.builtin_num) {
|
|
||||||
if class_ast.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Err(e) = analyze(class_def, class_ast) {
|
|
||||||
errors.extend(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if !errors.is_empty() {
|
|
||||||
return Err(errors);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// step 2, base classes.
|
// Add class ancestors
|
||||||
/// now that the type vars of all classes are done, handle base classes and
|
Self::analyze_class_ancestors(class_def, &temp_def_list);
|
||||||
/// put Self class into the ancestors list. We only allow single inheritance
|
|
||||||
fn analyze_top_level_class_bases(&mut self) -> Result<(), HashSet<String>> {
|
|
||||||
if self.unifier.top_level.is_none() {
|
|
||||||
let ctx = Arc::new(self.make_top_level_context());
|
|
||||||
self.unifier.top_level = Some(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
let temp_def_list = self.extract_def_list();
|
// special case classes that inherit from Exception
|
||||||
let unifier = self.unifier.borrow_mut();
|
let TopLevelDef::Class { ancestors: class_ancestors, .. } = &*class_def.read()
|
||||||
let primitive_types = self.primitives_ty;
|
|
||||||
|
|
||||||
let mut get_direct_parents =
|
|
||||||
|class_def: &Arc<RwLock<TopLevelDef>>, class_ast: &Option<Stmt>| {
|
|
||||||
let mut class_def = class_def.write();
|
|
||||||
let (class_def_id, class_bases, class_ancestors, class_resolver, class_type_vars) = {
|
|
||||||
if let TopLevelDef::Class {
|
|
||||||
ancestors, resolver, object_id, type_vars, ..
|
|
||||||
} = &mut *class_def
|
|
||||||
{
|
|
||||||
let Some(ast::Located {
|
|
||||||
node: ast::StmtKind::ClassDef { bases, .. }, ..
|
|
||||||
}) = class_ast
|
|
||||||
else {
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
(object_id, bases, ancestors, resolver, type_vars)
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let class_resolver = class_resolver.as_ref().unwrap();
|
|
||||||
let class_resolver = &**class_resolver;
|
|
||||||
|
|
||||||
let mut has_base = false;
|
|
||||||
for b in class_bases {
|
|
||||||
// type vars have already been handled, so skip on `Generic[...]`
|
|
||||||
if matches!(
|
|
||||||
&b.node,
|
|
||||||
ast::ExprKind::Subscript { value, .. }
|
|
||||||
if matches!(
|
|
||||||
&value.node,
|
|
||||||
ast::ExprKind::Name { id, .. } if id == &"Generic".into()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_base {
|
|
||||||
return Err(HashSet::from([format!(
|
|
||||||
"a class definition can only have at most one base class \
|
|
||||||
declaration and one generic declaration (at {})",
|
|
||||||
b.location
|
|
||||||
)]));
|
|
||||||
}
|
|
||||||
has_base = true;
|
|
||||||
|
|
||||||
// the function parse_ast_to make sure that no type var occurred in
|
|
||||||
// bast_ty if it is a CustomClassKind
|
|
||||||
let base_ty = parse_ast_to_type_annotation_kinds(
|
|
||||||
class_resolver,
|
|
||||||
&temp_def_list,
|
|
||||||
unifier,
|
|
||||||
&primitive_types,
|
|
||||||
b,
|
|
||||||
vec![(*class_def_id, class_type_vars.clone())]
|
|
||||||
.into_iter()
|
|
||||||
.collect::<HashMap<_, _>>(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let TypeAnnotation::CustomClass { .. } = &base_ty {
|
|
||||||
class_ancestors.push(base_ty);
|
|
||||||
} else {
|
|
||||||
return Err(HashSet::from([format!(
|
|
||||||
"class base declaration can only be custom class (at {})",
|
|
||||||
b.location,
|
|
||||||
)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
// first, only push direct parent into the list
|
|
||||||
let mut errors = HashSet::new();
|
|
||||||
for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
|
|
||||||
if class_ast.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Err(e) = get_direct_parents(class_def, class_ast) {
|
|
||||||
errors.extend(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !errors.is_empty() {
|
|
||||||
return Err(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
// second, get all ancestors
|
|
||||||
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = HashMap::default();
|
|
||||||
let mut get_all_ancestors =
|
|
||||||
|class_def: &Arc<RwLock<TopLevelDef>>| -> Result<(), HashSet<String>> {
|
|
||||||
let class_def = class_def.read();
|
|
||||||
let (class_ancestors, class_id) = {
|
|
||||||
if let TopLevelDef::Class { ancestors, object_id, .. } = &*class_def {
|
|
||||||
(ancestors, *object_id)
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ancestors_store.insert(
|
|
||||||
class_id,
|
|
||||||
// if class has direct parents, get all ancestors of its parents. Else just empty
|
|
||||||
if class_ancestors.is_empty() {
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
Self::get_all_ancestors_helper(
|
|
||||||
&class_ancestors[0],
|
|
||||||
temp_def_list.as_slice(),
|
|
||||||
)?
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
for (class_def, ast) in self.definition_ast_list.iter().skip(self.builtin_num) {
|
|
||||||
if ast.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Err(e) = get_all_ancestors(class_def) {
|
|
||||||
errors.extend(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !errors.is_empty() {
|
|
||||||
return Err(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert the ancestors to the def list
|
|
||||||
for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
|
|
||||||
if class_ast.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut class_def = class_def.write();
|
|
||||||
let (class_ancestors, class_id, class_type_vars) = {
|
|
||||||
if let TopLevelDef::Class { ancestors, object_id, type_vars, .. } = &mut *class_def
|
|
||||||
{
|
|
||||||
(ancestors, *object_id, type_vars)
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ans = ancestors_store.get_mut(&class_id).unwrap();
|
|
||||||
class_ancestors.append(ans);
|
|
||||||
|
|
||||||
// insert self type annotation to the front of the vector to maintain the order
|
|
||||||
class_ancestors
|
|
||||||
.insert(0, make_self_type_annotation(class_type_vars.as_slice(), class_id));
|
|
||||||
|
|
||||||
// special case classes that inherit from Exception
|
|
||||||
if class_ancestors
|
if class_ancestors
|
||||||
.iter()
|
.iter()
|
||||||
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
|
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
|
||||||
{
|
{
|
||||||
// if inherited from Exception, the body should be a pass
|
// if inherited from Exception, the body should be a pass
|
||||||
let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node else {
|
let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node
|
||||||
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
if matches!(
|
if matches!(
|
||||||
stmt.node,
|
stmt.node,
|
||||||
ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. }
|
ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. }
|
||||||
) {
|
) {
|
||||||
return Err(HashSet::from([
|
errors.extend(Err(HashSet::from(["Classes inherited from exception should have no custom fields/methods"])));
|
||||||
"Classes inherited from exception should have no custom fields/methods"
|
|
||||||
.into(),
|
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
temp_def_list.push(class_def.clone());
|
||||||
|
}
|
||||||
|
|
||||||
// deal with ancestor of Exception object
|
// deal with ancestors of Exception object
|
||||||
let TopLevelDef::Class { name, ancestors, object_id, .. } =
|
let TopLevelDef::Class { name, ancestors, object_id, .. } = &mut *def_list[7].0.write()
|
||||||
&mut *self.definition_ast_list[7].0.write()
|
|
||||||
else {
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(*name, "Exception".into());
|
assert_eq!(*name, "Exception".into());
|
||||||
ancestors.push(make_self_type_annotation(&[], *object_id));
|
ancestors.push(make_self_type_annotation(&[], *object_id));
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(errors);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// step 3, class fields and methods
|
/// step 2, class fields and methods
|
||||||
fn analyze_top_level_class_fields_methods(&mut self) -> Result<(), HashSet<String>> {
|
fn analyze_top_level_class_fields_methods(&mut self) -> Result<(), HashSet<String>> {
|
||||||
let temp_def_list = self.extract_def_list();
|
// Allow resolving definition IDs in error messages
|
||||||
let primitives = &self.primitives_ty;
|
if self.unifier.top_level.is_none() {
|
||||||
let def_ast_list = &self.definition_ast_list;
|
let ctx = Arc::new(self.make_top_level_context());
|
||||||
let unifier = self.unifier.borrow_mut();
|
self.unifier.top_level = Some(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
let def_list = &self.definition_ast_list;
|
||||||
|
let temp_def_list = self.extract_def_list();
|
||||||
|
let unifier = self.unifier.borrow_mut();
|
||||||
|
let primitives_store = &self.primitives_ty;
|
||||||
|
|
||||||
|
let mut errors: HashSet<String> = HashSet::new();
|
||||||
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new();
|
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new();
|
||||||
|
|
||||||
let mut errors = HashSet::new();
|
for (class_def, class_ast) in def_list.iter().skip(self.builtin_num) {
|
||||||
for (class_def, class_ast) in def_ast_list.iter().skip(self.builtin_num) {
|
if class_ast.is_some() && matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
|
||||||
if class_ast.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
|
|
||||||
if let Err(e) = Self::analyze_single_class_methods_fields(
|
if let Err(e) = Self::analyze_single_class_methods_fields(
|
||||||
class_def,
|
class_def,
|
||||||
&class_ast.as_ref().unwrap().node,
|
&class_ast.as_ref().unwrap().node,
|
||||||
&temp_def_list,
|
&temp_def_list,
|
||||||
unifier,
|
unifier,
|
||||||
primitives,
|
primitives_store,
|
||||||
&mut type_var_to_concrete_def,
|
&mut type_var_to_concrete_def,
|
||||||
(&self.keyword_list, &self.core_config),
|
(&self.keyword_list, &self.core_config),
|
||||||
) {
|
) {
|
||||||
errors.extend(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
// The errors need to be reported before copying methods from parent to child classes
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors);
|
return Err(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle the inherited methods and fields
|
// The lock on `class_def` must be released once the ancestors are updated
|
||||||
// Note: we cannot defer error handling til the end of the loop, because there is loop
|
{
|
||||||
// carried dependency, ignoring the error (temporarily) will cause all assumptions to break
|
|
||||||
// and produce weird error messages
|
|
||||||
let mut current_ancestor_depth: usize = 2;
|
|
||||||
loop {
|
|
||||||
let mut finished = true;
|
|
||||||
|
|
||||||
for (class_def, class_ast) in def_ast_list.iter().skip(self.builtin_num) {
|
|
||||||
if class_ast.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut class_def = class_def.write();
|
let mut class_def = class_def.write();
|
||||||
if let TopLevelDef::Class { ancestors, .. } = &*class_def {
|
let TopLevelDef::Class { ancestors, .. } = &*class_def else { unreachable!() };
|
||||||
// if the length of the ancestor is equal to the current depth
|
// Methods/fields needs to be processed only if class inherits from another class
|
||||||
// it means that all the ancestors of the class is handled
|
if ancestors.len() > 1 {
|
||||||
if ancestors.len() == current_ancestor_depth {
|
if let Err(e) = Self::analyze_single_class_ancestors(
|
||||||
finished = false;
|
|
||||||
Self::analyze_single_class_ancestors(
|
|
||||||
&mut class_def,
|
&mut class_def,
|
||||||
&temp_def_list,
|
&temp_def_list,
|
||||||
unifier,
|
unifier,
|
||||||
primitives,
|
primitives_store,
|
||||||
&mut type_var_to_concrete_def,
|
&mut type_var_to_concrete_def,
|
||||||
)?;
|
) {
|
||||||
}
|
errors.extend(e);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if finished {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_ancestor_depth += 1;
|
|
||||||
if current_ancestor_depth > def_ast_list.len() + 1 {
|
|
||||||
unreachable!("cannot be longer than the whole top level def list")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,7 +608,7 @@ impl TopLevelComposer {
|
||||||
let target_ty = get_type_from_type_annotation_kinds(
|
let target_ty = get_type_from_type_annotation_kinds(
|
||||||
&temp_def_list,
|
&temp_def_list,
|
||||||
unifier,
|
unifier,
|
||||||
primitives,
|
primitives_store,
|
||||||
&def,
|
&def,
|
||||||
&mut subst_list,
|
&mut subst_list,
|
||||||
)?;
|
)?;
|
||||||
|
@ -873,8 +617,8 @@ impl TopLevelComposer {
|
||||||
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?;
|
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
for (ty, def) in type_var_to_concrete_def {
|
for (ty, def) in &type_var_to_concrete_def {
|
||||||
if let Err(e) = unification_helper(ty, def) {
|
if let Err(e) = unification_helper(*ty, def.clone()) {
|
||||||
errors.extend(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -901,16 +645,15 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
}
|
||||||
return Err(errors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (def, _) in def_ast_list.iter().skip(self.builtin_num) {
|
for (def, _) in def_list.iter().skip(self.builtin_num) {
|
||||||
match &*def.read() {
|
match &*def.read() {
|
||||||
TopLevelDef::Class { resolver: Some(resolver), .. }
|
TopLevelDef::Class { resolver: Some(resolver), .. }
|
||||||
| TopLevelDef::Function { resolver: Some(resolver), .. } => {
|
| TopLevelDef::Function { resolver: Some(resolver), .. } => {
|
||||||
if let Err(e) =
|
if let Err(e) =
|
||||||
resolver.handle_deferred_eval(unifier, &temp_def_list, primitives)
|
resolver.handle_deferred_eval(unifier, &temp_def_list, primitives_store)
|
||||||
{
|
{
|
||||||
errors.insert(e);
|
errors.insert(e);
|
||||||
}
|
}
|
||||||
|
@ -919,10 +662,13 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(errors);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// step 4, after class methods are done, top level functions have nothing unknown
|
/// step 3, after class methods are done, top level functions have nothing unknown
|
||||||
fn analyze_top_level_function(&mut self) -> Result<(), HashSet<String>> {
|
fn analyze_top_level_function(&mut self) -> Result<(), HashSet<String>> {
|
||||||
let def_list = &self.definition_ast_list;
|
let def_list = &self.definition_ast_list;
|
||||||
let keyword_list = &self.keyword_list;
|
let keyword_list = &self.keyword_list;
|
||||||
|
@ -1277,65 +1023,38 @@ impl TopLevelComposer {
|
||||||
let mut method_var_map = VarMap::new();
|
let mut method_var_map = VarMap::new();
|
||||||
|
|
||||||
let arg_types: Vec<FuncArg> = {
|
let arg_types: Vec<FuncArg> = {
|
||||||
// check method parameters cannot have same name
|
// Function arguments must have:
|
||||||
|
// 1) `self` as first argument (we currently do not support staticmethods)
|
||||||
|
// 2) unique names
|
||||||
|
// 3) names different than keywords
|
||||||
|
match args.args.first() {
|
||||||
|
Some(id) if id.node.arg == "self".into() => {},
|
||||||
|
_ => return Err(HashSet::from([format!(
|
||||||
|
"{name} method must have a `self` parameter (at {})", b.location
|
||||||
|
)])),
|
||||||
|
}
|
||||||
let mut defined_parameter_name: HashSet<_> = HashSet::new();
|
let mut defined_parameter_name: HashSet<_> = HashSet::new();
|
||||||
let zelf: StrRef = "self".into();
|
for arg in args.args.iter().skip(1) {
|
||||||
for x in &args.args {
|
if !defined_parameter_name.insert(arg.node.arg) {
|
||||||
if !defined_parameter_name.insert(x.node.arg)
|
return Err(HashSet::from([format!("class method must have a unique parameter names (at {})", b.location)]));
|
||||||
|| (keyword_list.contains(&x.node.arg) && x.node.arg != zelf)
|
}
|
||||||
{
|
if keyword_list.contains(&arg.node.arg) {
|
||||||
return Err(HashSet::from([
|
return Err(HashSet::from([format!("parameter names should not be the same as the keywords (at {})", b.location)]));
|
||||||
format!("top level function must have unique parameter names \
|
|
||||||
and names should not be the same as the keywords (at {})",
|
|
||||||
x.location),
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == &"__init__".into() && !defined_parameter_name.contains(&zelf) {
|
// `self` must not be provided type annotation or default value
|
||||||
return Err(HashSet::from([
|
if args.args.len() == args.defaults.len() {
|
||||||
format!("__init__ method must have a `self` parameter (at {})", b.location),
|
return Err(HashSet::from([format!("`self` cannot have a default value (at {})", b.location)]));
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
if !defined_parameter_name.contains(&zelf) {
|
if args.args[0].node.annotation.is_some() {
|
||||||
return Err(HashSet::from([
|
return Err(HashSet::from([format!("`self` cannot have a type annotation (at {})", b.location)]));
|
||||||
format!("class method must have a `self` parameter (at {})", b.location),
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
let no_defaults = args.args.len() - args.defaults.len() - 1;
|
||||||
let arg_with_default: Vec<(
|
for (idx, x) in itertools::enumerate(args.args.iter().skip(1)) {
|
||||||
&ast::Located<ast::ArgData<()>>,
|
|
||||||
Option<&Expr>,
|
|
||||||
)> = args
|
|
||||||
.args
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.zip(
|
|
||||||
args.defaults
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.map(|x| -> Option<&Expr> { Some(x) })
|
|
||||||
.chain(std::iter::repeat(None)),
|
|
||||||
)
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
for (x, default) in arg_with_default.into_iter().rev() {
|
|
||||||
let name = x.node.arg;
|
|
||||||
if name != zelf {
|
|
||||||
let type_ann = {
|
let type_ann = {
|
||||||
let annotation_expr = x
|
let Some(annotation_expr) = x.node.annotation.as_ref() else {return Err(HashSet::from([format!("type annotation needed for `{}` (at {})", x.node.arg, x.location)]));};
|
||||||
.node
|
|
||||||
.annotation
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| HashSet::from([
|
|
||||||
format!(
|
|
||||||
"type annotation needed for `{}` at {}",
|
|
||||||
x.node.arg, x.location
|
|
||||||
),
|
|
||||||
]))?
|
|
||||||
.as_ref();
|
|
||||||
parse_ast_to_type_annotation_kinds(
|
parse_ast_to_type_annotation_kinds(
|
||||||
class_resolver,
|
class_resolver,
|
||||||
temp_def_list,
|
temp_def_list,
|
||||||
|
@ -1348,8 +1067,7 @@ impl TopLevelComposer {
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
// find type vars within this method parameter type annotation
|
// find type vars within this method parameter type annotation
|
||||||
let type_vars_within =
|
let type_vars_within = get_type_var_contained_in_type_annotation(&type_ann);
|
||||||
get_type_var_contained_in_type_annotation(&type_ann);
|
|
||||||
// handle the class type var and the method type var
|
// handle the class type var and the method type var
|
||||||
for type_var_within in type_vars_within {
|
for type_var_within in type_vars_within {
|
||||||
let TypeAnnotation::TypeVar(ty) = type_var_within else {
|
let TypeAnnotation::TypeVar(ty) = type_var_within else {
|
||||||
|
@ -1364,30 +1082,16 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
// finish handling type vars
|
// finish handling type vars
|
||||||
let dummy_func_arg = FuncArg {
|
let dummy_func_arg = FuncArg {
|
||||||
name,
|
name: x.node.arg,
|
||||||
ty: unifier.get_dummy_var().ty,
|
ty: unifier.get_dummy_var().ty,
|
||||||
default_value: match default {
|
default_value: if idx < no_defaults { None } else {
|
||||||
None => None,
|
let default_idx = idx - no_defaults;
|
||||||
Some(default) => {
|
|
||||||
if name == "self".into() {
|
|
||||||
return Err(HashSet::from([
|
|
||||||
format!("`self` parameter cannot take default value (at {})", x.location),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
Some({
|
Some({
|
||||||
let v = Self::parse_parameter_default_value(
|
let v = Self::parse_parameter_default_value(&args.defaults[default_idx], class_resolver)?;
|
||||||
default,
|
Self::check_default_param_type(&v, &type_ann, primitives, unifier).map_err(|err| HashSet::from([format!("{} (at {})", err, x.location)]))?;
|
||||||
class_resolver,
|
|
||||||
)?;
|
|
||||||
Self::check_default_param_type(
|
|
||||||
&v, &type_ann, primitives, unifier,
|
|
||||||
)
|
|
||||||
.map_err(|err| HashSet::from([
|
|
||||||
format!("{} (at {})", err, x.location),
|
|
||||||
]))?;
|
|
||||||
v
|
v
|
||||||
})
|
})
|
||||||
}
|
|
||||||
},
|
},
|
||||||
is_vararg: false,
|
is_vararg: false,
|
||||||
};
|
};
|
||||||
|
@ -1397,7 +1101,6 @@ impl TopLevelComposer {
|
||||||
.insert(dummy_func_arg.ty, type_ann.clone());
|
.insert(dummy_func_arg.ty, type_ann.clone());
|
||||||
result.push(dummy_func_arg);
|
result.push(dummy_func_arg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
result
|
result
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1559,7 +1262,7 @@ impl TopLevelComposer {
|
||||||
unreachable!("must be type var annotation")
|
unreachable!("must be type var annotation")
|
||||||
};
|
};
|
||||||
|
|
||||||
if !class_type_vars_def.contains(&t) {
|
if !class_type_vars_def.contains(&t){
|
||||||
return Err(HashSet::from([
|
return Err(HashSet::from([
|
||||||
format!(
|
format!(
|
||||||
"class fields can only use type \
|
"class fields can only use type \
|
||||||
|
@ -1593,7 +1296,7 @@ impl TopLevelComposer {
|
||||||
_ => {
|
_ => {
|
||||||
return Err(HashSet::from([
|
return Err(HashSet::from([
|
||||||
format!(
|
format!(
|
||||||
"unsupported statement in class definition body (at {})",
|
"unsupported statement type in class definition body (at {})",
|
||||||
b.location
|
b.location
|
||||||
),
|
),
|
||||||
]))
|
]))
|
||||||
|
@ -1639,7 +1342,6 @@ impl TopLevelComposer {
|
||||||
let TypeAnnotation::CustomClass { id, params: _ } = base else {
|
let TypeAnnotation::CustomClass { id, params: _ } = base else {
|
||||||
unreachable!("must be class type annotation")
|
unreachable!("must be class type annotation")
|
||||||
};
|
};
|
||||||
|
|
||||||
let base = temp_def_list.get(id.0).unwrap();
|
let base = temp_def_list.get(id.0).unwrap();
|
||||||
let base = base.read();
|
let base = base.read();
|
||||||
let TopLevelDef::Class { methods, fields, attributes, .. } = &*base else {
|
let TopLevelDef::Class { methods, fields, attributes, .. } = &*base else {
|
||||||
|
@ -1648,19 +1350,17 @@ impl TopLevelComposer {
|
||||||
|
|
||||||
// handle methods override
|
// handle methods override
|
||||||
// since we need to maintain the order, create a new list
|
// since we need to maintain the order, create a new list
|
||||||
let mut new_child_methods: Vec<(StrRef, Type, DefinitionId)> = Vec::new();
|
let mut new_child_methods: IndexMap<StrRef, (Type, DefinitionId)> =
|
||||||
let mut is_override: HashSet<StrRef> = HashSet::new();
|
methods.iter().map(|m| (m.0, (m.1, m.2))).collect();
|
||||||
for (anc_method_name, anc_method_ty, anc_method_def_id) in methods {
|
|
||||||
// find if there is a method with same name in the child class
|
|
||||||
let mut to_be_added = (*anc_method_name, *anc_method_ty, *anc_method_def_id);
|
|
||||||
for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def {
|
for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def {
|
||||||
if class_method_name == anc_method_name {
|
if let Some((ty, _)) = new_child_methods
|
||||||
// ignore and handle self
|
.insert(*class_method_name, (*class_method_ty, *class_method_defid))
|
||||||
// if is __init__ method, no need to check return type
|
{
|
||||||
let ok = class_method_name == &"__init__".into()
|
let ok = class_method_name == &"__init__".into()
|
||||||
|| Self::check_overload_function_type(
|
|| Self::check_overload_function_type(
|
||||||
*class_method_ty,
|
*class_method_ty,
|
||||||
*anc_method_ty,
|
ty,
|
||||||
unifier,
|
unifier,
|
||||||
type_var_to_concrete_def,
|
type_var_to_concrete_def,
|
||||||
);
|
);
|
||||||
|
@ -1669,72 +1369,48 @@ impl TopLevelComposer {
|
||||||
"method {class_method_name} has same name as ancestors' method, but incompatible type"),
|
"method {class_method_name} has same name as ancestors' method, but incompatible type"),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
// mark it as added
|
|
||||||
is_override.insert(*class_method_name);
|
|
||||||
to_be_added = (*class_method_name, *class_method_ty, *class_method_defid);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_child_methods.push(to_be_added);
|
|
||||||
}
|
|
||||||
// add those that are not overriding method to the new_child_methods
|
|
||||||
for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def {
|
|
||||||
if !is_override.contains(class_method_name) {
|
|
||||||
new_child_methods.push((*class_method_name, *class_method_ty, *class_method_defid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// use the new_child_methods to replace all the elements in `class_methods_def`
|
|
||||||
class_methods_def.clear();
|
class_methods_def.clear();
|
||||||
class_methods_def.extend(new_child_methods);
|
class_methods_def
|
||||||
|
.extend(new_child_methods.iter().map(|f| (*f.0, f.1 .0, f.1 .1)).collect_vec());
|
||||||
|
|
||||||
// handle class fields
|
// handle class fields
|
||||||
let mut new_child_fields: Vec<(StrRef, Type, bool)> = Vec::new();
|
let mut new_child_fields: IndexMap<StrRef, (Type, bool)> =
|
||||||
// let mut is_override: HashSet<_> = HashSet::new();
|
fields.iter().map(|f| (f.0, (f.1, f.2))).collect();
|
||||||
for (anc_field_name, anc_field_ty, mutable) in fields {
|
let mut new_child_attributes: IndexMap<StrRef, (Type, ast::Constant)> =
|
||||||
let to_be_added = (*anc_field_name, *anc_field_ty, *mutable);
|
attributes.iter().map(|f| (f.0, (f.1, f.2.clone()))).collect();
|
||||||
// find if there is a fields with the same name in the child class
|
// Overriding class fields and attributes is currently not supported
|
||||||
for (class_field_name, ..) in &*class_fields_def {
|
for (name, ty, mutable) in &*class_fields_def {
|
||||||
if class_field_name == anc_field_name
|
if new_child_fields.insert(*name, (*ty, *mutable)).is_some()
|
||||||
|| attributes.iter().any(|f| f.0 == *class_field_name)
|
|| new_child_attributes.contains_key(name)
|
||||||
{
|
{
|
||||||
return Err(HashSet::from([format!(
|
return Err(HashSet::from([format!(
|
||||||
"field `{class_field_name}` has already declared in the ancestor classes"
|
"field `{name}` has already declared in the ancestor classes"
|
||||||
)]));
|
)]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_child_fields.push(to_be_added);
|
for (name, ty, val) in &*class_attribute_def {
|
||||||
}
|
if new_child_attributes.insert(*name, (*ty, val.clone())).is_some()
|
||||||
|
|| new_child_fields.contains_key(name)
|
||||||
// handle class attributes
|
|
||||||
let mut new_child_attributes: Vec<(StrRef, Type, ast::Constant)> = Vec::new();
|
|
||||||
for (anc_attr_name, anc_attr_ty, attr_value) in attributes {
|
|
||||||
let to_be_added = (*anc_attr_name, *anc_attr_ty, attr_value.clone());
|
|
||||||
// find if there is a attribute with the same name in the child class
|
|
||||||
for (class_attr_name, ..) in &*class_attribute_def {
|
|
||||||
if class_attr_name == anc_attr_name
|
|
||||||
|| fields.iter().any(|f| f.0 == *class_attr_name)
|
|
||||||
{
|
{
|
||||||
return Err(HashSet::from([format!(
|
return Err(HashSet::from([format!(
|
||||||
"attribute `{class_attr_name}` has already declared in the ancestor classes"
|
"attribute `{name}` has already declared in the ancestor classes"
|
||||||
)]));
|
)]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_child_attributes.push(to_be_added);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (class_field_name, class_field_ty, mutable) in &*class_fields_def {
|
|
||||||
if !is_override.contains(class_field_name) {
|
|
||||||
new_child_fields.push((*class_field_name, *class_field_ty, *mutable));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class_fields_def.clear();
|
class_fields_def.clear();
|
||||||
class_fields_def.extend(new_child_fields);
|
class_fields_def
|
||||||
|
.extend(new_child_fields.iter().map(|f| (*f.0, f.1 .0, f.1 .1)).collect_vec());
|
||||||
class_attribute_def.clear();
|
class_attribute_def.clear();
|
||||||
class_attribute_def.extend(new_child_attributes);
|
class_attribute_def.extend(
|
||||||
|
new_child_attributes.iter().map(|f| (*f.0, f.1 .0, f.1 .1.clone())).collect_vec(),
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// step 5, analyze and call type inferencer to fill the `instance_to_stmt` of
|
/// step 4, analyze and call type inferencer to fill the `instance_to_stmt` of
|
||||||
/// [`TopLevelDef::Function`]
|
/// [`TopLevelDef::Function`]
|
||||||
fn analyze_function_instance(&mut self) -> Result<(), HashSet<String>> {
|
fn analyze_function_instance(&mut self) -> Result<(), HashSet<String>> {
|
||||||
// first get the class constructor type correct for the following type check in function body
|
// first get the class constructor type correct for the following type check in function body
|
||||||
|
@ -2265,7 +1941,7 @@ impl TopLevelComposer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Step 6. Analyze and populate the types of global variables.
|
/// Step 5. Analyze and populate the types of global variables.
|
||||||
fn analyze_top_level_variables(&mut self) -> Result<(), HashSet<String>> {
|
fn analyze_top_level_variables(&mut self) -> Result<(), HashSet<String>> {
|
||||||
let def_list = &self.definition_ast_list;
|
let def_list = &self.definition_ast_list;
|
||||||
let temp_def_list = self.extract_def_list();
|
let temp_def_list = self.extract_def_list();
|
||||||
|
|
|
@ -624,64 +624,6 @@ impl TopLevelComposer {
|
||||||
Err(HashSet::from([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. \
|
|
||||||
/// this function should called only after the direct parent is set
|
|
||||||
/// and before all the ancestors are set
|
|
||||||
/// and when we allow single inheritance \
|
|
||||||
/// the order of the returned list is from the child to the deepest ancestor
|
|
||||||
pub fn get_all_ancestors_helper(
|
|
||||||
child: &TypeAnnotation,
|
|
||||||
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
|
||||||
) -> Result<Vec<TypeAnnotation>, HashSet<String>> {
|
|
||||||
let mut result: Vec<TypeAnnotation> = Vec::new();
|
|
||||||
let mut parent = Self::get_parent(child, temp_def_list);
|
|
||||||
while let Some(p) = parent {
|
|
||||||
parent = Self::get_parent(&p, temp_def_list);
|
|
||||||
let p_id = if let TypeAnnotation::CustomClass { id, .. } = &p {
|
|
||||||
*id
|
|
||||||
} else {
|
|
||||||
unreachable!("must be class kind annotation")
|
|
||||||
};
|
|
||||||
// check cycle
|
|
||||||
let no_cycle = result.iter().all(|x| {
|
|
||||||
let TypeAnnotation::CustomClass { id, .. } = x else {
|
|
||||||
unreachable!("must be class kind annotation")
|
|
||||||
};
|
|
||||||
|
|
||||||
id.0 != p_id.0
|
|
||||||
});
|
|
||||||
if no_cycle {
|
|
||||||
result.push(p);
|
|
||||||
} else {
|
|
||||||
return Err(HashSet::from(["cyclic inheritance detected".into()]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// should only be called when finding all ancestors, so panic when wrong
|
|
||||||
fn get_parent(
|
|
||||||
child: &TypeAnnotation,
|
|
||||||
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
|
||||||
) -> Option<TypeAnnotation> {
|
|
||||||
let child_id = if let TypeAnnotation::CustomClass { id, .. } = child {
|
|
||||||
*id
|
|
||||||
} else {
|
|
||||||
unreachable!("should be class type annotation")
|
|
||||||
};
|
|
||||||
let child_def = temp_def_list.get(child_id.0).unwrap();
|
|
||||||
let child_def = child_def.read();
|
|
||||||
let TopLevelDef::Class { ancestors, .. } = &*child_def else {
|
|
||||||
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<TypeVarId, HashSet<String>> {
|
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<TypeVarId, 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() {
|
||||||
|
@ -991,6 +933,139 @@ impl TopLevelComposer {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses the class type variables and direct parents
|
||||||
|
/// we only allow single inheritance
|
||||||
|
pub fn analyze_class_bases(
|
||||||
|
class_def: &Arc<RwLock<TopLevelDef>>,
|
||||||
|
class_ast: &Option<Stmt>,
|
||||||
|
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
||||||
|
unifier: &mut Unifier,
|
||||||
|
primitives_store: &PrimitiveStore,
|
||||||
|
) -> Result<(), HashSet<String>> {
|
||||||
|
let mut class_def = class_def.write();
|
||||||
|
let (class_def_id, class_ancestors, class_bases_ast, class_type_vars, class_resolver) = {
|
||||||
|
let TopLevelDef::Class { object_id, ancestors, type_vars, resolver, .. } =
|
||||||
|
&mut *class_def
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let Some(ast::Located { node: ast::StmtKind::ClassDef { bases, .. }, .. }) = class_ast
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
(object_id, ancestors, bases, type_vars, resolver.as_ref().unwrap().as_ref())
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut is_generic = false;
|
||||||
|
let mut has_base = false;
|
||||||
|
// Check class bases for typevars
|
||||||
|
for b in class_bases_ast {
|
||||||
|
match &b.node {
|
||||||
|
// analyze typevars bounded to the class,
|
||||||
|
// only support things like `class A(Generic[T, V])`,
|
||||||
|
// things like `class A(Generic[T, V, ImportedModule.T])` is not supported
|
||||||
|
// i.e. only simple names are allowed in the subscript
|
||||||
|
// should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params
|
||||||
|
ast::ExprKind::Subscript { value, slice, .. } if matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"Generic".into()) =>
|
||||||
|
{
|
||||||
|
if is_generic {
|
||||||
|
return Err(HashSet::from([format!(
|
||||||
|
"only single Generic[...] is allowed (at {})",
|
||||||
|
b.location
|
||||||
|
)]));
|
||||||
|
}
|
||||||
|
is_generic = true;
|
||||||
|
|
||||||
|
let type_var_list: Vec<&ast::Expr<()>>;
|
||||||
|
// if `class A(Generic[T, V, G])`
|
||||||
|
if let ast::ExprKind::Tuple { elts, .. } = &slice.node {
|
||||||
|
type_var_list = elts.iter().collect_vec();
|
||||||
|
// `class A(Generic[T])`
|
||||||
|
} else {
|
||||||
|
type_var_list = vec![&**slice];
|
||||||
|
}
|
||||||
|
|
||||||
|
let type_vars = type_var_list
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| {
|
||||||
|
class_resolver.parse_type_annotation(
|
||||||
|
temp_def_list,
|
||||||
|
unifier,
|
||||||
|
primitives_store,
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
class_type_vars.extend(type_vars);
|
||||||
|
}
|
||||||
|
ast::ExprKind::Name { .. } | ast::ExprKind::Subscript { .. } => {
|
||||||
|
if has_base {
|
||||||
|
return Err(HashSet::from([format!("a class definition can only have at most one base class declaration and one generic declaration (at {})", b.location )]));
|
||||||
|
}
|
||||||
|
has_base = true;
|
||||||
|
// the function parse_ast_to make sure that no type var occurred in
|
||||||
|
// bast_ty if it is a CustomClassKind
|
||||||
|
let base_ty = parse_ast_to_type_annotation_kinds(
|
||||||
|
class_resolver,
|
||||||
|
temp_def_list,
|
||||||
|
unifier,
|
||||||
|
primitives_store,
|
||||||
|
b,
|
||||||
|
vec![(*class_def_id, class_type_vars.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect::<HashMap<_, _>>(),
|
||||||
|
)?;
|
||||||
|
if let TypeAnnotation::CustomClass { .. } = &base_ty {
|
||||||
|
class_ancestors.push(base_ty);
|
||||||
|
} else {
|
||||||
|
return Err(HashSet::from([format!(
|
||||||
|
"class base declaration can only be custom class (at {})",
|
||||||
|
b.location
|
||||||
|
)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(HashSet::from([format!(
|
||||||
|
"unsupported statement in class defintion (at {})",
|
||||||
|
b.location
|
||||||
|
)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// gets all ancestors of a class
|
||||||
|
pub fn analyze_class_ancestors(
|
||||||
|
class_def: &Arc<RwLock<TopLevelDef>>,
|
||||||
|
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
||||||
|
) {
|
||||||
|
// Check if class has a direct parent
|
||||||
|
let mut class_def = class_def.write();
|
||||||
|
let TopLevelDef::Class { ancestors, type_vars, object_id, .. } = &mut *class_def else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let mut anc_set = HashMap::new();
|
||||||
|
|
||||||
|
if let Some(ancestor) = ancestors.first() {
|
||||||
|
let TypeAnnotation::CustomClass { id, .. } = ancestor else { unreachable!() };
|
||||||
|
let TopLevelDef::Class { ancestors: parent_ancestors, .. } =
|
||||||
|
&*temp_def_list[id.0].read()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
for anc in parent_ancestors.iter().skip(1) {
|
||||||
|
let TypeAnnotation::CustomClass { id, .. } = anc else { unreachable!() };
|
||||||
|
anc_set.insert(id, anc.clone());
|
||||||
|
}
|
||||||
|
ancestors.extend(anc_set.into_values());
|
||||||
|
}
|
||||||
|
// push `self` as first ancestor of class
|
||||||
|
ancestors.insert(0, make_self_type_annotation(type_vars.as_slice(), *object_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_parameter_default_value(
|
pub fn parse_parameter_default_value(
|
||||||
|
|
|
@ -3,10 +3,10 @@ source: nac3core/src/toplevel/test.rs
|
||||||
expression: res_vec
|
expression: res_vec
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
"Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
|
|
||||||
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
|
||||||
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [TypeVarId(241)]\n}\n",
|
|
||||||
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
|
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
|
||||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
|
||||||
|
"Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
|
||||||
|
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
|
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [TypeVarId(246)]\n}\n",
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,11 +7,11 @@ expression: res_vec
|
||||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [TypeVarId(249)]\n}\n",
|
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [TypeVarId(249)]\n}\n",
|
||||||
"Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
|
||||||
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
|
||||||
"Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
"Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
||||||
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
|
||||||
|
"Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
|
||||||
|
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n",
|
"Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n",
|
||||||
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(257)]\n}\n",
|
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(257)]\n}\n",
|
||||||
]
|
]
|
||||||
|
|
|
@ -229,11 +229,6 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
||||||
def foo(self, a: T, b: V):
|
def foo(self, a: T, b: V):
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
indoc! {"
|
|
||||||
class B(C):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
"},
|
|
||||||
indoc! {"
|
indoc! {"
|
||||||
class C(A):
|
class C(A):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -242,6 +237,11 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
||||||
a = 1
|
a = 1
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
|
indoc! {"
|
||||||
|
class B(C):
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
"},
|
||||||
indoc! {"
|
indoc! {"
|
||||||
def foo(a: A):
|
def foo(a: A):
|
||||||
pass
|
pass
|
||||||
|
@ -256,14 +256,6 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
||||||
)]
|
)]
|
||||||
#[test_case(
|
#[test_case(
|
||||||
&[
|
&[
|
||||||
indoc! {"
|
|
||||||
class Generic_A(Generic[V], B):
|
|
||||||
a: int64
|
|
||||||
def __init__(self):
|
|
||||||
self.a = 123123123123
|
|
||||||
def fun(self, a: int32) -> V:
|
|
||||||
pass
|
|
||||||
"},
|
|
||||||
indoc! {"
|
indoc! {"
|
||||||
class B:
|
class B:
|
||||||
aa: bool
|
aa: bool
|
||||||
|
@ -271,6 +263,14 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
||||||
self.aa = False
|
self.aa = False
|
||||||
def foo(self, b: T):
|
def foo(self, b: T):
|
||||||
pass
|
pass
|
||||||
|
"},
|
||||||
|
indoc! {"
|
||||||
|
class Generic_A(Generic[V], B):
|
||||||
|
a: int64
|
||||||
|
def __init__(self):
|
||||||
|
self.a = 123123123123
|
||||||
|
def fun(self, a: int32) -> V:
|
||||||
|
pass
|
||||||
"}
|
"}
|
||||||
],
|
],
|
||||||
&[];
|
&[];
|
||||||
|
@ -390,18 +390,18 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
||||||
pass
|
pass
|
||||||
"}
|
"}
|
||||||
],
|
],
|
||||||
&["cyclic inheritance detected"];
|
&["NameError: name 'B' is not defined (at unknown:1:9)"];
|
||||||
"cyclic1"
|
"cyclic1"
|
||||||
)]
|
)]
|
||||||
#[test_case(
|
#[test_case(
|
||||||
&[
|
&[
|
||||||
indoc! {"
|
indoc! {"
|
||||||
class A(B[bool, int64]):
|
class B(Generic[V, T], C[int32]):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
indoc! {"
|
indoc! {"
|
||||||
class B(Generic[V, T], C[int32]):
|
class A(B[bool, int64]):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
|
@ -411,7 +411,7 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
],
|
],
|
||||||
&["cyclic inheritance detected"];
|
&["NameError: name 'C' is not defined (at unknown:1:25)"];
|
||||||
"cyclic2"
|
"cyclic2"
|
||||||
)]
|
)]
|
||||||
#[test_case(
|
#[test_case(
|
||||||
|
@ -435,11 +435,6 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
||||||
)]
|
)]
|
||||||
#[test_case(
|
#[test_case(
|
||||||
&[
|
&[
|
||||||
indoc! {"
|
|
||||||
class A(B, Generic[T], C):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
"},
|
|
||||||
indoc! {"
|
indoc! {"
|
||||||
class B:
|
class B:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -449,6 +444,11 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
||||||
class C:
|
class C:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
"},
|
||||||
|
indoc! {"
|
||||||
|
class A(B, Generic[T], C):
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
"}
|
"}
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
|
@ -100,7 +100,13 @@ pub fn parse_ast_to_type_annotation_kinds<T, S: std::hash::BuildHasher + Clone>(
|
||||||
Ok(TypeAnnotation::CustomClass { id: PrimDef::Exception.id(), params: Vec::default() })
|
Ok(TypeAnnotation::CustomClass { id: PrimDef::Exception.id(), params: Vec::default() })
|
||||||
} else if let Ok(obj_id) = resolver.get_identifier_def(*id) {
|
} else if let Ok(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 Some(top_level_def) = top_level_defs.get(obj_id.0) else {
|
||||||
|
return Err(HashSet::from([format!(
|
||||||
|
"NameError: name '{id}' is not defined (at {})",
|
||||||
|
expr.location
|
||||||
|
)]));
|
||||||
|
};
|
||||||
|
let def_read = top_level_def.try_read();
|
||||||
if let Some(def_read) = def_read {
|
if let Some(def_read) = def_read {
|
||||||
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
||||||
type_vars.clone()
|
type_vars.clone()
|
||||||
|
@ -155,12 +161,17 @@ pub fn parse_ast_to_type_annotation_kinds<T, S: std::hash::BuildHasher + Clone>(
|
||||||
}
|
}
|
||||||
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 Some(top_level_def) = top_level_defs.get(obj_id.0) else {
|
||||||
|
return Err(HashSet::from([format!(
|
||||||
|
"NameError: name '{id}' is not defined (at {})",
|
||||||
|
expr.location
|
||||||
|
)]));
|
||||||
|
};
|
||||||
|
let def_read = top_level_def.try_read();
|
||||||
if let Some(def_read) = def_read {
|
if let Some(def_read) = def_read {
|
||||||
let TopLevelDef::Class { type_vars, .. } = &*def_read else {
|
let TopLevelDef::Class { type_vars, .. } = &*def_read else {
|
||||||
unreachable!("must be class here")
|
unreachable!("must be class here")
|
||||||
};
|
};
|
||||||
|
|
||||||
type_vars.clone()
|
type_vars.clone()
|
||||||
} else {
|
} else {
|
||||||
locked.get(&obj_id).unwrap().clone()
|
locked.get(&obj_id).unwrap().clone()
|
||||||
|
|
|
@ -24,26 +24,15 @@ pub const DW_EH_PE_aligned: u8 = 0x50;
|
||||||
|
|
||||||
pub const DW_EH_PE_indirect: u8 = 0x80;
|
pub const DW_EH_PE_indirect: u8 = 0x80;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct DwarfReader<'a> {
|
pub struct DwarfReader<'a> {
|
||||||
pub slice: &'a [u8],
|
pub slice: &'a [u8],
|
||||||
pub virt_addr: u32,
|
pub virt_addr: u32,
|
||||||
base_slice: &'a [u8],
|
|
||||||
base_virt_addr: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DwarfReader<'a> {
|
impl<'a> DwarfReader<'a> {
|
||||||
pub fn new(slice: &[u8], virt_addr: u32) -> DwarfReader {
|
pub fn new(slice: &[u8], virt_addr: u32) -> DwarfReader {
|
||||||
DwarfReader { slice, virt_addr, base_slice: slice, base_virt_addr: virt_addr }
|
DwarfReader { slice, virt_addr }
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new instance from another instance of [DwarfReader], optionally removing any
|
|
||||||
/// offsets previously applied to the other instance.
|
|
||||||
pub fn from_reader(other: &DwarfReader<'a>, reset_offset: bool) -> DwarfReader<'a> {
|
|
||||||
if reset_offset {
|
|
||||||
DwarfReader::new(other.base_slice, other.base_virt_addr)
|
|
||||||
} else {
|
|
||||||
DwarfReader::new(other.slice, other.virt_addr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(&mut self, offset: u32) {
|
pub fn offset(&mut self, offset: u32) {
|
||||||
|
@ -90,6 +79,7 @@ impl<'a> DwarfReader<'a> {
|
||||||
pub fn read_u8(&mut self) -> u8 {
|
pub fn read_u8(&mut self) -> u8 {
|
||||||
let val = self.slice[0];
|
let val = self.slice[0];
|
||||||
self.slice = &self.slice[1..];
|
self.slice = &self.slice[1..];
|
||||||
|
self.virt_addr += 1;
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +91,7 @@ macro_rules! impl_read_fn {
|
||||||
pub fn $byteorder_fn(&mut self) -> $type {
|
pub fn $byteorder_fn(&mut self) -> $type {
|
||||||
let val = LittleEndian::$byteorder_fn(self.slice);
|
let val = LittleEndian::$byteorder_fn(self.slice);
|
||||||
self.slice = &self.slice[mem::size_of::<$type>()..];
|
self.slice = &self.slice[mem::size_of::<$type>()..];
|
||||||
|
self.virt_addr += mem::size_of::<$type>() as u32;
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
@ -226,7 +217,7 @@ impl<'a> EH_Frame<'a> {
|
||||||
|
|
||||||
/// Returns an [Iterator] over all Call Frame Information (CFI) records.
|
/// Returns an [Iterator] over all Call Frame Information (CFI) records.
|
||||||
pub fn cfi_records(&self) -> CFI_Records<'a> {
|
pub fn cfi_records(&self) -> CFI_Records<'a> {
|
||||||
let reader = DwarfReader::from_reader(&self.reader, true);
|
let reader = self.reader.clone();
|
||||||
let len = reader.slice.len();
|
let len = reader.slice.len();
|
||||||
|
|
||||||
CFI_Records { reader, available: len }
|
CFI_Records { reader, available: len }
|
||||||
|
@ -257,7 +248,7 @@ impl<'a> CFI_Record<'a> {
|
||||||
0xFFFF_FFFF => unimplemented!(),
|
0xFFFF_FFFF => unimplemented!(),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let mut fde_reader = DwarfReader::from_reader(cie_reader, false);
|
let mut fde_reader = cie_reader.clone();
|
||||||
fde_reader.offset(length);
|
fde_reader.offset(length);
|
||||||
fde_reader
|
fde_reader
|
||||||
}
|
}
|
||||||
|
@ -276,7 +267,7 @@ impl<'a> CFI_Record<'a> {
|
||||||
// Skip code/data alignment factors & return address register along the way as well
|
// Skip code/data alignment factors & return address register along the way as well
|
||||||
// We only tackle the case where 'z' and 'R' are part of the augmentation string, otherwise
|
// We only tackle the case where 'z' and 'R' are part of the augmentation string, otherwise
|
||||||
// we cannot get the addresses to make .eh_frame_hdr
|
// we cannot get the addresses to make .eh_frame_hdr
|
||||||
let mut aug_data_reader = DwarfReader::from_reader(cie_reader, false);
|
let mut aug_data_reader = cie_reader.clone();
|
||||||
let mut aug_str_len = 0;
|
let mut aug_str_len = 0;
|
||||||
loop {
|
loop {
|
||||||
if aug_data_reader.read_u8() == b'\0' {
|
if aug_data_reader.read_u8() == b'\0' {
|
||||||
|
@ -319,7 +310,7 @@ impl<'a> CFI_Record<'a> {
|
||||||
/// Returns a [DwarfReader] initialized to the first Frame Description Entry (FDE) of this CFI
|
/// Returns a [DwarfReader] initialized to the first Frame Description Entry (FDE) of this CFI
|
||||||
/// record.
|
/// record.
|
||||||
pub fn get_fde_reader(&self) -> DwarfReader<'a> {
|
pub fn get_fde_reader(&self) -> DwarfReader<'a> {
|
||||||
DwarfReader::from_reader(&self.fde_reader, true)
|
self.fde_reader.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an [Iterator] over all Frame Description Entries (FDEs).
|
/// Returns an [Iterator] over all Frame Description Entries (FDEs).
|
||||||
|
@ -347,7 +338,7 @@ impl<'a> Iterator for CFI_Records<'a> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut this_reader = DwarfReader::from_reader(&self.reader, false);
|
let mut this_reader = self.reader.clone();
|
||||||
|
|
||||||
// Remove the length of the header and the content from the counter
|
// Remove the length of the header and the content from the counter
|
||||||
let length = self.reader.read_u32();
|
let length = self.reader.read_u32();
|
||||||
|
@ -360,7 +351,7 @@ impl<'a> Iterator for CFI_Records<'a> {
|
||||||
|
|
||||||
// Remove the length of the header and the content from the counter
|
// Remove the length of the header and the content from the counter
|
||||||
self.available -= length + mem::size_of::<u32>();
|
self.available -= length + mem::size_of::<u32>();
|
||||||
let mut next_reader = DwarfReader::from_reader(&self.reader, false);
|
let mut next_reader = self.reader.clone();
|
||||||
next_reader.offset(length as u32);
|
next_reader.offset(length as u32);
|
||||||
|
|
||||||
let cie_ptr = self.reader.read_u32();
|
let cie_ptr = self.reader.read_u32();
|
||||||
|
@ -396,6 +387,8 @@ impl<'a> Iterator for FDE_Records<'a> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fde_addr = self.reader.virt_addr;
|
||||||
|
|
||||||
// Remove the length of the header and the content from the counter
|
// Remove the length of the header and the content from the counter
|
||||||
let length = match self.reader.read_u32() {
|
let length = match self.reader.read_u32() {
|
||||||
// eh_frame with 0-length means the CIE is terminated
|
// eh_frame with 0-length means the CIE is terminated
|
||||||
|
@ -406,14 +399,14 @@ impl<'a> Iterator for FDE_Records<'a> {
|
||||||
|
|
||||||
// Remove the length of the header and the content from the counter
|
// Remove the length of the header and the content from the counter
|
||||||
self.available -= length + mem::size_of::<u32>();
|
self.available -= length + mem::size_of::<u32>();
|
||||||
let mut next_fde_reader = DwarfReader::from_reader(&self.reader, false);
|
let mut next_fde_reader = self.reader.clone();
|
||||||
next_fde_reader.offset(length as u32);
|
next_fde_reader.offset(length as u32);
|
||||||
|
|
||||||
let cie_ptr = self.reader.read_u32();
|
let cie_ptr = self.reader.read_u32();
|
||||||
let next_val = if cie_ptr != 0 {
|
let next_val = if cie_ptr != 0 {
|
||||||
let pc_begin = read_encoded_pointer_with_pc(&mut self.reader, self.pointer_encoding)
|
let pc_begin = read_encoded_pointer_with_pc(&mut self.reader, self.pointer_encoding)
|
||||||
.expect("Failed to read PC Begin");
|
.expect("Failed to read PC Begin");
|
||||||
Some((pc_begin as u32, self.reader.virt_addr))
|
Some((pc_begin as u32, fde_addr))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -446,9 +439,7 @@ impl<'a> EH_Frame_Hdr<'a> {
|
||||||
writer.write_u8(0x03); // fde_count_enc - 4-byte unsigned value
|
writer.write_u8(0x03); // fde_count_enc - 4-byte unsigned value
|
||||||
writer.write_u8(0x3B); // table_enc - .eh_frame_hdr section-relative 4-byte signed value
|
writer.write_u8(0x3B); // table_enc - .eh_frame_hdr section-relative 4-byte signed value
|
||||||
|
|
||||||
let eh_frame_offset = eh_frame_addr.wrapping_sub(
|
let eh_frame_offset = eh_frame_addr.wrapping_sub(eh_frame_hdr_addr + writer.offset as u32);
|
||||||
eh_frame_hdr_addr + writer.offset as u32 + ((mem::size_of::<u8>() as u32) * 4),
|
|
||||||
);
|
|
||||||
writer.write_u32(eh_frame_offset); // eh_frame_ptr
|
writer.write_u32(eh_frame_offset); // eh_frame_ptr
|
||||||
writer.write_u32(0); // `fde_count`, will be written in finalize_fde
|
writer.write_u32(0); // `fde_count`, will be written in finalize_fde
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
no-escape-analysis = ["nac3core/no-escape-analysis"]
|
no-escape-analysis = ["nac3core/no-escape-analysis"]
|
||||||
tracing = ["nac3core/tracing"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
|
|
|
@ -65,7 +65,7 @@ struct cslice {
|
||||||
size_t len;
|
size_t len;
|
||||||
};
|
};
|
||||||
|
|
||||||
void output_int32_list(const struct cslice* slice) {
|
void output_int32_list(struct cslice* slice) {
|
||||||
const int32_t* data = (int32_t*)slice->data;
|
const int32_t* data = (int32_t*)slice->data;
|
||||||
|
|
||||||
putchar('[');
|
putchar('[');
|
||||||
|
@ -80,7 +80,7 @@ void output_int32_list(const struct cslice* slice) {
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_str(const struct cslice* slice) {
|
void output_str(struct cslice* slice) {
|
||||||
const char* data = (const char*)slice->data;
|
const char* data = (const char*)slice->data;
|
||||||
|
|
||||||
for (size_t i = 0; i < slice->len; ++i) {
|
for (size_t i = 0; i < slice->len; ++i) {
|
||||||
|
@ -88,12 +88,12 @@ void output_str(const struct cslice* slice) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_strln(const struct cslice* slice) {
|
void output_strln(struct cslice* slice) {
|
||||||
output_str(slice);
|
output_str(slice);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t dbg_stack_address(__attribute__((unused)) const struct cslice* slice) {
|
uint64_t dbg_stack_address(__attribute__((unused)) struct cslice* slice) {
|
||||||
int i;
|
int i;
|
||||||
void* ptr = (void*)&i;
|
void* ptr = (void*)&i;
|
||||||
return (uintptr_t)ptr;
|
return (uintptr_t)ptr;
|
||||||
|
|
|
@ -15,11 +15,8 @@ use parking_lot::{Mutex, RwLock};
|
||||||
|
|
||||||
use nac3core::{
|
use nac3core::{
|
||||||
codegen::{
|
codegen::{
|
||||||
concrete_type::ConcreteTypeStore,
|
concrete_type::ConcreteTypeStore, irrt::load_irrt, CodeGenLLVMOptions,
|
||||||
irrt::load_irrt,
|
CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
|
||||||
tracert::{load_tracert, TraceRuntimeConfig},
|
|
||||||
CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator,
|
|
||||||
WithCall, WorkerRegistry,
|
|
||||||
},
|
},
|
||||||
inkwell::{
|
inkwell::{
|
||||||
memory_buffer::MemoryBuffer, module::Linkage, passes::PassBuilderOptions,
|
memory_buffer::MemoryBuffer, module::Linkage, passes::PassBuilderOptions,
|
||||||
|
@ -79,9 +76,6 @@ struct CommandLineArgs {
|
||||||
/// Additional target features to enable/disable, specified using the `+`/`-` prefixes.
|
/// Additional target features to enable/disable, specified using the `+`/`-` prefixes.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
target_features: Option<String>,
|
target_features: Option<String>,
|
||||||
|
|
||||||
#[arg(long)]
|
|
||||||
trace: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_typevar_definition(
|
fn handle_typevar_definition(
|
||||||
|
@ -282,16 +276,8 @@ fn handle_global_var(
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cli = CommandLineArgs::parse();
|
let cli = CommandLineArgs::parse();
|
||||||
let CommandLineArgs {
|
let CommandLineArgs { file_name, threads, opt_level, emit_llvm, triple, mcpu, target_features } =
|
||||||
file_name,
|
cli;
|
||||||
threads,
|
|
||||||
opt_level,
|
|
||||||
emit_llvm,
|
|
||||||
triple,
|
|
||||||
mcpu,
|
|
||||||
target_features,
|
|
||||||
trace,
|
|
||||||
} = cli;
|
|
||||||
|
|
||||||
Target::initialize_all(&InitializationConfig::default());
|
Target::initialize_all(&InitializationConfig::default());
|
||||||
|
|
||||||
|
@ -320,7 +306,6 @@ fn main() {
|
||||||
// The default behavior for -O<n> where n>3 defaults to O3 for both Clang and GCC
|
// The default behavior for -O<n> where n>3 defaults to O3 for both Clang and GCC
|
||||||
_ => OptimizationLevel::Aggressive,
|
_ => OptimizationLevel::Aggressive,
|
||||||
};
|
};
|
||||||
let tracert_config = TraceRuntimeConfig { enabled_tags: trace };
|
|
||||||
|
|
||||||
let target_machine_options = CodeGenTargetMachineOptions {
|
let target_machine_options = CodeGenTargetMachineOptions {
|
||||||
triple,
|
triple,
|
||||||
|
@ -365,14 +350,6 @@ fn main() {
|
||||||
irrt.write_bitcode_to_path(Path::new("irrt.bc"));
|
irrt.write_bitcode_to_path(Path::new("irrt.bc"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process tracert
|
|
||||||
let tracert = load_tracert(&context, &tracert_config);
|
|
||||||
if let Some(tracert) = &tracert {
|
|
||||||
if emit_llvm {
|
|
||||||
tracert.write_bitcode_to_path(Path::new("tracert.bc"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the Python script
|
// Process the Python script
|
||||||
let parser_result = parser::parse_program(&program, file_name.into()).unwrap();
|
let parser_result = parser::parse_program(&program, file_name.into()).unwrap();
|
||||||
|
|
||||||
|
@ -481,8 +458,7 @@ fn main() {
|
||||||
let threads = (0..threads)
|
let threads = (0..threads)
|
||||||
.map(|i| Box::new(DefaultCodeGenerator::new(format!("module{i}"), size_t)))
|
.map(|i| Box::new(DefaultCodeGenerator::new(format!("module{i}"), size_t)))
|
||||||
.collect();
|
.collect();
|
||||||
let (registry, handles) =
|
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, &llvm_options, &f);
|
||||||
WorkerRegistry::create_workers(threads, top_level, &llvm_options, &tracert_config, &f);
|
|
||||||
registry.add_task(task);
|
registry.add_task(task);
|
||||||
registry.wait_tasks_complete(handles);
|
registry.wait_tasks_complete(handles);
|
||||||
|
|
||||||
|
@ -508,9 +484,6 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
main.link_in_module(irrt).unwrap();
|
main.link_in_module(irrt).unwrap();
|
||||||
if let Some(tracert) = tracert {
|
|
||||||
main.link_in_module(tracert).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private all functions except "run"
|
// Private all functions except "run"
|
||||||
let mut function_iter = main.get_first_function();
|
let mut function_iter = main.get_first_function();
|
||||||
|
|
Loading…
Reference in New Issue