forked from M-Labs/nac3
nac3core: top level fix cyclic ancestor analysis, add tests
This commit is contained in:
parent
bbcec6ae6f
commit
247b364191
@ -1,5 +1,55 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
impl TopLevelDef {
|
||||||
|
pub fn to_string<F, G>(&self, unifier: &mut Unifier, obj_to_name: &mut F, var_to_name: &mut G) -> String
|
||||||
|
where
|
||||||
|
F: FnMut(usize) -> String,
|
||||||
|
G: FnMut(u32) -> String,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
TopLevelDef::Class {
|
||||||
|
name,
|
||||||
|
ancestors,
|
||||||
|
fields,
|
||||||
|
methods,
|
||||||
|
object_id,
|
||||||
|
type_vars,
|
||||||
|
..
|
||||||
|
} =>{
|
||||||
|
let fields_str = fields
|
||||||
|
.iter()
|
||||||
|
.map(|(n, ty)| (n.to_string(), unifier.stringify(*ty, obj_to_name, var_to_name)))
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let methods_str = methods
|
||||||
|
.iter()
|
||||||
|
.map(|(n, ty, id)|
|
||||||
|
(n.to_string(), unifier.stringify(*ty, obj_to_name, var_to_name), *id)
|
||||||
|
)
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"Class {{\nname: {:?},\ndef_id: {:?},\nancestors: {:?},\nfields: {:?},\nmethods: {:?},\ntype_vars: {:?}\n}}",
|
||||||
|
name,
|
||||||
|
object_id,
|
||||||
|
ancestors,
|
||||||
|
fields_str,
|
||||||
|
methods_str,
|
||||||
|
type_vars,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TopLevelDef::Function { name, signature, var_id, .. } =>
|
||||||
|
format!(
|
||||||
|
"Function {{\nname: {:?},\nsig: {:?},\nvar_id: {:?}\n}}",
|
||||||
|
name,
|
||||||
|
unifier.stringify(*signature, obj_to_name, var_to_name),
|
||||||
|
var_id
|
||||||
|
),
|
||||||
|
TopLevelDef::Initializer { class_id } => format!("Initializer {{ {:?} }}", class_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TopLevelComposer {
|
impl TopLevelComposer {
|
||||||
pub fn make_primitives() -> (PrimitiveStore, Unifier) {
|
pub fn make_primitives() -> (PrimitiveStore, Unifier) {
|
||||||
let mut unifier = Unifier::new();
|
let mut unifier = Unifier::new();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{borrow::BorrowMut, collections::{HashMap, HashSet}, iter::FromIterator, ops::{Deref, DerefMut}, sync::Arc};
|
use std::{borrow::BorrowMut, collections::{HashMap, HashSet}, fmt::Debug, iter::FromIterator, ops::{Deref, DerefMut}, sync::Arc};
|
||||||
|
|
||||||
use super::typecheck::type_inferencer::PrimitiveStore;
|
use super::typecheck::type_inferencer::PrimitiveStore;
|
||||||
use super::typecheck::typedef::{FunSignature, FuncArg, SharedUnifier, Type, TypeEnum, Unifier};
|
use super::typecheck::typedef::{FunSignature, FuncArg, SharedUnifier, Type, TypeEnum, Unifier};
|
||||||
@ -6,7 +6,7 @@ use crate::{
|
|||||||
symbol_resolver::SymbolResolver,
|
symbol_resolver::SymbolResolver,
|
||||||
typecheck::{type_inferencer::CodeLocation, typedef::CallId},
|
typecheck::{type_inferencer::CodeLocation, typedef::CallId},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::{Itertools, izip};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use rustpython_parser::ast::{self, Stmt};
|
use rustpython_parser::ast::{self, Stmt};
|
||||||
|
|
||||||
@ -104,11 +104,19 @@ impl TopLevelComposer {
|
|||||||
let primitives = Self::make_primitives();
|
let primitives = Self::make_primitives();
|
||||||
|
|
||||||
TopLevelComposer {
|
TopLevelComposer {
|
||||||
definition_ast_list: Default::default(),
|
definition_ast_list: {
|
||||||
|
let top_level_def_list = vec![
|
||||||
|
Arc::new(RwLock::new(Self::make_top_level_class_def(0, None, "int32"))),
|
||||||
|
Arc::new(RwLock::new(Self::make_top_level_class_def(1, None, "int64"))),
|
||||||
|
Arc::new(RwLock::new(Self::make_top_level_class_def(2, None, "float"))),
|
||||||
|
Arc::new(RwLock::new(Self::make_top_level_class_def(3, None, "bool"))),
|
||||||
|
Arc::new(RwLock::new(Self::make_top_level_class_def(4, None, "none"))),
|
||||||
|
];
|
||||||
|
let ast_list: Vec<Option<ast::Stmt<()>>> = vec![None, None, None, None, None];
|
||||||
|
izip!(top_level_def_list, ast_list).collect_vec()
|
||||||
|
},
|
||||||
primitives_ty: primitives.0,
|
primitives_ty: primitives.0,
|
||||||
unifier: primitives.1,
|
unifier: primitives.1,
|
||||||
// class_method_to_def_id: Default::default(),
|
|
||||||
// to_be_analyzed_class: Default::default(),
|
|
||||||
keyword_list: HashSet::from_iter(vec![
|
keyword_list: HashSet::from_iter(vec![
|
||||||
"Generic".into(),
|
"Generic".into(),
|
||||||
"virtual".into(),
|
"virtual".into(),
|
||||||
@ -303,7 +311,8 @@ impl TopLevelComposer {
|
|||||||
let unifier = self.unifier.borrow_mut();
|
let unifier = self.unifier.borrow_mut();
|
||||||
let primitives_store = &self.primitives_ty;
|
let primitives_store = &self.primitives_ty;
|
||||||
|
|
||||||
for (class_def, class_ast) in def_list {
|
// skip 5 to skip analyzing the primitives
|
||||||
|
for (class_def, class_ast) in def_list.iter().skip(5) {
|
||||||
// only deal with class def here
|
// only deal with class def here
|
||||||
let mut class_def = class_def.write();
|
let mut class_def = class_def.write();
|
||||||
let (class_bases_ast, class_def_type_vars, class_resolver) = {
|
let (class_bases_ast, class_def_type_vars, class_resolver) = {
|
||||||
@ -403,7 +412,8 @@ impl TopLevelComposer {
|
|||||||
let unifier = self.unifier.borrow_mut();
|
let unifier = self.unifier.borrow_mut();
|
||||||
|
|
||||||
// first, only push direct parent into the list
|
// first, only push direct parent into the list
|
||||||
for (class_def, class_ast) in self.definition_ast_list.iter_mut() {
|
// skip 5 to skip analyzing the primitives
|
||||||
|
for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(5) {
|
||||||
let mut class_def = class_def.write();
|
let mut class_def = class_def.write();
|
||||||
let (class_bases, class_ancestors, class_resolver) = {
|
let (class_bases, class_ancestors, class_resolver) = {
|
||||||
if let TopLevelDef::Class { ancestors, resolver, .. } = class_def.deref_mut() {
|
if let TopLevelDef::Class { ancestors, resolver, .. } = class_def.deref_mut() {
|
||||||
@ -463,10 +473,11 @@ impl TopLevelComposer {
|
|||||||
|
|
||||||
// second, get all ancestors
|
// second, get all ancestors
|
||||||
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = Default::default();
|
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = Default::default();
|
||||||
for (class_def, _) in self.definition_ast_list.iter_mut() {
|
// skip 5 to skip analyzing the primitives
|
||||||
let mut class_def = class_def.write();
|
for (class_def, _) in self.definition_ast_list.iter().skip(5) {
|
||||||
|
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.deref_mut() {
|
if let TopLevelDef::Class { ancestors, object_id, .. } = class_def.deref() {
|
||||||
(ancestors, *object_id)
|
(ancestors, *object_id)
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
@ -484,7 +495,8 @@ impl TopLevelComposer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// insert the ancestors to the def list
|
// insert the ancestors to the def list
|
||||||
for (class_def, _) in self.definition_ast_list.iter_mut() {
|
// skip 5 to skip analyzing the primitives
|
||||||
|
for (class_def, _) in self.definition_ast_list.iter_mut().skip(5) {
|
||||||
let mut class_def = class_def.write();
|
let mut class_def = class_def.write();
|
||||||
let (class_ancestors, class_id, class_type_vars) = {
|
let (class_ancestors, class_id, class_type_vars) = {
|
||||||
if let TopLevelDef::Class { ancestors, object_id, type_vars, .. } = class_def.deref_mut() {
|
if let TopLevelDef::Class { ancestors, object_id, type_vars, .. } = class_def.deref_mut() {
|
||||||
@ -514,7 +526,8 @@ impl TopLevelComposer {
|
|||||||
|
|
||||||
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new();
|
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new();
|
||||||
|
|
||||||
for (class_def, class_ast) in def_ast_list {
|
// skip 5 to skip analyzing the primitives
|
||||||
|
for (class_def, class_ast) in def_ast_list.iter().skip(5) {
|
||||||
if matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
|
if matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
|
||||||
Self::analyze_single_class_methods_fields(
|
Self::analyze_single_class_methods_fields(
|
||||||
class_def.clone(),
|
class_def.clone(),
|
||||||
@ -533,7 +546,7 @@ impl TopLevelComposer {
|
|||||||
loop {
|
loop {
|
||||||
let mut finished = true;
|
let mut finished = true;
|
||||||
|
|
||||||
for (class_def, _) in def_ast_list {
|
for (class_def, _) in def_ast_list.iter().skip(5) {
|
||||||
let mut class_def = class_def.write();
|
let mut class_def = class_def.write();
|
||||||
if let TopLevelDef::Class { ancestors, .. } = class_def.deref() {
|
if let TopLevelDef::Class { ancestors, .. } = class_def.deref() {
|
||||||
// if the length of the ancestor is equal to the current depth
|
// if the length of the ancestor is equal to the current depth
|
||||||
@ -580,7 +593,8 @@ impl TopLevelComposer {
|
|||||||
let unifier = self.unifier.borrow_mut();
|
let unifier = self.unifier.borrow_mut();
|
||||||
let primitives_store = &self.primitives_ty;
|
let primitives_store = &self.primitives_ty;
|
||||||
|
|
||||||
for (function_def, function_ast) in def_list {
|
// skip 5 to skip analyzing the primitives
|
||||||
|
for (function_def, function_ast) in def_list.iter().skip(5) {
|
||||||
let function_def = function_def.read();
|
let function_def = function_def.read();
|
||||||
let function_def = function_def.deref();
|
let function_def = function_def.deref();
|
||||||
let function_ast = if let Some(function_ast) = function_ast {
|
let function_ast = if let Some(function_ast) = function_ast {
|
||||||
@ -802,6 +816,9 @@ impl TopLevelComposer {
|
|||||||
and names thould not be the same as the keywords"
|
and names thould not be the same as the keywords"
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
if name == "__init__" && !defined_paramter_name.contains("self") {
|
||||||
|
return Err("class __init__ function must contain the `self` parameter".into());
|
||||||
|
}
|
||||||
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for x in &args.args {
|
for x in &args.args {
|
||||||
|
@ -145,27 +145,102 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
|
|||||||
#[test_case(
|
#[test_case(
|
||||||
vec![
|
vec![
|
||||||
indoc! {"
|
indoc! {"
|
||||||
class A:
|
class A():
|
||||||
def __init__():
|
def __init__(self):
|
||||||
|
self.a: int32 = 3
|
||||||
|
def fun(self, b: B):
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
indoc! {"
|
indoc! {"
|
||||||
class B(C):
|
class B(C):
|
||||||
def __init__():
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
indoc! {"
|
indoc! {"
|
||||||
class C(A):
|
class C(A):
|
||||||
def __init__():
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
def fun(self, b: B):
|
||||||
|
a = 1
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
indoc! {"
|
indoc! {"
|
||||||
def foo(a: A):
|
def foo(a: A):
|
||||||
pass
|
pass
|
||||||
"},
|
"},
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
indoc! {"5: Class {
|
||||||
|
name: \"A\",
|
||||||
|
def_id: DefinitionId(5),
|
||||||
|
ancestors: [CustomClassKind { id: DefinitionId(5), params: [] }],
|
||||||
|
fields: [(\"a\", \"0\")],
|
||||||
|
methods: [(\"__init__\", \"fn[[self=5], 5]\", DefinitionId(6)), (\"fun\", \"fn[[self=5, b=9], 4]\", DefinitionId(7))],
|
||||||
|
type_vars: []
|
||||||
|
}"},
|
||||||
|
|
||||||
|
indoc! {"6: Function {
|
||||||
|
name: \"A__init__\",
|
||||||
|
sig: \"fn[[self=5], 5]\",
|
||||||
|
var_id: []
|
||||||
|
}"},
|
||||||
|
|
||||||
|
indoc! {"7: Function {
|
||||||
|
name: \"Afun\",
|
||||||
|
sig: \"fn[[self=5, b=9], 4]\",
|
||||||
|
var_id: []
|
||||||
|
}"},
|
||||||
|
|
||||||
|
indoc! {"8: Initializer { DefinitionId(5) }"},
|
||||||
|
|
||||||
|
indoc! {"9: Class {
|
||||||
|
name: \"B\",
|
||||||
|
def_id: DefinitionId(9),
|
||||||
|
ancestors: [CustomClassKind { id: DefinitionId(9), params: [] }, CustomClassKind { id: DefinitionId(12), params: [] }, CustomClassKind { id: DefinitionId(5), params: [] }],
|
||||||
|
fields: [(\"a\", \"0\")],
|
||||||
|
methods: [(\"__init__\", \"fn[[self=9], 9]\", DefinitionId(10)), (\"fun\", \"fn[[self=12, b=9], 4]\", DefinitionId(14))],
|
||||||
|
type_vars: []
|
||||||
|
}"},
|
||||||
|
|
||||||
|
indoc! {"10: Function {
|
||||||
|
name: \"B__init__\",
|
||||||
|
sig: \"fn[[self=9], 9]\",
|
||||||
|
var_id: []
|
||||||
|
}"},
|
||||||
|
|
||||||
|
indoc! {"11: Initializer { DefinitionId(9) }"},
|
||||||
|
|
||||||
|
indoc! {"12: Class {
|
||||||
|
name: \"C\",
|
||||||
|
def_id: DefinitionId(12),
|
||||||
|
ancestors: [CustomClassKind { id: DefinitionId(12), params: [] }, CustomClassKind { id: DefinitionId(5), params: [] }],
|
||||||
|
fields: [(\"a\", \"0\")],
|
||||||
|
methods: [(\"__init__\", \"fn[[self=12], 12]\", DefinitionId(13)), (\"fun\", \"fn[[self=12, b=9], 4]\", DefinitionId(14))],
|
||||||
|
type_vars: []
|
||||||
|
}"},
|
||||||
|
|
||||||
|
indoc! {"13: Function {
|
||||||
|
name: \"C__init__\",
|
||||||
|
sig: \"fn[[self=12], 12]\",
|
||||||
|
var_id: []
|
||||||
|
}"},
|
||||||
|
|
||||||
|
indoc! {"14: Function {
|
||||||
|
name: \"Cfun\",
|
||||||
|
sig: \"fn[[self=12, b=9], 4]\",
|
||||||
|
var_id: []
|
||||||
|
}"},
|
||||||
|
|
||||||
|
indoc! {"15: Initializer { DefinitionId(12) }"},
|
||||||
|
|
||||||
|
indoc! {"16: Function {
|
||||||
|
name: \"foo\",
|
||||||
|
sig: \"fn[[a=5], 4]\",
|
||||||
|
var_id: []
|
||||||
|
}"},
|
||||||
]
|
]
|
||||||
)]
|
)]
|
||||||
fn test_simple_class_analyze(source: Vec<&str>) {
|
fn test_simple_class_analyze(source: Vec<&str>, res: Vec<&str>) {
|
||||||
let mut composer = TopLevelComposer::new();
|
let mut composer = TopLevelComposer::new();
|
||||||
|
|
||||||
let resolver = Arc::new(Mutex::new(Box::new(Resolver {
|
let resolver = Arc::new(Mutex::new(Box::new(Resolver {
|
||||||
@ -184,12 +259,29 @@ fn test_simple_class_analyze(source: Vec<&str>) {
|
|||||||
|
|
||||||
composer.start_analysis().unwrap();
|
composer.start_analysis().unwrap();
|
||||||
|
|
||||||
// for (i, (def, _)) in composer.definition_ast_list.into_iter().enumerate() {
|
// skip 5 to skip primitives
|
||||||
// let def = &*def.read();
|
for (i, (def, _)) in composer.definition_ast_list.iter().skip(5).enumerate() {
|
||||||
// if let TopLevelDef::Function { signature, name, .. } = def {
|
let def = &*def.read();
|
||||||
// let ty_str = composer.unifier.stringify(*signature, &mut |id| id.to_string(), &mut |id| id.to_string());
|
// println!(
|
||||||
// assert_eq!(ty_str, tys[i]);
|
// "{}: {}\n",
|
||||||
// assert_eq!(name, names[i]);
|
// i + 5,
|
||||||
// }
|
// def.to_string(
|
||||||
// }
|
// composer.unifier.borrow_mut(),
|
||||||
|
// &mut |id| id.to_string(),
|
||||||
|
// &mut |id| id.to_string()
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
assert_eq!(
|
||||||
|
format!(
|
||||||
|
"{}: {}",
|
||||||
|
i + 5,
|
||||||
|
def.to_string(
|
||||||
|
composer.unifier.borrow_mut(),
|
||||||
|
&mut |id| id.to_string(),
|
||||||
|
&mut |id| id.to_string()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
res[i]
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user