Refactor to use Diagnostic Class

This commit is contained in:
David Mak 2023-11-15 17:30:26 +08:00
parent 1c071a294c
commit 316c16d5a6
18 changed files with 2051 additions and 979 deletions

2
Cargo.lock generated
View File

@ -641,6 +641,7 @@ name = "nac3artiq"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"inkwell", "inkwell",
"itertools 0.11.0",
"nac3core", "nac3core",
"nac3ld", "nac3ld",
"nac3parser", "nac3parser",
@ -663,6 +664,7 @@ dependencies = [
name = "nac3core" name = "nac3core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap",
"crossbeam", "crossbeam",
"indoc", "indoc",
"inkwell", "inkwell",

View File

@ -9,6 +9,7 @@ name = "nac3artiq"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
itertools = "0.11"
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"

View File

@ -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};
@ -474,11 +475,11 @@ 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
if !e.contains("<nac3_synthesized_modinit>") { return if !e.iter().any(|err| err.message().contains("<nac3_synthesized_modinit>")) {
return Err(CompileError::new_err(format!( Err(CompileError::new_err(format!(
"compilation failed\n----------\n{}", "compilation failed\n----------\n{}",
e e.into_iter().sorted().join("\n----------\n")
))); )))
} else { } else {
let msg = Self::report_modinit( let msg = Self::report_modinit(
&arg_names, &arg_names,
@ -488,10 +489,10 @@ impl Nac3 {
&mut composer.unifier, &mut composer.unifier,
&self.primitive, &self.primitive,
); );
return 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.into_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());

View File

@ -1,14 +1,9 @@
use inkwell::{types::BasicType, values::BasicValueEnum, AddressSpace}; use inkwell::{types::BasicType, values::BasicValueEnum, AddressSpace};
use nac3core::{ use nac3core::{codegen::{CodeGenContext, CodeGenerator}, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, symbol_resolver::{StaticValue, SymbolResolver, SymbolValue, ValueEnum}, toplevel::{DefinitionId, TopLevelDef}, typecheck::{
codegen::{CodeGenContext, CodeGenerator}, type_inferencer::PrimitiveStore,
symbol_resolver::{StaticValue, SymbolResolver, SymbolValue, ValueEnum}, typedef::{Type, TypeEnum, Unifier},
toplevel::{DefinitionId, TopLevelDef}, }};
typecheck::{ use nac3parser::ast::{self, Location, StrRef};
type_inferencer::PrimitiveStore,
typedef::{Type, TypeEnum, Unifier},
},
};
use nac3parser::ast::{self, StrRef};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use pyo3::{ use pyo3::{
types::{PyDict, PyTuple}, types::{PyDict, PyTuple},
@ -21,6 +16,7 @@ use std::{
atomic::{AtomicBool, Ordering::Relaxed} atomic::{AtomicBool, Ordering::Relaxed}
} }
}; };
use nac3core::Diagnostic::DiagErr;
use crate::PrimitivePythonId; use crate::PrimitivePythonId;
@ -1169,20 +1165,28 @@ impl SymbolResolver for Resolver {
}) })
} }
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> { fn get_identifier_def(&self, id: StrRef, id_loc: Option<Location>) -> ResultOrDiagnostics<DefinitionId> {
{ {
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).cloned().ok_or_else(|| "".to_string()) id_to_def.get(&id).cloned()
.map(|res| DiagnosticsResult::ok(res))
.ok_or_else(|| "".to_string())
} }
.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(DiagnosticEngine::single(DiagErr(
let result = self.0.pyid_to_def.read().get(py_id).copied().ok_or(format!( format!("undefined identifier `{id}`"),
"`{}` is not registered with NAC3 (@nac3 decorator missing?)", id_loc.unwrap()
id )))?;
))?; let result = self.0.pyid_to_def.read()
.get(py_id)
.copied()
.ok_or(DiagnosticEngine::single(DiagErr(
format!("`{}` is not registered with NAC3 (@nac3 decorator missing?)", id),
id_loc.unwrap(),
)))?;
self.0.id_to_def.write().insert(id, result); self.0.id_to_def.write().insert(id, result);
Ok(result) Ok(DiagnosticsResult::ok(result))
}) })
} }

View File

@ -10,6 +10,7 @@ crossbeam = "0.8"
parking_lot = "0.12" parking_lot = "0.12"
rayon = "1.5" rayon = "1.5"
nac3parser = { path = "../nac3parser" } nac3parser = { path = "../nac3parser" }
clap = { version = "4.4.7", features = ["derive"] }
[dependencies.inkwell] [dependencies.inkwell]
version = "0.2" version = "0.2"

View File

@ -1,22 +1,17 @@
use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip}; use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
use crate::{ use crate::{codegen::{
codegen::{ concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore}, gen_in_range_check,
gen_in_range_check, get_llvm_type,
get_llvm_type, get_llvm_abi_type,
get_llvm_abi_type, irrt::*,
irrt::*, stmt::{gen_raise, gen_var},
stmt::{gen_raise, gen_var}, CodeGenContext, CodeGenTask,
CodeGenContext, CodeGenTask, }, DiagnosticsResult, symbol_resolver::{SymbolValue, ValueEnum}, toplevel::{DefinitionId, TopLevelDef}, typecheck::{
}, typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
symbol_resolver::{SymbolValue, ValueEnum}, magic_methods::{binop_name, binop_assign_name},
toplevel::{DefinitionId, TopLevelDef}, }};
typecheck::{
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
magic_methods::{binop_name, binop_assign_name},
},
};
use inkwell::{ use inkwell::{
AddressSpace, AddressSpace,
attributes::{Attribute, AttributeLoc}, attributes::{Attribute, AttributeLoc},
@ -1584,10 +1579,16 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
match &func.node { match &func.node {
ExprKind::Name { id, .. } => { ExprKind::Name { id, .. } => {
// TODO: handle primitive casts and function pointers // TODO: handle primitive casts and function pointers
let fun = ctx // TODO(Derppening): Add fatal error and convert this (and all `gen_*` to fatal)
let DiagnosticsResult { value: fun, engine: diags } = ctx
.resolver .resolver
.get_identifier_def(*id) .get_identifier_def(*id, Some(func.location))
.map_err(|e| format!("{} (at {})", e, func.location))?; .map_err(|e| e.iter().join("\n"))?;
if !diags.is_empty() {
todo!("Emitting warnings in gen_expr is not supported yet");
}
return Ok(generator return Ok(generator
.gen_call(ctx, None, (&signature, fun), params)? .gen_call(ctx, None, (&signature, fun), params)?
.map(|v| v.into())); .map(|v| v.into()));

View File

@ -1,17 +1,12 @@
use crate::{ use crate::{codegen::{
codegen::{ concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions,
concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry, }, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, symbol_resolver::{SymbolResolver, ValueEnum}, toplevel::{
}, composer::TopLevelComposer, DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
symbol_resolver::{SymbolResolver, ValueEnum}, }, typecheck::{
toplevel::{ type_inferencer::{FunctionData, Inferencer, PrimitiveStore},
composer::TopLevelComposer, DefinitionId, FunInstance, TopLevelContext, TopLevelDef, typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
}, }};
typecheck::{
type_inferencer::{FunctionData, Inferencer, PrimitiveStore},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
},
};
use indoc::indoc; use indoc::indoc;
use inkwell::{ use inkwell::{
targets::{InitializationConfig, Target}, targets::{InitializationConfig, Target},
@ -24,6 +19,8 @@ use nac3parser::{
use parking_lot::RwLock; use parking_lot::RwLock;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::sync::Arc; use std::sync::Arc;
use nac3parser::ast::Location;
use crate::Diagnostic::DiagErr;
struct Resolver { struct Resolver {
id_to_type: HashMap<StrRef, Type>, id_to_type: HashMap<StrRef, Type>,
@ -63,12 +60,13 @@ impl SymbolResolver for Resolver {
unimplemented!() unimplemented!()
} }
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> { fn get_identifier_def(&self, id: StrRef, loc: Option<Location>) -> ResultOrDiagnostics<DefinitionId> {
self.id_to_def self.id_to_def
.read() .read()
.get(&id) .get(&id)
.cloned() .cloned()
.ok_or_else(|| format!("cannot find symbol `{}`", id)) .map(|res| DiagnosticsResult::ok(res))
.ok_or_else(|| DiagnosticEngine::single(DiagErr(format!("cannot find symbol `{id}`"), loc.unwrap())))
} }
fn get_string_id(&self, _: &str) -> i32 { fn get_string_id(&self, _: &str) -> i32 {
@ -144,7 +142,9 @@ fn test_primitives() {
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
.unwrap(); .unwrap();
inferencer.check_block(&statements, &mut identifiers).unwrap(); let DiagnosticsResult { engine: errs, .. } = inferencer.check_block(&statements, &mut identifiers);
assert_eq!(errs.is_empty(), true);
let top_level = Arc::new(TopLevelContext { let top_level = Arc::new(TopLevelContext {
definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))), definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))),
unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])), unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])),
@ -346,7 +346,9 @@ fn test_simple_call() {
unreachable!() unreachable!()
} }
inferencer.check_block(&statements_1, &mut identifiers).unwrap(); let DiagnosticsResult { engine: errs, .. } = inferencer.check_block(&statements_1, &mut identifiers);
assert_eq!(errs.is_empty(), true);
let top_level = Arc::new(TopLevelContext { let top_level = Arc::new(TopLevelContext {
definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))), definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))),
unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])), unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])),

View File

@ -7,7 +7,9 @@ use std::collections::HashSet;
use self::Diagnostic::*; use self::Diagnostic::*;
use std::fmt; use std::fmt;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use itertools::Itertools;
use nac3parser::ast::Location; use nac3parser::ast::Location;
pub mod codegen; pub mod codegen;
@ -39,7 +41,7 @@ impl Diagnostic {
} }
/// Returns the message of this [Diagnostic]. /// Returns the message of this [Diagnostic].
fn message(&self) -> &str { pub fn message(&self) -> &str {
match self { match self {
DiagErr(msg, _) => msg, DiagErr(msg, _) => msg,
DiagWarn(msg, _) => msg, DiagWarn(msg, _) => msg,
@ -47,7 +49,7 @@ impl Diagnostic {
} }
/// Returns the location of where this [Diagnostic] is created from. /// Returns the location of where this [Diagnostic] is created from.
fn location(&self) -> &Location { pub fn location(&self) -> &Location {
match self { match self {
DiagErr(_, loc) => loc, DiagErr(_, loc) => loc,
DiagWarn(_, loc) => loc, DiagWarn(_, loc) => loc,
@ -121,37 +123,52 @@ impl Hash for Diagnostic {
} }
} }
#[deprecated]
pub fn contains_errors(diagnostics: &HashSet<Diagnostic>) -> bool {
diagnostics.iter().any(|d| match d {
DiagErr(_, _) => true,
_ => false,
})
}
/// Type alias for [Result] encapsulating a success value of type [T], and any diagnostics emitted /// Type alias for [Result] encapsulating a success value of type [T], and any diagnostics emitted
/// during the generation of the result. /// during the generation of the result.
/// ///
/// This type should be used where a result cannot be meaningfully generated when a subset of the /// This type should be used where a result cannot be meaningfully generated when a subset of the
/// possible diagnostics is emitted. /// possible diagnostics is emitted.
pub type DiagnosticsResult<T> = Result<(T, DiagnosticEngine), DiagnosticEngine>; pub type ResultOrDiagnostics<T> = Result<DiagnosticsResult<T>, DiagnosticEngine>;
/// Type representing a result of type `T` with any diagnostics emitted during the generation of the /// Type representing a result of type `T` with any diagnostics emitted during the generation of the
/// result. /// result.
/// ///
/// This type should be used where, even when diagnostics are generated, a meaningful result can /// This type should be used where, even when diagnostics are generated, a meaningful result can
/// still be generated. /// still be generated.
pub struct DiagnosticsPartialResult<T> { #[derive(Clone)]
pub struct DiagnosticsResult<T> {
value: T, value: T,
engine: DiagnosticEngine, engine: DiagnosticEngine,
} }
impl <T> Into<DiagnosticsResult<T>> for DiagnosticsPartialResult<T> { impl <T> DiagnosticsResult<T> {
fn into(self) -> DiagnosticsResult<T> { pub fn ok(value: T) -> DiagnosticsResult<T> {
DiagnosticsResult {
value,
engine: DiagnosticEngine::default(),
}
}
pub fn err(value: T, engine: DiagnosticEngine) -> DiagnosticsResult<T> {
DiagnosticsResult {
value,
engine
}
}
}
impl <T> Into<(T, DiagnosticEngine)> for DiagnosticsResult<T> {
fn into(self) -> (T, DiagnosticEngine) {
(self.value, self.engine)
}
}
impl <T> Into<ResultOrDiagnostics<T>> for DiagnosticsResult<T> {
fn into(self) -> ResultOrDiagnostics<T> {
if self.engine.has_errors() { if self.engine.has_errors() {
Err(self.engine) Err(self.engine)
} else { } else {
Ok((self.value, self.engine)) Ok(self)
} }
} }
} }
@ -200,16 +217,16 @@ impl DiagnosticEngine {
self.extend(other.diagnostics) self.extend(other.diagnostics)
} }
pub fn consume_partial_result<T>(&mut self, result: DiagnosticsPartialResult<T>) -> T { pub fn consume_partial_result<T>(&mut self, result: DiagnosticsResult<T>) -> T {
self.merge_with(result.engine); self.merge_with(result.engine);
result.value result.value
} }
pub fn consume_result<T>(&mut self, result: DiagnosticsResult<T>) -> Result<T, Self> { pub fn consume_result<T>(&mut self, result: ResultOrDiagnostics<T>) -> Result<T, Self> {
match result { match result {
Ok((v, diags)) => { Ok(DiagnosticsResult { value, engine }) => {
self.merge_with(diags); self.merge_with(engine);
Ok(v) Ok(value)
} }
Err(diags) => { Err(diags) => {
@ -219,11 +236,11 @@ impl DiagnosticEngine {
} }
} }
pub fn consume_result_to_option<T>(&mut self, result: DiagnosticsResult<T>) -> Option<T> { pub fn consume_result_to_option<T>(&mut self, result: ResultOrDiagnostics<T>) -> Option<T> {
match result { match result {
Ok((v, diags)) => { Ok(DiagnosticsResult { value, engine }) => {
self.merge_with(diags); self.merge_with(engine);
Some(v) Some(value)
} }
Err(diags) => { Err(diags) => {
@ -241,18 +258,36 @@ impl DiagnosticEngine {
self.diagnostics.into_iter() self.diagnostics.into_iter()
} }
pub fn into_partial_result<T>(self, value: T) -> DiagnosticsPartialResult<T> { pub fn into_partial_result<T>(self, value: T) -> DiagnosticsResult<T> {
DiagnosticsPartialResult { DiagnosticsResult {
value, value,
engine: self engine: self
} }
} }
pub fn into_result<T>(self, value: T) -> DiagnosticsResult<T> { pub fn into_result<T>(self, value: T) -> ResultOrDiagnostics<T> {
self.into_result_with(|| value)
}
pub fn into_result_with<T, F>(self, value: F) -> ResultOrDiagnostics<T>
where F: FnOnce() -> T,
{
if self.has_errors() { if self.has_errors() {
Err(self) Err(self)
} else { } else {
Ok((value, self)) Ok(DiagnosticsResult { value: value(), engine: self })
} }
} }
pub fn as_string(&self) -> Vec<String> {
self.diagnostics.iter()
.map(|diag| diag.to_string())
.collect_vec()
}
}
impl Debug for DiagnosticEngine {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_string().join("\n----------\n"))
}
} }

View File

@ -3,10 +3,7 @@ use std::sync::Arc;
use std::{collections::HashMap, fmt::Display}; use std::{collections::HashMap, fmt::Display};
use crate::typecheck::typedef::TypeEnum; use crate::typecheck::typedef::TypeEnum;
use crate::{ use crate::{codegen::CodeGenContext, Diagnostic::DiagErr, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, toplevel::{DefinitionId, TopLevelDef}};
codegen::CodeGenContext,
toplevel::{DefinitionId, TopLevelDef},
};
use crate::{ use crate::{
codegen::CodeGenerator, codegen::CodeGenerator,
typecheck::{ typecheck::{
@ -147,8 +144,14 @@ pub trait SymbolResolver {
str: StrRef, str: StrRef,
) -> Result<Type, String>; ) -> Result<Type, String>;
// get the top-level definition of identifiers /// Returns the top-level definition of identifiers.
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, String>; ///
/// * `id_src`: The location where the identifier is used in.
fn get_identifier_def(
&self,
str: StrRef,
id_src: Option<Location>
) -> ResultOrDiagnostics<DefinitionId>;
fn get_symbol_value<'ctx, 'a>( fn get_symbol_value<'ctx, 'a>(
&self, &self,
@ -186,15 +189,19 @@ thread_local! {
]; ];
} }
// convert type annotation into type /// Converts type annotation into [Type].
///
/// Returns [Type] of this `expr` if the operation is successful, and a [HashSet] containing all
/// emitted [Diagnostic] messages.
pub fn parse_type_annotation<T>( pub fn parse_type_annotation<T>(
resolver: &dyn SymbolResolver, resolver: &dyn SymbolResolver,
top_level_defs: &[Arc<RwLock<TopLevelDef>>], top_level_defs: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier, unifier: &mut Unifier,
primitives: &PrimitiveStore, primitives: &PrimitiveStore,
expr: &Expr<T>, expr: &Expr<T>,
) -> Result<Type, String> { ) -> ResultOrDiagnostics<Type> {
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];
let int64_id = ids[1]; let int64_id = ids[1];
@ -209,56 +216,87 @@ pub fn parse_type_annotation<T>(
let uint64_id = ids[10]; let uint64_id = ids[10];
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| { let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
let mut diagnostics = DiagnosticEngine::new();
if *id == int32_id { if *id == int32_id {
Ok(primitives.int32) Ok(DiagnosticsResult::ok(primitives.int32))
} else if *id == int64_id { } else if *id == int64_id {
Ok(primitives.int64) Ok(DiagnosticsResult::ok(primitives.int64))
} else if *id == uint32_id { } else if *id == uint32_id {
Ok(primitives.uint32) Ok(DiagnosticsResult::ok(primitives.uint32))
} else if *id == uint64_id { } else if *id == uint64_id {
Ok(primitives.uint64) Ok(DiagnosticsResult::ok(primitives.uint64))
} else if *id == float_id { } else if *id == float_id {
Ok(primitives.float) Ok(DiagnosticsResult::ok(primitives.float))
} else if *id == bool_id { } else if *id == bool_id {
Ok(primitives.bool) Ok(DiagnosticsResult::ok(primitives.bool))
} else if *id == str_id { } else if *id == str_id {
Ok(primitives.str) Ok(DiagnosticsResult::ok(primitives.str))
} else if *id == exn_id { } else if *id == exn_id {
Ok(primitives.exception) Ok(DiagnosticsResult::ok(primitives.exception))
} else { } else {
let obj_id = resolver.get_identifier_def(*id); let obj_id = resolver.get_identifier_def(*id, Some(expr.location));
match obj_id { match obj_id {
Ok(obj_id) => { Ok(DiagnosticsResult { value: obj_id, engine: diags }) => {
diagnostics.merge_with(diags);
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!( diagnostics.insert(DiagErr(
"Unexpected number of type parameters: expected {} but got 0", format!("expected {} type parameters but got 0", type_vars.len()),
type_vars.len() loc,
)); ));
return Err(diagnostics)
} }
let fields = chain( let fields = chain(
fields.iter().map(|(k, v, m)| (*k, (*v, *m))), fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
methods.iter().map(|(k, v, _)| (*k, (*v, false))), methods.iter().map(|(k, v, _)| (*k, (*v, false))),
) )
.collect(); .collect();
Ok(unifier.add_ty(TypeEnum::TObj { Ok(DiagnosticsResult::err(
obj_id, unifier.add_ty(TypeEnum::TObj {
fields, obj_id,
params: Default::default(), fields,
})) params: Default::default(),
}),
diagnostics,
))
} else { } else {
Err(format!("Cannot use function name as type at {}", loc)) diagnostics.insert(DiagErr(
"cannot use function name as type".into(),
loc,
));
Err(diagnostics)
} }
} }
Err(_) => { Err(_) => {
let ty = resolver let ty = match resolver.get_symbol_type(
.get_symbol_type(unifier, top_level_defs, primitives, *id) unifier,
.map_err(|e| format!("Unknown type annotation at {}: {}", loc, e))?; top_level_defs,
primitives,
*id,
) {
Ok(ty) => ty,
Err(e) => {
diagnostics.insert(DiagErr(
format!("Unknown type annotation {e}"),
loc,
));
return Err(diagnostics)
}
};
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) { if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
Ok(ty) Ok(DiagnosticsResult::err(ty, diagnostics))
} else { } else {
Err(format!("Unknown type annotation {} at {}", id, loc)) diagnostics.insert(DiagErr(
format!("unknown type annotation {}", id),
loc,
));
Err(diagnostics)
} }
} }
} }
@ -266,23 +304,37 @@ pub fn parse_type_annotation<T>(
}; };
let subscript_name_handle = |id: &StrRef, slice: &Expr<T>, unifier: &mut Unifier| { let subscript_name_handle = |id: &StrRef, slice: &Expr<T>, unifier: &mut Unifier| {
let mut diagnostics = DiagnosticEngine::new();
if *id == virtual_id { if *id == virtual_id {
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?; let DiagnosticsResult { value: ty, engine: diag } =
Ok(unifier.add_ty(TypeEnum::TVirtual { ty })) parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
diagnostics.merge_with(diag);
Ok(DiagnosticsResult::err(unifier.add_ty(TypeEnum::TVirtual { ty }), diagnostics))
} else if *id == list_id { } else if *id == list_id {
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?; let DiagnosticsResult { value: ty, engine: diag } =
Ok(unifier.add_ty(TypeEnum::TList { ty })) parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
diagnostics.merge_with(diag);
Ok(DiagnosticsResult::err(unifier.add_ty(TypeEnum::TList { ty }), diagnostics))
} else if *id == tuple_id { } else if *id == tuple_id {
if let Tuple { elts, .. } = &slice.node { if let Tuple { elts, .. } = &slice.node {
let ty = elts let (ty, diags) = elts
.iter() .iter()
.map(|elt| { .map(|elt| {
parse_type_annotation(resolver, top_level_defs, unifier, primitives, elt) parse_type_annotation(resolver, top_level_defs, unifier, primitives, elt)
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?
Ok(unifier.add_ty(TypeEnum::TTuple { ty })) .into_iter()
.map(|res| res.into())
.unzip::<_, _, Vec<_>, Vec<_>>();
diagnostics.extend(diags.into_iter().flat_map(|d| d.into_iter()));
Ok(DiagnosticsResult::err(unifier.add_ty(TypeEnum::TTuple { ty }), diagnostics))
} else { } else {
Err("Expected multiple elements for tuple".into()) diagnostics.insert(DiagErr(
"expected multiple elements for tuple".into(),
slice.location,
));
Err(diagnostics)
} }
} else { } else {
let types = if let Tuple { elts, .. } = &slice.node { let types = if let Tuple { elts, .. } = &slice.node {
@ -295,25 +347,38 @@ pub fn parse_type_annotation<T>(
vec![parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?] vec![parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?]
}; };
let obj_id = resolver.get_identifier_def(*id)?; let DiagnosticsResult { value: obj_id, engine: diags } = resolver.get_identifier_def(*id, Some(slice.location))?;
diagnostics.merge_with(diags);
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!( diagnostics.insert(DiagErr(
"Unexpected number of type parameters: expected {} but got {}", format!("expected {} type parameters but got {}", type_vars.len(), types.len()),
type_vars.len(), slice.location,
types.len()
)); ));
return Err(diagnostics)
} }
let mut subst = HashMap::new(); let mut subst = HashMap::new();
for (var, ty) in izip!(type_vars.iter(), types.iter()) { let mut subst_diags = DiagnosticEngine::new();
for (var, DiagnosticsResult { value: ty, engine: diag }) in izip!(type_vars.iter(), types.iter()) {
let id = if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*var) { let id = if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*var) {
*id *id
} else { } else {
unreachable!() unreachable!()
}; };
subst.insert(id, *ty); subst.insert(id, *ty);
subst_diags.extend(diag.iter().cloned());
} }
let subst_has_errors = subst_diags.has_errors();
diagnostics.merge_with(subst_diags);
if subst_has_errors {
return Err(diagnostics)
}
let mut fields = fields let mut fields = fields
.iter() .iter()
.map(|(attr, ty, is_mutable)| { .map(|(attr, ty, is_mutable)| {
@ -325,9 +390,14 @@ pub fn parse_type_annotation<T>(
let ty = unifier.subst(*ty, &subst).unwrap_or(*ty); let ty = unifier.subst(*ty, &subst).unwrap_or(*ty);
(*attr, (ty, false)) (*attr, (ty, false))
})); }));
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }))
Ok(DiagnosticsResult::err(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }), diagnostics))
} else { } else {
Err("Cannot use function name as type".into()) diagnostics.insert(DiagErr(
"cannot use function name as type".into(),
slice.location,
));
Err(diagnostics)
} }
} }
}; };
@ -338,10 +408,17 @@ 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(DiagnosticEngine::single(DiagErr(
"unsupported type expression".into(),
expr.location,
)))
} }
} }
_ => Err(format!("unsupported type expression at {}", expr.location)),
_ => Err(DiagnosticEngine::single(DiagErr(
"unsupported type expression".into(),
expr.location,
))),
} }
} }
@ -352,7 +429,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> { ) -> ResultOrDiagnostics<Type> {
parse_type_annotation(self, top_level_defs, unifier, primitives, expr) parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
} }

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@ use std::convert::TryInto;
use crate::symbol_resolver::SymbolValue; use crate::symbol_resolver::SymbolValue;
use nac3parser::ast::{Constant, Location}; use nac3parser::ast::{Constant, Location};
use crate::Diagnostic::DiagErr;
use crate::{Diagnostic, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics};
use super::*; use super::*;
@ -200,13 +202,17 @@ 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> { loc: Location,
) -> ResultOrDiagnostics<(Type, DefinitionId)> {
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(DiagnosticsResult::ok((*ty, *def_id)));
} }
} }
Err(format!("no method {} in the current class", method_name))
Err(DiagnosticEngine::single(
DiagErr(format!("no method {method_name} in the current class"), loc)
))
} }
/// get all base class def id of a class, excluding itself. \ /// get all base class def id of a class, excluding itself. \
@ -217,7 +223,10 @@ 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> { unifier: &mut Unifier,
loc: &Option<Location>,
) -> ResultOrDiagnostics<Vec<TypeAnnotation>> {
let mut diagnostics = DiagnosticEngine::new();
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 {
@ -238,10 +247,25 @@ impl TopLevelComposer {
if no_cycle { if no_cycle {
result.push(p); result.push(p);
} else { } else {
return Err("cyclic inheritance detected".into()); let child_name = child.stringify(unifier);
let parent_name = p.stringify(unifier);
diagnostics.insert(DiagErr(
format!(
"cyclic inheritance detected - child class `{}` is an ancestor of class `{}`",
child_name,
parent_name,
),
loc.expect(format!(
"Unexpected error: Cyclic Inheriance detected in intrinsic class! Child Class `{}` is an ancestor of class `{}`",
child_name,
parent_name,
).as_str())
));
} }
} }
Ok(result)
diagnostics.into_result(result)
} }
/// should only be called when finding all ancestors, so panic when wrong /// should only be called when finding all ancestors, so panic when wrong
@ -334,8 +358,10 @@ impl TopLevelComposer {
) )
} }
pub fn get_all_assigned_field(stmts: &[ast::Stmt<()>]) -> Result<HashSet<StrRef>, String> { pub fn get_all_assigned_field(stmts: &[ast::Stmt<()>]) -> ResultOrDiagnostics<HashSet<StrRef>> {
let mut result = HashSet::new(); let mut result = HashSet::new();
let mut diagnostics = DiagnosticEngine::new();
for s in stmts { for s in stmts {
match &s.node { match &s.node {
ast::StmtKind::AnnAssign { target, .. } ast::StmtKind::AnnAssign { target, .. }
@ -351,10 +377,11 @@ impl TopLevelComposer {
} }
} => } =>
{ {
return Err(format!( diagnostics.insert(DiagErr(
"redundant type annotation for class fields at {}", "redundant type annotation for class fields".into(),
s.location s.location,
)) ));
return Err(diagnostics)
} }
ast::StmtKind::Assign { targets, .. } => { ast::StmtKind::Assign { targets, .. } => {
for t in targets { for t in targets {
@ -370,26 +397,26 @@ impl TopLevelComposer {
// TODO: do not check for For and While? // TODO: do not check for For and While?
ast::StmtKind::For { body, orelse, .. } ast::StmtKind::For { body, orelse, .. }
| ast::StmtKind::While { body, orelse, .. } => { | ast::StmtKind::While { body, orelse, .. } => {
result.extend(Self::get_all_assigned_field(body.as_slice())?); result.extend(diagnostics.consume_result(Self::get_all_assigned_field(body.as_slice()))?);
result.extend(Self::get_all_assigned_field(orelse.as_slice())?); result.extend(diagnostics.consume_result(Self::get_all_assigned_field(orelse.as_slice()))?);
} }
ast::StmtKind::If { body, orelse, .. } => { ast::StmtKind::If { body, orelse, .. } => {
let inited_for_sure = Self::get_all_assigned_field(body.as_slice())? let inited_for_sure = diagnostics.consume_result(Self::get_all_assigned_field(body.as_slice()))?
.intersection(&Self::get_all_assigned_field(orelse.as_slice())?) .intersection(&diagnostics.consume_result(Self::get_all_assigned_field(orelse.as_slice()))?)
.cloned() .cloned()
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
result.extend(inited_for_sure); result.extend(inited_for_sure);
} }
ast::StmtKind::Try { body, orelse, finalbody, .. } => { ast::StmtKind::Try { body, orelse, finalbody, .. } => {
let inited_for_sure = Self::get_all_assigned_field(body.as_slice())? let inited_for_sure = diagnostics.consume_result(Self::get_all_assigned_field(body.as_slice()))?
.intersection(&Self::get_all_assigned_field(orelse.as_slice())?) .intersection(&diagnostics.consume_result(Self::get_all_assigned_field(orelse.as_slice()))?)
.cloned() .cloned()
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
result.extend(inited_for_sure); result.extend(inited_for_sure);
result.extend(Self::get_all_assigned_field(finalbody.as_slice())?); result.extend(diagnostics.consume_result(Self::get_all_assigned_field(finalbody.as_slice()))?);
} }
ast::StmtKind::With { body, .. } => { ast::StmtKind::With { body, .. } => {
result.extend(Self::get_all_assigned_field(body.as_slice())?); result.extend(diagnostics.consume_result(Self::get_all_assigned_field(body.as_slice()))?);
} }
ast::StmtKind::Pass { .. } => {} ast::StmtKind::Pass { .. } => {}
ast::StmtKind::Assert { .. } => {} ast::StmtKind::Assert { .. } => {}
@ -400,13 +427,14 @@ impl TopLevelComposer {
} }
} }
} }
Ok(result)
Ok(DiagnosticsResult::err(result, diagnostics))
} }
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> { ) -> ResultOrDiagnostics<SymbolValue> {
parse_parameter_default_value(default, resolver) parse_parameter_default_value(default, resolver)
} }
@ -497,25 +525,35 @@ 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> { ) -> ResultOrDiagnostics<SymbolValue> {
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, String> { fn handle_constant(val: &Constant, loc: &Location) -> ResultOrDiagnostics<SymbolValue> {
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(DiagnosticsResult::ok(SymbolValue::I32(v)))
} else { } else {
Err(format!("integer value out of range at {}", loc)) Err(DiagnosticEngine::single(DiagErr(
"integer value out of range".into(),
*loc,
)))
} }
} }
Constant::Float(v) => Ok(SymbolValue::Double(*v)), Constant::Float(v) => Ok(DiagnosticsResult::ok(SymbolValue::Double(*v))),
Constant::Bool(v) => Ok(SymbolValue::Bool(*v)), Constant::Bool(v) => Ok(DiagnosticsResult::ok(SymbolValue::Bool(*v))),
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple( Constant::Tuple(tuple) => {
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?, let val = tuple.iter()
)), .map(|x| handle_constant(x, loc))
Constant::None => Err(format!( .collect::<Result<Vec<_>, _>>()?;
"`None` is not supported, use `none` for option type instead ({})", let diagnostics = DiagnosticEngine::from(val.iter().cloned()
loc .flat_map(|res| res.engine.into_iter())
)), .collect());
diagnostics.into_result_with(|| SymbolValue::Tuple(val.into_iter().map(|res| res.value).collect_vec()))
}
Constant::None => Err(DiagnosticEngine::single(DiagErr(
"`None` is not supported, use `none` for option type instead".into(),
*loc,
))),
_ => unimplemented!("this constant is not supported at {}", loc), _ => unimplemented!("this constant is not supported at {}", loc),
} }
} }
@ -527,59 +565,101 @@ pub fn parse_parameter_default_value(
ast::ExprKind::Constant { value: Constant::Int(v), .. } => { ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
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(DiagnosticsResult::ok(SymbolValue::I64(v))),
_ => Err(format!("default param value out of range at {}", default.location)), _ => Err(DiagnosticEngine::single(DiagErr(
"default param value out of range".into(),
default.location,
))),
} }
} }
_ => Err(format!("only allow constant integer here at {}", default.location)) _ => Err(DiagnosticEngine::single(DiagErr(
"only allow constant integer here".into(),
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(DiagnosticsResult::ok(SymbolValue::U32(v))),
_ => Err(format!("default param value out of range at {}", default.location)), _ => Err(DiagnosticEngine::single(DiagErr(
"default param value out of range".into(),
default.location,
))),
} }
} }
_ => Err(format!("only allow constant integer here at {}", default.location)) _ => Err(DiagnosticEngine::single(DiagErr(
"only allow constant integer here".into(),
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(DiagnosticsResult::ok(SymbolValue::U64(v))),
_ => Err(format!("default param value out of range at {}", default.location)), _ => Err(DiagnosticEngine::single(DiagErr(
"default param value out of range".into(),
default.location
))),
} }
} }
_ => Err(format!("only allow constant integer here at {}", default.location)) _ => Err(DiagnosticEngine::single(DiagErr(
"only allow constant integer here".into(),
default.location
)))
} }
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok( ast::ExprKind::Name { id, .. } if *id == "Some".into() => {
SymbolValue::OptionSome( let param_def_value = parse_parameter_default_value(&args[0], resolver)?;
Box::new(parse_parameter_default_value(&args[0], resolver)?) let errs = param_def_value.engine;
Ok(
DiagnosticsResult::err(SymbolValue::OptionSome(
Box::new(param_def_value.value),
), errs)
) )
), }
_ => Err(format!("unsupported default parameter at {}", default.location)), _ => Err(DiagnosticEngine::single(DiagErr(
"unsupported default parameter".into(),
default.location,
))),
} }
} }
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts ast::ExprKind::Tuple { elts, .. } => {
.iter() let param_def_values = elts
.map(|x| parse_parameter_default_value(x, resolver)) .iter()
.collect::<Result<Vec<_>, _>>()? .map(|x| parse_parameter_default_value(x, resolver))
)), .collect::<Result<Vec<_>, _>>()?;
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone), let diagnostics = DiagnosticEngine::from(
ast::ExprKind::Name { id, .. } => { param_def_values.iter().cloned()
resolver.get_default_param_value(default).ok_or_else( .flat_map(|res| res.engine.into_iter())
|| format!( .collect::<HashSet<Diagnostic>>()
"`{}` cannot be used as a default parameter at {} \ );
(not primitive type, option or tuple / not defined?)", let param_def_values = param_def_values.into_iter()
id, .map(|res| res.value)
default.location .collect_vec();
)
) Ok(DiagnosticsResult::err(
SymbolValue::Tuple(param_def_values),
diagnostics,
))
} }
_ => Err(format!(
"unsupported default parameter (not primitive type, option or tuple) at {}", ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(DiagnosticsResult::ok(SymbolValue::OptionNone)),
default.location ast::ExprKind::Name { id, .. } => {
)) resolver.get_default_param_value(default)
.map(|res| DiagnosticsResult::ok(res))
.ok_or_else(||
DiagnosticEngine::single(DiagErr(
format!("`{}` cannot be used as a default parameter (not primitive type, \
option or tuple / not defined?)", id),
default.location,
))
)
}
_ => Err(DiagnosticEngine::single(DiagErr(
"unsupported default parameter (not primitive type, option or tuple)".into(),
default.location,
)))
} }
} }

View File

@ -1,17 +1,13 @@
use crate::{ use crate::{codegen::CodeGenContext, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, symbol_resolver::{SymbolResolver, ValueEnum}, toplevel::DefinitionId, typecheck::{
codegen::CodeGenContext, type_inferencer::PrimitiveStore,
symbol_resolver::{SymbolResolver, ValueEnum}, typedef::{Type, Unifier},
toplevel::DefinitionId, }};
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, Unifier},
},
};
use indoc::indoc; use indoc::indoc;
use nac3parser::{ast::fold::Fold, parser::parse_program}; use nac3parser::{ast::fold::Fold, parser::parse_program};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use test_case::test_case; use test_case::test_case;
use crate::Diagnostic::DiagErr;
use super::*; use super::*;
@ -64,8 +60,10 @@ impl SymbolResolver for Resolver {
unimplemented!() unimplemented!()
} }
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> { fn get_identifier_def(&self, id: StrRef, loc: Option<Location>) -> ResultOrDiagnostics<DefinitionId> {
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()
.map(|res| DiagnosticsResult::ok(res))
.ok_or_else(|| DiagnosticEngine::single(DiagErr("undefined identifier".into(), loc.unwrap())))
} }
fn get_string_id(&self, _: &str) -> i32 { fn get_string_id(&self, _: &str) -> i32 {
@ -551,9 +549,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.into_iter().sorted().join("\n----------\n"));
} else { } else {
assert_eq!(res[0], msg); assert_eq!(res[0], msg.into_iter().sorted().next().unwrap().message());
} }
} else { } else {
// skip 5 to skip primitives // skip 5 to skip primitives
@ -735,9 +733,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.into_iter().sorted().join("\n----------\n"));
} else { } else {
assert_eq!(res[0], msg); assert_eq!(res[0], msg.into_iter().sorted().next().unwrap().message());
} }
} else { } else {
// skip 5 to skip primitives // skip 5 to skip primitives

View File

@ -1,3 +1,4 @@
use crate::{Diagnostic::DiagErr, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics};
use super::*; use super::*;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -64,74 +65,103 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
expr: &ast::Expr<T>, expr: &ast::Expr<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>>,
) -> Result<TypeAnnotation, String> { ) -> ResultOrDiagnostics<TypeAnnotation> {
let mut diagnostics = DiagnosticEngine::new();
let name_handle = |id: &StrRef, let name_handle = |id: &StrRef,
id_loc: Location,
unifier: &mut Unifier, unifier: &mut Unifier,
locked: HashMap<DefinitionId, Vec<Type>>| { locked: HashMap<DefinitionId, Vec<Type>>| {
let mut diagnostics = DiagnosticEngine::new();
if id == &"int32".into() { if id == &"int32".into() {
Ok(TypeAnnotation::Primitive(primitives.int32)) Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.int32)))
} else if id == &"int64".into() { } else if id == &"int64".into() {
Ok(TypeAnnotation::Primitive(primitives.int64)) Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.int64)))
} else if id == &"uint32".into() { } else if id == &"uint32".into() {
Ok(TypeAnnotation::Primitive(primitives.uint32)) Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.uint32)))
} else if id == &"uint64".into() { } else if id == &"uint64".into() {
Ok(TypeAnnotation::Primitive(primitives.uint64)) Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.uint64)))
} else if id == &"float".into() { } else if id == &"float".into() {
Ok(TypeAnnotation::Primitive(primitives.float)) Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.float)))
} else if id == &"bool".into() { } else if id == &"bool".into() {
Ok(TypeAnnotation::Primitive(primitives.bool)) Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.bool)))
} else if id == &"str".into() { } else if id == &"str".into() {
Ok(TypeAnnotation::Primitive(primitives.str)) Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.str)))
} else if id == &"Exception".into() { } else if id == &"Exception".into() {
Ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }) Ok(DiagnosticsResult::ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }))
} else if let Ok(obj_id) = resolver.get_identifier_def(*id) { } else if let Ok(DiagnosticsResult { value: obj_id, engine: diags }) = resolver.get_identifier_def(*id, Some(id_loc)) {
diagnostics.merge_with(diags);
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 { if let TopLevelDef::Class { type_vars, .. } = &*def_read {
type_vars.clone() type_vars.clone()
} else { } else {
return Err(format!( diagnostics.insert(DiagErr(
"function cannot be used as a type (at {})", "function cannot be used as a type".into(),
expr.location expr.location,
)); ));
return Err(diagnostics)
} }
} else { } else {
locked.get(&obj_id).unwrap().clone() locked.get(&obj_id).unwrap().clone()
} }
}; };
// check param number here // check param number here
if !type_vars.is_empty() { if !type_vars.is_empty() {
return Err(format!( diagnostics.insert(DiagErr(
"expect {} type variable parameter but got 0 (at {})", format!("expect {} type variable parameters but got 0", type_vars.len()),
type_vars.len(),
expr.location, expr.location,
)); ));
return Err(diagnostics)
} }
Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] })
Ok(DiagnosticsResult::err(TypeAnnotation::CustomClass { id: obj_id, params: vec![] }, diagnostics))
} 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) {
if let TypeEnum::TVar { .. } = unifier.get_ty(ty).as_ref() { if let TypeEnum::TVar { .. } = unifier.get_ty(ty).as_ref() {
let var = unifier.get_fresh_var(Some(*id), Some(expr.location)).0; let var = unifier.get_fresh_var(Some(*id), Some(expr.location)).0;
unifier.unify(var, ty).unwrap(); unifier.unify(var, ty).unwrap();
Ok(TypeAnnotation::TypeVar(ty)) Ok(DiagnosticsResult::err(TypeAnnotation::TypeVar(ty), diagnostics))
} else { } else {
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location)) diagnostics.insert(DiagErr(
format!("`{}` is not a valid type annotation", id),
expr.location,
));
Err(diagnostics)
} }
} else { } else {
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location)) diagnostics.insert(DiagErr(
format!("`{}` is not a valid type annotation", id),
expr.location,
));
Err(diagnostics)
} }
}; };
let class_name_handle = let class_name_handle =
|id: &StrRef, |id: &StrRef,
id_loc: Location,
slice: &ast::Expr<T>, slice: &ast::Expr<T>,
unifier: &mut Unifier, unifier: &mut Unifier,
mut locked: HashMap<DefinitionId, Vec<Type>>| { mut locked: HashMap<DefinitionId, Vec<Type>>| {
let mut diagnostics = DiagnosticEngine::new();
if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into()].contains(id) if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into()].contains(id)
{ {
return Err(format!("keywords cannot be class name (at {})", expr.location)); diagnostics.insert(DiagErr(
"keywords cannot be class name".into(),
expr.location,
));
return Err(diagnostics)
} }
let obj_id = resolver.get_identifier_def(*id)?;
let DiagnosticsResult { value: obj_id, engine: diags } =
resolver.get_identifier_def(*id, Some(id_loc))?;
diagnostics.merge_with(diags);
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 {
@ -144,6 +174,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
locked.get(&obj_id).unwrap().clone() locked.get(&obj_id).unwrap().clone()
} }
}; };
// we do not check whether the application of type variables are compatible here // we do not check whether the application of type variables are compatible here
let param_type_infos = { let param_type_infos = {
let params_ast = if let ast::ExprKind::Tuple { elts, .. } = &slice.node { let params_ast = if let ast::ExprKind::Tuple { elts, .. } = &slice.node {
@ -151,14 +182,18 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
} else { } else {
vec![slice] vec![slice]
}; };
if type_vars.len() != params_ast.len() { if type_vars.len() != params_ast.len() {
return Err(format!( diagnostics.insert(DiagErr(
"expect {} type parameters but got {} (at {})", format!(
type_vars.len(), "expect {} type parameters but got {}",
params_ast.len(), type_vars.len(),
params_ast.len(),
),
params_ast[0].location, params_ast[0].location,
)); ));
} }
let result = params_ast let result = params_ast
.iter() .iter()
.map(|x| { .map(|x| {
@ -174,31 +209,47 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
}, },
) )
}) })
.collect::<Result<Vec<_>, _>>()?; .collect_vec();
diagnostics.extend(result.iter().cloned().flat_map(|res| match res {
Ok(DiagnosticsResult { engine: diags, .. }) => diags,
Err(diags) => diags,
}.into_iter()));
if diagnostics.has_errors() {
return Err(diagnostics)
}
let result = result.into_iter()
.map(|res| res.ok().unwrap().value)
.collect_vec();
// make sure the result do not contain any type vars // make sure the result do not contain any type vars
let no_type_var = let no_type_var = result.iter()
result.iter().all(|x| get_type_var_contained_in_type_annotation(x).is_empty()); .all(|x| get_type_var_contained_in_type_annotation(x).is_empty());
if no_type_var { if no_type_var {
result result
} else { } else {
return Err(format!( diagnostics.insert(DiagErr(
"application of type vars to generic class \ "application of type vars to generic class is not currently supported".into(),
is not currently supported (at {})", params_ast[0].location,
params_ast[0].location
)); ));
return Err(diagnostics)
} }
}; };
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
diagnostics.into_result(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
}; };
match &expr.node { match &expr.node {
ast::ExprKind::Name { id, .. } => name_handle(id, unifier, locked), ast::ExprKind::Name { id, .. } => name_handle(id, expr.location, unifier, locked),
// virtual // virtual
ast::ExprKind::Subscript { value, slice, .. } ast::ExprKind::Subscript { value, slice, .. }
if { if {
matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"virtual".into()) matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"virtual".into())
} => } =>
{ {
let def = parse_ast_to_type_annotation_kinds( let DiagnosticsResult { value: def, engine: diags } = parse_ast_to_type_annotation_kinds(
resolver, resolver,
top_level_defs, top_level_defs,
unifier, unifier,
@ -206,10 +257,13 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
slice.as_ref(), slice.as_ref(),
locked, locked,
)?; )?;
diagnostics.merge_with(diags);
if !matches!(def, TypeAnnotation::CustomClass { .. }) { if !matches!(def, TypeAnnotation::CustomClass { .. }) {
unreachable!("must be concretized custom class kind in the virtual") unreachable!("must be concretized custom class kind in the virtual")
} }
Ok(TypeAnnotation::Virtual(def.into()))
Ok(DiagnosticsResult::err(TypeAnnotation::Virtual(def.into()), diagnostics))
} }
// list // list
@ -218,7 +272,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"list".into()) matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"list".into())
} => } =>
{ {
let def_ann = parse_ast_to_type_annotation_kinds( let DiagnosticsResult { value: def_ann, engine: diags } = parse_ast_to_type_annotation_kinds(
resolver, resolver,
top_level_defs, top_level_defs,
unifier, unifier,
@ -226,7 +280,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
slice.as_ref(), slice.as_ref(),
locked, locked,
)?; )?;
Ok(TypeAnnotation::List(def_ann.into())) diagnostics.merge_with(diags);
Ok(DiagnosticsResult::err(TypeAnnotation::List(def_ann.into()), diagnostics))
} }
// option // option
@ -242,14 +298,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
primitives, primitives,
slice.as_ref(), slice.as_ref(),
locked, locked,
)?; )?.value;
let id = let id =
if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty(primitives.option).as_ref() { if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty(primitives.option).as_ref() {
*obj_id *obj_id
} else { } else {
unreachable!() unreachable!()
}; };
Ok(TypeAnnotation::CustomClass { id, params: vec![def_ann] }) Ok(DiagnosticsResult::err(TypeAnnotation::CustomClass { id, params: vec![def_ann] }, diagnostics))
} }
// tuple // tuple
@ -277,20 +333,43 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
locked.clone(), locked.clone(),
) )
}) })
.collect::<Result<Vec<_>, _>>()?; .collect_vec();
Ok(TypeAnnotation::Tuple(type_annotations)) diagnostics.extend(type_annotations.iter().cloned().flat_map(|res| match res {
Ok(DiagnosticsResult { engine: diags, .. }) => diags,
Err(diags) => diags,
}.into_iter()));
if diagnostics.has_errors() {
return Err(diagnostics)
}
let type_annotations = type_annotations.into_iter()
.map(|res| res.ok().unwrap().value)
.collect_vec();
Ok(DiagnosticsResult::err(TypeAnnotation::Tuple(type_annotations), diagnostics))
} }
// custom class // custom class
ast::ExprKind::Subscript { value, slice, .. } => { ast::ExprKind::Subscript { value, slice, .. } => {
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, value.location, slice, unifier, locked)
} else { } else {
Err(format!("unsupported expression type for class name (at {})", value.location)) diagnostics.insert(DiagErr(
"unsupported expression type for class name".into(),
value.location
));
return Err(diagnostics)
} }
} }
_ => Err(format!("unsupported expression for type annotation (at {})", expr.location)), _ => {
diagnostics.insert(DiagErr(
"unsupported expression for type annotation".into(),
expr.location
));
Err(diagnostics)
}
} }
} }
@ -302,19 +381,27 @@ pub fn get_type_from_type_annotation_kinds(
unifier: &mut Unifier, unifier: &mut Unifier,
primitives: &PrimitiveStore, primitives: &PrimitiveStore,
ann: &TypeAnnotation, ann: &TypeAnnotation,
subst_list: &mut Option<Vec<Type>> subst_list: &mut Option<Vec<Type>>,
) -> Result<Type, String> { loc: &Option<Location>,
) -> ResultOrDiagnostics<Type> {
let mut diagnostics = DiagnosticEngine::new();
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();
let class_def: &TopLevelDef = def_read.deref(); let class_def: &TopLevelDef = def_read.deref();
if let TopLevelDef::Class { fields, methods, type_vars, .. } = class_def { if let TopLevelDef::Class { name, fields, methods, type_vars, .. } = class_def {
if type_vars.len() != params.len() { if type_vars.len() != params.len() {
Err(format!( diagnostics.insert(DiagErr(
"unexpected number of type parameters: expected {} but got {}", format!(
type_vars.len(), "expected {} type parameters for class `{}` but got {}",
params.len() type_vars.len(),
)) name,
params.len(),
),
loc.unwrap(),
));
Err(diagnostics)
} else { } else {
let param_ty = params let param_ty = params
.iter() .iter()
@ -324,10 +411,16 @@ pub fn get_type_from_type_annotation_kinds(
unifier, unifier,
primitives, primitives,
x, x,
subst_list subst_list,
loc,
) )
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
diagnostics.extend(param_ty.iter().cloned().flat_map(|res| res.engine.into_iter()));
let param_ty = param_ty.into_iter()
.map(|res| res.value)
.collect_vec();
let subst = { let subst = {
// check for compatible range // check for compatible range
@ -351,16 +444,20 @@ 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!( diagnostics.insert(DiagErr(
"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 loc.unwrap(),
)); ));
return Err(diagnostics)
} }
} else { } else {
unreachable!("must be generic type var") unreachable!("must be generic type var")
@ -389,22 +486,25 @@ pub fn get_type_from_type_annotation_kinds(
if need_subst { if need_subst {
subst_list.as_mut().map(|wl| wl.push(ty)); subst_list.as_mut().map(|wl| wl.push(ty));
} }
Ok(ty) Ok(DiagnosticsResult::err(ty, diagnostics))
} }
} else { } else {
unreachable!("should be class def here") unreachable!("should be class def here")
} }
} }
TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) => Ok(*ty), TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) => Ok(DiagnosticsResult::err(*ty, diagnostics)),
TypeAnnotation::Virtual(ty) => { TypeAnnotation::Virtual(ty) => {
let ty = get_type_from_type_annotation_kinds( let ty = get_type_from_type_annotation_kinds(
top_level_defs, top_level_defs,
unifier, unifier,
primitives, primitives,
ty.as_ref(), ty.as_ref(),
subst_list subst_list,
loc,
)?; )?;
Ok(unifier.add_ty(TypeEnum::TVirtual { ty })) let ty = diagnostics.consume_partial_result(ty);
Ok(DiagnosticsResult::err(unifier.add_ty(TypeEnum::TVirtual { ty }), diagnostics))
} }
TypeAnnotation::List(ty) => { TypeAnnotation::List(ty) => {
let ty = get_type_from_type_annotation_kinds( let ty = get_type_from_type_annotation_kinds(
@ -412,18 +512,26 @@ pub fn get_type_from_type_annotation_kinds(
unifier, unifier,
primitives, primitives,
ty.as_ref(), ty.as_ref(),
subst_list subst_list,
loc,
)?; )?;
Ok(unifier.add_ty(TypeEnum::TList { ty })) let ty = diagnostics.consume_partial_result(ty);
Ok(DiagnosticsResult::err(unifier.add_ty(TypeEnum::TList { ty }), diagnostics))
} }
TypeAnnotation::Tuple(tys) => { TypeAnnotation::Tuple(tys) => {
let tys = tys let tys = tys
.iter() .iter()
.map(|x| { .map(|x| {
get_type_from_type_annotation_kinds(top_level_defs, unifier, primitives, x, subst_list) get_type_from_type_annotation_kinds(top_level_defs, unifier, primitives, x, subst_list, loc)
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
Ok(unifier.add_ty(TypeEnum::TTuple { ty: tys })) diagnostics.extend(tys.iter().cloned().flat_map(|res| res.engine.into_iter()));
let tys = tys.into_iter()
.map(|res| res.value)
.collect_vec();
Ok(DiagnosticsResult::err(unifier.add_ty(TypeEnum::TTuple { ty: tys }), diagnostics))
} }
} }
} }

View File

@ -1,4 +1,5 @@
use crate::typecheck::typedef::TypeEnum; use crate::typecheck::typedef::TypeEnum;
use crate::{Diagnostic::*, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics};
use super::type_inferencer::Inferencer; use super::type_inferencer::Inferencer;
use super::typedef::Type; use super::typedef::Type;
@ -6,11 +7,14 @@ 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>>) -> ResultOrDiagnostics<()> {
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(DiagnosticEngine::single(DiagErr(
"cannot have value none".into(),
expr.location,
)))
} else { } else {
Ok(()) Ok(DiagnosticsResult::ok(()))
} }
} }
@ -18,100 +22,150 @@ 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> { ) -> ResultOrDiagnostics<()> {
let mut diagnostics = DiagnosticEngine::new();
match &pattern.node { match &pattern.node {
ast::ExprKind::Name { id, .. } if id == &"none".into() => ExprKind::Name { id, .. } if id == &"none".into() => {
Err(format!("cannot assign to a `none` (at {})", pattern.location)), diagnostics.insert(DiagErr(
"cannot assign to a `none`".into(),
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);
} }
self.should_have_value(pattern)?;
Ok(()) if let Err(e) = self.should_have_value(pattern) {
diagnostics.merge_with(e);
}
} }
ExprKind::Tuple { elts, .. } => { ExprKind::Tuple { elts, .. } => {
for elt in elts.iter() { for elt in elts.iter() {
self.check_pattern(elt, defined_identifiers)?; if let Err(e) = self.check_pattern(elt, defined_identifiers) {
self.should_have_value(elt)?; diagnostics.merge_with(e);
}
if let Err(e) = self.should_have_value(elt) {
diagnostics.merge_with(e);
}
} }
Ok(())
} }
ExprKind::Subscript { value, slice, .. } => { ExprKind::Subscript { value, slice, .. } => {
self.check_expr(value, defined_identifiers)?; if let Err(e) = self.check_expr(value, defined_identifiers) {
self.should_have_value(value)?; diagnostics.merge_with(e);
self.check_expr(slice, defined_identifiers)?; }
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
if let Err(e) = self.check_expr(slice, defined_identifiers) {
diagnostics.merge_with(e);
}
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!( diagnostics.insert(DiagErr(
"Error at {}: cannot assign to tuple element", "cannot assign to tuple element".into(),
value.location value.location,
)); ));
} }
Ok(())
} }
ExprKind::Constant { .. } => { ExprKind::Constant { .. } => {
Err(format!("cannot assign to a constant (at {})", pattern.location)) diagnostics.insert(DiagErr(
"cannot assign to a constant".into(),
pattern.location,
));
}
_ => {
if let Err(e) = self.check_expr(pattern, defined_identifiers) {
diagnostics.merge_with(e);
}
} }
_ => self.check_expr(pattern, defined_identifiers),
} }
diagnostics.into_result(())
} }
fn check_expr( fn check_expr(
&mut self, &mut self,
expr: &Expr<Option<Type>>, expr: &Expr<Option<Type>>,
defined_identifiers: &mut HashSet<StrRef>, defined_identifiers: &mut HashSet<StrRef>,
) -> Result<(), String> { ) -> ResultOrDiagnostics<()> {
let mut diagnostics = DiagnosticEngine::new();
// 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 !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) { if !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
return Err(format!( diagnostics.insert(DiagErr(
"expected concrete type at {} but got {}", format!("expected concrete type but got {}", self.unifier.get_ty(*ty).get_type_name()),
expr.location, expr.location,
self.unifier.get_ty(*ty).get_type_name()
)); ));
} }
} }
match &expr.node { match &expr.node {
ExprKind::Name { id, .. } => { ExprKind::Name { id, .. } => {
if id == &"none".into() { if id != &"none".into() {
return Ok(()); if let Err(e) = self.should_have_value(expr) {
} diagnostics.merge_with(e);
self.should_have_value(expr)?; }
if !defined_identifiers.contains(id) {
match self.function_data.resolver.get_symbol_type( if !defined_identifiers.contains(id) {
self.unifier, match self.function_data.resolver.get_symbol_type(
&self.top_level.definitions.read(), self.unifier,
self.primitives, &self.top_level.definitions.read(),
*id, self.primitives,
) { *id,
Ok(_) => { ) {
self.defined_identifiers.insert(*id); Ok(_) => {
} self.defined_identifiers.insert(*id);
Err(e) => { }
return Err(format!(
"type error at identifier `{}` ({}) at {}", Err(e) => {
id, e, expr.location diagnostics.insert(DiagErr(
)); format!("type error at identifier `{}` ({})", id, e),
expr.location,
));
}
} }
} }
} }
} }
ExprKind::List { elts, .. } ExprKind::List { elts, .. }
| ExprKind::Tuple { elts, .. } | ExprKind::Tuple { elts, .. }
| ExprKind::BoolOp { values: elts, .. } => { | ExprKind::BoolOp { values: elts, .. } => {
for elt in elts.iter() { for elt in elts.iter() {
self.check_expr(elt, defined_identifiers)?; diagnostics.consume_result_to_option(self.check_expr(elt, defined_identifiers));
self.should_have_value(elt)?; diagnostics.consume_result_to_option(self.should_have_value(elt));
} }
} }
ExprKind::Attribute { value, .. } => { ExprKind::Attribute { value, .. } => {
self.check_expr(value, defined_identifiers)?; if let Err(errs) = self.check_expr(value, defined_identifiers) {
self.should_have_value(value)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
} }
ExprKind::BinOp { left, op, right } => {
self.check_expr(left, defined_identifiers)?; ExprKind::BinOp { left, op, right, .. } => {
self.check_expr(right, defined_identifiers)?; if let Err(errs) = self.check_expr(left, defined_identifiers) {
self.should_have_value(left)?; diagnostics.merge_with(errs);
self.should_have_value(right)?; }
if let Err(errs) = self.check_expr(right, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(left) {
diagnostics.merge_with(e);
}
if let Err(e) = self.should_have_value(right) {
diagnostics.merge_with(e);
}
// Check whether a bitwise shift has a negative RHS constant value // Check whether a bitwise shift has a negative RHS constant value
if *op == LShift || *op == RShift { if *op == LShift || *op == RShift {
@ -122,40 +176,70 @@ impl<'a> Inferencer<'a> {
}; };
if *rhs_val < 0 { if *rhs_val < 0 {
return Err(format!( diagnostics.insert(DiagErr(
"shift count is negative at {}", "shift count is negative".into(),
right.location right.location,
)); ));
} }
} }
} }
} }
ExprKind::UnaryOp { operand, .. } => { ExprKind::UnaryOp { operand, .. } => {
self.check_expr(operand, defined_identifiers)?; if let Err(errs) = self.check_expr(operand, defined_identifiers) {
self.should_have_value(operand)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(operand) {
diagnostics.merge_with(e);
}
} }
ExprKind::Compare { left, comparators, .. } => { ExprKind::Compare { left, comparators, .. } => {
for elt in once(left.as_ref()).chain(comparators.iter()) { for elt in once(left.as_ref()).chain(comparators.iter()) {
self.check_expr(elt, defined_identifiers)?; if let Err(errs) = self.check_expr(elt, defined_identifiers) {
self.should_have_value(elt)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(elt) {
diagnostics.merge_with(e);
}
} }
} }
ExprKind::Subscript { value, slice, .. } => { ExprKind::Subscript { value, slice, .. } => {
self.should_have_value(value)?; if let Err(e) = self.should_have_value(value) {
self.check_expr(value, defined_identifiers)?; diagnostics.merge_with(e);
self.check_expr(slice, defined_identifiers)?; }
if let Err(errs) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(errs) = self.check_expr(slice, defined_identifiers) {
diagnostics.merge_with(errs);
}
} }
ExprKind::IfExp { test, body, orelse } => { ExprKind::IfExp { test, body, orelse } => {
self.check_expr(test, defined_identifiers)?; if let Err(errs) = self.check_expr(test, defined_identifiers) {
self.check_expr(body, defined_identifiers)?; diagnostics.merge_with(errs);
self.check_expr(orelse, defined_identifiers)?; }
if let Err(errs) = self.check_expr(body, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(errs) = self.check_expr(orelse, defined_identifiers) {
diagnostics.merge_with(errs);
}
} }
ExprKind::Slice { lower, upper, step } => { ExprKind::Slice { lower, upper, step } => {
for elt in [lower.as_ref(), upper.as_ref(), step.as_ref()].iter().flatten() { for elt in [lower.as_ref(), upper.as_ref(), step.as_ref()].iter().flatten() {
self.should_have_value(elt)?; if let Err(e) = self.should_have_value(elt) {
self.check_expr(elt, defined_identifiers)?; diagnostics.merge_with(e);
}
if let Err(errs) = self.check_expr(elt, defined_identifiers) {
diagnostics.merge_with(errs);
}
} }
} }
ExprKind::Lambda { args, body } => { ExprKind::Lambda { args, body } => {
let mut defined_identifiers = defined_identifiers.clone(); let mut defined_identifiers = defined_identifiers.clone();
for arg in args.args.iter() { for arg in args.args.iter() {
@ -164,160 +248,286 @@ impl<'a> Inferencer<'a> {
defined_identifiers.insert(arg.node.arg); defined_identifiers.insert(arg.node.arg);
} }
} }
self.check_expr(body, &mut defined_identifiers)?;
if let Err(errs) = self.check_expr(body, &mut defined_identifiers) {
diagnostics.merge_with(errs);
}
} }
ExprKind::ListComp { elt, generators, .. } => { ExprKind::ListComp { elt, generators, .. } => {
// in our type inference stage, we already make sure that there is only 1 generator // in our type inference stage, we already make sure that there is only 1 generator
let ast::Comprehension { target, iter, ifs, .. } = &generators[0]; let ast::Comprehension { target, iter, ifs, .. } = &generators[0];
self.check_expr(iter, defined_identifiers)?; if let Err(errs) = self.check_expr(iter, defined_identifiers) {
self.should_have_value(iter)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(iter) {
diagnostics.merge_with(e);
}
let mut defined_identifiers = defined_identifiers.clone(); let mut defined_identifiers = defined_identifiers.clone();
self.check_pattern(target, &mut defined_identifiers)?; if let Err(errs) = self.check_pattern(target, &mut defined_identifiers) {
self.should_have_value(target)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(target) {
diagnostics.merge_with(e);
}
for term in once(elt.as_ref()).chain(ifs.iter()) { for term in once(elt.as_ref()).chain(ifs.iter()) {
self.check_expr(term, &mut defined_identifiers)?; if let Err(errs) = self.check_expr(term, &mut defined_identifiers) {
self.should_have_value(term)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(term) {
diagnostics.merge_with(e);
}
} }
} }
ExprKind::Call { func, args, keywords } => { ExprKind::Call { func, args, keywords } => {
for expr in once(func.as_ref()) for expr in once(func.as_ref())
.chain(args.iter()) .chain(args.iter())
.chain(keywords.iter().map(|v| v.node.value.as_ref())) .chain(keywords.iter().map(|v| v.node.value.as_ref()))
{ {
self.check_expr(expr, defined_identifiers)?; if let Err(errs) = self.check_expr(expr, defined_identifiers) {
self.should_have_value(expr)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(expr) {
diagnostics.merge_with(e);
}
} }
} }
ExprKind::Constant { .. } => {} ExprKind::Constant { .. } => {}
_ => { _ => {
unimplemented!() unimplemented!()
} }
} }
Ok(())
diagnostics.into_result(())
} }
// check statements for proper identifier def-use and return on all paths /// Check statements for proper identifier def-use and return on all paths.
///
/// Returns a tuple containing a `bool` representing whether the statement returns on all paths,
/// and a [HashSet] of all collected errors.
fn check_stmt( fn check_stmt(
&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> { ) -> DiagnosticsResult<bool> {
match &stmt.node { let mut diagnostics = DiagnosticEngine::new();
StmtKind::For { target, iter, body, orelse, .. } => { let stmt_returns = match &stmt.node {
self.check_expr(iter, defined_identifiers)?; StmtKind::For {
self.should_have_value(iter)?; target,
iter,
body,
orelse,
..
} => {
if let Err(errs) = self.check_expr(iter, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(iter) {
diagnostics.merge_with(e);
}
let mut local_defined_identifiers = defined_identifiers.clone(); let mut local_defined_identifiers = defined_identifiers.clone();
for stmt in orelse.iter() { for stmt in orelse.iter() {
self.check_stmt(stmt, &mut local_defined_identifiers)?; let DiagnosticsResult { engine: errs, .. } = self.check_stmt(stmt, &mut local_defined_identifiers);
diagnostics.merge_with(errs);
} }
let mut local_defined_identifiers = defined_identifiers.clone(); let mut local_defined_identifiers = defined_identifiers.clone();
self.check_pattern(target, &mut local_defined_identifiers)?; if let Err(errs) = self.check_pattern(target, &mut local_defined_identifiers) {
self.should_have_value(target)?; diagnostics.merge_with(errs);
for stmt in body.iter() {
self.check_stmt(stmt, &mut local_defined_identifiers)?;
} }
Ok(false) if let Err(e) = self.should_have_value(target) {
diagnostics.merge_with(e);
}
for stmt in body.iter() {
let DiagnosticsResult { engine: errs, .. } = self.check_stmt(stmt, &mut local_defined_identifiers);
diagnostics.merge_with(errs);
}
false
} }
StmtKind::If { test, body, orelse, .. } => { StmtKind::If { test, body, orelse, .. } => {
self.check_expr(test, defined_identifiers)?; if let Err(errs) = self.check_expr(test, defined_identifiers) {
self.should_have_value(test)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(test) {
diagnostics.merge_with(e);
}
let mut body_identifiers = defined_identifiers.clone(); let mut body_identifiers = defined_identifiers.clone();
let mut orelse_identifiers = defined_identifiers.clone(); let mut orelse_identifiers = defined_identifiers.clone();
let body_returned = self.check_block(body, &mut body_identifiers)?; let DiagnosticsResult { value: body_returned, engine: errs } = self.check_block(body, &mut body_identifiers);
let orelse_returned = self.check_block(orelse, &mut orelse_identifiers)?; diagnostics.merge_with(errs);
let DiagnosticsResult { value: orelse_returned, engine: errs } = self.check_block(orelse, &mut orelse_identifiers);
diagnostics.merge_with(errs);
for ident in body_identifiers.iter() { for ident in body_identifiers.iter() {
if !defined_identifiers.contains(ident) && orelse_identifiers.contains(ident) { if !defined_identifiers.contains(ident) && orelse_identifiers.contains(ident) {
defined_identifiers.insert(*ident); defined_identifiers.insert(*ident);
} }
} }
Ok(body_returned && orelse_returned)
body_returned && orelse_returned
} }
StmtKind::While { test, body, orelse, .. } => { StmtKind::While { test, body, orelse, .. } => {
self.check_expr(test, defined_identifiers)?; if let Err(errs) = self.check_expr(test, defined_identifiers) {
self.should_have_value(test)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(test) {
diagnostics.merge_with(e);
}
let mut defined_identifiers = defined_identifiers.clone(); let mut defined_identifiers = defined_identifiers.clone();
self.check_block(body, &mut defined_identifiers)?; let DiagnosticsResult { engine: errs, .. } = self.check_block(body, &mut defined_identifiers);
self.check_block(orelse, &mut defined_identifiers)?; diagnostics.merge_with(errs);
Ok(false) let DiagnosticsResult { engine: errs, .. } = self.check_block(orelse, &mut defined_identifiers);
diagnostics.merge_with(errs);
false
} }
StmtKind::With { items, body, .. } => { StmtKind::With { items, body, .. } => {
let mut new_defined_identifiers = defined_identifiers.clone(); let mut new_defined_identifiers = defined_identifiers.clone();
for item in items.iter() { for item in items.iter() {
self.check_expr(&item.context_expr, defined_identifiers)?; if let Err(errs) = self.check_expr(&item.context_expr, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Some(var) = item.optional_vars.as_ref() { if let Some(var) = item.optional_vars.as_ref() {
self.check_pattern(var, &mut new_defined_identifiers)?; if let Err(errs) = self.check_pattern(var, &mut new_defined_identifiers) {
diagnostics.merge_with(errs);
}
} }
} }
self.check_block(body, &mut new_defined_identifiers)?;
Ok(false) let DiagnosticsResult { engine: errs, .. } = self.check_block(body, &mut new_defined_identifiers);
diagnostics.merge_with(errs);
false
} }
StmtKind::Try { body, handlers, orelse, finalbody, .. } => { StmtKind::Try { body, handlers, orelse, finalbody, .. } => {
self.check_block(body, &mut defined_identifiers.clone())?; let DiagnosticsResult { engine: errs, .. } = self.check_block(body, &mut defined_identifiers.clone());
self.check_block(orelse, &mut defined_identifiers.clone())?; diagnostics.merge_with(errs);
let DiagnosticsResult { engine: errs, .. } = self.check_block(orelse, &mut defined_identifiers.clone());
diagnostics.merge_with(errs);
for handler in handlers.iter() { for handler in handlers.iter() {
let mut defined_identifiers = defined_identifiers.clone(); let mut defined_identifiers = defined_identifiers.clone();
let ast::ExcepthandlerKind::ExceptHandler { name, body, .. } = &handler.node; let ast::ExcepthandlerKind::ExceptHandler { name, body, .. } = &handler.node;
if let Some(name) = name { if let Some(name) = name {
defined_identifiers.insert(*name); defined_identifiers.insert(*name);
} }
self.check_block(body, &mut defined_identifiers)?;
let DiagnosticsResult { engine: errs, .. } = self.check_block(body, &mut defined_identifiers);
diagnostics.merge_with(errs);
} }
self.check_block(finalbody, defined_identifiers)?;
Ok(false) let DiagnosticsResult { engine: errs, .. } = self.check_block(finalbody, defined_identifiers);
diagnostics.merge_with(errs);
false
} }
StmtKind::Expr { value, .. } => { StmtKind::Expr { value, .. } => {
self.check_expr(value, defined_identifiers)?; if let Err(e) = self.check_expr(value, defined_identifiers) {
Ok(false) diagnostics.merge_with(e);
}
StmtKind::Assign { targets, value, .. } => {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
for target in targets {
self.check_pattern(target, defined_identifiers)?;
} }
Ok(false)
false
} }
StmtKind::Assign { targets, value, .. } => {
if let Err(errs) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
for target in targets {
if let Err(errs) = self.check_pattern(target, defined_identifiers) {
diagnostics.merge_with(errs);
}
}
false
}
StmtKind::AnnAssign { target, value, .. } => { StmtKind::AnnAssign { target, value, .. } => {
if let Some(value) = value { if let Some(value) = value {
self.check_expr(value, defined_identifiers)?; if let Err(errs) = self.check_expr(value, defined_identifiers) {
self.should_have_value(value)?; diagnostics.merge_with(errs);
self.check_pattern(target, defined_identifiers)?; }
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
if let Err(errs) = self.check_pattern(target, defined_identifiers) {
diagnostics.merge_with(errs);
}
} }
Ok(false)
false
} }
StmtKind::Return { value, .. } => { StmtKind::Return { value, .. } => {
if let Some(value) = value { if let Some(value) = value {
self.check_expr(value, defined_identifiers)?; if let Err(errs) = self.check_expr(value, defined_identifiers) {
self.should_have_value(value)?; diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
} }
Ok(true)
true
} }
StmtKind::Raise { exc, .. } => { StmtKind::Raise { exc, .. } => {
if let Some(value) = exc { if let Some(value) = exc {
self.check_expr(value, defined_identifiers)?; if let Err(errs) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(errs);
}
} }
Ok(true)
true
} }
// break, raise, etc. // break, raise, etc.
_ => Ok(false), _ => false,
} };
DiagnosticsResult::err(stmt_returns, diagnostics)
} }
pub fn check_block( pub fn check_block(
&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> { ) -> DiagnosticsResult<bool> {
let mut diagnostics = DiagnosticEngine::new();
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) diagnostics.insert(DiagWarn(
} "unreachable statement".into(),
if self.check_stmt(stmt, defined_identifiers)? { stmt.location,
ret = true; ));
} }
let DiagnosticsResult { value: stmt_rets, engine: stmt_errs } =
self.check_stmt(stmt, defined_identifiers);
ret |= stmt_rets;
diagnostics.merge_with(stmt_errs);
} }
Ok(ret)
DiagnosticsResult::err(ret, diagnostics)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -43,8 +43,10 @@ impl SymbolResolver for Resolver {
unimplemented!() unimplemented!()
} }
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> { fn get_identifier_def(&self, id: StrRef, loc: Option<Location>) -> ResultOrDiagnostics<DefinitionId> {
self.id_to_def.get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string()) self.id_to_def.get(&id).cloned()
.map(|res| DiagnosticsResult::ok(res))
.ok_or_else(|| DiagnosticEngine::single(DiagErr("undefined identifier".into(), loc.unwrap())))
} }
fn get_string_id(&self, _: &str) -> i32 { fn get_string_id(&self, _: &str) -> i32 {
@ -543,7 +545,8 @@ fn test_basic(source: &str, mapping: HashMap<&str, &str>, virtuals: &[(&str, &st
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
.unwrap(); .unwrap();
inferencer.check_block(&statements, &mut defined_identifiers).unwrap(); let DiagnosticsResult { engine: errs, .. } = inferencer.check_block(&statements, &mut defined_identifiers);
assert_eq!(errs.is_empty(), true);
for (k, v) in inferencer.variable_mapping.iter() { for (k, v) in inferencer.variable_mapping.iter() {
let name = inferencer.unifier.internal_stringify( let name = inferencer.unifier.internal_stringify(
@ -689,7 +692,8 @@ fn test_primitive_magic_methods(source: &str, mapping: HashMap<&str, &str>) {
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
.unwrap(); .unwrap();
inferencer.check_block(&statements, &mut defined_identifiers).unwrap(); let DiagnosticsResult { engine: errs, .. } = inferencer.check_block(&statements, &mut defined_identifiers);
assert_eq!(errs.is_empty(), true);
for (k, v) in inferencer.variable_mapping.iter() { for (k, v) in inferencer.variable_mapping.iter() {
let name = inferencer.unifier.internal_stringify( let name = inferencer.unifier.internal_stringify(

View File

@ -1,15 +1,11 @@
use nac3core::{ use nac3core::{codegen::CodeGenContext, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, symbol_resolver::{SymbolResolver, SymbolValue, ValueEnum}, toplevel::{DefinitionId, TopLevelDef}, typecheck::{
codegen::CodeGenContext, type_inferencer::PrimitiveStore,
symbol_resolver::{SymbolResolver, SymbolValue, ValueEnum}, typedef::{Type, Unifier},
toplevel::{DefinitionId, TopLevelDef}, }};
typecheck::{ use nac3parser::ast::{self, Location, StrRef};
type_inferencer::PrimitiveStore,
typedef::{Type, Unifier},
},
};
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 nac3core::Diagnostic::DiagErr;
pub struct ResolverInternal { pub struct ResolverInternal {
pub id_to_type: Mutex<HashMap<StrRef, Type>>, pub id_to_type: Mutex<HashMap<StrRef, Type>>,
@ -61,8 +57,10 @@ impl SymbolResolver for Resolver {
unimplemented!() unimplemented!()
} }
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> { fn get_identifier_def(&self, id: StrRef, loc: Option<Location>) -> ResultOrDiagnostics<DefinitionId> {
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Undefined identifier".to_string()) self.0.id_to_def.lock().get(&id).cloned()
.map(|res| DiagnosticsResult::ok(res))
.ok_or_else(|| DiagnosticEngine::single(DiagErr("undefined identifier".into(), loc.unwrap())))
} }
fn get_string_id(&self, s: &str) -> i32 { fn get_string_id(&self, s: &str) -> i32 {

View File

@ -9,21 +9,16 @@ use inkwell::{
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use std::{borrow::Borrow, collections::HashMap, fs, path::Path, sync::Arc}; use std::{borrow::Borrow, collections::HashMap, fs, path::Path, sync::Arc};
use nac3core::{ use nac3core::{codegen::{
codegen::{ concrete_type::ConcreteTypeStore, irrt::load_irrt, CodeGenLLVMOptions,
concrete_type::ConcreteTypeStore, irrt::load_irrt, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry, }, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, symbol_resolver::SymbolResolver, toplevel::{
}, composer::TopLevelComposer, helper::parse_parameter_default_value, type_annotation::*,
symbol_resolver::SymbolResolver, TopLevelDef,
toplevel::{ }, typecheck::{
composer::TopLevelComposer, helper::parse_parameter_default_value, type_annotation::*, type_inferencer::PrimitiveStore,
TopLevelDef, typedef::{FunSignature, Type, Unifier},
}, }};
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{FunSignature, Type, Unifier},
},
};
use nac3parser::{ use nac3parser::{
ast::{Expr, ExprKind, StmtKind}, ast::{Expr, ExprKind, StmtKind},
parser, parser,
@ -31,6 +26,7 @@ use nac3parser::{
mod basic_symbol_resolver; mod basic_symbol_resolver;
use basic_symbol_resolver::*; use basic_symbol_resolver::*;
use nac3core::Diagnostic::DiagErr;
/// Command-line argument parser definition. /// Command-line argument parser definition.
#[derive(Parser)] #[derive(Parser)]
@ -73,13 +69,17 @@ 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> { ) -> ResultOrDiagnostics<Type> {
let mut diagnostics = DiagnosticEngine::new();
if let ExprKind::Call { func, args, .. } = &var.node { if let ExprKind::Call { func, args, .. } = &var.node {
if matches!(&func.node, ExprKind::Name { id, .. } if id == &"TypeVar".into()) { if matches!(&func.node, ExprKind::Name { id, .. } if id == &"TypeVar".into()) {
let constraints = args let constraints = args
.iter() .iter()
.skip(1) .skip(1)
.map(|x| -> Result<Type, String> { .map(|x| {
let mut diagnostics = DiagnosticEngine::new();
let ty = parse_ast_to_type_annotation_kinds( let ty = parse_ast_to_type_annotation_kinds(
resolver, resolver,
def_list, def_list,
@ -88,23 +88,69 @@ fn handle_typevar_definition(
x, x,
Default::default(), Default::default(),
)?; )?;
get_type_from_type_annotation_kinds( let (ty, diags) = ty.into();
def_list, unifier, primitives, &ty, &mut None diagnostics.merge_with(diags);
)
match get_type_from_type_annotation_kinds(
def_list,
unifier,
primitives,
&ty,
&mut None,
&Some(x.location),
) {
Ok(v) => {
let v: (Type, DiagnosticEngine) = v.into();
diagnostics.merge_with(v.1);
Ok(DiagnosticsResult::err(v.0, diagnostics))
},
Err(diags) => {
diagnostics.merge_with(diags);
Err(diagnostics)
}
}
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Vec<_>>();
Ok(unifier.get_fresh_var_with_range(&constraints, None, None).0)
diagnostics.extend(constraints.iter().cloned().flat_map(|res| match res {
Ok(v) => {
let (_, diags) = v.into();
diags
}
Err(diags) => diags
}.into_iter()));
if diagnostics.has_errors() {
return Err(diagnostics)
}
let constraints: Vec<Type> = constraints.into_iter()
.map(|res|
if let Ok(res) = res {
let (v, _) = res.into();
v
} else { unreachable!() }
)
.collect();
Ok(DiagnosticsResult::err(unifier.get_fresh_var_with_range(&constraints, None, None).0, diagnostics))
} else { } else {
Err(format!( Err(DiagnosticEngine::single(DiagErr(
"expression {:?} cannot be handled as a TypeVar in global scope", format!(
var "expression {:?} cannot be handled as a TypeVar in global scope",
)) var
),
var.location
)))
} }
} else { } else {
Err(format!( Err(DiagnosticEngine::single(DiagErr(
"expression {:?} cannot be handled as a TypeVar in global scope", format!(
var "expression {:?} cannot be handled as a TypeVar in global scope",
)) var
),
var.location
)))
} }
} }
@ -116,7 +162,9 @@ fn handle_assignment_pattern(
def_list: &[Arc<RwLock<TopLevelDef>>], def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier, unifier: &mut Unifier,
primitives: &PrimitiveStore, primitives: &PrimitiveStore,
) -> Result<(), String> { ) -> ResultOrDiagnostics<()> {
let mut diagnostics = DiagnosticEngine::new();
if targets.len() == 1 { if targets.len() == 1 {
match &targets[0].node { match &targets[0].node {
ExprKind::Name { id, .. } => { ExprKind::Name { id, .. } => {
@ -127,18 +175,23 @@ fn handle_assignment_pattern(
unifier, unifier,
primitives, primitives,
) { ) {
let (var, diags) = var.into();
diagnostics.merge_with(diags);
internal_resolver.add_id_type(*id, var); internal_resolver.add_id_type(*id, var);
Ok(())
Ok(DiagnosticsResult::err((), diagnostics))
} else if let Ok(val) = } else if let Ok(val) =
parse_parameter_default_value(value.borrow(), resolver) parse_parameter_default_value(value.borrow(), resolver)
{ {
let (val, diags) = val.into();
diagnostics.merge_with(diags);
internal_resolver.add_module_global(*id, val); internal_resolver.add_module_global(*id, val);
Ok(()) Ok(DiagnosticsResult::err((), diagnostics))
} else { } else {
Err(format!("fails to evaluate this expression `{:?}` as a constant or TypeVar at {}", Err(DiagnosticEngine::single(DiagErr(
targets[0].node, format!("fails to evaluate this expression `{:?}` as a constant or TypeVar", targets[0].node),
targets[0].location, targets[0].location,
)) )))
} }
} }
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => { ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
@ -151,23 +204,21 @@ fn handle_assignment_pattern(
unifier, unifier,
primitives, primitives,
)?; )?;
Ok(()) Ok(DiagnosticsResult::ok(()))
} }
_ => Err(format!( _ => Err(DiagnosticEngine::single(DiagErr(
"assignment to {:?} is not supported at {}", format!("assignment to {:?} is not supported", targets[0]),
targets[0], targets[0].location targets[0].location
)), ))),
} }
} else { } else {
match &value.node { match &value.node {
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => { ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
if elts.len() != targets.len() { if elts.len() != targets.len() {
Err(format!( Err(DiagnosticEngine::single(DiagErr(
"number of elements to unpack does not match (expect {}, found {}) at {}", format!("number of elements to unpack does not match (expect {}, found {})", targets.len(), elts.len()),
targets.len(), value.location,
elts.len(), )))
value.location
))
} else { } else {
for (tar, val) in targets.iter().zip(elts) { for (tar, val) in targets.iter().zip(elts) {
handle_assignment_pattern( handle_assignment_pattern(
@ -180,13 +231,13 @@ fn handle_assignment_pattern(
primitives, primitives,
)?; )?;
} }
Ok(()) Ok(DiagnosticsResult::ok(()))
} }
} }
_ => Err(format!( _ => Err(DiagnosticEngine::single(DiagErr(
"unpack of this expression is not supported at {}", "unpack of this expression is not supported".into(),
value.location value.location
)), ))),
} }
} }
} }
@ -272,7 +323,7 @@ fn main() {
unifier, unifier,
primitives, primitives,
) { ) {
eprintln!("{}", err); eprintln!("{}", err.as_string().join("\n----------\n"));
return; return;
} }
}, },
@ -303,9 +354,12 @@ fn main() {
let instance = { let instance = {
let defs = top_level.definitions.read(); let defs = top_level.definitions.read();
let mut instance = defs[resolver let mut instance = defs[resolver
.get_identifier_def("run".into()) .get_identifier_def("run".into(), None)
.unwrap_or_else(|_| panic!("cannot find run() entry point")) .map(|res| {
.0] let (res, _) = res.into();
res.0
})
.unwrap_or_else(|_| panic!("cannot find run() entry point"))]
.write(); .write();
if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } = &mut *instance { if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } = &mut *instance {
instance_to_symbol.insert("".to_string(), "run".to_string()); instance_to_symbol.insert("".to_string(), "run".to_string());