Refactor to use Diagnostic Class
parent
1c071a294c
commit
316c16d5a6
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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)])),
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue