Refactor to use Diagnostic Class

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"
dependencies = [
"inkwell",
"itertools 0.11.0",
"nac3core",
"nac3ld",
"nac3parser",
@ -663,6 +664,7 @@ dependencies = [
name = "nac3core"
version = "0.1.0"
dependencies = [
"clap",
"crossbeam",
"indoc",
"inkwell",

View File

@ -9,6 +9,7 @@ name = "nac3artiq"
crate-type = ["cdylib"]
[dependencies]
itertools = "0.11"
pyo3 = { version = "0.20", features = ["extension-module"] }
parking_lot = "0.12"
tempfile = "3.8"

View File

@ -13,6 +13,7 @@ use inkwell::{
targets::*,
OptimizationLevel,
};
use itertools::Itertools;
use nac3core::codegen::{CodeGenLLVMOptions, CodeGenTargetMachineOptions, gen_func_impl};
use nac3core::toplevel::builtins::get_exn_constructor;
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
@ -474,11 +475,11 @@ impl Nac3 {
if let Err(e) = composer.start_analysis(true) {
// report error of __modinit__ separately
if !e.contains("<nac3_synthesized_modinit>") {
return Err(CompileError::new_err(format!(
return if !e.iter().any(|err| err.message().contains("<nac3_synthesized_modinit>")) {
Err(CompileError::new_err(format!(
"compilation failed\n----------\n{}",
e
)));
e.into_iter().sorted().join("\n----------\n")
)))
} else {
let msg = Self::report_modinit(
&arg_names,
@ -488,10 +489,10 @@ impl Nac3 {
&mut composer.unifier,
&self.primitive,
);
return Err(CompileError::new_err(format!(
Err(CompileError::new_err(format!(
"compilation failed\n----------\n{}",
msg.unwrap_or(e)
)));
msg.unwrap_or(e.into_iter().sorted().join("\n----------\n"))
)))
}
}
let top_level = Arc::new(composer.make_top_level_context());

View File

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

View File

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

View File

@ -1,22 +1,17 @@
use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
use crate::{
codegen::{
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
gen_in_range_check,
get_llvm_type,
get_llvm_abi_type,
irrt::*,
stmt::{gen_raise, gen_var},
CodeGenContext, CodeGenTask,
},
symbol_resolver::{SymbolValue, ValueEnum},
toplevel::{DefinitionId, TopLevelDef},
typecheck::{
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
magic_methods::{binop_name, binop_assign_name},
},
};
use crate::{codegen::{
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
gen_in_range_check,
get_llvm_type,
get_llvm_abi_type,
irrt::*,
stmt::{gen_raise, gen_var},
CodeGenContext, CodeGenTask,
}, DiagnosticsResult, symbol_resolver::{SymbolValue, ValueEnum}, toplevel::{DefinitionId, TopLevelDef}, typecheck::{
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
magic_methods::{binop_name, binop_assign_name},
}};
use inkwell::{
AddressSpace,
attributes::{Attribute, AttributeLoc},
@ -1584,10 +1579,16 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
match &func.node {
ExprKind::Name { id, .. } => {
// 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
.get_identifier_def(*id)
.map_err(|e| format!("{} (at {})", e, func.location))?;
.get_identifier_def(*id, Some(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
.gen_call(ctx, None, (&signature, fun), params)?
.map(|v| v.into()));

View File

@ -1,17 +1,12 @@
use crate::{
codegen::{
concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions,
CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
},
symbol_resolver::{SymbolResolver, ValueEnum},
toplevel::{
composer::TopLevelComposer, DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
},
typecheck::{
type_inferencer::{FunctionData, Inferencer, PrimitiveStore},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
},
};
use crate::{codegen::{
concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions,
CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
}, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, symbol_resolver::{SymbolResolver, ValueEnum}, toplevel::{
composer::TopLevelComposer, DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
}, typecheck::{
type_inferencer::{FunctionData, Inferencer, PrimitiveStore},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
}};
use indoc::indoc;
use inkwell::{
targets::{InitializationConfig, Target},
@ -24,6 +19,8 @@ use nac3parser::{
use parking_lot::RwLock;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use nac3parser::ast::Location;
use crate::Diagnostic::DiagErr;
struct Resolver {
id_to_type: HashMap<StrRef, Type>,
@ -63,12 +60,13 @@ impl SymbolResolver for Resolver {
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
.read()
.get(&id)
.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 {
@ -144,7 +142,9 @@ fn test_primitives() {
.collect::<Result<Vec<_>, _>>()
.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 {
definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))),
unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])),
@ -346,7 +346,9 @@ fn test_simple_call() {
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 {
definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))),
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 std::fmt;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use itertools::Itertools;
use nac3parser::ast::Location;
pub mod codegen;
@ -39,7 +41,7 @@ impl Diagnostic {
}
/// Returns the message of this [Diagnostic].
fn message(&self) -> &str {
pub fn message(&self) -> &str {
match self {
DiagErr(msg, _) => msg,
DiagWarn(msg, _) => msg,
@ -47,7 +49,7 @@ impl Diagnostic {
}
/// Returns the location of where this [Diagnostic] is created from.
fn location(&self) -> &Location {
pub fn location(&self) -> &Location {
match self {
DiagErr(_, 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
/// during the generation of the result.
///
/// This type should be used where a result cannot be meaningfully generated when a subset of the
/// 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
/// result.
///
/// This type should be used where, even when diagnostics are generated, a meaningful result can
/// still be generated.
pub struct DiagnosticsPartialResult<T> {
#[derive(Clone)]
pub struct DiagnosticsResult<T> {
value: T,
engine: DiagnosticEngine,
}
impl <T> Into<DiagnosticsResult<T>> for DiagnosticsPartialResult<T> {
fn into(self) -> DiagnosticsResult<T> {
impl <T> 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() {
Err(self.engine)
} else {
Ok((self.value, self.engine))
Ok(self)
}
}
}
@ -200,16 +217,16 @@ impl DiagnosticEngine {
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);
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 {
Ok((v, diags)) => {
self.merge_with(diags);
Ok(v)
Ok(DiagnosticsResult { value, engine }) => {
self.merge_with(engine);
Ok(value)
}
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 {
Ok((v, diags)) => {
self.merge_with(diags);
Some(v)
Ok(DiagnosticsResult { value, engine }) => {
self.merge_with(engine);
Some(value)
}
Err(diags) => {
@ -241,18 +258,36 @@ impl DiagnosticEngine {
self.diagnostics.into_iter()
}
pub fn into_partial_result<T>(self, value: T) -> DiagnosticsPartialResult<T> {
DiagnosticsPartialResult {
pub fn into_partial_result<T>(self, value: T) -> DiagnosticsResult<T> {
DiagnosticsResult {
value,
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() {
Err(self)
} 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 crate::typecheck::typedef::TypeEnum;
use crate::{
codegen::CodeGenContext,
toplevel::{DefinitionId, TopLevelDef},
};
use crate::{codegen::CodeGenContext, Diagnostic::DiagErr, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, toplevel::{DefinitionId, TopLevelDef}};
use crate::{
codegen::CodeGenerator,
typecheck::{
@ -147,8 +144,14 @@ pub trait SymbolResolver {
str: StrRef,
) -> Result<Type, String>;
// get the top-level definition of identifiers
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, String>;
/// Returns the top-level definition of identifiers.
///
/// * `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>(
&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>(
resolver: &dyn SymbolResolver,
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
expr: &Expr<T>,
) -> Result<Type, String> {
) -> ResultOrDiagnostics<Type> {
use nac3parser::ast::ExprKind::*;
let ids = IDENTIFIER_ID.with(|ids| *ids);
let int32_id = ids[0];
let int64_id = ids[1];
@ -209,56 +216,87 @@ pub fn parse_type_annotation<T>(
let uint64_id = ids[10];
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
let mut diagnostics = DiagnosticEngine::new();
if *id == int32_id {
Ok(primitives.int32)
Ok(DiagnosticsResult::ok(primitives.int32))
} else if *id == int64_id {
Ok(primitives.int64)
Ok(DiagnosticsResult::ok(primitives.int64))
} else if *id == uint32_id {
Ok(primitives.uint32)
Ok(DiagnosticsResult::ok(primitives.uint32))
} else if *id == uint64_id {
Ok(primitives.uint64)
Ok(DiagnosticsResult::ok(primitives.uint64))
} else if *id == float_id {
Ok(primitives.float)
Ok(DiagnosticsResult::ok(primitives.float))
} else if *id == bool_id {
Ok(primitives.bool)
Ok(DiagnosticsResult::ok(primitives.bool))
} else if *id == str_id {
Ok(primitives.str)
Ok(DiagnosticsResult::ok(primitives.str))
} else if *id == exn_id {
Ok(primitives.exception)
Ok(DiagnosticsResult::ok(primitives.exception))
} else {
let obj_id = resolver.get_identifier_def(*id);
let obj_id = resolver.get_identifier_def(*id, Some(expr.location));
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();
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
if !type_vars.is_empty() {
return Err(format!(
"Unexpected number of type parameters: expected {} but got 0",
type_vars.len()
diagnostics.insert(DiagErr(
format!("expected {} type parameters but got 0", type_vars.len()),
loc,
));
return Err(diagnostics)
}
let fields = chain(
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
methods.iter().map(|(k, v, _)| (*k, (*v, false))),
)
.collect();
Ok(unifier.add_ty(TypeEnum::TObj {
obj_id,
fields,
params: Default::default(),
}))
Ok(DiagnosticsResult::err(
unifier.add_ty(TypeEnum::TObj {
obj_id,
fields,
params: Default::default(),
}),
diagnostics,
))
} 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(_) => {
let ty = resolver
.get_symbol_type(unifier, top_level_defs, primitives, *id)
.map_err(|e| format!("Unknown type annotation at {}: {}", loc, e))?;
let ty = match resolver.get_symbol_type(
unifier,
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) {
Ok(ty)
Ok(DiagnosticsResult::err(ty, diagnostics))
} 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 mut diagnostics = DiagnosticEngine::new();
if *id == virtual_id {
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
Ok(unifier.add_ty(TypeEnum::TVirtual { ty }))
let DiagnosticsResult { value: ty, engine: diag } =
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 {
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, slice)?;
Ok(unifier.add_ty(TypeEnum::TList { ty }))
let DiagnosticsResult { value: ty, engine: diag } =
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 {
if let Tuple { elts, .. } = &slice.node {
let ty = elts
let (ty, diags) = elts
.iter()
.map(|elt| {
parse_type_annotation(resolver, top_level_defs, unifier, primitives, elt)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
.collect::<Result<Vec<_>, _>>()?
.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 {
Err("Expected multiple elements for tuple".into())
diagnostics.insert(DiagErr(
"expected multiple elements for tuple".into(),
slice.location,
));
Err(diagnostics)
}
} else {
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)?]
};
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();
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
if types.len() != type_vars.len() {
return Err(format!(
"Unexpected number of type parameters: expected {} but got {}",
type_vars.len(),
types.len()
diagnostics.insert(DiagErr(
format!("expected {} type parameters but got {}", type_vars.len(), types.len()),
slice.location,
));
return Err(diagnostics)
}
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) {
*id
} else {
unreachable!()
};
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
.iter()
.map(|(attr, ty, is_mutable)| {
@ -325,9 +390,14 @@ pub fn parse_type_annotation<T>(
let ty = unifier.subst(*ty, &subst).unwrap_or(*ty);
(*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 {
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 {
subscript_name_handle(id, slice, unifier)
} 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,
primitives: &PrimitiveStore,
expr: &Expr<T>,
) -> Result<Type, String> {
) -> ResultOrDiagnostics<Type> {
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 nac3parser::ast::{Constant, Location};
use crate::Diagnostic::DiagErr;
use crate::{Diagnostic, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics};
use super::*;
@ -200,13 +202,17 @@ impl TopLevelComposer {
pub fn get_class_method_def_info(
class_methods_def: &[(StrRef, Type, DefinitionId)],
method_name: StrRef,
) -> Result<(Type, DefinitionId), String> {
loc: Location,
) -> ResultOrDiagnostics<(Type, DefinitionId)> {
for (name, ty, def_id) in class_methods_def {
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. \
@ -217,7 +223,10 @@ impl TopLevelComposer {
pub fn get_all_ancestors_helper(
child: &TypeAnnotation,
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 parent = Self::get_parent(child, temp_def_list);
while let Some(p) = parent {
@ -238,10 +247,25 @@ impl TopLevelComposer {
if no_cycle {
result.push(p);
} 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
@ -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 diagnostics = DiagnosticEngine::new();
for s in stmts {
match &s.node {
ast::StmtKind::AnnAssign { target, .. }
@ -351,10 +377,11 @@ impl TopLevelComposer {
}
} =>
{
return Err(format!(
"redundant type annotation for class fields at {}",
s.location
))
diagnostics.insert(DiagErr(
"redundant type annotation for class fields".into(),
s.location,
));
return Err(diagnostics)
}
ast::StmtKind::Assign { targets, .. } => {
for t in targets {
@ -370,26 +397,26 @@ impl TopLevelComposer {
// TODO: do not check for For and While?
ast::StmtKind::For { body, orelse, .. }
| ast::StmtKind::While { body, orelse, .. } => {
result.extend(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(body.as_slice()))?);
result.extend(diagnostics.consume_result(Self::get_all_assigned_field(orelse.as_slice()))?);
}
ast::StmtKind::If { body, orelse, .. } => {
let inited_for_sure = Self::get_all_assigned_field(body.as_slice())?
.intersection(&Self::get_all_assigned_field(orelse.as_slice())?)
let inited_for_sure = diagnostics.consume_result(Self::get_all_assigned_field(body.as_slice()))?
.intersection(&diagnostics.consume_result(Self::get_all_assigned_field(orelse.as_slice()))?)
.cloned()
.collect::<HashSet<_>>();
result.extend(inited_for_sure);
}
ast::StmtKind::Try { body, orelse, finalbody, .. } => {
let inited_for_sure = Self::get_all_assigned_field(body.as_slice())?
.intersection(&Self::get_all_assigned_field(orelse.as_slice())?)
let inited_for_sure = diagnostics.consume_result(Self::get_all_assigned_field(body.as_slice()))?
.intersection(&diagnostics.consume_result(Self::get_all_assigned_field(orelse.as_slice()))?)
.cloned()
.collect::<HashSet<_>>();
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, .. } => {
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::Assert { .. } => {}
@ -400,13 +427,14 @@ impl TopLevelComposer {
}
}
}
Ok(result)
Ok(DiagnosticsResult::err(result, diagnostics))
}
pub fn parse_parameter_default_value(
default: &ast::Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
) -> Result<SymbolValue, String> {
) -> ResultOrDiagnostics<SymbolValue> {
parse_parameter_default_value(default, resolver)
}
@ -497,25 +525,35 @@ impl TopLevelComposer {
pub fn parse_parameter_default_value(
default: &ast::Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
) -> Result<SymbolValue, String> {
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, String> {
) -> ResultOrDiagnostics<SymbolValue> {
fn handle_constant(val: &Constant, loc: &Location) -> ResultOrDiagnostics<SymbolValue> {
match val {
Constant::Int(v) => {
if let Ok(v) = (*v).try_into() {
Ok(SymbolValue::I32(v))
Ok(DiagnosticsResult::ok(SymbolValue::I32(v)))
} 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::Bool(v) => Ok(SymbolValue::Bool(*v)),
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
)),
Constant::None => Err(format!(
"`None` is not supported, use `none` for option type instead ({})",
loc
)),
Constant::Float(v) => Ok(DiagnosticsResult::ok(SymbolValue::Double(*v))),
Constant::Bool(v) => Ok(DiagnosticsResult::ok(SymbolValue::Bool(*v))),
Constant::Tuple(tuple) => {
let val = tuple.iter()
.map(|x| handle_constant(x, loc))
.collect::<Result<Vec<_>, _>>()?;
let diagnostics = DiagnosticEngine::from(val.iter().cloned()
.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),
}
}
@ -527,59 +565,101 @@ pub fn parse_parameter_default_value(
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<i64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::I64(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
Ok(v) => Ok(DiagnosticsResult::ok(SymbolValue::I64(v))),
_ => 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::Constant { value: Constant::Int(v), .. } => {
let v: Result<u32, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U32(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
Ok(v) => Ok(DiagnosticsResult::ok(SymbolValue::U32(v))),
_ => 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::Constant { value: Constant::Int(v), .. } => {
let v: Result<u64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U64(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
Ok(v) => Ok(DiagnosticsResult::ok(SymbolValue::U64(v))),
_ => 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(
SymbolValue::OptionSome(
Box::new(parse_parameter_default_value(&args[0], resolver)?)
ast::ExprKind::Name { id, .. } if *id == "Some".into() => {
let param_def_value = 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
.iter()
.map(|x| parse_parameter_default_value(x, resolver))
.collect::<Result<Vec<_>, _>>()?
)),
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
ast::ExprKind::Name { id, .. } => {
resolver.get_default_param_value(default).ok_or_else(
|| format!(
"`{}` cannot be used as a default parameter at {} \
(not primitive type, option or tuple / not defined?)",
id,
default.location
)
)
ast::ExprKind::Tuple { elts, .. } => {
let param_def_values = elts
.iter()
.map(|x| parse_parameter_default_value(x, resolver))
.collect::<Result<Vec<_>, _>>()?;
let diagnostics = DiagnosticEngine::from(
param_def_values.iter().cloned()
.flat_map(|res| res.engine.into_iter())
.collect::<HashSet<Diagnostic>>()
);
let param_def_values = param_def_values.into_iter()
.map(|res| res.value)
.collect_vec();
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::{
codegen::CodeGenContext,
symbol_resolver::{SymbolResolver, ValueEnum},
toplevel::DefinitionId,
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, Unifier},
},
};
use crate::{codegen::CodeGenContext, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics, symbol_resolver::{SymbolResolver, ValueEnum}, toplevel::DefinitionId, typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, Unifier},
}};
use indoc::indoc;
use nac3parser::{ast::fold::Fold, parser::parse_program};
use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc};
use test_case::test_case;
use crate::Diagnostic::DiagErr;
use super::*;
@ -64,8 +60,10 @@ impl SymbolResolver for Resolver {
unimplemented!()
}
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
fn get_identifier_def(&self, id: StrRef, loc: Option<Location>) -> ResultOrDiagnostics<DefinitionId> {
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 {
@ -551,9 +549,9 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
if let Err(msg) = composer.start_analysis(false) {
if print {
println!("{}", msg);
println!("{}", msg.into_iter().sorted().join("\n----------\n"));
} else {
assert_eq!(res[0], msg);
assert_eq!(res[0], msg.into_iter().sorted().next().unwrap().message());
}
} else {
// 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 print {
println!("{}", msg);
println!("{}", msg.into_iter().sorted().join("\n----------\n"));
} else {
assert_eq!(res[0], msg);
assert_eq!(res[0], msg.into_iter().sorted().next().unwrap().message());
}
} else {
// skip 5 to skip primitives

View File

@ -1,3 +1,4 @@
use crate::{Diagnostic::DiagErr, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics};
use super::*;
#[derive(Clone, Debug)]
@ -64,74 +65,103 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
expr: &ast::Expr<T>,
// the key stores the type_var of this topleveldef::class, we only need this field here
locked: HashMap<DefinitionId, Vec<Type>>,
) -> Result<TypeAnnotation, String> {
) -> ResultOrDiagnostics<TypeAnnotation> {
let mut diagnostics = DiagnosticEngine::new();
let name_handle = |id: &StrRef,
id_loc: Location,
unifier: &mut Unifier,
locked: HashMap<DefinitionId, Vec<Type>>| {
let mut diagnostics = DiagnosticEngine::new();
if id == &"int32".into() {
Ok(TypeAnnotation::Primitive(primitives.int32))
Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.int32)))
} else if id == &"int64".into() {
Ok(TypeAnnotation::Primitive(primitives.int64))
Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.int64)))
} else if id == &"uint32".into() {
Ok(TypeAnnotation::Primitive(primitives.uint32))
Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.uint32)))
} else if id == &"uint64".into() {
Ok(TypeAnnotation::Primitive(primitives.uint64))
Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.uint64)))
} else if id == &"float".into() {
Ok(TypeAnnotation::Primitive(primitives.float))
Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.float)))
} else if id == &"bool".into() {
Ok(TypeAnnotation::Primitive(primitives.bool))
Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.bool)))
} else if id == &"str".into() {
Ok(TypeAnnotation::Primitive(primitives.str))
Ok(DiagnosticsResult::ok(TypeAnnotation::Primitive(primitives.str)))
} else if id == &"Exception".into() {
Ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() })
} else if let Ok(obj_id) = resolver.get_identifier_def(*id) {
Ok(DiagnosticsResult::ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }))
} 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 def_read = top_level_defs[obj_id.0].try_read();
if let Some(def_read) = def_read {
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
type_vars.clone()
} else {
return Err(format!(
"function cannot be used as a type (at {})",
expr.location
diagnostics.insert(DiagErr(
"function cannot be used as a type".into(),
expr.location,
));
return Err(diagnostics)
}
} else {
locked.get(&obj_id).unwrap().clone()
}
};
// check param number here
if !type_vars.is_empty() {
return Err(format!(
"expect {} type variable parameter but got 0 (at {})",
type_vars.len(),
diagnostics.insert(DiagErr(
format!("expect {} type variable parameters but got 0", type_vars.len()),
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) {
if let TypeEnum::TVar { .. } = unifier.get_ty(ty).as_ref() {
let var = unifier.get_fresh_var(Some(*id), Some(expr.location)).0;
unifier.unify(var, ty).unwrap();
Ok(TypeAnnotation::TypeVar(ty))
Ok(DiagnosticsResult::err(TypeAnnotation::TypeVar(ty), diagnostics))
} 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 {
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 =
|id: &StrRef,
id_loc: Location,
slice: &ast::Expr<T>,
unifier: &mut Unifier,
mut locked: HashMap<DefinitionId, Vec<Type>>| {
let mut diagnostics = DiagnosticEngine::new();
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 def_read = top_level_defs[obj_id.0].try_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()
}
};
// we do not check whether the application of type variables are compatible here
let param_type_infos = {
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 {
vec![slice]
};
if type_vars.len() != params_ast.len() {
return Err(format!(
"expect {} type parameters but got {} (at {})",
type_vars.len(),
params_ast.len(),
diagnostics.insert(DiagErr(
format!(
"expect {} type parameters but got {}",
type_vars.len(),
params_ast.len(),
),
params_ast[0].location,
));
}
let result = params_ast
.iter()
.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
let no_type_var =
result.iter().all(|x| get_type_var_contained_in_type_annotation(x).is_empty());
let no_type_var = result.iter()
.all(|x| get_type_var_contained_in_type_annotation(x).is_empty());
if no_type_var {
result
} else {
return Err(format!(
"application of type vars to generic class \
is not currently supported (at {})",
params_ast[0].location
diagnostics.insert(DiagErr(
"application of type vars to generic class is not currently supported".into(),
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 {
ast::ExprKind::Name { id, .. } => name_handle(id, unifier, locked),
ast::ExprKind::Name { id, .. } => name_handle(id, expr.location, unifier, locked),
// virtual
ast::ExprKind::Subscript { value, slice, .. }
if {
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,
top_level_defs,
unifier,
@ -206,10 +257,13 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
slice.as_ref(),
locked,
)?;
diagnostics.merge_with(diags);
if !matches!(def, TypeAnnotation::CustomClass { .. }) {
unreachable!("must be concretized custom class kind in the virtual")
}
Ok(TypeAnnotation::Virtual(def.into()))
Ok(DiagnosticsResult::err(TypeAnnotation::Virtual(def.into()), diagnostics))
}
// 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())
} =>
{
let def_ann = parse_ast_to_type_annotation_kinds(
let DiagnosticsResult { value: def_ann, engine: diags } = parse_ast_to_type_annotation_kinds(
resolver,
top_level_defs,
unifier,
@ -226,7 +280,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
slice.as_ref(),
locked,
)?;
Ok(TypeAnnotation::List(def_ann.into()))
diagnostics.merge_with(diags);
Ok(DiagnosticsResult::err(TypeAnnotation::List(def_ann.into()), diagnostics))
}
// option
@ -242,14 +298,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
primitives,
slice.as_ref(),
locked,
)?;
)?.value;
let id =
if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty(primitives.option).as_ref() {
*obj_id
} else {
unreachable!()
};
Ok(TypeAnnotation::CustomClass { id, params: vec![def_ann] })
Ok(DiagnosticsResult::err(TypeAnnotation::CustomClass { id, params: vec![def_ann] }, diagnostics))
}
// tuple
@ -277,20 +333,43 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
locked.clone(),
)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(TypeAnnotation::Tuple(type_annotations))
.collect_vec();
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
ast::ExprKind::Subscript { value, slice, .. } => {
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 {
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,
primitives: &PrimitiveStore,
ann: &TypeAnnotation,
subst_list: &mut Option<Vec<Type>>
) -> Result<Type, String> {
subst_list: &mut Option<Vec<Type>>,
loc: &Option<Location>,
) -> ResultOrDiagnostics<Type> {
let mut diagnostics = DiagnosticEngine::new();
match ann {
TypeAnnotation::CustomClass { id: obj_id, params } => {
let def_read = top_level_defs[obj_id.0].read();
let class_def: &TopLevelDef = def_read.deref();
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() {
Err(format!(
"unexpected number of type parameters: expected {} but got {}",
type_vars.len(),
params.len()
))
diagnostics.insert(DiagErr(
format!(
"expected {} type parameters for class `{}` but got {}",
type_vars.len(),
name,
params.len(),
),
loc.unwrap(),
));
Err(diagnostics)
} else {
let param_ty = params
.iter()
@ -324,10 +411,16 @@ pub fn get_type_from_type_annotation_kinds(
unifier,
primitives,
x,
subst_list
subst_list,
loc,
)
})
.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 = {
// check for compatible range
@ -351,16 +444,20 @@ pub fn get_type_from_type_annotation_kinds(
if ok {
result.insert(*id, p);
} else {
return Err(format!(
"cannot apply type {} to type variable with id {:?}",
unifier.internal_stringify(
p,
&mut |id| format!("class{}", id),
&mut |id| format!("typevar{}", id),
&mut None
diagnostics.insert(DiagErr(
format!(
"cannot apply type {} to type variable with id {:?}",
unifier.internal_stringify(
p,
&mut |id| format!("class{}", id),
&mut |id| format!("typevar{}", id),
&mut None
),
*id
),
*id
loc.unwrap(),
));
return Err(diagnostics)
}
} else {
unreachable!("must be generic type var")
@ -389,22 +486,25 @@ pub fn get_type_from_type_annotation_kinds(
if need_subst {
subst_list.as_mut().map(|wl| wl.push(ty));
}
Ok(ty)
Ok(DiagnosticsResult::err(ty, diagnostics))
}
} else {
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) => {
let ty = get_type_from_type_annotation_kinds(
top_level_defs,
unifier,
primitives,
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) => {
let ty = get_type_from_type_annotation_kinds(
@ -412,18 +512,26 @@ pub fn get_type_from_type_annotation_kinds(
unifier,
primitives,
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) => {
let tys = tys
.iter()
.map(|x| {
get_type_from_type_annotation_kinds(top_level_defs, unifier, primitives, x, subst_list)
get_type_from_type_annotation_kinds(top_level_defs, unifier, primitives, x, subst_list, loc)
})
.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::{Diagnostic::*, DiagnosticEngine, DiagnosticsResult, ResultOrDiagnostics};
use super::type_inferencer::Inferencer;
use super::typedef::Type;
@ -6,11 +7,14 @@ use nac3parser::ast::{self, Constant, Expr, ExprKind, Operator::{LShift, RShift}
use std::{collections::HashSet, iter::once};
impl<'a> Inferencer<'a> {
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), String> {
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> ResultOrDiagnostics<()> {
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 {
Ok(())
Ok(DiagnosticsResult::ok(()))
}
}
@ -18,100 +22,150 @@ impl<'a> Inferencer<'a> {
&mut self,
pattern: &Expr<Option<Type>>,
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<(), String> {
) -> ResultOrDiagnostics<()> {
let mut diagnostics = DiagnosticEngine::new();
match &pattern.node {
ast::ExprKind::Name { id, .. } if id == &"none".into() =>
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
ExprKind::Name { id, .. } if id == &"none".into() => {
diagnostics.insert(DiagErr(
"cannot assign to a `none`".into(),
pattern.location,
));
}
ExprKind::Name { id, .. } => {
if !defined_identifiers.contains(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, .. } => {
for elt in elts.iter() {
self.check_pattern(elt, defined_identifiers)?;
self.should_have_value(elt)?;
if let Err(e) = self.check_pattern(elt, defined_identifiers) {
diagnostics.merge_with(e);
}
if let Err(e) = self.should_have_value(elt) {
diagnostics.merge_with(e);
}
}
Ok(())
}
ExprKind::Subscript { value, slice, .. } => {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
self.check_expr(slice, defined_identifiers)?;
if let Err(e) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(e);
}
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()) {
return Err(format!(
"Error at {}: cannot assign to tuple element",
value.location
diagnostics.insert(DiagErr(
"cannot assign to tuple element".into(),
value.location,
));
}
Ok(())
}
ExprKind::Constant { .. } => {
Err(format!("cannot assign to a constant (at {})", pattern.location))
diagnostics.insert(DiagErr(
"cannot assign to a constant".into(),
pattern.location,
));
}
_ => {
if let Err(e) = self.check_expr(pattern, defined_identifiers) {
diagnostics.merge_with(e);
}
}
_ => self.check_expr(pattern, defined_identifiers),
}
diagnostics.into_result(())
}
fn check_expr(
&mut self,
expr: &Expr<Option<Type>>,
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<(), String> {
) -> ResultOrDiagnostics<()> {
let mut diagnostics = DiagnosticEngine::new();
// there are some cases where the custom field is None
if let Some(ty) = &expr.custom {
if !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
return Err(format!(
"expected concrete type at {} but got {}",
diagnostics.insert(DiagErr(
format!("expected concrete type but got {}", self.unifier.get_ty(*ty).get_type_name()),
expr.location,
self.unifier.get_ty(*ty).get_type_name()
));
}
}
match &expr.node {
ExprKind::Name { id, .. } => {
if id == &"none".into() {
return Ok(());
}
self.should_have_value(expr)?;
if !defined_identifiers.contains(id) {
match self.function_data.resolver.get_symbol_type(
self.unifier,
&self.top_level.definitions.read(),
self.primitives,
*id,
) {
Ok(_) => {
self.defined_identifiers.insert(*id);
}
Err(e) => {
return Err(format!(
"type error at identifier `{}` ({}) at {}",
id, e, expr.location
));
if id != &"none".into() {
if let Err(e) = self.should_have_value(expr) {
diagnostics.merge_with(e);
}
if !defined_identifiers.contains(id) {
match self.function_data.resolver.get_symbol_type(
self.unifier,
&self.top_level.definitions.read(),
self.primitives,
*id,
) {
Ok(_) => {
self.defined_identifiers.insert(*id);
}
Err(e) => {
diagnostics.insert(DiagErr(
format!("type error at identifier `{}` ({})", id, e),
expr.location,
));
}
}
}
}
}
ExprKind::List { elts, .. }
| ExprKind::Tuple { elts, .. }
| ExprKind::BoolOp { values: elts, .. } => {
for elt in elts.iter() {
self.check_expr(elt, defined_identifiers)?;
self.should_have_value(elt)?;
diagnostics.consume_result_to_option(self.check_expr(elt, defined_identifiers));
diagnostics.consume_result_to_option(self.should_have_value(elt));
}
}
ExprKind::Attribute { value, .. } => {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
if let Err(errs) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
}
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)?;
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);
}
// Check whether a bitwise shift has a negative RHS constant value
if *op == LShift || *op == RShift {
@ -122,40 +176,70 @@ impl<'a> Inferencer<'a> {
};
if *rhs_val < 0 {
return Err(format!(
"shift count is negative at {}",
right.location
diagnostics.insert(DiagErr(
"shift count is negative".into(),
right.location,
));
}
}
}
}
ExprKind::UnaryOp { operand, .. } => {
self.check_expr(operand, defined_identifiers)?;
self.should_have_value(operand)?;
if let Err(errs) = self.check_expr(operand, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(operand) {
diagnostics.merge_with(e);
}
}
ExprKind::Compare { left, comparators, .. } => {
for elt in once(left.as_ref()).chain(comparators.iter()) {
self.check_expr(elt, defined_identifiers)?;
self.should_have_value(elt)?;
if let Err(errs) = self.check_expr(elt, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(elt) {
diagnostics.merge_with(e);
}
}
}
ExprKind::Subscript { value, slice, .. } => {
self.should_have_value(value)?;
self.check_expr(value, defined_identifiers)?;
self.check_expr(slice, defined_identifiers)?;
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
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 } => {
self.check_expr(test, defined_identifiers)?;
self.check_expr(body, defined_identifiers)?;
self.check_expr(orelse, defined_identifiers)?;
if let Err(errs) = self.check_expr(test, defined_identifiers) {
diagnostics.merge_with(errs);
}
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 } => {
for elt in [lower.as_ref(), upper.as_ref(), step.as_ref()].iter().flatten() {
self.should_have_value(elt)?;
self.check_expr(elt, defined_identifiers)?;
if let Err(e) = self.should_have_value(elt) {
diagnostics.merge_with(e);
}
if let Err(errs) = self.check_expr(elt, defined_identifiers) {
diagnostics.merge_with(errs);
}
}
}
ExprKind::Lambda { args, body } => {
let mut defined_identifiers = defined_identifiers.clone();
for arg in args.args.iter() {
@ -164,160 +248,286 @@ impl<'a> Inferencer<'a> {
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, .. } => {
// in our type inference stage, we already make sure that there is only 1 generator
let ast::Comprehension { target, iter, ifs, .. } = &generators[0];
self.check_expr(iter, defined_identifiers)?;
self.should_have_value(iter)?;
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 defined_identifiers = defined_identifiers.clone();
self.check_pattern(target, &mut defined_identifiers)?;
self.should_have_value(target)?;
if let Err(errs) = self.check_pattern(target, &mut defined_identifiers) {
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()) {
self.check_expr(term, &mut defined_identifiers)?;
self.should_have_value(term)?;
if let Err(errs) = self.check_expr(term, &mut defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(term) {
diagnostics.merge_with(e);
}
}
}
ExprKind::Call { func, args, keywords } => {
for expr in once(func.as_ref())
.chain(args.iter())
.chain(keywords.iter().map(|v| v.node.value.as_ref()))
{
self.check_expr(expr, defined_identifiers)?;
self.should_have_value(expr)?;
if let Err(errs) = self.check_expr(expr, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(expr) {
diagnostics.merge_with(e);
}
}
}
ExprKind::Constant { .. } => {}
_ => {
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(
&mut self,
stmt: &Stmt<Option<Type>>,
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<bool, String> {
match &stmt.node {
StmtKind::For { target, iter, body, orelse, .. } => {
self.check_expr(iter, defined_identifiers)?;
self.should_have_value(iter)?;
) -> DiagnosticsResult<bool> {
let mut diagnostics = DiagnosticEngine::new();
let stmt_returns = match &stmt.node {
StmtKind::For {
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();
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();
self.check_pattern(target, &mut local_defined_identifiers)?;
self.should_have_value(target)?;
for stmt in body.iter() {
self.check_stmt(stmt, &mut local_defined_identifiers)?;
if let Err(errs) = self.check_pattern(target, &mut local_defined_identifiers) {
diagnostics.merge_with(errs);
}
Ok(false)
if let Err(e) = self.should_have_value(target) {
diagnostics.merge_with(e);
}
for stmt in body.iter() {
let DiagnosticsResult { engine: errs, .. } = self.check_stmt(stmt, &mut local_defined_identifiers);
diagnostics.merge_with(errs);
}
false
}
StmtKind::If { test, body, orelse, .. } => {
self.check_expr(test, defined_identifiers)?;
self.should_have_value(test)?;
if let Err(errs) = self.check_expr(test, defined_identifiers) {
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 orelse_identifiers = defined_identifiers.clone();
let body_returned = self.check_block(body, &mut body_identifiers)?;
let orelse_returned = self.check_block(orelse, &mut orelse_identifiers)?;
let DiagnosticsResult { value: body_returned, engine: errs } = self.check_block(body, &mut body_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() {
if !defined_identifiers.contains(ident) && orelse_identifiers.contains(ident) {
defined_identifiers.insert(*ident);
}
}
Ok(body_returned && orelse_returned)
body_returned && orelse_returned
}
StmtKind::While { test, body, orelse, .. } => {
self.check_expr(test, defined_identifiers)?;
self.should_have_value(test)?;
if let Err(errs) = self.check_expr(test, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(test) {
diagnostics.merge_with(e);
}
let mut defined_identifiers = defined_identifiers.clone();
self.check_block(body, &mut defined_identifiers)?;
self.check_block(orelse, &mut defined_identifiers)?;
Ok(false)
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, .. } => {
let mut new_defined_identifiers = defined_identifiers.clone();
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() {
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, .. } => {
self.check_block(body, &mut defined_identifiers.clone())?;
self.check_block(orelse, &mut defined_identifiers.clone())?;
let DiagnosticsResult { engine: errs, .. } = self.check_block(body, &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() {
let mut defined_identifiers = defined_identifiers.clone();
let ast::ExcepthandlerKind::ExceptHandler { name, body, .. } = &handler.node;
if let Some(name) = 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, .. } => {
self.check_expr(value, defined_identifiers)?;
Ok(false)
}
StmtKind::Assign { targets, value, .. } => {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
for target in targets {
self.check_pattern(target, defined_identifiers)?;
if let Err(e) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(e);
}
Ok(false)
false
}
StmtKind::Assign { targets, value, .. } => {
if let Err(errs) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
for target in targets {
if let Err(errs) = self.check_pattern(target, defined_identifiers) {
diagnostics.merge_with(errs);
}
}
false
}
StmtKind::AnnAssign { target, value, .. } => {
if let Some(value) = value {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
self.check_pattern(target, defined_identifiers)?;
if let Err(errs) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
if let Err(errs) = self.check_pattern(target, defined_identifiers) {
diagnostics.merge_with(errs);
}
}
Ok(false)
false
}
StmtKind::Return { value, .. } => {
if let Some(value) = value {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
if let Err(errs) = self.check_expr(value, defined_identifiers) {
diagnostics.merge_with(errs);
}
if let Err(e) = self.should_have_value(value) {
diagnostics.merge_with(e);
}
}
Ok(true)
true
}
StmtKind::Raise { 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.
_ => Ok(false),
}
_ => false,
};
DiagnosticsResult::err(stmt_returns, diagnostics)
}
pub fn check_block(
&mut self,
block: &[Stmt<Option<Type>>],
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<bool, String> {
) -> DiagnosticsResult<bool> {
let mut diagnostics = DiagnosticEngine::new();
let mut ret = false;
for stmt in block {
if ret {
println!("warning: dead code at {:?}\n", stmt.location)
}
if self.check_stmt(stmt, defined_identifiers)? {
ret = true;
diagnostics.insert(DiagWarn(
"unreachable statement".into(),
stmt.location,
));
}
let DiagnosticsResult { value: stmt_rets, engine: stmt_errs } =
self.check_stmt(stmt, defined_identifiers);
ret |= stmt_rets;
diagnostics.merge_with(stmt_errs);
}
Ok(ret)
DiagnosticsResult::err(ret, diagnostics)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -43,8 +43,10 @@ impl SymbolResolver for Resolver {
unimplemented!()
}
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.id_to_def.get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
fn get_identifier_def(&self, id: StrRef, loc: Option<Location>) -> ResultOrDiagnostics<DefinitionId> {
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 {
@ -543,7 +545,8 @@ fn test_basic(source: &str, mapping: HashMap<&str, &str>, virtuals: &[(&str, &st
.collect::<Result<Vec<_>, _>>()
.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() {
let name = inferencer.unifier.internal_stringify(
@ -689,7 +692,8 @@ fn test_primitive_magic_methods(source: &str, mapping: HashMap<&str, &str>) {
.collect::<Result<Vec<_>, _>>()
.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() {
let name = inferencer.unifier.internal_stringify(

View File

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

View File

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