artiq, core: Return HashSet of error messages for analysis #351
|
@ -502,9 +502,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
@ -641,6 +641,7 @@ name = "nac3artiq"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"inkwell",
|
"inkwell",
|
||||||
|
"itertools 0.12.0",
|
||||||
"nac3core",
|
"nac3core",
|
||||||
"nac3ld",
|
"nac3ld",
|
||||||
"nac3parser",
|
"nac3parser",
|
||||||
|
@ -667,7 +668,7 @@ dependencies = [
|
||||||
"indoc",
|
"indoc",
|
||||||
"inkwell",
|
"inkwell",
|
||||||
"insta",
|
"insta",
|
||||||
"itertools 0.11.0",
|
"itertools 0.12.0",
|
||||||
"nac3parser",
|
"nac3parser",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
|
|
@ -9,6 +9,7 @@ name = "nac3artiq"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
itertools = "0.12"
|
||||||
pyo3 = { version = "0.20", features = ["extension-module"] }
|
pyo3 = { version = "0.20", features = ["extension-module"] }
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
tempfile = "3.8"
|
tempfile = "3.8"
|
||||||
|
|
|
@ -13,6 +13,7 @@ use inkwell::{
|
||||||
targets::*,
|
targets::*,
|
||||||
OptimizationLevel,
|
OptimizationLevel,
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use nac3core::codegen::{CodeGenLLVMOptions, CodeGenTargetMachineOptions, gen_func_impl};
|
use nac3core::codegen::{CodeGenLLVMOptions, CodeGenTargetMachineOptions, gen_func_impl};
|
||||||
use nac3core::toplevel::builtins::get_exn_constructor;
|
use nac3core::toplevel::builtins::get_exn_constructor;
|
||||||
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
|
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
|
||||||
|
@ -470,7 +471,7 @@ impl Nac3 {
|
||||||
|
|
||||||
if let Err(e) = composer.start_analysis(true) {
|
if let Err(e) = composer.start_analysis(true) {
|
||||||
// report error of __modinit__ separately
|
// report error of __modinit__ separately
|
||||||
return if e.contains("<nac3_synthesized_modinit>") {
|
return if e.iter().any(|err| err.contains("<nac3_synthesized_modinit>")) {
|
||||||
let msg = Self::report_modinit(
|
let msg = Self::report_modinit(
|
||||||
&arg_names,
|
&arg_names,
|
||||||
method_name,
|
method_name,
|
||||||
|
@ -481,12 +482,15 @@ impl Nac3 {
|
||||||
);
|
);
|
||||||
Err(CompileError::new_err(format!(
|
Err(CompileError::new_err(format!(
|
||||||
"compilation failed\n----------\n{}",
|
"compilation failed\n----------\n{}",
|
||||||
msg.unwrap_or(e)
|
msg.unwrap_or(e.iter().sorted().join("\n----------\n"))
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Err(CompileError::new_err(format!(
|
Err(CompileError::new_err(
|
||||||
"compilation failed\n----------\n{e}"
|
format!(
|
||||||
)))
|
"compilation failed\n----------\n{}",
|
||||||
|
e.iter().sorted().join("\n----------\n"),
|
||||||
|
),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let top_level = Arc::new(composer.make_top_level_context());
|
let top_level = Arc::new(composer.make_top_level_context());
|
||||||
|
|
|
@ -15,7 +15,7 @@ use pyo3::{
|
||||||
PyAny, PyObject, PyResult, Python,
|
PyAny, PyObject, PyResult, Python,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, Ordering::Relaxed}
|
atomic::{AtomicBool, Ordering::Relaxed}
|
||||||
|
@ -1172,17 +1172,21 @@ impl SymbolResolver for Resolver {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||||
{
|
{
|
||||||
let id_to_def = self.0.id_to_def.read();
|
let id_to_def = self.0.id_to_def.read();
|
||||||
id_to_def.get(&id).copied().ok_or_else(String::new)
|
id_to_def.get(&id).copied().ok_or_else(String::new)
|
||||||
}
|
}
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
let py_id =
|
let py_id = self.0.name_to_pyid.get(&id)
|
||||||
self.0.name_to_pyid.get(&id).ok_or(format!("Undefined identifier `{id}`"))?;
|
.ok_or_else(|| HashSet::from([
|
||||||
let result = self.0.pyid_to_def.read().get(py_id).copied().ok_or(format!(
|
format!("Undefined identifier `{id}`"),
|
||||||
"`{id}` is not registered with NAC3 (@nac3 decorator missing?)"
|
]))?;
|
||||||
))?;
|
let result = self.0.pyid_to_def.read().get(py_id)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| HashSet::from([
|
||||||
|
format!("`{id}` is not registered with NAC3 (@nac3 decorator missing?)"),
|
||||||
|
]))?;
|
||||||
self.0.id_to_def.write().insert(id, result);
|
self.0.id_to_def.write().insert(id, result);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//! Datatypes to support source location information.
|
//! Datatypes to support source location information.
|
||||||
|
use std::cmp::Ordering;
|
||||||
use crate::ast_gen::StrRef;
|
use crate::ast_gen::StrRef;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct FileName(pub StrRef);
|
pub struct FileName(pub StrRef);
|
||||||
impl Default for FileName {
|
impl Default for FileName {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -17,7 +18,7 @@ impl From<String> for FileName {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A location somewhere in the sourcecode.
|
/// A location somewhere in the sourcecode.
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
pub row: usize,
|
pub row: usize,
|
||||||
pub column: usize,
|
pub column: usize,
|
||||||
|
@ -30,6 +31,28 @@ impl fmt::Display for Location {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Ord for Location {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
let file_cmp = self.file.0.to_string().cmp(&other.file.0.to_string());
|
||||||
|
if file_cmp != Ordering::Equal {
|
||||||
|
return file_cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
let row_cmp = self.row.cmp(&other.row);
|
||||||
|
if row_cmp != Ordering::Equal {
|
||||||
|
return row_cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
self.column.cmp(&other.column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Location {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Location {
|
impl Location {
|
||||||
pub fn visualize<'a>(
|
pub fn visualize<'a>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -5,7 +5,7 @@ authors = ["M-Labs"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
itertools = "0.11"
|
itertools = "0.12"
|
||||||
crossbeam = "0.8"
|
crossbeam = "0.8"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
rayon = "1.5"
|
rayon = "1.5"
|
||||||
|
|
|
@ -1649,7 +1649,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||||
let fun = ctx
|
let fun = ctx
|
||||||
.resolver
|
.resolver
|
||||||
.get_identifier_def(*id)
|
.get_identifier_def(*id)
|
||||||
.map_err(|e| format!("{} (at {})", e, func.location))?;
|
.map_err(|e| format!("{} (at {})", e.iter().next().unwrap(), func.location))?;
|
||||||
return Ok(generator
|
return Ok(generator
|
||||||
.gen_call(ctx, None, (&signature, fun), params)?
|
.gen_call(ctx, None, (&signature, fun), params)?
|
||||||
.map(Into::into));
|
.map(Into::into));
|
||||||
|
|
|
@ -63,12 +63,14 @@ impl SymbolResolver for Resolver {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||||
self.id_to_def
|
self.id_to_def
|
||||||
.read()
|
.read()
|
||||||
.get(&id)
|
.get(&id)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| format!("cannot find symbol `{}`", id))
|
.ok_or_else(|| HashSet::from([
|
||||||
|
format!("cannot find symbol `{}`", id),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_string_id(&self, _: &str) -> i32 {
|
fn get_string_id(&self, _: &str) -> i32 {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{collections::HashMap, fmt::Display};
|
use std::{collections::HashMap, collections::HashSet, fmt::Display};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::typecheck::typedef::TypeEnum;
|
use crate::typecheck::typedef::TypeEnum;
|
||||||
|
@ -296,7 +296,7 @@ pub trait SymbolResolver {
|
||||||
) -> Result<Type, String>;
|
) -> Result<Type, String>;
|
||||||
|
|
||||||
/// Get the top-level definition of identifiers.
|
/// Get the top-level definition of identifiers.
|
||||||
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, String>;
|
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, HashSet<String>>;
|
||||||
|
|
||||||
fn get_symbol_value<'ctx>(
|
fn get_symbol_value<'ctx>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -341,7 +341,7 @@ pub fn parse_type_annotation<T>(
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
primitives: &PrimitiveStore,
|
primitives: &PrimitiveStore,
|
||||||
expr: &Expr<T>,
|
expr: &Expr<T>,
|
||||||
) -> Result<Type, String> {
|
) -> Result<Type, HashSet<String>> {
|
||||||
use nac3parser::ast::ExprKind::*;
|
use nac3parser::ast::ExprKind::*;
|
||||||
let ids = IDENTIFIER_ID.with(|ids| *ids);
|
let ids = IDENTIFIER_ID.with(|ids| *ids);
|
||||||
let int32_id = ids[0];
|
let int32_id = ids[0];
|
||||||
|
@ -379,10 +379,12 @@ pub fn parse_type_annotation<T>(
|
||||||
let def = top_level_defs[obj_id.0].read();
|
let def = top_level_defs[obj_id.0].read();
|
||||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||||
if !type_vars.is_empty() {
|
if !type_vars.is_empty() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Unexpected number of type parameters: expected {} but got 0",
|
format!(
|
||||||
type_vars.len()
|
"Unexpected number of type parameters: expected {} but got 0",
|
||||||
));
|
type_vars.len()
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
let fields = chain(
|
let fields = chain(
|
||||||
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
|
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
|
||||||
|
@ -395,16 +397,22 @@ pub fn parse_type_annotation<T>(
|
||||||
params: HashMap::default(),
|
params: HashMap::default(),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Cannot use function name as type at {loc}"))
|
Err(HashSet::from([
|
||||||
|
format!("Cannot use function name as type at {loc}"),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ty = resolver
|
let ty = resolver
|
||||||
.get_symbol_type(unifier, top_level_defs, primitives, *id)
|
.get_symbol_type(unifier, top_level_defs, primitives, *id)
|
||||||
.map_err(|e| format!("Unknown type annotation at {loc}: {e}"))?;
|
.map_err(|e| HashSet::from([
|
||||||
|
format!("Unknown type annotation at {loc}: {e}"),
|
||||||
|
]))?;
|
||||||
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Unknown type annotation {id} at {loc}"))
|
Err(HashSet::from([
|
||||||
|
format!("Unknown type annotation {id} at {loc}"),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,7 +435,9 @@ pub fn parse_type_annotation<T>(
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
|
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
|
||||||
} else {
|
} else {
|
||||||
Err("Expected multiple elements for tuple".into())
|
Err(HashSet::from([
|
||||||
|
"Expected multiple elements for tuple".into()
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let types = if let Tuple { elts, .. } = &slice.node {
|
let types = if let Tuple { elts, .. } = &slice.node {
|
||||||
|
@ -444,11 +454,13 @@ pub fn parse_type_annotation<T>(
|
||||||
let def = top_level_defs[obj_id.0].read();
|
let def = top_level_defs[obj_id.0].read();
|
||||||
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
|
||||||
if types.len() != type_vars.len() {
|
if types.len() != type_vars.len() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Unexpected number of type parameters: expected {} but got {}",
|
format!(
|
||||||
type_vars.len(),
|
"Unexpected number of type parameters: expected {} but got {}",
|
||||||
types.len()
|
type_vars.len(),
|
||||||
));
|
types.len()
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
let mut subst = HashMap::new();
|
let mut subst = HashMap::new();
|
||||||
for (var, ty) in izip!(type_vars.iter(), types.iter()) {
|
for (var, ty) in izip!(type_vars.iter(), types.iter()) {
|
||||||
|
@ -472,7 +484,9 @@ pub fn parse_type_annotation<T>(
|
||||||
}));
|
}));
|
||||||
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }))
|
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }))
|
||||||
} else {
|
} else {
|
||||||
Err("Cannot use function name as type".into())
|
Err(HashSet::from([
|
||||||
|
"Cannot use function name as type".into(),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -483,10 +497,14 @@ pub fn parse_type_annotation<T>(
|
||||||
if let Name { id, .. } = &value.node {
|
if let Name { id, .. } = &value.node {
|
||||||
subscript_name_handle(id, slice, unifier)
|
subscript_name_handle(id, slice, unifier)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("unsupported type expression at {}", expr.location))
|
Err(HashSet::from([
|
||||||
|
format!("unsupported type expression at {}", expr.location),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(format!("unsupported type expression at {}", expr.location)),
|
_ => Err(HashSet::from([
|
||||||
|
format!("unsupported type expression at {}", expr.location),
|
||||||
|
])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,7 +515,7 @@ impl dyn SymbolResolver + Send + Sync {
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
primitives: &PrimitiveStore,
|
primitives: &PrimitiveStore,
|
||||||
expr: &Expr<T>,
|
expr: &Expr<T>,
|
||||||
) -> Result<Type, String> {
|
) -> Result<Type, HashSet<String>> {
|
||||||
parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
|
parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -362,7 +362,7 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_analysis(&mut self, inference: bool) -> Result<(), String> {
|
pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> {
|
||||||
self.analyze_top_level_class_type_var()?;
|
self.analyze_top_level_class_type_var()?;
|
||||||
self.analyze_top_level_class_bases()?;
|
self.analyze_top_level_class_bases()?;
|
||||||
self.analyze_top_level_class_fields_methods()?;
|
self.analyze_top_level_class_fields_methods()?;
|
||||||
|
@ -374,7 +374,7 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// step 1, analyze the type vars associated with top level class
|
/// step 1, analyze the type vars associated with top level class
|
||||||
fn analyze_top_level_class_type_var(&mut self) -> Result<(), String> {
|
fn analyze_top_level_class_type_var(&mut self) -> Result<(), HashSet<String>> {
|
||||||
let def_list = &self.definition_ast_list;
|
let def_list = &self.definition_ast_list;
|
||||||
let temp_def_list = self.extract_def_list();
|
let temp_def_list = self.extract_def_list();
|
||||||
let unifier = self.unifier.borrow_mut();
|
let unifier = self.unifier.borrow_mut();
|
||||||
|
@ -417,10 +417,12 @@ impl TopLevelComposer {
|
||||||
} =>
|
} =>
|
||||||
{
|
{
|
||||||
if is_generic {
|
if is_generic {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"only single Generic[...] is allowed (at {})",
|
format!(
|
||||||
b.location
|
"only single Generic[...] is allowed (at {})",
|
||||||
));
|
b.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
is_generic = true;
|
is_generic = true;
|
||||||
|
|
||||||
|
@ -459,10 +461,12 @@ impl TopLevelComposer {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
if !all_unique_type_var {
|
if !all_unique_type_var {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"duplicate type variable occurs (at {})",
|
format!(
|
||||||
slice.location
|
"duplicate type variable occurs (at {})",
|
||||||
));
|
slice.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to TopLevelDef
|
// add to TopLevelDef
|
||||||
|
@ -481,11 +485,11 @@ impl TopLevelComposer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Err(e) = analyze(class_def, class_ast) {
|
if let Err(e) = analyze(class_def, class_ast) {
|
||||||
errors.insert(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into_iter().sorted().join("\n----------\n"));
|
return Err(errors)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -493,7 +497,7 @@ impl TopLevelComposer {
|
||||||
/// step 2, base classes.
|
/// step 2, base classes.
|
||||||
/// now that the type vars of all classes are done, handle base classes and
|
/// now that the type vars of all classes are done, handle base classes and
|
||||||
/// put Self class into the ancestors list. We only allow single inheritance
|
/// put Self class into the ancestors list. We only allow single inheritance
|
||||||
fn analyze_top_level_class_bases(&mut self) -> Result<(), String> {
|
fn analyze_top_level_class_bases(&mut self) -> Result<(), HashSet<String>> {
|
||||||
if self.unifier.top_level.is_none() {
|
if self.unifier.top_level.is_none() {
|
||||||
let ctx = Arc::new(self.make_top_level_context());
|
let ctx = Arc::new(self.make_top_level_context());
|
||||||
self.unifier.top_level = Some(ctx);
|
self.unifier.top_level = Some(ctx);
|
||||||
|
@ -542,11 +546,13 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_base {
|
if has_base {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"a class definition can only have at most one base class \
|
format!(
|
||||||
declaration and one generic declaration (at {})",
|
"a class definition can only have at most one base class \
|
||||||
b.location
|
declaration and one generic declaration (at {})",
|
||||||
));
|
b.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
has_base = true;
|
has_base = true;
|
||||||
|
|
||||||
|
@ -565,10 +571,12 @@ impl TopLevelComposer {
|
||||||
if let TypeAnnotation::CustomClass { .. } = &base_ty {
|
if let TypeAnnotation::CustomClass { .. } = &base_ty {
|
||||||
class_ancestors.push(base_ty);
|
class_ancestors.push(base_ty);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"class base declaration can only be custom class (at {})",
|
format!(
|
||||||
b.location,
|
"class base declaration can only be custom class (at {})",
|
||||||
));
|
b.location,
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -581,16 +589,16 @@ impl TopLevelComposer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Err(e) = get_direct_parents(class_def, class_ast) {
|
if let Err(e) = get_direct_parents(class_def, class_ast) {
|
||||||
errors.insert(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into_iter().sorted().join("\n----------\n"));
|
return Err(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// second, get all ancestors
|
// second, get all ancestors
|
||||||
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = HashMap::default();
|
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = HashMap::default();
|
||||||
let mut get_all_ancestors = |class_def: &Arc<RwLock<TopLevelDef>>| {
|
let mut get_all_ancestors = |class_def: &Arc<RwLock<TopLevelDef>>| -> Result<(), HashSet<String>> {
|
||||||
let class_def = class_def.read();
|
let class_def = class_def.read();
|
||||||
let (class_ancestors, class_id) = {
|
let (class_ancestors, class_id) = {
|
||||||
if let TopLevelDef::Class { ancestors, object_id, .. } = &*class_def {
|
if let TopLevelDef::Class { ancestors, object_id, .. } = &*class_def {
|
||||||
|
@ -615,11 +623,11 @@ impl TopLevelComposer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Err(e) = get_all_ancestors(class_def) {
|
if let Err(e) = get_all_ancestors(class_def) {
|
||||||
errors.insert(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into_iter().sorted().join("\n----------\n"));
|
return Err(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the ancestors to the def list
|
// insert the ancestors to the def list
|
||||||
|
@ -657,7 +665,9 @@ impl TopLevelComposer {
|
||||||
stmt.node,
|
stmt.node,
|
||||||
ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. }
|
ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. }
|
||||||
) {
|
) {
|
||||||
return Err("Classes inherited from exception should have no custom fields/methods".into());
|
return Err(HashSet::from([
|
||||||
|
"Classes inherited from exception should have no custom fields/methods".into()
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -680,7 +690,7 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// step 3, class fields and methods
|
/// step 3, class fields and methods
|
||||||
fn analyze_top_level_class_fields_methods(&mut self) -> Result<(), String> {
|
fn analyze_top_level_class_fields_methods(&mut self) -> Result<(), HashSet<String>> {
|
||||||
let temp_def_list = self.extract_def_list();
|
let temp_def_list = self.extract_def_list();
|
||||||
let primitives = &self.primitives_ty;
|
let primitives = &self.primitives_ty;
|
||||||
let def_ast_list = &self.definition_ast_list;
|
let def_ast_list = &self.definition_ast_list;
|
||||||
|
@ -703,12 +713,12 @@ impl TopLevelComposer {
|
||||||
&mut type_var_to_concrete_def,
|
&mut type_var_to_concrete_def,
|
||||||
(&self.keyword_list, &self.core_config),
|
(&self.keyword_list, &self.core_config),
|
||||||
) {
|
) {
|
||||||
errors.insert(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into_iter().sorted().join("\n----------\n"));
|
return Err(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle the inherited methods and fields
|
// handle the inherited methods and fields
|
||||||
|
@ -752,15 +762,16 @@ impl TopLevelComposer {
|
||||||
|
|
||||||
let mut subst_list = Some(Vec::new());
|
let mut subst_list = Some(Vec::new());
|
||||||
// unification of previously assigned typevar
|
// unification of previously assigned typevar
|
||||||
let mut unification_helper = |ty, def| {
|
let mut unification_helper = |ty, def| -> Result<(), HashSet<String>> {
|
||||||
let target_ty =
|
let target_ty =
|
||||||
get_type_from_type_annotation_kinds(&temp_def_list, unifier, &def, &mut subst_list)?;
|
get_type_from_type_annotation_kinds(&temp_def_list, unifier, &def, &mut subst_list)?;
|
||||||
unifier.unify(ty, target_ty).map_err(|e| e.to_display(unifier).to_string())?;
|
unifier.unify(ty, target_ty)
|
||||||
|
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
for (ty, def) in type_var_to_concrete_def {
|
for (ty, def) in type_var_to_concrete_def {
|
||||||
if let Err(e) = unification_helper(ty, def) {
|
if let Err(e) = unification_helper(ty, def) {
|
||||||
errors.insert(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for ty in subst_list.unwrap() {
|
for ty in subst_list.unwrap() {
|
||||||
|
@ -787,7 +798,7 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into_iter().sorted().join("\n----------\n"));
|
return Err(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (def, _) in def_ast_list.iter().skip(self.builtin_num) {
|
for (def, _) in def_ast_list.iter().skip(self.builtin_num) {
|
||||||
|
@ -806,7 +817,7 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// step 4, after class methods are done, top level functions have nothing unknown
|
/// step 4, after class methods are done, top level functions have nothing unknown
|
||||||
fn analyze_top_level_function(&mut self) -> Result<(), String> {
|
fn analyze_top_level_function(&mut self) -> Result<(), HashSet<String>> {
|
||||||
let def_list = &self.definition_ast_list;
|
let def_list = &self.definition_ast_list;
|
||||||
let keyword_list = &self.keyword_list;
|
let keyword_list = &self.keyword_list;
|
||||||
let temp_def_list = self.extract_def_list();
|
let temp_def_list = self.extract_def_list();
|
||||||
|
@ -842,11 +853,13 @@ impl TopLevelComposer {
|
||||||
if !defined_parameter_name.insert(x.node.arg)
|
if !defined_parameter_name.insert(x.node.arg)
|
||||||
|| keyword_list.contains(&x.node.arg)
|
|| keyword_list.contains(&x.node.arg)
|
||||||
{
|
{
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"top level function must have unique parameter names \
|
format!(
|
||||||
and names should not be the same as the keywords (at {})",
|
"top level function must have unique parameter names \
|
||||||
x.location
|
and names should not be the same as the keywords (at {})",
|
||||||
));
|
x.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,17 +882,17 @@ impl TopLevelComposer {
|
||||||
arg_with_default
|
arg_with_default
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|(x, default)| -> Result<FuncArg, String> {
|
.map(|(x, default)| -> Result<FuncArg, HashSet<String>> {
|
||||||
let annotation = x
|
let annotation = x
|
||||||
.node
|
.node
|
||||||
.annotation
|
.annotation
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| HashSet::from([
|
||||||
format!(
|
format!(
|
||||||
"function parameter `{}` needs type annotation at {}",
|
"function parameter `{}` needs type annotation at {}",
|
||||||
x.node.arg, x.location
|
x.node.arg, x.location
|
||||||
)
|
),
|
||||||
})?
|
]))?
|
||||||
.as_ref();
|
.as_ref();
|
||||||
|
|
||||||
let type_annotation = parse_ast_to_type_annotation_kinds(
|
let type_annotation = parse_ast_to_type_annotation_kinds(
|
||||||
|
@ -897,7 +910,7 @@ impl TopLevelComposer {
|
||||||
let type_vars_within =
|
let type_vars_within =
|
||||||
get_type_var_contained_in_type_annotation(&type_annotation)
|
get_type_var_contained_in_type_annotation(&type_annotation)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| -> Result<(u32, Type), String> {
|
.map(|x| -> Result<(u32, Type), HashSet<String>> {
|
||||||
if let TypeAnnotation::TypeVar(ty) = x {
|
if let TypeAnnotation::TypeVar(ty) = x {
|
||||||
Ok((Self::get_var_id(ty, unifier)?, ty))
|
Ok((Self::get_var_id(ty, unifier)?, ty))
|
||||||
} else {
|
} else {
|
||||||
|
@ -934,9 +947,9 @@ impl TopLevelComposer {
|
||||||
primitives_store,
|
primitives_store,
|
||||||
unifier,
|
unifier,
|
||||||
)
|
)
|
||||||
.map_err(
|
.map_err(|err| HashSet::from([
|
||||||
|err| format!("{} (at {})", err, x.location),
|
format!("{} (at {})", err, x.location),
|
||||||
)?;
|
]))?;
|
||||||
v
|
v
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
@ -965,7 +978,7 @@ impl TopLevelComposer {
|
||||||
let type_vars_within =
|
let type_vars_within =
|
||||||
get_type_var_contained_in_type_annotation(&return_ty_annotation)
|
get_type_var_contained_in_type_annotation(&return_ty_annotation)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| -> Result<(u32, Type), String> {
|
.map(|x| -> Result<(u32, Type), HashSet<String>> {
|
||||||
if let TypeAnnotation::TypeVar(ty) = x {
|
if let TypeAnnotation::TypeVar(ty) = x {
|
||||||
Ok((Self::get_var_id(ty, unifier)?, ty))
|
Ok((Self::get_var_id(ty, unifier)?, ty))
|
||||||
} else {
|
} else {
|
||||||
|
@ -1007,9 +1020,9 @@ impl TopLevelComposer {
|
||||||
ret: return_ty,
|
ret: return_ty,
|
||||||
vars: function_var_map,
|
vars: function_var_map,
|
||||||
}));
|
}));
|
||||||
unifier.unify(*dummy_ty, function_ty).map_err(|e| {
|
unifier.unify(*dummy_ty, function_ty).map_err(|e| HashSet::from([
|
||||||
e.at(Some(function_ast.location)).to_display(unifier).to_string()
|
e.at(Some(function_ast.location)).to_display(unifier).to_string(),
|
||||||
})?;
|
]))?;
|
||||||
} else {
|
} else {
|
||||||
unreachable!("must be both function");
|
unreachable!("must be both function");
|
||||||
}
|
}
|
||||||
|
@ -1024,11 +1037,11 @@ impl TopLevelComposer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Err(e) = analyze(function_def, function_ast) {
|
if let Err(e) = analyze(function_def, function_ast) {
|
||||||
errors.insert(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into_iter().sorted().join("\n----------\n"));
|
return Err(errors)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1041,7 +1054,7 @@ impl TopLevelComposer {
|
||||||
primitives: &PrimitiveStore,
|
primitives: &PrimitiveStore,
|
||||||
type_var_to_concrete_def: &mut HashMap<Type, TypeAnnotation>,
|
type_var_to_concrete_def: &mut HashMap<Type, TypeAnnotation>,
|
||||||
core_info: (&HashSet<StrRef>, &ComposerConfig),
|
core_info: (&HashSet<StrRef>, &ComposerConfig),
|
||||||
) -> Result<(), String> {
|
) -> Result<(), HashSet<String>> {
|
||||||
let (keyword_list, core_config) = core_info;
|
let (keyword_list, core_config) = core_info;
|
||||||
let mut class_def = class_def.write();
|
let mut class_def = class_def.write();
|
||||||
let (
|
let (
|
||||||
|
@ -1092,25 +1105,23 @@ impl TopLevelComposer {
|
||||||
if !defined_parameter_name.insert(x.node.arg)
|
if !defined_parameter_name.insert(x.node.arg)
|
||||||
|| (keyword_list.contains(&x.node.arg) && x.node.arg != zelf)
|
|| (keyword_list.contains(&x.node.arg) && x.node.arg != zelf)
|
||||||
{
|
{
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"top level function must have unique parameter names \
|
format!("top level function must have unique parameter names \
|
||||||
and names should not be the same as the keywords (at {})",
|
and names should not be the same as the keywords (at {})",
|
||||||
x.location
|
x.location),
|
||||||
));
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == &"__init__".into() && !defined_parameter_name.contains(&zelf) {
|
if name == &"__init__".into() && !defined_parameter_name.contains(&zelf) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"__init__ method must have a `self` parameter (at {})",
|
format!("__init__ method must have a `self` parameter (at {})", b.location),
|
||||||
b.location
|
]))
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if !defined_parameter_name.contains(&zelf) {
|
if !defined_parameter_name.contains(&zelf) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"class method must have a `self` parameter (at {})",
|
format!("class method must have a `self` parameter (at {})", b.location),
|
||||||
b.location
|
]))
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
@ -1139,12 +1150,12 @@ impl TopLevelComposer {
|
||||||
.node
|
.node
|
||||||
.annotation
|
.annotation
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| HashSet::from([
|
||||||
format!(
|
format!(
|
||||||
"type annotation needed for `{}` at {}",
|
"type annotation needed for `{}` at {}",
|
||||||
x.node.arg, x.location
|
x.node.arg, x.location
|
||||||
)
|
),
|
||||||
})?
|
]))?
|
||||||
.as_ref();
|
.as_ref();
|
||||||
parse_ast_to_type_annotation_kinds(
|
parse_ast_to_type_annotation_kinds(
|
||||||
class_resolver,
|
class_resolver,
|
||||||
|
@ -1181,7 +1192,9 @@ impl TopLevelComposer {
|
||||||
None => None,
|
None => None,
|
||||||
Some(default) => {
|
Some(default) => {
|
||||||
if name == "self".into() {
|
if name == "self".into() {
|
||||||
return Err(format!("`self` parameter cannot take default value (at {})", x.location));
|
return Err(HashSet::from([
|
||||||
|
format!("`self` parameter cannot take default value (at {})", x.location),
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
Some({
|
Some({
|
||||||
let v = Self::parse_parameter_default_value(
|
let v = Self::parse_parameter_default_value(
|
||||||
|
@ -1191,9 +1204,9 @@ impl TopLevelComposer {
|
||||||
Self::check_default_param_type(
|
Self::check_default_param_type(
|
||||||
&v, &type_ann, primitives, unifier,
|
&v, &type_ann, primitives, unifier,
|
||||||
)
|
)
|
||||||
.map_err(|err| {
|
.map_err(|err| HashSet::from([
|
||||||
format!("{} (at {})", err, x.location)
|
format!("{} (at {})", err, x.location),
|
||||||
})?;
|
]))?;
|
||||||
v
|
v
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1279,7 +1292,7 @@ impl TopLevelComposer {
|
||||||
// which should be fine since type within method_type will be subst later
|
// which should be fine since type within method_type will be subst later
|
||||||
unifier
|
unifier
|
||||||
.unify(method_dummy_ty, method_type)
|
.unify(method_dummy_ty, method_type)
|
||||||
.map_err(|e| e.to_display(unifier).to_string())?;
|
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?;
|
||||||
}
|
}
|
||||||
ast::StmtKind::AnnAssign { target, annotation, value: None, .. } => {
|
ast::StmtKind::AnnAssign { target, annotation, value: None, .. } => {
|
||||||
if let ast::ExprKind::Name { id: attr, .. } = &target.node {
|
if let ast::ExprKind::Name { id: attr, .. } = &target.node {
|
||||||
|
@ -1325,11 +1338,13 @@ impl TopLevelComposer {
|
||||||
for type_var_within in type_vars_within {
|
for type_var_within in type_vars_within {
|
||||||
if let TypeAnnotation::TypeVar(t) = type_var_within {
|
if let TypeAnnotation::TypeVar(t) = type_var_within {
|
||||||
if !class_type_vars_def.contains(&t) {
|
if !class_type_vars_def.contains(&t) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"class fields can only use type \
|
format!(
|
||||||
vars over which the class is generic (at {})",
|
"class fields can only use type \
|
||||||
annotation.location
|
vars over which the class is generic (at {})",
|
||||||
));
|
annotation.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unreachable!("must be type var annotation");
|
unreachable!("must be type var annotation");
|
||||||
|
@ -1337,26 +1352,32 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
type_var_to_concrete_def.insert(dummy_field_type, parsed_annotation);
|
type_var_to_concrete_def.insert(dummy_field_type, parsed_annotation);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"same class fields `{}` defined twice (at {})",
|
format!(
|
||||||
attr, target.location
|
"same class fields `{}` defined twice (at {})",
|
||||||
));
|
attr, target.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"unsupported statement type in class definition body (at {})",
|
format!(
|
||||||
target.location
|
"unsupported statement type in class definition body (at {})",
|
||||||
));
|
target.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::StmtKind::Assign { .. } => {}, // we don't class attributes
|
ast::StmtKind::Assign { .. } => {}, // we don't class attributes
|
||||||
ast::StmtKind::Pass { .. } => {}
|
ast::StmtKind::Pass { .. } => {}
|
||||||
ast::StmtKind::Expr { value: _, .. } => {} // typically a docstring; ignoring all expressions matches CPython behavior
|
ast::StmtKind::Expr { value: _, .. } => {} // typically a docstring; ignoring all expressions matches CPython behavior
|
||||||
_ => {
|
_ => {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"unsupported statement in class definition body (at {})",
|
format!(
|
||||||
b.location
|
"unsupported statement in class definition body (at {})",
|
||||||
))
|
b.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1369,7 +1390,7 @@ impl TopLevelComposer {
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
_primitives: &PrimitiveStore,
|
_primitives: &PrimitiveStore,
|
||||||
type_var_to_concrete_def: &mut HashMap<Type, TypeAnnotation>,
|
type_var_to_concrete_def: &mut HashMap<Type, TypeAnnotation>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), HashSet<String>> {
|
||||||
let (
|
let (
|
||||||
_class_id,
|
_class_id,
|
||||||
class_ancestor_def,
|
class_ancestor_def,
|
||||||
|
@ -1420,9 +1441,11 @@ impl TopLevelComposer {
|
||||||
type_var_to_concrete_def,
|
type_var_to_concrete_def,
|
||||||
);
|
);
|
||||||
if !ok {
|
if !ok {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"method {class_method_name} has same name as ancestors' method, but incompatible type"
|
format!(
|
||||||
));
|
"method {class_method_name} has same name as ancestors' method, but incompatible type"
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
// mark it as added
|
// mark it as added
|
||||||
is_override.insert(*class_method_name);
|
is_override.insert(*class_method_name);
|
||||||
|
@ -1457,9 +1480,11 @@ impl TopLevelComposer {
|
||||||
// find if there is a fields with the same name in the child class
|
// find if there is a fields with the same name in the child class
|
||||||
for (class_field_name, ..) in &*class_fields_def {
|
for (class_field_name, ..) in &*class_fields_def {
|
||||||
if class_field_name == anc_field_name {
|
if class_field_name == anc_field_name {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"field `{class_field_name}` has already declared in the ancestor classes"
|
format!(
|
||||||
));
|
"field `{class_field_name}` has already declared in the ancestor classes"
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_child_fields.push(to_be_added);
|
new_child_fields.push(to_be_added);
|
||||||
|
@ -1483,7 +1508,7 @@ impl TopLevelComposer {
|
||||||
|
|
||||||
/// step 5, analyze and call type inferencer to fill the `instance_to_stmt` of
|
/// step 5, analyze and call type inferencer to fill the `instance_to_stmt` of
|
||||||
/// [`TopLevelDef::Function`]
|
/// [`TopLevelDef::Function`]
|
||||||
fn analyze_function_instance(&mut self) -> Result<(), String> {
|
fn analyze_function_instance(&mut self) -> Result<(), HashSet<String>> {
|
||||||
// first get the class constructor type correct for the following type check in function body
|
// first get the class constructor type correct for the following type check in function body
|
||||||
// also do class field instantiation check
|
// also do class field instantiation check
|
||||||
let init_str_id = "__init__".into();
|
let init_str_id = "__init__".into();
|
||||||
|
@ -1588,9 +1613,9 @@ impl TopLevelComposer {
|
||||||
};
|
};
|
||||||
constructors.push((i, signature, definition_extension.len()));
|
constructors.push((i, signature, definition_extension.len()));
|
||||||
definition_extension.push((Arc::new(RwLock::new(cons_fun)), None));
|
definition_extension.push((Arc::new(RwLock::new(cons_fun)), None));
|
||||||
unifier.unify(constructor.unwrap(), signature).map_err(|e| {
|
unifier.unify(constructor.unwrap(), signature).map_err(|e| HashSet::from([
|
||||||
e.at(Some(ast.as_ref().unwrap().location)).to_display(unifier).to_string()
|
e.at(Some(ast.as_ref().unwrap().location)).to_display(unifier).to_string()
|
||||||
})?;
|
]))?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut init_id: Option<DefinitionId> = None;
|
let mut init_id: Option<DefinitionId> = None;
|
||||||
|
@ -1618,9 +1643,9 @@ impl TopLevelComposer {
|
||||||
ret: self_type,
|
ret: self_type,
|
||||||
vars: contor_type_vars,
|
vars: contor_type_vars,
|
||||||
}));
|
}));
|
||||||
unifier.unify(constructor.unwrap(), contor_type).map_err(|e| {
|
unifier.unify(constructor.unwrap(), contor_type).map_err(|e| HashSet::from([
|
||||||
e.at(Some(ast.as_ref().unwrap().location)).to_display(unifier).to_string()
|
e.at(Some(ast.as_ref().unwrap().location)).to_display(unifier).to_string()
|
||||||
})?;
|
]))?;
|
||||||
|
|
||||||
// class field instantiation check
|
// class field instantiation check
|
||||||
if let (Some(init_id), false) = (init_id, fields.is_empty()) {
|
if let (Some(init_id), false) = (init_id, fields.is_empty()) {
|
||||||
|
@ -1632,12 +1657,14 @@ impl TopLevelComposer {
|
||||||
let all_inited = Self::get_all_assigned_field(body.as_slice())?;
|
let all_inited = Self::get_all_assigned_field(body.as_slice())?;
|
||||||
for (f, _, _) in fields {
|
for (f, _, _) in fields {
|
||||||
if !all_inited.contains(f) {
|
if !all_inited.contains(f) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"fields `{}` of class `{}` not fully initialized in the initializer (at {})",
|
format!(
|
||||||
f,
|
"fields `{}` of class `{}` not fully initialized in the initializer (at {})",
|
||||||
class_name,
|
f,
|
||||||
body[0].location,
|
class_name,
|
||||||
));
|
body[0].location,
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1650,11 +1677,11 @@ impl TopLevelComposer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Err(e) = analyze(i, def, ast) {
|
if let Err(e) = analyze(i, def, ast) {
|
||||||
errors.insert(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into_iter().sorted().join("\n---------\n"));
|
return Err(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, signature, id) in constructors {
|
for (i, signature, id) in constructors {
|
||||||
|
@ -1869,9 +1896,9 @@ impl TopLevelComposer {
|
||||||
if let TypeEnum::TObj { obj_id, .. } = &*base {
|
if let TypeEnum::TObj { obj_id, .. } = &*base {
|
||||||
*obj_id
|
*obj_id
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Base type should be a class (at {loc})"
|
format!("Base type should be a class (at {loc})"),
|
||||||
));
|
]))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let subtype_id = {
|
let subtype_id = {
|
||||||
|
@ -1881,9 +1908,11 @@ impl TopLevelComposer {
|
||||||
} else {
|
} else {
|
||||||
let base_repr = inferencer.unifier.stringify(*base);
|
let base_repr = inferencer.unifier.stringify(*base);
|
||||||
let subtype_repr = inferencer.unifier.stringify(*subtype);
|
let subtype_repr = inferencer.unifier.stringify(*subtype);
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"
|
format!(
|
||||||
));
|
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let subtype_entry = defs[subtype_id.0].read();
|
let subtype_entry = defs[subtype_id.0].read();
|
||||||
|
@ -1893,9 +1922,11 @@ impl TopLevelComposer {
|
||||||
if m.is_none() {
|
if m.is_none() {
|
||||||
let base_repr = inferencer.unifier.stringify(*base);
|
let base_repr = inferencer.unifier.stringify(*base);
|
||||||
let subtype_repr = inferencer.unifier.stringify(*subtype);
|
let subtype_repr = inferencer.unifier.stringify(*subtype);
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"
|
format!(
|
||||||
));
|
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
|
@ -1918,12 +1949,14 @@ impl TopLevelComposer {
|
||||||
&mut |id| format!("typevar{id}"),
|
&mut |id| format!("typevar{id}"),
|
||||||
&mut None,
|
&mut None,
|
||||||
);
|
);
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expected return type of `{}` in function `{}` (at {})",
|
format!(
|
||||||
ret_str,
|
"expected return type of `{}` in function `{}` (at {})",
|
||||||
name,
|
ret_str,
|
||||||
ast.as_ref().unwrap().location
|
name,
|
||||||
));
|
ast.as_ref().unwrap().location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
instance_to_stmt.insert(
|
instance_to_stmt.insert(
|
||||||
|
@ -1947,11 +1980,11 @@ impl TopLevelComposer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Err(e) = analyze_2(id, def, ast) {
|
if let Err(e) = analyze_2(id, def, ast) {
|
||||||
errors.insert(e);
|
errors.extend(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into_iter().sorted().join("\n----------\n"));
|
return Err(errors)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,13 +204,13 @@ impl TopLevelComposer {
|
||||||
pub fn get_class_method_def_info(
|
pub fn get_class_method_def_info(
|
||||||
class_methods_def: &[(StrRef, Type, DefinitionId)],
|
class_methods_def: &[(StrRef, Type, DefinitionId)],
|
||||||
method_name: StrRef,
|
method_name: StrRef,
|
||||||
) -> Result<(Type, DefinitionId), String> {
|
) -> Result<(Type, DefinitionId), HashSet<String>> {
|
||||||
for (name, ty, def_id) in class_methods_def {
|
for (name, ty, def_id) in class_methods_def {
|
||||||
if name == &method_name {
|
if name == &method_name {
|
||||||
return Ok((*ty, *def_id));
|
return Ok((*ty, *def_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(format!("no method {method_name} in the current class"))
|
Err(HashSet::from([format!("no method {method_name} in the current class")]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get all base class def id of a class, excluding itself. \
|
/// get all base class def id of a class, excluding itself. \
|
||||||
|
@ -221,7 +221,7 @@ impl TopLevelComposer {
|
||||||
pub fn get_all_ancestors_helper(
|
pub fn get_all_ancestors_helper(
|
||||||
child: &TypeAnnotation,
|
child: &TypeAnnotation,
|
||||||
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
|
||||||
) -> Result<Vec<TypeAnnotation>, String> {
|
) -> Result<Vec<TypeAnnotation>, HashSet<String>> {
|
||||||
let mut result: Vec<TypeAnnotation> = Vec::new();
|
let mut result: Vec<TypeAnnotation> = Vec::new();
|
||||||
let mut parent = Self::get_parent(child, temp_def_list);
|
let mut parent = Self::get_parent(child, temp_def_list);
|
||||||
while let Some(p) = parent {
|
while let Some(p) = parent {
|
||||||
|
@ -242,7 +242,7 @@ impl TopLevelComposer {
|
||||||
if no_cycle {
|
if no_cycle {
|
||||||
result.push(p);
|
result.push(p);
|
||||||
} else {
|
} else {
|
||||||
return Err("cyclic inheritance detected".into());
|
return Err(HashSet::from(["cyclic inheritance detected".into()]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@ -272,11 +272,13 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the `var_id` of a given `TVar` type
|
/// get the `var_id` of a given `TVar` type
|
||||||
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<u32, String> {
|
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<u32, HashSet<String>> {
|
||||||
if let TypeEnum::TVar { id, .. } = unifier.get_ty(var_ty).as_ref() {
|
if let TypeEnum::TVar { id, .. } = unifier.get_ty(var_ty).as_ref() {
|
||||||
Ok(*id)
|
Ok(*id)
|
||||||
} else {
|
} else {
|
||||||
Err("not type var".to_string())
|
Err(HashSet::from([
|
||||||
|
"not type var".to_string(),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +340,7 @@ impl TopLevelComposer {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_assigned_field(stmts: &[Stmt<()>]) -> Result<HashSet<StrRef>, String> {
|
pub fn get_all_assigned_field(stmts: &[Stmt<()>]) -> Result<HashSet<StrRef>, HashSet<String>> {
|
||||||
let mut result = HashSet::new();
|
let mut result = HashSet::new();
|
||||||
for s in stmts {
|
for s in stmts {
|
||||||
match &s.node {
|
match &s.node {
|
||||||
|
@ -355,10 +357,12 @@ impl TopLevelComposer {
|
||||||
}
|
}
|
||||||
} =>
|
} =>
|
||||||
{
|
{
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"redundant type annotation for class fields at {}",
|
format!(
|
||||||
s.location
|
"redundant type annotation for class fields at {}",
|
||||||
))
|
s.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
ast::StmtKind::Assign { targets, .. } => {
|
ast::StmtKind::Assign { targets, .. } => {
|
||||||
for t in targets {
|
for t in targets {
|
||||||
|
@ -410,7 +414,7 @@ impl TopLevelComposer {
|
||||||
pub fn parse_parameter_default_value(
|
pub fn parse_parameter_default_value(
|
||||||
default: &ast::Expr,
|
default: &ast::Expr,
|
||||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||||
) -> Result<SymbolValue, String> {
|
) -> Result<SymbolValue, HashSet<String>> {
|
||||||
parse_parameter_default_value(default, resolver)
|
parse_parameter_default_value(default, resolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,14 +471,14 @@ impl TopLevelComposer {
|
||||||
pub fn parse_parameter_default_value(
|
pub fn parse_parameter_default_value(
|
||||||
default: &ast::Expr,
|
default: &ast::Expr,
|
||||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
resolver: &(dyn SymbolResolver + Send + Sync),
|
||||||
) -> Result<SymbolValue, String> {
|
) -> Result<SymbolValue, HashSet<String>> {
|
||||||
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, String> {
|
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, HashSet<String>> {
|
||||||
match val {
|
match val {
|
||||||
Constant::Int(v) => {
|
Constant::Int(v) => {
|
||||||
if let Ok(v) = (*v).try_into() {
|
if let Ok(v) = (*v).try_into() {
|
||||||
Ok(SymbolValue::I32(v))
|
Ok(SymbolValue::I32(v))
|
||||||
} else {
|
} else {
|
||||||
Err(format!("integer value out of range at {loc}"))
|
Err(HashSet::from([format!("integer value out of range at {loc}")]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Constant::Float(v) => Ok(SymbolValue::Double(*v)),
|
Constant::Float(v) => Ok(SymbolValue::Double(*v)),
|
||||||
|
@ -482,9 +486,11 @@ pub fn parse_parameter_default_value(
|
||||||
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
|
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
|
||||||
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
|
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
|
||||||
)),
|
)),
|
||||||
Constant::None => Err(format!(
|
Constant::None => Err(HashSet::from([
|
||||||
"`None` is not supported, use `none` for option type instead ({loc})"
|
format!(
|
||||||
)),
|
"`None` is not supported, use `none` for option type instead ({loc})"
|
||||||
|
),
|
||||||
|
])),
|
||||||
_ => unimplemented!("this constant is not supported at {}", loc),
|
_ => unimplemented!("this constant is not supported at {}", loc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,37 +503,51 @@ pub fn parse_parameter_default_value(
|
||||||
let v: Result<i64, _> = (*v).try_into();
|
let v: Result<i64, _> = (*v).try_into();
|
||||||
match v {
|
match v {
|
||||||
Ok(v) => Ok(SymbolValue::I64(v)),
|
Ok(v) => Ok(SymbolValue::I64(v)),
|
||||||
_ => Err(format!("default param value out of range at {}", default.location)),
|
_ => Err(HashSet::from([
|
||||||
|
format!("default param value out of range at {}", default.location)
|
||||||
|
])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(format!("only allow constant integer here at {}", default.location))
|
_ => Err(HashSet::from([
|
||||||
|
format!("only allow constant integer here at {}", default.location),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
ast::ExprKind::Name { id, .. } if *id == "uint32".into() => match &args[0].node {
|
ast::ExprKind::Name { id, .. } if *id == "uint32".into() => match &args[0].node {
|
||||||
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
||||||
let v: Result<u32, _> = (*v).try_into();
|
let v: Result<u32, _> = (*v).try_into();
|
||||||
match v {
|
match v {
|
||||||
Ok(v) => Ok(SymbolValue::U32(v)),
|
Ok(v) => Ok(SymbolValue::U32(v)),
|
||||||
_ => Err(format!("default param value out of range at {}", default.location)),
|
_ => Err(HashSet::from([
|
||||||
|
format!("default param value out of range at {}", default.location),
|
||||||
|
])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(format!("only allow constant integer here at {}", default.location))
|
_ => Err(HashSet::from([
|
||||||
|
format!("only allow constant integer here at {}", default.location),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
ast::ExprKind::Name { id, .. } if *id == "uint64".into() => match &args[0].node {
|
ast::ExprKind::Name { id, .. } if *id == "uint64".into() => match &args[0].node {
|
||||||
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
|
||||||
let v: Result<u64, _> = (*v).try_into();
|
let v: Result<u64, _> = (*v).try_into();
|
||||||
match v {
|
match v {
|
||||||
Ok(v) => Ok(SymbolValue::U64(v)),
|
Ok(v) => Ok(SymbolValue::U64(v)),
|
||||||
_ => Err(format!("default param value out of range at {}", default.location)),
|
_ => Err(HashSet::from([
|
||||||
|
format!("default param value out of range at {}", default.location),
|
||||||
|
])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(format!("only allow constant integer here at {}", default.location))
|
_ => Err(HashSet::from([
|
||||||
|
format!("only allow constant integer here at {}", default.location),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(
|
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(
|
||||||
SymbolValue::OptionSome(
|
SymbolValue::OptionSome(
|
||||||
Box::new(parse_parameter_default_value(&args[0], resolver)?)
|
Box::new(parse_parameter_default_value(&args[0], resolver)?)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
_ => Err(format!("unsupported default parameter at {}", default.location)),
|
_ => Err(HashSet::from([
|
||||||
|
format!("unsupported default parameter at {}", default.location),
|
||||||
|
])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts
|
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts
|
||||||
|
@ -538,17 +558,21 @@ pub fn parse_parameter_default_value(
|
||||||
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
|
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
|
||||||
ast::ExprKind::Name { id, .. } => {
|
ast::ExprKind::Name { id, .. } => {
|
||||||
resolver.get_default_param_value(default).ok_or_else(
|
resolver.get_default_param_value(default).ok_or_else(
|
||||||
|| format!(
|
|| HashSet::from([
|
||||||
"`{}` cannot be used as a default parameter at {} \
|
format!(
|
||||||
(not primitive type, option or tuple / not defined?)",
|
"`{}` cannot be used as a default parameter at {} \
|
||||||
id,
|
(not primitive type, option or tuple / not defined?)",
|
||||||
default.location
|
id,
|
||||||
)
|
default.location
|
||||||
|
),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => Err(format!(
|
_ => Err(HashSet::from([
|
||||||
"unsupported default parameter (not primitive type, option or tuple) at {}",
|
format!(
|
||||||
default.location
|
"unsupported default parameter (not primitive type, option or tuple) at {}",
|
||||||
))
|
default.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,9 @@ impl SymbolResolver for Resolver {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||||
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
|
self.0.id_to_def.lock().get(&id).cloned()
|
||||||
|
.ok_or_else(|| HashSet::from(["Unknown identifier".to_string()]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_string_id(&self, _: &str) -> i32 {
|
fn get_string_id(&self, _: &str) -> i32 {
|
||||||
|
@ -551,9 +552,9 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
|
||||||
|
|
||||||
if let Err(msg) = composer.start_analysis(false) {
|
if let Err(msg) = composer.start_analysis(false) {
|
||||||
if print {
|
if print {
|
||||||
println!("{}", msg);
|
println!("{}", msg.iter().sorted().join("\n----------\n"));
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(res[0], msg);
|
assert_eq!(res[0], msg.iter().next().unwrap());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// skip 5 to skip primitives
|
// skip 5 to skip primitives
|
||||||
|
@ -735,9 +736,9 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
|
||||||
|
|
||||||
if let Err(msg) = composer.start_analysis(true) {
|
if let Err(msg) = composer.start_analysis(true) {
|
||||||
if print {
|
if print {
|
||||||
println!("{}", msg);
|
println!("{}", msg.iter().sorted().join("\n----------\n"));
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(res[0], msg);
|
assert_eq!(res[0], msg.iter().next().unwrap());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// skip 5 to skip primitives
|
// skip 5 to skip primitives
|
||||||
|
|
|
@ -82,7 +82,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
// the key stores the type_var of this topleveldef::class, we only need this field here
|
// the key stores the type_var of this topleveldef::class, we only need this field here
|
||||||
locked: HashMap<DefinitionId, Vec<Type>>,
|
locked: HashMap<DefinitionId, Vec<Type>>,
|
||||||
type_var: Option<Type>,
|
type_var: Option<Type>,
|
||||||
) -> Result<TypeAnnotation, String> {
|
) -> Result<TypeAnnotation, HashSet<String>> {
|
||||||
let name_handle = |id: &StrRef,
|
let name_handle = |id: &StrRef,
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
locked: HashMap<DefinitionId, Vec<Type>>| {
|
locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||||
|
@ -109,10 +109,12 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
|
||||||
type_vars.clone()
|
type_vars.clone()
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"function cannot be used as a type (at {})",
|
format!(
|
||||||
expr.location
|
"function cannot be used as a type (at {})",
|
||||||
));
|
expr.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
locked.get(&obj_id).unwrap().clone()
|
locked.get(&obj_id).unwrap().clone()
|
||||||
|
@ -120,11 +122,13 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
};
|
};
|
||||||
// check param number here
|
// check param number here
|
||||||
if !type_vars.is_empty() {
|
if !type_vars.is_empty() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expect {} type variable parameter but got 0 (at {})",
|
format!(
|
||||||
type_vars.len(),
|
"expect {} type variable parameter but got 0 (at {})",
|
||||||
expr.location,
|
type_vars.len(),
|
||||||
));
|
expr.location,
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] })
|
Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] })
|
||||||
} else if let Ok(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) {
|
} else if let Ok(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) {
|
||||||
|
@ -133,10 +137,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
unifier.unify(var, ty).unwrap();
|
unifier.unify(var, ty).unwrap();
|
||||||
Ok(TypeAnnotation::TypeVar(ty))
|
Ok(TypeAnnotation::TypeVar(ty))
|
||||||
} else {
|
} else {
|
||||||
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location))
|
Err(HashSet::from([
|
||||||
|
format!("`{}` is not a valid type annotation (at {})", id, expr.location),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location))
|
Err(HashSet::from([
|
||||||
|
format!("`{}` is not a valid type annotation (at {})", id, expr.location),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -147,7 +155,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
mut locked: HashMap<DefinitionId, Vec<Type>>| {
|
mut locked: HashMap<DefinitionId, Vec<Type>>| {
|
||||||
if ["virtual".into(), "Generic".into(), "list".into(), "tuple".into(), "Option".into()].contains(id)
|
if ["virtual".into(), "Generic".into(), "list".into(), "tuple".into(), "Option".into()].contains(id)
|
||||||
{
|
{
|
||||||
return Err(format!("keywords cannot be class name (at {})", expr.location));
|
return Err(HashSet::from([
|
||||||
|
format!("keywords cannot be class name (at {})", expr.location),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
let obj_id = resolver.get_identifier_def(*id)?;
|
let obj_id = resolver.get_identifier_def(*id)?;
|
||||||
let type_vars = {
|
let type_vars = {
|
||||||
|
@ -170,12 +180,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
vec![slice]
|
vec![slice]
|
||||||
};
|
};
|
||||||
if type_vars.len() != params_ast.len() {
|
if type_vars.len() != params_ast.len() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expect {} type parameters but got {} (at {})",
|
format!(
|
||||||
type_vars.len(),
|
"expect {} type parameters but got {} (at {})",
|
||||||
params_ast.len(),
|
type_vars.len(),
|
||||||
params_ast[0].location,
|
params_ast.len(),
|
||||||
));
|
params_ast[0].location,
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
let result = params_ast
|
let result = params_ast
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -201,11 +213,12 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
if no_type_var {
|
if no_type_var {
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"application of type vars to generic class \
|
format!(
|
||||||
is not currently supported (at {})",
|
"application of type vars to generic class is not currently supported (at {})",
|
||||||
params_ast[0].location
|
params_ast[0].location
|
||||||
));
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
|
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
|
||||||
|
@ -311,7 +324,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
if let ast::ExprKind::Name { id, .. } = &value.node {
|
if let ast::ExprKind::Name { id, .. } = &value.node {
|
||||||
class_name_handle(id, slice, unifier, locked)
|
class_name_handle(id, slice, unifier, locked)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("unsupported expression type for class name (at {})", value.location))
|
Err(HashSet::from([
|
||||||
|
format!("unsupported expression type for class name (at {})", value.location)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,13 +339,16 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
};
|
};
|
||||||
let underlying_ty = underlying_ty[0];
|
let underlying_ty = underlying_ty[0];
|
||||||
|
|
||||||
let value = SymbolValue::from_constant(value, underlying_ty, primitives, unifier)?;
|
let value = SymbolValue::from_constant(value, underlying_ty, primitives, unifier)
|
||||||
|
.map_err(|err| HashSet::from([err]))?;
|
||||||
|
|
||||||
if matches!(value, SymbolValue::Str(_) | SymbolValue::Tuple(_) | SymbolValue::OptionSome(_)) {
|
if matches!(value, SymbolValue::Str(_) | SymbolValue::Tuple(_) | SymbolValue::OptionSome(_)) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expression {value} is not allowed for constant type annotation (at {})",
|
format!(
|
||||||
expr.location
|
"expression {value} is not allowed for constant type annotation (at {})",
|
||||||
))
|
expr.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(TypeAnnotation::Constant {
|
Ok(TypeAnnotation::Constant {
|
||||||
|
@ -339,7 +357,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Err(format!("unsupported expression for type annotation (at {})", expr.location)),
|
_ => Err(HashSet::from([
|
||||||
|
format!("unsupported expression for type annotation (at {})", expr.location),
|
||||||
|
])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +371,7 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
ann: &TypeAnnotation,
|
ann: &TypeAnnotation,
|
||||||
subst_list: &mut Option<Vec<Type>>
|
subst_list: &mut Option<Vec<Type>>
|
||||||
) -> Result<Type, String> {
|
) -> Result<Type, HashSet<String>> {
|
||||||
match ann {
|
match ann {
|
||||||
TypeAnnotation::CustomClass { id: obj_id, params } => {
|
TypeAnnotation::CustomClass { id: obj_id, params } => {
|
||||||
let def_read = top_level_defs[obj_id.0].read();
|
let def_read = top_level_defs[obj_id.0].read();
|
||||||
|
@ -361,11 +381,13 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
};
|
};
|
||||||
|
|
||||||
if type_vars.len() != params.len() {
|
if type_vars.len() != params.len() {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"unexpected number of type parameters: expected {} but got {}",
|
format!(
|
||||||
type_vars.len(),
|
"unexpected number of type parameters: expected {} but got {}",
|
||||||
params.len()
|
type_vars.len(),
|
||||||
))
|
params.len()
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
let param_ty = params
|
let param_ty = params
|
||||||
|
@ -401,16 +423,18 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
if ok {
|
if ok {
|
||||||
result.insert(*id, p);
|
result.insert(*id, p);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"cannot apply type {} to type variable with id {:?}",
|
format!(
|
||||||
unifier.internal_stringify(
|
"cannot apply type {} to type variable with id {:?}",
|
||||||
p,
|
unifier.internal_stringify(
|
||||||
&mut |id| format!("class{id}"),
|
p,
|
||||||
&mut |id| format!("typevar{id}"),
|
&mut |id| format!("class{id}"),
|
||||||
&mut None
|
&mut |id| format!("typevar{id}"),
|
||||||
),
|
&mut None
|
||||||
*id
|
),
|
||||||
));
|
*id
|
||||||
|
)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,11 +454,13 @@ pub fn get_type_from_type_annotation_kinds(
|
||||||
if ok {
|
if ok {
|
||||||
result.insert(*id, p);
|
result.insert(*id, p);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"cannot apply type {} to type variable {}",
|
format!(
|
||||||
unifier.stringify(p),
|
"cannot apply type {} to type variable {}",
|
||||||
name.unwrap_or_else(|| format!("typevar{id}").into()),
|
unifier.stringify(p),
|
||||||
))
|
name.unwrap_or_else(|| format!("typevar{id}").into()),
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,11 @@ use nac3parser::ast::{self, Constant, Expr, ExprKind, Operator::{LShift, RShift}
|
||||||
use std::{collections::HashSet, iter::once};
|
use std::{collections::HashSet, iter::once};
|
||||||
|
|
||||||
impl<'a> Inferencer<'a> {
|
impl<'a> Inferencer<'a> {
|
||||||
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), String> {
|
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), HashSet<String>> {
|
||||||
if matches!(expr.custom, Some(ty) if self.unifier.unioned(ty, self.primitives.none)) {
|
if matches!(expr.custom, Some(ty) if self.unifier.unioned(ty, self.primitives.none)) {
|
||||||
Err(format!("Error at {}: cannot have value none", expr.location))
|
Err(HashSet::from([
|
||||||
|
format!("Error at {}: cannot have value none", expr.location),
|
||||||
|
]))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -18,10 +20,11 @@ impl<'a> Inferencer<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: &Expr<Option<Type>>,
|
pattern: &Expr<Option<Type>>,
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), HashSet<String>> {
|
||||||
match &pattern.node {
|
match &pattern.node {
|
||||||
ExprKind::Name { id, .. } if id == &"none".into() =>
|
ExprKind::Name { id, .. } if id == &"none".into() => Err(HashSet::from([
|
||||||
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
|
format!("cannot assign to a `none` (at {})", pattern.location),
|
||||||
|
])),
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
if !defined_identifiers.contains(id) {
|
if !defined_identifiers.contains(id) {
|
||||||
defined_identifiers.insert(*id);
|
defined_identifiers.insert(*id);
|
||||||
|
@ -41,15 +44,19 @@ impl<'a> Inferencer<'a> {
|
||||||
self.should_have_value(value)?;
|
self.should_have_value(value)?;
|
||||||
self.check_expr(slice, defined_identifiers)?;
|
self.check_expr(slice, defined_identifiers)?;
|
||||||
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap()) {
|
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap()) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Error at {}: cannot assign to tuple element",
|
format!(
|
||||||
value.location
|
"Error at {}: cannot assign to tuple element",
|
||||||
));
|
value.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ExprKind::Constant { .. } => {
|
ExprKind::Constant { .. } => {
|
||||||
Err(format!("cannot assign to a constant (at {})", pattern.location))
|
Err(HashSet::from([
|
||||||
|
format!("cannot assign to a constant (at {})", pattern.location),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
_ => self.check_expr(pattern, defined_identifiers),
|
_ => self.check_expr(pattern, defined_identifiers),
|
||||||
}
|
}
|
||||||
|
@ -59,15 +66,17 @@ impl<'a> Inferencer<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
expr: &Expr<Option<Type>>,
|
expr: &Expr<Option<Type>>,
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), HashSet<String>> {
|
||||||
// there are some cases where the custom field is None
|
// there are some cases where the custom field is None
|
||||||
if let Some(ty) = &expr.custom {
|
if let Some(ty) = &expr.custom {
|
||||||
if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) && !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
|
if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) && !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expected concrete type at {} but got {}",
|
format!(
|
||||||
expr.location,
|
"expected concrete type at {} but got {}",
|
||||||
self.unifier.get_ty(*ty).get_type_name()
|
expr.location,
|
||||||
));
|
self.unifier.get_ty(*ty).get_type_name()
|
||||||
|
)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
|
@ -87,10 +96,12 @@ impl<'a> Inferencer<'a> {
|
||||||
self.defined_identifiers.insert(*id);
|
self.defined_identifiers.insert(*id);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"type error at identifier `{}` ({}) at {}",
|
format!(
|
||||||
id, e, expr.location
|
"type error at identifier `{}` ({}) at {}",
|
||||||
));
|
id, e, expr.location
|
||||||
|
)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,10 +132,12 @@ impl<'a> Inferencer<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if *rhs_val < 0 {
|
if *rhs_val < 0 {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"shift count is negative at {}",
|
format!(
|
||||||
right.location
|
"shift count is negative at {}",
|
||||||
));
|
right.location
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +213,7 @@ impl<'a> Inferencer<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
stmt: &Stmt<Option<Type>>,
|
stmt: &Stmt<Option<Type>>,
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, HashSet<String>> {
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
StmtKind::For { target, iter, body, orelse, .. } => {
|
StmtKind::For { target, iter, body, orelse, .. } => {
|
||||||
self.check_expr(iter, defined_identifiers)?;
|
self.check_expr(iter, defined_identifiers)?;
|
||||||
|
@ -307,11 +320,11 @@ impl<'a> Inferencer<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
block: &[Stmt<Option<Type>>],
|
block: &[Stmt<Option<Type>>],
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, HashSet<String>> {
|
||||||
let mut ret = false;
|
let mut ret = false;
|
||||||
for stmt in block {
|
for stmt in block {
|
||||||
if ret {
|
if ret {
|
||||||
println!("warning: dead code at {:?}\n", stmt.location);
|
eprintln!("warning: dead code at {:?}\n", stmt.location);
|
||||||
}
|
}
|
||||||
if self.check_stmt(stmt, defined_identifiers)? {
|
if self.check_stmt(stmt, defined_identifiers)? {
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
|
@ -64,19 +64,19 @@ pub struct Inferencer<'a> {
|
||||||
struct NaiveFolder();
|
struct NaiveFolder();
|
||||||
impl Fold<()> for NaiveFolder {
|
impl Fold<()> for NaiveFolder {
|
||||||
type TargetU = Option<Type>;
|
type TargetU = Option<Type>;
|
||||||
type Error = String;
|
type Error = HashSet<String>;
|
||||||
fn map_user(&mut self, (): ()) -> Result<Self::TargetU, Self::Error> {
|
fn map_user(&mut self, (): ()) -> Result<Self::TargetU, Self::Error> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_error<T>(msg: &str, location: Location) -> Result<T, String> {
|
fn report_error<T>(msg: &str, location: Location) -> Result<T, HashSet<String>> {
|
||||||
Err(format!("{msg} at {location}"))
|
Err(HashSet::from([format!("{msg} at {location}")]))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Fold<()> for Inferencer<'a> {
|
impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
type TargetU = Option<Type>;
|
type TargetU = Option<Type>;
|
||||||
type Error = String;
|
type Error = HashSet<String>;
|
||||||
|
|
||||||
fn map_user(&mut self, (): ()) -> Result<Self::TargetU, Self::Error> {
|
fn map_user(&mut self, (): ()) -> Result<Self::TargetU, Self::Error> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -159,9 +159,9 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
}
|
}
|
||||||
if let Some(old_typ) = self.variable_mapping.insert(name, typ) {
|
if let Some(old_typ) = self.variable_mapping.insert(name, typ) {
|
||||||
let loc = handler.location;
|
let loc = handler.location;
|
||||||
self.unifier.unify(old_typ, typ).map_err(|e| {
|
self.unifier.unify(old_typ, typ).map_err(|e| HashSet::from([
|
||||||
e.at(Some(loc)).to_display(self.unifier).to_string()
|
e.at(Some(loc)).to_display(self.unifier).to_string(),
|
||||||
})?;
|
]))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut type_ = naive_folder.fold_expr(*type_)?;
|
let mut type_ = naive_folder.fold_expr(*type_)?;
|
||||||
|
@ -274,7 +274,7 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
.collect();
|
.collect();
|
||||||
let loc = node.location;
|
let loc = node.location;
|
||||||
let targets = targets
|
let targets = targets
|
||||||
.map_err(|e| e.at(Some(loc)).to_display(self.unifier).to_string())?;
|
.map_err(|e| HashSet::from([e.at(Some(loc)).to_display(self.unifier).to_string()]))?;
|
||||||
return Ok(Located {
|
return Ok(Located {
|
||||||
location: node.location,
|
location: node.location,
|
||||||
node: ast::StmtKind::Assign {
|
node: ast::StmtKind::Assign {
|
||||||
|
@ -528,22 +528,24 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type InferenceResult = Result<Type, String>;
|
type InferenceResult = Result<Type, HashSet<String>>;
|
||||||
|
|
||||||
impl<'a> Inferencer<'a> {
|
impl<'a> Inferencer<'a> {
|
||||||
/// Constrain a <: b
|
/// Constrain a <: b
|
||||||
/// Currently implemented as unification
|
/// Currently implemented as unification
|
||||||
fn constrain(&mut self, a: Type, b: Type, location: &Location) -> Result<(), String> {
|
fn constrain(&mut self, a: Type, b: Type, location: &Location) -> Result<(), HashSet<String>> {
|
||||||
self.unify(a, b, location)
|
self.unify(a, b, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify(&mut self, a: Type, b: Type, location: &Location) -> Result<(), String> {
|
fn unify(&mut self, a: Type, b: Type, location: &Location) -> Result<(), HashSet<String>> {
|
||||||
self.unifier
|
self.unifier
|
||||||
.unify(a, b)
|
.unify(a, b)
|
||||||
.map_err(|e| e.at(Some(*location)).to_display(self.unifier).to_string())
|
.map_err(|e| HashSet::from([
|
||||||
|
e.at(Some(*location)).to_display(self.unifier).to_string(),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_pattern(&mut self, pattern: &ast::Expr<()>) -> Result<(), String> {
|
fn infer_pattern(&mut self, pattern: &ast::Expr<()>) -> Result<(), HashSet<String>> {
|
||||||
match &pattern.node {
|
match &pattern.node {
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
if !self.defined_identifiers.contains(id) {
|
if !self.defined_identifiers.contains(id) {
|
||||||
|
@ -592,9 +594,9 @@ impl<'a> Inferencer<'a> {
|
||||||
.map(|v| v.name)
|
.map(|v| v.name)
|
||||||
.rev()
|
.rev()
|
||||||
.collect();
|
.collect();
|
||||||
self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| {
|
self.unifier.unify_call(&call, ty, sign, &required).map_err(|e| HashSet::from([
|
||||||
e.at(Some(location)).to_display(self.unifier).to_string()
|
e.at(Some(location)).to_display(self.unifier).to_string(),
|
||||||
})?;
|
]))?;
|
||||||
return Ok(sign.ret);
|
return Ok(sign.ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,7 +625,7 @@ impl<'a> Inferencer<'a> {
|
||||||
location: Location,
|
location: Location,
|
||||||
args: Arguments,
|
args: Arguments,
|
||||||
body: ast::Expr<()>,
|
body: ast::Expr<()>,
|
||||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||||
if !args.posonlyargs.is_empty()
|
if !args.posonlyargs.is_empty()
|
||||||
|| args.vararg.is_some()
|
|| args.vararg.is_some()
|
||||||
|| !args.kwonlyargs.is_empty()
|
|| !args.kwonlyargs.is_empty()
|
||||||
|
@ -692,7 +694,7 @@ impl<'a> Inferencer<'a> {
|
||||||
location: Location,
|
location: Location,
|
||||||
elt: ast::Expr<()>,
|
elt: ast::Expr<()>,
|
||||||
mut generators: Vec<Comprehension>,
|
mut generators: Vec<Comprehension>,
|
||||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||||
if generators.len() != 1 {
|
if generators.len() != 1 {
|
||||||
return report_error(
|
return report_error(
|
||||||
"Only 1 generator statement for list comprehension is supported",
|
"Only 1 generator statement for list comprehension is supported",
|
||||||
|
@ -765,7 +767,7 @@ impl<'a> Inferencer<'a> {
|
||||||
func: ast::Expr<()>,
|
func: ast::Expr<()>,
|
||||||
mut args: Vec<ast::Expr<()>>,
|
mut args: Vec<ast::Expr<()>>,
|
||||||
keywords: Vec<Located<ast::KeywordData>>,
|
keywords: Vec<Located<ast::KeywordData>>,
|
||||||
) -> Result<ast::Expr<Option<Type>>, String> {
|
) -> Result<ast::Expr<Option<Type>>, HashSet<String>> {
|
||||||
let func =
|
let func =
|
||||||
if let Located { location: func_location, custom, node: ExprKind::Name { id, ctx } } =
|
if let Located { location: func_location, custom, node: ExprKind::Name { id, ctx } } =
|
||||||
func
|
func
|
||||||
|
@ -899,7 +901,9 @@ impl<'a> Inferencer<'a> {
|
||||||
.collect();
|
.collect();
|
||||||
self.unifier
|
self.unifier
|
||||||
.unify_call(&call, func.custom.unwrap(), sign, &required)
|
.unify_call(&call, func.custom.unwrap(), sign, &required)
|
||||||
.map_err(|e| e.at(Some(location)).to_display(self.unifier).to_string())?;
|
.map_err(|e| HashSet::from([
|
||||||
|
e.at(Some(location)).to_display(self.unifier).to_string(),
|
||||||
|
]))?;
|
||||||
return Ok(Located {
|
return Ok(Located {
|
||||||
location,
|
location,
|
||||||
custom: Some(sign.ret),
|
custom: Some(sign.ret),
|
||||||
|
@ -1073,8 +1077,11 @@ impl<'a> Inferencer<'a> {
|
||||||
) -> InferenceResult {
|
) -> InferenceResult {
|
||||||
let boolean = self.primitives.bool;
|
let boolean = self.primitives.bool;
|
||||||
for (a, b, c) in izip!(once(left).chain(comparators), comparators, ops) {
|
for (a, b, c) in izip!(once(left).chain(comparators), comparators, ops) {
|
||||||
let method =
|
let method = comparison_name(c)
|
||||||
comparison_name(c).ok_or_else(|| "unsupported comparator".to_string())?.into();
|
.ok_or_else(|| HashSet::from([
|
||||||
|
"unsupported comparator".to_string()
|
||||||
|
]))?
|
||||||
|
.into();
|
||||||
self.build_method_call(
|
self.build_method_call(
|
||||||
a.location,
|
a.location,
|
||||||
method,
|
method,
|
||||||
|
@ -1105,7 +1112,7 @@ impl<'a> Inferencer<'a> {
|
||||||
ExprKind::Constant { value: ast::Constant::Int(val), .. } => {
|
ExprKind::Constant { value: ast::Constant::Int(val), .. } => {
|
||||||
// the index is a constant, so value can be a sequence.
|
// the index is a constant, so value can be a sequence.
|
||||||
let ind: Option<i32> = (*val).try_into().ok();
|
let ind: Option<i32> = (*val).try_into().ok();
|
||||||
let ind = ind.ok_or_else(|| "Index must be int32".to_string())?;
|
let ind = ind.ok_or_else(|| HashSet::from(["Index must be int32".to_string()]))?;
|
||||||
let map = once((
|
let map = once((
|
||||||
ind.into(),
|
ind.into(),
|
||||||
RecordField::new(ty, ctx == &ExprContext::Store, Some(value.location)),
|
RecordField::new(ty, ctx == &ExprContext::Store, Some(value.location)),
|
||||||
|
|
|
@ -43,8 +43,9 @@ impl SymbolResolver for Resolver {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||||
self.id_to_def.get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
|
self.id_to_def.get(&id).cloned()
|
||||||
|
.ok_or_else(|| HashSet::from(["Unknown identifier".to_string()]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_string_id(&self, _: &str) -> i32 {
|
fn get_string_id(&self, _: &str) -> i32 {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use nac3core::{
|
||||||
use nac3parser::ast::{self, StrRef};
|
use nac3parser::ast::{self, StrRef};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub struct ResolverInternal {
|
pub struct ResolverInternal {
|
||||||
pub id_to_type: Mutex<HashMap<StrRef, Type>>,
|
pub id_to_type: Mutex<HashMap<StrRef, Type>>,
|
||||||
|
@ -61,8 +62,9 @@ impl SymbolResolver for Resolver {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
|
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
||||||
self.0.id_to_def.lock().get(&id).copied().ok_or_else(|| "Undefined identifier".to_string())
|
self.0.id_to_def.lock().get(&id).copied()
|
||||||
|
.ok_or_else(|| HashSet::from(["Undefined identifier".to_string()]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_string_id(&self, s: &str) -> i32 {
|
fn get_string_id(&self, s: &str) -> i32 {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use inkwell::{
|
||||||
};
|
};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use std::{collections::HashMap, fs, path::Path, sync::Arc};
|
use std::{collections::HashMap, fs, path::Path, sync::Arc};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use nac3core::{
|
use nac3core::{
|
||||||
codegen::{
|
codegen::{
|
||||||
|
@ -74,24 +75,28 @@ fn handle_typevar_definition(
|
||||||
def_list: &[Arc<RwLock<TopLevelDef>>],
|
def_list: &[Arc<RwLock<TopLevelDef>>],
|
||||||
unifier: &mut Unifier,
|
unifier: &mut Unifier,
|
||||||
primitives: &PrimitiveStore,
|
primitives: &PrimitiveStore,
|
||||||
) -> Result<Type, String> {
|
) -> Result<Type, HashSet<String>> {
|
||||||
let ExprKind::Call { func, args, .. } = &var.node else {
|
let ExprKind::Call { func, args, .. } = &var.node else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
format!(
|
||||||
))
|
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
||||||
|
),
|
||||||
|
]))
|
||||||
};
|
};
|
||||||
|
|
||||||
match &func.node {
|
match &func.node {
|
||||||
ExprKind::Name { id, .. } if id == &"TypeVar".into() => {
|
ExprKind::Name { id, .. } if id == &"TypeVar".into() => {
|
||||||
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
|
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
|
||||||
return Err(format!("Expected string constant for first parameter of `TypeVar`, got {:?}", &args[0].node))
|
return Err(HashSet::from([
|
||||||
|
format!("Expected string constant for first parameter of `TypeVar`, got {:?}", &args[0].node),
|
||||||
|
]))
|
||||||
};
|
};
|
||||||
let generic_name: StrRef = ty_name.to_string().into();
|
let generic_name: StrRef = ty_name.to_string().into();
|
||||||
|
|
||||||
let constraints = args
|
let constraints = args
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|x| -> Result<Type, String> {
|
.map(|x| -> Result<Type, HashSet<String>> {
|
||||||
let ty = parse_ast_to_type_annotation_kinds(
|
let ty = parse_ast_to_type_annotation_kinds(
|
||||||
resolver,
|
resolver,
|
||||||
def_list,
|
def_list,
|
||||||
|
@ -109,7 +114,9 @@ fn handle_typevar_definition(
|
||||||
let loc = func.location;
|
let loc = func.location;
|
||||||
|
|
||||||
if constraints.len() == 1 {
|
if constraints.len() == 1 {
|
||||||
return Err(format!("A single constraint is not allowed (at {loc})"))
|
return Err(HashSet::from([
|
||||||
|
format!("A single constraint is not allowed (at {loc})"),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(unifier.get_fresh_var_with_range(&constraints, Some(generic_name), Some(loc)).0)
|
Ok(unifier.get_fresh_var_with_range(&constraints, Some(generic_name), Some(loc)).0)
|
||||||
|
@ -117,14 +124,18 @@ fn handle_typevar_definition(
|
||||||
|
|
||||||
ExprKind::Name { id, .. } if id == &"ConstGeneric".into() => {
|
ExprKind::Name { id, .. } if id == &"ConstGeneric".into() => {
|
||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
return Err(format!("Expected 2 arguments for `ConstGeneric`, got {}", args.len()))
|
return Err(HashSet::from([
|
||||||
|
format!("Expected 2 arguments for `ConstGeneric`, got {}", args.len()),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
|
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
|
||||||
return Err(format!(
|
return Err(HashSet::from([
|
||||||
"Expected string constant for first parameter of `ConstGeneric`, got {:?}",
|
format!(
|
||||||
&args[0].node
|
"Expected string constant for first parameter of `ConstGeneric`, got {:?}",
|
||||||
))
|
&args[0].node
|
||||||
|
),
|
||||||
|
]))
|
||||||
};
|
};
|
||||||
let generic_name: StrRef = ty_name.to_string().into();
|
let generic_name: StrRef = ty_name.to_string().into();
|
||||||
|
|
||||||
|
@ -145,9 +156,11 @@ fn handle_typevar_definition(
|
||||||
Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).0)
|
Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).0)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Err(format!(
|
_ => Err(HashSet::from([
|
||||||
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
format!(
|
||||||
))
|
"expression {var:?} cannot be handled as a generic parameter in global scope"
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue