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},
symbol_resolver::{StaticValue, SymbolResolver, SymbolValue, ValueEnum},
toplevel::{DefinitionId, TopLevelDef},
typecheck::{
type_inferencer::PrimitiveStore, type_inferencer::PrimitiveStore,
typedef::{Type, TypeEnum, Unifier}, typedef::{Type, TypeEnum, Unifier},
}, }};
}; use nac3parser::ast::{self, Location, StrRef};
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,7 +1,6 @@
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,
@ -9,14 +8,10 @@ use crate::{
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::{
symbol_resolver::{SymbolValue, ValueEnum},
toplevel::{DefinitionId, TopLevelDef},
typecheck::{
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier}, typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
magic_methods::{binop_name, binop_assign_name}, 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::{
symbol_resolver::{SymbolResolver, ValueEnum},
toplevel::{
composer::TopLevelComposer, DefinitionId, FunInstance, TopLevelContext, TopLevelDef, composer::TopLevelComposer, DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
}, }, typecheck::{
typecheck::{
type_inferencer::{FunctionData, Inferencer, PrimitiveStore}, type_inferencer::{FunctionData, Inferencer, PrimitiveStore},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier}, 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(
unifier.add_ty(TypeEnum::TObj {
obj_id, obj_id,
fields, fields,
params: Default::default(), 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, .. } => {
let param_def_values = elts
.iter() .iter()
.map(|x| parse_parameter_default_value(x, resolver)) .map(|x| parse_parameter_default_value(x, resolver))
.collect::<Result<Vec<_>, _>>()? .collect::<Result<Vec<_>, _>>()?;
)), let diagnostics = DiagnosticEngine::from(
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone), param_def_values.iter().cloned()
ast::ExprKind::Name { id, .. } => { .flat_map(|res| res.engine.into_iter())
resolver.get_default_param_value(default).ok_or_else( .collect::<HashSet<Diagnostic>>()
|| format!( );
"`{}` cannot be used as a default parameter at {} \ let param_def_values = param_def_values.into_iter()
(not primitive type, option or tuple / not defined?)", .map(|res| res.value)
id, .collect_vec();
default.location
) Ok(DiagnosticsResult::err(
) SymbolValue::Tuple(param_def_values),
} diagnostics,
_ => Err(format!(
"unsupported default parameter (not primitive type, option or tuple) at {}",
default.location
)) ))
} }
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(DiagnosticsResult::ok(SymbolValue::OptionNone)),
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,
symbol_resolver::{SymbolResolver, ValueEnum},
toplevel::DefinitionId,
typecheck::{
type_inferencer::PrimitiveStore, type_inferencer::PrimitiveStore,
typedef::{Type, Unifier}, 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!(
"expect {} type parameters but got {}",
type_vars.len(), type_vars.len(),
params_ast.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!(
"expected {} type parameters for class `{}` but got {}",
type_vars.len(), type_vars.len(),
params.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,7 +444,8 @@ 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(
format!(
"cannot apply type {} to type variable with id {:?}", "cannot apply type {} to type variable with id {:?}",
unifier.internal_stringify( unifier.internal_stringify(
p, p,
@ -360,7 +454,10 @@ pub fn get_type_from_type_annotation_kinds(
&mut None &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,64 +22,97 @@ 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);
} }
Ok(()) if let Err(e) = self.should_have_value(elt) {
diagnostics.merge_with(e);
} }
}
}
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,
));
} }
_ => self.check_expr(pattern, defined_identifiers),
_ => {
if let Err(e) = self.check_expr(pattern, defined_identifiers) {
diagnostics.merge_with(e);
} }
} }
}
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) { if !defined_identifiers.contains(id) {
match self.function_data.resolver.get_symbol_type( match self.function_data.resolver.get_symbol_type(
self.unifier, self.unifier,
@ -86,32 +123,49 @@ impl<'a> Inferencer<'a> {
Ok(_) => { Ok(_) => {
self.defined_identifiers.insert(*id); self.defined_identifiers.insert(*id);
} }
Err(e) => { Err(e) => {
return Err(format!( diagnostics.insert(DiagErr(
"type error at identifier `{}` ({}) at {}", format!("type error at identifier `{}` ({})", id, e),
id, e, expr.location 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, .. } => {
if let Err(errs) = self.check_expr(left, defined_identifiers) {
diagnostics.merge_with(errs);
}
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);
} }
ExprKind::BinOp { left, op, right } => {
self.check_expr(left, defined_identifiers)?;
self.check_expr(right, defined_identifiers)?;
self.should_have_value(left)?;
self.should_have_value(right)?;
// 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);
}
if let Err(e) = self.should_have_value(target) {
diagnostics.merge_with(e);
}
for stmt in body.iter() { for stmt in body.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);
} }
Ok(false)
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);
let mut defined_identifiers = defined_identifiers.clone();
self.check_block(body, &mut defined_identifiers)?;
self.check_block(orelse, &mut defined_identifiers)?;
Ok(false)
} }
if let Err(e) = self.should_have_value(test) {
diagnostics.merge_with(e);
}
let mut defined_identifiers = defined_identifiers.clone();
let DiagnosticsResult { engine: errs, .. } = self.check_block(body, &mut defined_identifiers);
diagnostics.merge_with(errs);
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);
} }
false
}
StmtKind::Assign { targets, value, .. } => { StmtKind::Assign { targets, 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);
}
for target in targets { for target in targets {
self.check_pattern(target, defined_identifiers)?; if let Err(errs) = self.check_pattern(target, defined_identifiers) {
diagnostics.merge_with(errs);
} }
Ok(false)
} }
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)?;
} }
Ok(false) 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);
}
}
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);
} }
Ok(true) if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
} }
}
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(),
stmt.location,
));
} }
if self.check_stmt(stmt, defined_identifiers)? {
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)
} }
} }

View File

@ -5,8 +5,8 @@ use std::{cell::RefCell, sync::Arc};
use super::typedef::{Call, FunSignature, FuncArg, RecordField, Type, TypeEnum, Unifier}; use super::typedef::{Call, FunSignature, FuncArg, RecordField, Type, TypeEnum, Unifier};
use super::{magic_methods::*, typedef::CallId}; use super::{magic_methods::*, typedef::CallId};
use crate::{symbol_resolver::SymbolResolver, toplevel::TopLevelContext}; use crate::{DiagErr, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, symbol_resolver::SymbolResolver, toplevel::TopLevelContext};
use itertools::izip; use itertools::{Itertools, izip};
use nac3parser::ast::{ use nac3parser::ast::{
self, self,
fold::{self, Fold}, fold::{self, Fold},
@ -64,19 +64,15 @@ pub struct Inferencer<'a> {
struct NaiveFolder(); struct NaiveFolder();
impl fold::Fold<()> for NaiveFolder { impl fold::Fold<()> for NaiveFolder {
type TargetU = Option<Type>; type TargetU = Option<Type>;
type Error = String; type Error = DiagnosticEngine;
fn map_user(&mut self, _: ()) -> Result<Self::TargetU, Self::Error> { fn map_user(&mut self, _: ()) -> Result<Self::TargetU, Self::Error> {
Ok(None) Ok(None)
} }
} }
fn report_error<T>(msg: &str, location: Location) -> Result<T, String> {
Err(format!("{} at {}", msg, location))
}
impl<'a> fold::Fold<()> for Inferencer<'a> { impl<'a> fold::Fold<()> for Inferencer<'a> {
type TargetU = Option<Type>; type TargetU = Option<Type>;
type Error = String; type Error = DiagnosticEngine;
fn map_user(&mut self, _: ()) -> Result<Self::TargetU, Self::Error> { fn map_user(&mut self, _: ()) -> Result<Self::TargetU, Self::Error> {
Ok(None) Ok(None)
@ -86,6 +82,8 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
&mut self, &mut self,
mut node: ast::Stmt<()>, mut node: ast::Stmt<()>,
) -> Result<ast::Stmt<Self::TargetU>, Self::Error> { ) -> Result<ast::Stmt<Self::TargetU>, Self::Error> {
let mut diagnostics = DiagnosticEngine::new();
let stmt = match node.node { let stmt = match node.node {
// we don't want fold over type annotation // we don't want fold over type annotation
ast::StmtKind::AnnAssign { mut target, annotation, value, simple, config_comment } => { ast::StmtKind::AnnAssign { mut target, annotation, value, simple, config_comment } => {
@ -101,19 +99,24 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
self.unify(target.custom.unwrap(), ty.custom.unwrap(), &node.location)?; self.unify(target.custom.unwrap(), ty.custom.unwrap(), &node.location)?;
Some(ty) Some(ty)
} else { } else {
return report_error( diagnostics.insert(DiagErr(
"declaration without definition is not yet supported", "declaration without definition is not yet supported".into(),
node.location, node.location,
); ));
return Err(diagnostics)
}; };
let top_level_defs = self.top_level.definitions.read(); let top_level_defs = self.top_level.definitions.read();
let annotation_type = self.function_data.resolver.parse_type_annotation( let DiagnosticsResult { value: annotation_type, engine: diags } = self.function_data.resolver
.parse_type_annotation(
top_level_defs.as_slice(), top_level_defs.as_slice(),
self.unifier, self.unifier,
self.primitives, self.primitives,
annotation.as_ref(), annotation.as_ref(),
)?; )?;
diagnostics.merge_with(diags);
self.unify(annotation_type, target.custom.unwrap(), &node.location)?; self.unify(annotation_type, target.custom.unwrap(), &node.location)?;
let annotation = Box::new(NaiveFolder().fold_expr(*annotation)?); let annotation = Box::new(NaiveFolder().fold_expr(*annotation)?);
Located { Located {
location: node.location, location: node.location,
@ -127,6 +130,7 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
}, },
} }
} }
ast::StmtKind::Try { body, handlers, orelse, finalbody, config_comment } => { ast::StmtKind::Try { body, handlers, orelse, finalbody, config_comment } => {
let body = body let body = body
.into_iter() .into_iter()
@ -142,28 +146,38 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
let ast::ExcepthandlerKind::ExceptHandler { type_, name, body } = let ast::ExcepthandlerKind::ExceptHandler { type_, name, body } =
handler.node; handler.node;
let type_ = if let Some(type_) = type_ { let type_ = if let Some(type_) = type_ {
let typ = self.function_data.resolver.parse_type_annotation( let DiagnosticsResult { value: typ, engine: diags } = self.function_data.resolver
.parse_type_annotation(
top_level_defs.as_slice(), top_level_defs.as_slice(),
self.unifier, self.unifier,
self.primitives, self.primitives,
&type_, &type_,
)?; )?;
diagnostics.merge_with(diags);
self.virtual_checks.push(( self.virtual_checks.push((
typ, typ,
self.primitives.exception, self.primitives.exception,
handler.location, handler.location,
)); ));
if let Some(name) = name { if let Some(name) = name {
if !self.defined_identifiers.contains(&name) { if !self.defined_identifiers.contains(&name) {
self.defined_identifiers.insert(name); self.defined_identifiers.insert(name);
} }
if let Some(old_typ) = self.variable_mapping.insert(name, typ) { if let Some(old_typ) = self.variable_mapping.insert(name, typ) {
let loc = handler.location; let loc = handler.location;
self.unifier.unify(old_typ, typ).map_err(|e| { self.unifier.unify(old_typ, typ).map_err(|e| {
e.at(Some(loc)).to_display(self.unifier).to_string() // TODO
DiagnosticEngine::single(DiagErr(
e.at(Some(loc)).to_display(self.unifier).to_string(),
loc,
))
})?; })?;
} }
} }
let mut type_ = naive_folder.fold_expr(*type_)?; let mut type_ = naive_folder.fold_expr(*type_)?;
type_.custom = Some(typ); type_.custom = Some(typ);
Some(Box::new(type_)) Some(Box::new(type_))
@ -270,8 +284,12 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
}) })
.collect(); .collect();
let loc = node.location; let loc = node.location;
let targets = targets let targets = targets.map_err(|e|
.map_err(|e| e.at(Some(loc)).to_display(self.unifier).to_string())?; DiagnosticEngine::single(DiagErr(
e.to_display(self.unifier).to_string(),
loc
))
)?;
return Ok(Located { return Ok(Located {
location: node.location, location: node.location,
node: ast::StmtKind::Assign { node: ast::StmtKind::Assign {
@ -307,19 +325,25 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
ast::StmtKind::If { test, .. } | ast::StmtKind::While { test, .. } => { ast::StmtKind::If { test, .. } | ast::StmtKind::While { test, .. } => {
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?; self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
} }
ast::StmtKind::Assign { targets, value, .. } => { ast::StmtKind::Assign { targets, value, .. } => {
for target in targets.iter() { for target in targets.iter() {
self.unify(target.custom.unwrap(), value.custom.unwrap(), &target.location)?; self.unify(target.custom.unwrap(), value.custom.unwrap(), &target.location)?;
} }
} }
ast::StmtKind::AnnAssign { .. } | ast::StmtKind::Expr { .. } => {} ast::StmtKind::AnnAssign { .. } | ast::StmtKind::Expr { .. } => {}
ast::StmtKind::Break { .. } ast::StmtKind::Break { .. }
| ast::StmtKind::Continue { .. } | ast::StmtKind::Continue { .. }
| ast::StmtKind::Pass { .. } => {} | ast::StmtKind::Pass { .. } => {}
ast::StmtKind::Raise { exc, cause, .. } => { ast::StmtKind::Raise { exc, cause, .. } => {
if let Some(cause) = cause { if let Some(cause) = cause {
return report_error("raise ... from cause is not supported", cause.location); diagnostics.insert(DiagErr(
"raise ... from cause is not supported".into(),
cause.location,
));
} }
if let Some(exc) = exc { if let Some(exc) = exc {
self.virtual_checks.push(( self.virtual_checks.push((
exc.custom.unwrap(), exc.custom.unwrap(),
@ -327,12 +351,17 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
exc.location, exc.location,
)); ));
} else if !self.in_handler { } else if !self.in_handler {
return report_error( diagnostics.insert(DiagErr(
"cannot reraise outside exception handlers", "cannot reraise outside exception handlers".into(),
stmt.location, stmt.location,
); ));
}
if diagnostics.has_errors() {
return Err(diagnostics)
} }
} }
ast::StmtKind::With { items, .. } => { ast::StmtKind::With { items, .. } => {
for item in items.iter() { for item in items.iter() {
let ty = item.context_expr.custom.unwrap(); let ty = item.context_expr.custom.unwrap();
@ -340,14 +369,16 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
let mut fast_path = false; let mut fast_path = false;
if let TypeEnum::TObj { fields, .. } = &*self.unifier.get_ty(ty) { if let TypeEnum::TObj { fields, .. } = &*self.unifier.get_ty(ty) {
fast_path = true; fast_path = true;
if let Some(enter) = fields.get(&"__enter__".into()).cloned() { if let Some(enter) = fields.get(&"__enter__".into()).cloned() {
if let TypeEnum::TFunc(signature) = &*self.unifier.get_ty(enter.0) { if let TypeEnum::TFunc(signature) = &*self.unifier.get_ty(enter.0) {
if !signature.args.is_empty() { if !signature.args.is_empty() {
return report_error( diagnostics.insert(DiagErr(
"__enter__ method should take no argument other than self", "__enter__ method should take no argument other than self".into(),
stmt.location, stmt.location,
); ));
} }
if let Some(var) = &item.optional_vars { if let Some(var) = &item.optional_vars {
if signature.vars.is_empty() { if signature.vars.is_empty() {
self.unify( self.unify(
@ -363,29 +394,31 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
fast_path = false; fast_path = false;
} }
} else { } else {
return report_error( diagnostics.insert(DiagErr(
"__enter__ method is required for context manager", "__enter__ method is required for context manager".into(),
stmt.location, stmt.location,
); ));
} }
if let Some(exit) = fields.get(&"__exit__".into()).cloned() { if let Some(exit) = fields.get(&"__exit__".into()).cloned() {
if let TypeEnum::TFunc(signature) = &*self.unifier.get_ty(exit.0) { if let TypeEnum::TFunc(signature) = &*self.unifier.get_ty(exit.0) {
if !signature.args.is_empty() { if !signature.args.is_empty() {
return report_error( diagnostics.insert(DiagErr(
"__exit__ method should take no argument other than self", "__exit__ method should take no argument other than self".into(),
stmt.location, stmt.location,
); ));
} }
} else { } else {
fast_path = false; fast_path = false;
} }
} else { } else {
return report_error( diagnostics.insert(DiagErr(
"__exit__ method is required for context manager", "__exit__ method is required for context manager".into(),
stmt.location, stmt.location,
); ));
} }
} }
if !fast_path { if !fast_path {
let enter = TypeEnum::TFunc(FunSignature { let enter = TypeEnum::TFunc(FunSignature {
args: vec![], args: vec![],
@ -409,51 +442,80 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
self.unify(ty, record, &stmt.location)?; self.unify(ty, record, &stmt.location)?;
} }
} }
if diagnostics.has_errors() {
return Err(diagnostics)
} }
}
ast::StmtKind::Return { value, .. } => match (value, self.function_data.return_type) { ast::StmtKind::Return { value, .. } => match (value, self.function_data.return_type) {
(Some(v), Some(v1)) => { (Some(v), Some(v1)) => {
self.unify(v.custom.unwrap(), v1, &v.location)?; self.unify(v.custom.unwrap(), v1, &v.location)?;
} }
(Some(_), None) => { (Some(_), None) => {
return report_error("Unexpected return value", stmt.location); diagnostics.insert(
DiagErr("Unexpected return value".into(), stmt.location),
);
return Err(diagnostics)
} }
(None, Some(_)) => { (None, Some(_)) => {
return report_error("Expected return value", stmt.location); diagnostics.insert(
DiagErr("Expected return value".into(), stmt.location),
);
return Err(diagnostics)
} }
(None, None) => {} (None, None) => {}
}, },
ast::StmtKind::AugAssign { target, op, value, .. } => { ast::StmtKind::AugAssign { target, op, value, .. } => {
let res_ty = self.infer_bin_ops(stmt.location, target, op, value, true)?; let res_ty = self.infer_bin_ops(stmt.location, target, op, value, true)?;
let res_ty = diagnostics.consume_partial_result(res_ty);
self.unify(res_ty, target.custom.unwrap(), &stmt.location)?; self.unify(res_ty, target.custom.unwrap(), &stmt.location)?;
} }
ast::StmtKind::Assert { test, msg, .. } => { ast::StmtKind::Assert { test, msg, .. } => {
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?; self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
match msg { match msg {
Some(m) => self.unify(m.custom.unwrap(), self.primitives.str, &m.location)?, Some(m) => {
None => () self.unify(m.custom.unwrap(), self.primitives.str, &m.location)?;
}
None => {}
} }
} }
_ => return report_error("Unsupported statement type", stmt.location),
_ => {
diagnostics.insert(
DiagErr("Unsupported statement type".into(), stmt.location)
);
return Err(diagnostics)
}
}; };
Ok(stmt) Ok(stmt)
} }
fn fold_expr(&mut self, node: ast::Expr<()>) -> Result<ast::Expr<Self::TargetU>, Self::Error> { fn fold_expr(&mut self, node: ast::Expr<()>) -> Result<ast::Expr<Self::TargetU>, Self::Error> {
let mut diagnostics = DiagnosticEngine::new();
let expr = match node.node { let expr = match node.node {
// TODO
ast::ExprKind::Call { func, args, keywords } => { ast::ExprKind::Call { func, args, keywords } => {
return self.fold_call(node.location, *func, args, keywords); return self.fold_call(node.location, *func, args, keywords)
.map(|res| res.value);
} }
ast::ExprKind::Lambda { args, body } => { ast::ExprKind::Lambda { args, body } => {
return self.fold_lambda(node.location, *args, *body); return self.fold_lambda(node.location, *args, *body)
.map(|res| res.value);
} }
ast::ExprKind::ListComp { elt, generators } => { ast::ExprKind::ListComp { elt, generators } => {
return self.fold_listcomp(node.location, *elt, generators); return self.fold_listcomp(node.location, *elt, generators)
.map(|res| res.value);
} }
_ => fold::fold_expr(self, node)?, _ => fold::fold_expr(self, node)?,
}; };
let custom = match &expr.node { let custom = match &expr.node {
ast::ExprKind::Constant { value, .. } => { ast::ExprKind::Constant { value, .. } => {
Some(self.infer_constant(value, &expr.location)?) // TODO
Some(self.infer_constant(value, &expr.location)?.value)
} }
ast::ExprKind::Name { id, .. } => { ast::ExprKind::Name { id, .. } => {
// the name `none` is special since it may have different types // the name `none` is special since it may have different types
@ -488,76 +550,96 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
self.defined_identifiers.insert(*id); self.defined_identifiers.insert(*id);
} }
Err(e) => { Err(e) => {
return report_error( diagnostics.insert(DiagErr(
&format!("type error at identifier `{}` ({})", id, e), format!("type error at identifier `{}` ({})", id, e),
expr.location, expr.location,
); ));
} }
} }
} }
Some(self.infer_identifier(*id)?)
diagnostics.consume_result_to_option(self.infer_identifier(*id))
} }
} }
ast::ExprKind::List { elts, .. } => Some(self.infer_list(elts)?),
ast::ExprKind::Tuple { elts, .. } => Some(self.infer_tuple(elts)?), ast::ExprKind::List { elts, .. } => {
Some(self.infer_list(elts)?.value)
}
ast::ExprKind::Tuple { elts, .. } => {
Some(self.infer_tuple(elts)?.value)
}
ast::ExprKind::Attribute { value, attr, ctx } => { ast::ExprKind::Attribute { value, attr, ctx } => {
Some(self.infer_attribute(value, *attr, ctx)?) Some(self.infer_attribute(value, *attr, ctx)?.value)
} }
ast::ExprKind::BoolOp { values, .. } => Some(self.infer_bool_ops(values)?), ast::ExprKind::BoolOp { values, .. } => Some(self.infer_bool_ops(values)?.value),
ast::ExprKind::BinOp { left, op, right } => { ast::ExprKind::BinOp { left, op, right } => {
Some(self.infer_bin_ops(expr.location, left, op, right, false)?) Some(self.infer_bin_ops(expr.location, left, op, right, false)?.value)
} }
ast::ExprKind::UnaryOp { op, operand } => Some(self.infer_unary_ops(op, operand)?), ast::ExprKind::UnaryOp { op, operand } => Some(self.infer_unary_ops(op, operand)?.value),
ast::ExprKind::Compare { left, ops, comparators } => { ast::ExprKind::Compare { left, ops, comparators } => {
Some(self.infer_compare(left, ops, comparators)?) Some(self.infer_compare(left, ops, comparators)?.value)
} }
ast::ExprKind::Subscript { value, slice, ctx, .. } => { ast::ExprKind::Subscript { value, slice, ctx, .. } => {
Some(self.infer_subscript(value.as_ref(), slice.as_ref(), ctx)?) Some(self.infer_subscript(value.as_ref(), slice.as_ref(), ctx)?.value)
} }
ast::ExprKind::IfExp { test, body, orelse } => { ast::ExprKind::IfExp { test, body, orelse } => {
Some(self.infer_if_expr(test, body.as_ref(), orelse.as_ref())?) Some(self.infer_if_expr(test, body.as_ref(), orelse.as_ref())?.value)
} }
ast::ExprKind::ListComp { .. } ast::ExprKind::ListComp { .. }
| ast::ExprKind::Lambda { .. } | ast::ExprKind::Lambda { .. }
| ast::ExprKind::Call { .. } => expr.custom, // already computed | ast::ExprKind::Call { .. } => expr.custom, // already computed
ast::ExprKind::Slice { .. } => None, // we don't need it for slice ast::ExprKind::Slice { .. } => None, // we don't need it for slice
_ => return report_error("not supported", expr.location), _ => {
diagnostics.insert(DiagErr(
"not supported".into(),
expr.location,
));
return Err(diagnostics)
}
}; };
Ok(ast::Expr { custom, location: expr.location, node: expr.node }) Ok(ast::Expr { custom, location: expr.location, node: expr.node })
} }
} }
type InferenceResult = Result<Type, String>; type InferenceResult = ResultOrDiagnostics<Type>;
impl<'a> Inferencer<'a> { impl<'a> Inferencer<'a> {
/// Constrain a <: b /// Constrain a <: b
/// Currently implemented as unification /// Currently implemented as unification
fn constrain(&mut self, a: Type, b: Type, location: &Location) -> Result<(), String> { fn constrain(&mut self, a: Type, b: Type, location: &Location) -> ResultOrDiagnostics<()> {
self.unify(a, b, location) self.unify(a, b, location)
} }
fn unify(&mut self, a: Type, b: Type, location: &Location) -> Result<(), String> { fn unify(&mut self, a: Type, b: Type, location: &Location) -> ResultOrDiagnostics<()> {
self.unifier self.unifier
.unify(a, b) .unify(a, b)
.map_err(|e| e.at(Some(*location)).to_display(self.unifier).to_string()) .map(|res| DiagnosticsResult::ok(res))
.map_err(|e| DiagnosticEngine::single(DiagErr(e.to_display(self.unifier).to_string(), *location)))
} }
fn infer_pattern(&mut self, pattern: &ast::Expr<()>) -> Result<(), String> { fn infer_pattern(&mut self, pattern: &ast::Expr<()>) -> ResultOrDiagnostics<()> {
let diagnostics = DiagnosticEngine::new();
match &pattern.node { match &pattern.node {
ExprKind::Name { id, .. } => { ExprKind::Name { id, .. } => {
if !self.defined_identifiers.contains(id) { if !self.defined_identifiers.contains(id) {
self.defined_identifiers.insert(*id); self.defined_identifiers.insert(*id);
} }
Ok(())
} }
ExprKind::Tuple { elts, .. } => { ExprKind::Tuple { elts, .. } => {
for elt in elts.iter() { for elt in elts.iter() {
self.infer_pattern(elt)?; self.infer_pattern(elt)?;
} }
Ok(())
} }
_ => Ok(()),
_ => ()
} }
Ok(DiagnosticsResult::err((), diagnostics))
} }
fn build_method_call( fn build_method_call(
@ -592,9 +674,12 @@ impl<'a> Inferencer<'a> {
.rev() .rev()
.collect(); .collect();
self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| { self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| {
e.at(Some(location)).to_display(self.unifier).to_string() DiagnosticEngine::single(DiagErr(
e.to_display(self.unifier).to_string(),
location,
))
})?; })?;
return Ok(sign.ret); return Ok(DiagnosticsResult::ok(sign.ret));
} }
} }
} }
@ -614,7 +699,7 @@ impl<'a> Inferencer<'a> {
let fields = once((method.into(), RecordField::new(call, false, Some(location)))).collect(); let fields = once((method.into(), RecordField::new(call, false, Some(location)))).collect();
let record = self.unifier.add_record(fields); let record = self.unifier.add_record(fields);
self.constrain(obj, record, &location)?; self.constrain(obj, record, &location)?;
Ok(ret) Ok(DiagnosticsResult::ok(ret))
} }
fn fold_lambda( fn fold_lambda(
@ -622,7 +707,9 @@ impl<'a> Inferencer<'a> {
location: Location, location: Location,
args: Arguments, args: Arguments,
body: ast::Expr<()>, body: ast::Expr<()>,
) -> Result<ast::Expr<Option<Type>>, String> { ) -> ResultOrDiagnostics<ast::Expr<Option<Type>>> {
let mut diagnostics = DiagnosticEngine::new();
if !args.posonlyargs.is_empty() if !args.posonlyargs.is_empty()
|| args.vararg.is_some() || args.vararg.is_some()
|| !args.kwonlyargs.is_empty() || !args.kwonlyargs.is_empty()
@ -630,10 +717,10 @@ impl<'a> Inferencer<'a> {
|| !args.defaults.is_empty() || !args.defaults.is_empty()
{ {
// actually I'm not sure whether programs violating this is a valid python program. // actually I'm not sure whether programs violating this is a valid python program.
return report_error( diagnostics.insert(DiagErr(
"We only support positional or keyword arguments without defaults for lambdas", "only positional or keyword arguments without defaults is supported for lambdas".into(),
if args.args.is_empty() { body.location } else { args.args[0].location }, if args.args.is_empty() { body.location } else { args.args[0].location },
); ));
} }
let mut defined_identifiers = self.defined_identifiers.clone(); let mut defined_identifiers = self.defined_identifiers.clone();
@ -675,15 +762,21 @@ impl<'a> Inferencer<'a> {
let body = new_context.fold_expr(body)?; let body = new_context.fold_expr(body)?;
new_context.unify(fun.ret, body.custom.unwrap(), &location)?; new_context.unify(fun.ret, body.custom.unwrap(), &location)?;
let mut args = new_context.fold_arguments(args)?; let mut args = new_context.fold_arguments(args)?;
if diagnostics.has_errors() {
return Err(diagnostics)
}
for (arg, (name, ty)) in args.args.iter_mut().zip(fn_args.iter()) { for (arg, (name, ty)) in args.args.iter_mut().zip(fn_args.iter()) {
assert_eq!(&arg.node.arg, name); assert_eq!(&arg.node.arg, name);
arg.custom = Some(*ty); arg.custom = Some(*ty);
} }
Ok(Located {
Ok(DiagnosticsResult::err(Located {
location, location,
node: ExprKind::Lambda { args: args.into(), body: body.into() }, node: ExprKind::Lambda { args: args.into(), body: body.into() },
custom: Some(self.unifier.add_ty(TypeEnum::TFunc(fun))), custom: Some(self.unifier.add_ty(TypeEnum::TFunc(fun))),
}) }, diagnostics))
} }
fn fold_listcomp( fn fold_listcomp(
@ -691,13 +784,17 @@ impl<'a> Inferencer<'a> {
location: Location, location: Location,
elt: ast::Expr<()>, elt: ast::Expr<()>,
mut generators: Vec<Comprehension>, mut generators: Vec<Comprehension>,
) -> Result<ast::Expr<Option<Type>>, String> { ) -> ResultOrDiagnostics<ast::Expr<Option<Type>>> {
let mut diagnostics = DiagnosticEngine::new();
if generators.len() != 1 { if generators.len() != 1 {
return report_error( diagnostics.insert(DiagErr(
"Only 1 generator statement for list comprehension is supported", "more than one generator statement for list comprehension is not supported".into(),
generators[0].target.location, generators[0].target.location,
); ));
return Err(diagnostics)
} }
let variable_mapping = self.variable_mapping.clone(); let variable_mapping = self.variable_mapping.clone();
let defined_identifiers = self.defined_identifiers.clone(); let defined_identifiers = self.defined_identifiers.clone();
let mut new_context = Inferencer { let mut new_context = Inferencer {
@ -712,10 +809,15 @@ impl<'a> Inferencer<'a> {
// listcomp expr should not be considered as inside an exception handler... // listcomp expr should not be considered as inside an exception handler...
in_handler: false, in_handler: false,
}; };
let generator = generators.pop().unwrap(); let generator = generators.pop().unwrap();
if generator.is_async { if generator.is_async {
return report_error("Async iterator not supported", generator.target.location); diagnostics.insert(DiagErr(
"async iterator not supported".into(),
generator.target.location,
));
} }
new_context.infer_pattern(&generator.target)?; new_context.infer_pattern(&generator.target)?;
let target = new_context.fold_expr(*generator.target)?; let target = new_context.fold_expr(*generator.target)?;
let iter = new_context.fold_expr(*generator.iter)?; let iter = new_context.fold_expr(*generator.iter)?;
@ -743,7 +845,11 @@ impl<'a> Inferencer<'a> {
new_context.unify(v.custom.unwrap(), new_context.primitives.bool, &v.location)?; new_context.unify(v.custom.unwrap(), new_context.primitives.bool, &v.location)?;
} }
Ok(Located { if diagnostics.has_errors() {
return Err(diagnostics)
}
Ok(DiagnosticsResult::err(Located {
location, location,
custom: Some(new_context.unifier.add_ty(TypeEnum::TList { ty: elt.custom.unwrap() })), custom: Some(new_context.unifier.add_ty(TypeEnum::TList { ty: elt.custom.unwrap() })),
node: ExprKind::ListComp { node: ExprKind::ListComp {
@ -755,7 +861,7 @@ impl<'a> Inferencer<'a> {
is_async: false, is_async: false,
}], }],
}, },
}) }, diagnostics))
} }
fn fold_call( fn fold_call(
@ -764,34 +870,42 @@ impl<'a> Inferencer<'a> {
func: ast::Expr<()>, func: ast::Expr<()>,
mut args: Vec<ast::Expr<()>>, mut args: Vec<ast::Expr<()>>,
keywords: Vec<Located<ast::KeywordData>>, keywords: Vec<Located<ast::KeywordData>>,
) -> Result<ast::Expr<Option<Type>>, String> { ) -> ResultOrDiagnostics<ast::Expr<Option<Type>>> {
let func = let mut diagnostics = DiagnosticEngine::new();
if let Located { location: func_location, custom, node: ExprKind::Name { id, ctx } } =
func let func = if let Located {
{ location: func_location,
custom,
node: ExprKind::Name { id, ctx }
} = func {
// handle special functions that cannot be typed in the usual way... // handle special functions that cannot be typed in the usual way...
if id == "virtual".into() { if id == "virtual".into() {
if args.is_empty() || args.len() > 2 || !keywords.is_empty() { if args.is_empty() || args.len() > 2 || !keywords.is_empty() {
return report_error( diagnostics.insert(DiagErr(
"`virtual` can only accept 1/2 positional arguments", "`virtual` can only accept 1/2 positional arguments".into(),
func_location, func_location,
); ));
} }
let arg0 = self.fold_expr(args.remove(0))?; let arg0 = self.fold_expr(args.remove(0))?;
let ty = if let Some(arg) = args.pop() { let ty = if let Some(arg) = args.pop() {
let top_level_defs = self.top_level.definitions.read(); let top_level_defs = self.top_level.definitions.read();
self.function_data.resolver.parse_type_annotation( let ty = self.function_data.resolver.parse_type_annotation(
top_level_defs.as_slice(), top_level_defs.as_slice(),
self.unifier, self.unifier,
self.primitives, self.primitives,
&arg, &arg,
)? )?;
let ty = diagnostics.consume_partial_result(ty);
ty
} else { } else {
self.unifier.get_dummy_var().0 self.unifier.get_dummy_var().0
}; };
self.virtual_checks.push((arg0.custom.unwrap(), ty, func_location)); self.virtual_checks.push((arg0.custom.unwrap(), ty, func_location));
let custom = Some(self.unifier.add_ty(TypeEnum::TVirtual { ty })); let custom = Some(self.unifier.add_ty(TypeEnum::TVirtual { ty }));
return Ok(Located { return Ok(DiagnosticsResult::err(Located {
location, location,
custom, custom,
node: ExprKind::Call { node: ExprKind::Call {
@ -803,8 +917,9 @@ impl<'a> Inferencer<'a> {
args: vec![arg0], args: vec![arg0],
keywords: vec![], keywords: vec![],
}, },
}); }, diagnostics));
} }
// int64 is special because its argument can be a constant larger than int32 // int64 is special because its argument can be a constant larger than int32
if id == "int64".into() && args.len() == 1 { if id == "int64".into() && args.len() == 1 {
if let ExprKind::Constant { value: ast::Constant::Int(val), kind } = if let ExprKind::Constant { value: ast::Constant::Int(val), kind } =
@ -812,64 +927,83 @@ impl<'a> Inferencer<'a> {
{ {
let custom = Some(self.primitives.int64); let custom = Some(self.primitives.int64);
let v: Result<i64, _> = (*val).try_into(); let v: Result<i64, _> = (*val).try_into();
if v.is_ok() {
return Ok(Located { return v
.map(|_| DiagnosticsResult::ok(Located {
location: args[0].location, location: args[0].location,
custom, custom,
node: ExprKind::Constant { node: ExprKind::Constant {
value: ast::Constant::Int(*val), value: ast::Constant::Int(*val),
kind: kind.clone(), kind: kind.clone(),
}, },
}); }))
} else { .map_err(|_| {
return report_error("Integer out of bound", args[0].location) diagnostics.insert(DiagErr(
} format!("integer {} out of bound for int64", *val),
args[0].location,
));
diagnostics
})
} }
} }
if id == "uint32".into() && args.len() == 1 { if id == "uint32".into() && args.len() == 1 {
if let ExprKind::Constant { value: ast::Constant::Int(val), kind } = if let ExprKind::Constant { value: ast::Constant::Int(val), kind } =
&args[0].node &args[0].node
{ {
let custom = Some(self.primitives.uint32); let custom = Some(self.primitives.uint32);
let v: Result<u32, _> = (*val).try_into(); let v: Result<u32, _> = (*val).try_into();
if v.is_ok() {
return Ok(Located { return v
.map(|_| DiagnosticsResult::ok(Located {
location: args[0].location, location: args[0].location,
custom, custom,
node: ExprKind::Constant { node: ExprKind::Constant {
value: ast::Constant::Int(*val), value: ast::Constant::Int(*val),
kind: kind.clone(), kind: kind.clone(),
}, },
}); }))
} else { .map_err(|_| {
return report_error("Integer out of bound", args[0].location) diagnostics.insert(DiagErr(
} format!("integer {} out of bound for uint32", *val),
args[0].location,
));
diagnostics
})
} }
} }
if id == "uint64".into() && args.len() == 1 { if id == "uint64".into() && args.len() == 1 {
if let ExprKind::Constant { value: ast::Constant::Int(val), kind } = if let ExprKind::Constant { value: ast::Constant::Int(val), kind } =
&args[0].node &args[0].node
{ {
let custom = Some(self.primitives.uint64); let custom = Some(self.primitives.uint64);
let v: Result<u64, _> = (*val).try_into(); let v: Result<u64, _> = (*val).try_into();
if v.is_ok() {
return Ok(Located { return v
.map(|_| DiagnosticsResult::ok(Located {
location: args[0].location, location: args[0].location,
custom, custom,
node: ExprKind::Constant { node: ExprKind::Constant {
value: ast::Constant::Int(*val), value: ast::Constant::Int(*val),
kind: kind.clone(), kind: kind.clone(),
}, },
}); }))
} else { .map_err(|_| {
return report_error("Integer out of bound", args[0].location) diagnostics.insert(DiagErr(
} format!("integer {} out of bound for uint64", *val),
args[0].location,
));
diagnostics
})
} }
} }
Located { location: func_location, custom, node: ExprKind::Name { id, ctx } } Located { location: func_location, custom, node: ExprKind::Name { id, ctx } }
} else { } else {
func func
}; };
let func = Box::new(self.fold_expr(func)?); let func = Box::new(self.fold_expr(func)?);
let args = args.into_iter().map(|v| self.fold_expr(v)).collect::<Result<Vec<_>, _>>()?; let args = args.into_iter().map(|v| self.fold_expr(v)).collect::<Result<Vec<_>, _>>()?;
let keywords = keywords let keywords = keywords
@ -898,12 +1032,18 @@ impl<'a> Inferencer<'a> {
.collect(); .collect();
self.unifier self.unifier
.unify_call(&call, func.custom.unwrap(), sign, &required) .unify_call(&call, func.custom.unwrap(), sign, &required)
.map_err(|e| e.at(Some(location)).to_display(self.unifier).to_string())?; .map_err(|e|
return Ok(Located { DiagnosticEngine::single(DiagErr(
e.to_display(self.unifier).to_string(),
location
))
)?;
return Ok(DiagnosticsResult::err(Located {
location, location,
custom: Some(sign.ret), custom: Some(sign.ret),
node: ExprKind::Call { func, args, keywords }, node: ExprKind::Call { func, args, keywords },
}); }, diagnostics));
} }
} }
@ -922,16 +1062,21 @@ impl<'a> Inferencer<'a> {
let call = self.unifier.add_ty(TypeEnum::TCall(vec![call])); let call = self.unifier.add_ty(TypeEnum::TCall(vec![call]));
self.unify(func.custom.unwrap(), call, &func.location)?; self.unify(func.custom.unwrap(), call, &func.location)?;
Ok(Located { location, custom: Some(ret), node: ExprKind::Call { func, args, keywords } }) Ok(DiagnosticsResult::err(Located {
location,
custom: Some(ret),
node: ExprKind::Call { func, args, keywords }
}, diagnostics))
} }
fn infer_identifier(&mut self, id: StrRef) -> InferenceResult { fn infer_identifier(&mut self, id: StrRef) -> InferenceResult {
if let Some(ty) = self.variable_mapping.get(&id) { if let Some(ty) = self.variable_mapping.get(&id) {
Ok(*ty) Ok(DiagnosticsResult::ok(*ty))
} else { } else {
let variable_mapping = &mut self.variable_mapping; let variable_mapping = &mut self.variable_mapping;
let unifier = &mut self.unifier; let unifier = &mut self.unifier;
Ok(self Ok(DiagnosticsResult::ok(
self
.function_data .function_data
.resolver .resolver
.get_symbol_type(unifier, &self.top_level.definitions.read(), self.primitives, id) .get_symbol_type(unifier, &self.top_level.definitions.read(), self.primitives, id)
@ -940,45 +1085,83 @@ impl<'a> Inferencer<'a> {
variable_mapping.insert(id, ty); variable_mapping.insert(id, ty);
ty ty
})) }))
)
} }
} }
fn infer_constant(&mut self, constant: &ast::Constant, loc: &Location) -> InferenceResult { fn infer_constant(&mut self, constant: &ast::Constant, loc: &Location) -> InferenceResult {
let mut diagnostics = DiagnosticEngine::new();
match constant { match constant {
ast::Constant::Bool(_) => Ok(self.primitives.bool), ast::Constant::Bool(_) => Ok(DiagnosticsResult::ok(self.primitives.bool)),
ast::Constant::Int(val) => { ast::Constant::Int(val) => {
let int32: Result<i32, _> = (*val).try_into(); let int32: Result<i32, _> = (*val).try_into();
// int64 and unsigned integers are handled separately in functions // int64 and unsigned integers are handled separately in functions
if int32.is_ok() { if int32.is_ok() {
Ok(self.primitives.int32) Ok(DiagnosticsResult::ok(self.primitives.int32))
} else { } else {
report_error("Integer out of bound", *loc) diagnostics.insert(DiagErr(
format!("integer {} out of bound for int32", *val),
*loc,
));
Err(diagnostics)
} }
} }
ast::Constant::Float(_) => Ok(self.primitives.float),
ast::Constant::Float(_) => Ok(DiagnosticsResult::ok(self.primitives.float)),
ast::Constant::Tuple(vals) => { ast::Constant::Tuple(vals) => {
let ty: Result<Vec<_>, _> = let ty = vals.iter()
vals.iter().map(|x| self.infer_constant(x, loc)).collect(); .map(|x| self.infer_constant(x, loc))
Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? })) .collect_vec();
diagnostics.extend(ty.iter().cloned().flat_map(|res| match res {
Ok(v) => v.engine,
Err(errs) => errs
}.into_iter()));
if diagnostics.has_errors() {
return Err(diagnostics)
}
let ty = ty.into_iter()
.map(|res| res.ok().unwrap().value)
.collect_vec();
Ok(DiagnosticsResult::err(self.unifier.add_ty(TypeEnum::TTuple { ty }), diagnostics))
}
ast::Constant::Str(_) => Ok(DiagnosticsResult::ok(self.primitives.str)),
ast::Constant::None => {
diagnostics.insert(DiagErr(
"CPython `None` not supported (nac3 uses `none` instead)".into(),
*loc,
));
Err(diagnostics)
},
_ => {
diagnostics.insert(DiagErr(
"not supported".into(),
*loc,
));
Err(diagnostics)
} }
ast::Constant::Str(_) => Ok(self.primitives.str),
ast::Constant::None
=> report_error("CPython `None` not supported (nac3 uses `none` instead)", *loc),
_ => report_error("not supported", *loc),
} }
} }
fn infer_list(&mut self, elts: &[ast::Expr<Option<Type>>]) -> InferenceResult { fn infer_list(&mut self, elts: &[ast::Expr<Option<Type>>]) -> InferenceResult {
let mut diagnostics = DiagnosticEngine::new();
let ty = self.unifier.get_dummy_var().0; let ty = self.unifier.get_dummy_var().0;
for t in elts.iter() { for t in elts.iter() {
self.unify(ty, t.custom.unwrap(), &t.location)?; diagnostics.consume_partial_result(self.unify(ty, t.custom.unwrap(), &t.location)?);
} }
Ok(self.unifier.add_ty(TypeEnum::TList { ty }))
Ok(DiagnosticsResult::err(self.unifier.add_ty(TypeEnum::TList { ty }), diagnostics))
} }
fn infer_tuple(&mut self, elts: &[ast::Expr<Option<Type>>]) -> InferenceResult { fn infer_tuple(&mut self, elts: &[ast::Expr<Option<Type>>]) -> InferenceResult {
let ty = elts.iter().map(|x| x.custom.unwrap()).collect(); let ty = elts.iter().map(|x| x.custom.unwrap()).collect();
Ok(self.unifier.add_ty(TypeEnum::TTuple { ty })) Ok(DiagnosticsResult::ok(self.unifier.add_ty(TypeEnum::TTuple { ty })))
} }
fn infer_attribute( fn infer_attribute(
@ -987,18 +1170,29 @@ impl<'a> Inferencer<'a> {
attr: StrRef, attr: StrRef,
ctx: &ExprContext, ctx: &ExprContext,
) -> InferenceResult { ) -> InferenceResult {
let mut diagnostics = DiagnosticEngine::new();
let ty = value.custom.unwrap(); let ty = value.custom.unwrap();
if let TypeEnum::TObj { fields, .. } = &*self.unifier.get_ty(ty) { if let TypeEnum::TObj { fields, .. } = &*self.unifier.get_ty(ty) {
// just a fast path // just a fast path
match (fields.get(&attr), ctx == &ExprContext::Store) { match (fields.get(&attr), ctx == &ExprContext::Store) {
(Some((ty, true)), _) => Ok(*ty), (Some((ty, true)), _) => Ok(DiagnosticsResult::ok(*ty)),
(Some((ty, false)), false) => Ok(*ty), (Some((ty, false)), false) => Ok(DiagnosticsResult::ok(*ty)),
(Some((_, false)), true) => { (Some((_, false)), true) => {
report_error(&format!("Field `{}` is immutable", attr), value.location) diagnostics.insert(DiagErr(
format!("Field `{}` is immutable", attr),
value.location,
));
Err(diagnostics)
} }
(None, _) => { (None, _) => {
let t = self.unifier.stringify(ty); let t = self.unifier.stringify(ty);
report_error(&format!("`{}::{}` field/method does not exist", t, attr), value.location)
diagnostics.insert(DiagErr(
format!("`{}::{}` field/method does not exist", t, attr),
value.location,
));
Err(diagnostics)
}, },
} }
} else { } else {
@ -1010,7 +1204,7 @@ impl<'a> Inferencer<'a> {
.collect(); .collect();
let record = self.unifier.add_record(fields); let record = self.unifier.add_record(fields);
self.constrain(value.custom.unwrap(), record, &value.location)?; self.constrain(value.custom.unwrap(), record, &value.location)?;
Ok(attr_ty) Ok(DiagnosticsResult::err(attr_ty, diagnostics))
} }
} }
@ -1019,7 +1213,7 @@ impl<'a> Inferencer<'a> {
for v in values { for v in values {
self.constrain(v.custom.unwrap(), b, &v.location)?; self.constrain(v.custom.unwrap(), b, &v.location)?;
} }
Ok(b) Ok(DiagnosticsResult::ok(b))
} }
fn infer_bin_ops( fn infer_bin_ops(
@ -1072,8 +1266,11 @@ impl<'a> Inferencer<'a> {
) -> InferenceResult { ) -> InferenceResult {
let boolean = self.primitives.bool; let boolean = self.primitives.bool;
for (a, b, c) in izip!(once(left).chain(comparators), comparators, ops) { for (a, b, c) in izip!(once(left).chain(comparators), comparators, ops) {
let method = let method = comparison_name(c)
comparison_name(c).ok_or_else(|| "unsupported comparator".to_string())?.into(); .ok_or_else(||
DiagnosticEngine::single(DiagErr("unsupported comparator".to_string(), left.location))
)?
.into();
self.build_method_call( self.build_method_call(
a.location, a.location,
method, method,
@ -1082,7 +1279,7 @@ impl<'a> Inferencer<'a> {
Some(boolean), Some(boolean),
)?; )?;
} }
Ok(boolean) Ok(DiagnosticsResult::ok(boolean))
} }
fn infer_subscript( fn infer_subscript(
@ -1091,6 +1288,8 @@ impl<'a> Inferencer<'a> {
slice: &ast::Expr<Option<Type>>, slice: &ast::Expr<Option<Type>>,
ctx: &ExprContext, ctx: &ExprContext,
) -> InferenceResult { ) -> InferenceResult {
let mut diagnostics = DiagnosticEngine::new();
let ty = self.unifier.get_dummy_var().0; let ty = self.unifier.get_dummy_var().0;
match &slice.node { match &slice.node {
ast::ExprKind::Slice { lower, upper, step } => { ast::ExprKind::Slice { lower, upper, step } => {
@ -1099,12 +1298,14 @@ impl<'a> Inferencer<'a> {
} }
let list = self.unifier.add_ty(TypeEnum::TList { ty }); let list = self.unifier.add_ty(TypeEnum::TList { ty });
self.constrain(value.custom.unwrap(), list, &value.location)?; self.constrain(value.custom.unwrap(), list, &value.location)?;
Ok(list) Ok(DiagnosticsResult::err(list, diagnostics))
} }
ast::ExprKind::Constant { value: ast::Constant::Int(val), .. } => { ast::ExprKind::Constant { value: ast::Constant::Int(val), .. } => {
// the index is a constant, so value can be a sequence. // the index is a constant, so value can be a sequence.
let ind: Option<i32> = (*val).try_into().ok(); let ind: Option<i32> = (*val).try_into().ok();
let ind = ind.ok_or_else(|| "Index must be int32".to_string())?; let ind = ind.ok_or_else(||
DiagnosticEngine::single(DiagErr("Index must be int32".to_string(), slice.location))
)?;
let map = once(( let map = once((
ind.into(), ind.into(),
RecordField::new(ty, ctx == &ExprContext::Store, Some(value.location)), RecordField::new(ty, ctx == &ExprContext::Store, Some(value.location)),
@ -1112,18 +1313,23 @@ impl<'a> Inferencer<'a> {
.collect(); .collect();
let seq = self.unifier.add_record(map); let seq = self.unifier.add_record(map);
self.constrain(value.custom.unwrap(), seq, &value.location)?; self.constrain(value.custom.unwrap(), seq, &value.location)?;
Ok(ty) Ok(DiagnosticsResult::err(ty, diagnostics))
} }
_ => { _ => {
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap()) if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap())
{ {
return report_error("Tuple index must be a constant (KernelInvariant is also not supported)", slice.location) diagnostics.insert(DiagErr(
"Tuple index must be a constant (KernelInvariant is also not supported)".into(),
slice.location,
));
return Err(diagnostics)
} }
// the index is not a constant, so value can only be a list // the index is not a constant, so value can only be a list
self.constrain(slice.custom.unwrap(), self.primitives.int32, &slice.location)?; self.constrain(slice.custom.unwrap(), self.primitives.int32, &slice.location)?;
let list = self.unifier.add_ty(TypeEnum::TList { ty }); let list = self.unifier.add_ty(TypeEnum::TList { ty });
self.constrain(value.custom.unwrap(), list, &value.location)?; self.constrain(value.custom.unwrap(), list, &value.location)?;
Ok(ty) Ok(DiagnosticsResult::err(ty, diagnostics))
} }
} }
} }
@ -1136,6 +1342,6 @@ impl<'a> Inferencer<'a> {
) -> InferenceResult { ) -> InferenceResult {
self.constrain(test.custom.unwrap(), self.primitives.bool, &test.location)?; self.constrain(test.custom.unwrap(), self.primitives.bool, &test.location)?;
self.constrain(body.custom.unwrap(), orelse.custom.unwrap(), &body.location)?; self.constrain(body.custom.unwrap(), orelse.custom.unwrap(), &body.location)?;
Ok(body.custom.unwrap()) Ok(DiagnosticsResult::ok(body.custom.unwrap()))
} }
} }

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,
symbol_resolver::{SymbolResolver, SymbolValue, ValueEnum},
toplevel::{DefinitionId, TopLevelDef},
typecheck::{
type_inferencer::PrimitiveStore, type_inferencer::PrimitiveStore,
typedef::{Type, Unifier}, typedef::{Type, Unifier},
}, }};
}; use nac3parser::ast::{self, Location, StrRef};
use nac3parser::ast::{self, StrRef};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use 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::{
symbol_resolver::SymbolResolver,
toplevel::{
composer::TopLevelComposer, helper::parse_parameter_default_value, type_annotation::*, composer::TopLevelComposer, helper::parse_parameter_default_value, type_annotation::*,
TopLevelDef, TopLevelDef,
}, }, typecheck::{
typecheck::{
type_inferencer::PrimitiveStore, type_inferencer::PrimitiveStore,
typedef::{FunSignature, Type, Unifier}, 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(
format!(
"expression {:?} cannot be handled as a TypeVar in global scope", "expression {:?} cannot be handled as a TypeVar in global scope",
var var
)) ),
var.location
)))
} }
} else { } else {
Err(format!( Err(DiagnosticEngine::single(DiagErr(
format!(
"expression {:?} cannot be handled as a TypeVar in global scope", "expression {:?} cannot be handled as a TypeVar in global scope",
var 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());