forked from M-Labs/nac3
1
0
Fork 0

nac3core: toplevel all ancestors are put into the def list, disallow generic base class for now

This commit is contained in:
ychenfo 2021-08-30 12:05:01 +08:00
parent e2a9bdd8bc
commit 40e58d02ed
3 changed files with 99 additions and 61 deletions

View File

@ -84,31 +84,36 @@ impl TopLevelComposer {
Err(format!("no method {} in the current class", method_name))
}
/// get all base class def id of a class, including itself
pub fn get_all_base(
child: DefinitionId,
/// get all base class def id of a class, excluding itself. \
/// this function should called only after the direct parent is set
/// and before all the ancestors are set
/// and when we allow single inheritance \
/// the order of the returned list is from the child to the deepest ancestor
pub fn get_all_ancestors_helper(
child: &TypeAnnotation,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
) -> Vec<DefinitionId> {
let mut result: Vec<DefinitionId> = Vec::new();
let child_def = temp_def_list.get(child.0).unwrap();
let child_def = child_def.read();
let child_def = child_def.deref();
if let TopLevelDef::Class { ancestors, .. } = child_def {
for a in ancestors {
if let TypeAnnotation::CustomClassKind { id, .. } = a {
if *id != child {
result.extend(Self::get_all_base(*id, temp_def_list));
}
} else {
unreachable!("must be class type annotation type")
}
}
} else {
unreachable!("this function should only be called with class def id as parameter")
) -> Vec<TypeAnnotation> {
let mut result: Vec<TypeAnnotation> = Vec::new();
let mut parent = Self::get_parent(child, temp_def_list);
while let Some(p) = parent {
parent = Self::get_parent(&p, temp_def_list);
result.push(p);
}
result.push(child);
result
}
fn get_parent(
child: &TypeAnnotation,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
) -> Option<TypeAnnotation> {
let child_id =
if let TypeAnnotation::CustomClassKind { id, .. } = child { *id } else { return None };
let child_def = temp_def_list.get(child_id.0).unwrap();
let child_def = child_def.read();
if let TopLevelDef::Class { ancestors, .. } = &*child_def {
Some(ancestors[0].clone())
} else {
None
}
}
}

View File

@ -16,7 +16,7 @@ use itertools::{izip, Itertools};
use parking_lot::RwLock;
use rustpython_parser::ast::{self, Stmt};
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct DefinitionId(pub usize);
mod type_annotation;
@ -393,6 +393,8 @@ impl TopLevelComposer {
fn analyze_top_level_class_bases(&mut self) -> Result<(), String> {
let temp_def_list = self.extract_def_list();
let unifier = self.unifier.borrow_mut();
// first, only push direct parent into the list
for (class_def, class_ast) in self.definition_ast_list.iter_mut() {
let mut class_def = class_def.write();
let (class_bases, class_ancestors, class_resolver, class_id, class_type_vars) = {
@ -414,7 +416,6 @@ impl TopLevelComposer {
let class_resolver = class_resolver.as_ref().unwrap();
let class_resolver = class_resolver.deref();
// only allow single inheritance
let mut has_base = false;
for b in class_bases {
// type vars have already been handled, so skip on `Generic[...]`
@ -436,6 +437,8 @@ impl TopLevelComposer {
}
has_base = true;
// the function parse_ast_to make sure that no type var occured in
// bast_ty if it is a CustomClassKind
let base_ty = parse_ast_to_type_annotation_kinds(
class_resolver,
&temp_def_list,
@ -444,36 +447,8 @@ impl TopLevelComposer {
b,
)?;
if let TypeAnnotation::CustomClassKind { id, .. } = &base_ty {
// TODO: change a way to check to prevent cyclic base class
let all_base = Self::get_all_base(*id, &temp_def_list);
if all_base.contains(&class_id) {
return Err("cyclic base detected".into());
}
// NOTE: type vars occured in the base type annotation must be
// a subset of the class decalred type annotation
let base_type_var_ids = get_type_var_contained_in_type_annotation(&base_ty)
.into_iter()
.map(|x| {
if let TypeAnnotation::TypeVarKind(ty) = x {
get_var_id(ty, unifier)
} else {
unreachable!("must be type var")
}
})
.collect::<Result<HashSet<_>, _>>()?;
let class_generic_type_var_ids = class_type_vars
.iter()
.map(|x| get_var_id(*x, unifier))
.collect::<Result<HashSet<_>, _>>()?;
if class_generic_type_var_ids.is_superset(&base_type_var_ids) {
// TODO: this base confirmed
// in what order to push all the ancestors?
// class_ancestors.push(base_ty);
} else {
return Err("base class generic type parameter must be declared".into());
}
if let TypeAnnotation::CustomClassKind { .. } = &base_ty {
class_ancestors.push(base_ty);
} else {
return Err("class base declaration can only be custom class".into());
}
@ -481,8 +456,57 @@ impl TopLevelComposer {
// TODO: ancestors should include all bases, need to rewrite
// push self to the ancestors
class_ancestors.push(make_self_type_annotation(&temp_def_list, class_id)?)
// class_ancestors.push(make_self_type_annotation(&temp_def_list, class_id)?)
}
// second get all ancestors
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = Default::default();
for (class_def, class_ast) in self.definition_ast_list.iter_mut() {
let mut class_def = class_def.write();
let (class_ancestors, class_id) = {
if let TopLevelDef::Class { ancestors, object_id, .. } = class_def.deref_mut() {
if let Some(ast::Located { node: ast::StmtKind::ClassDef { .. }, .. }) =
class_ast
{
(ancestors, *object_id)
} else {
unreachable!("must be both class")
}
} else {
continue;
}
};
ancestors_store.insert(
class_id,
Self::get_all_ancestors_helper(&class_ancestors[0], temp_def_list.as_slice()),
);
}
// insert the ancestors to the def list
for (class_def, class_ast) in self.definition_ast_list.iter_mut() {
let mut class_def = class_def.write();
let (class_ancestors, class_id) = {
if let TopLevelDef::Class { ancestors, object_id, .. } = class_def.deref_mut() {
if let Some(ast::Located { node: ast::StmtKind::ClassDef { .. }, .. }) =
class_ast
{
(ancestors, *object_id)
} else {
unreachable!("must be both class")
}
} else {
continue;
}
};
let ans = ancestors_store.get_mut(&class_id).unwrap();
class_ancestors.append(ans);
// insert self type annotation
class_ancestors
.insert(0, make_self_type_annotation(temp_def_list.as_slice(), class_id)?);
}
Ok(())
}

View File

@ -95,7 +95,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
params_ast.len()
));
}
params_ast
let result = params_ast
.into_iter()
.map(|x| {
parse_ast_to_type_annotation_kinds(
@ -106,7 +106,19 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
x,
)
})
.collect::<Result<Vec<_>, _>>()?
.collect::<Result<Vec<_>, _>>()?;
// make sure the result do not contain any type vars
let no_type_var = result
.iter()
.all(|x| get_type_var_contained_in_type_annotation(x).is_empty());
if no_type_var {
result
} else {
return Err("application of type vars to generic class \
not currently supported"
.into());
}
};
// allow type var in class generic application list
@ -156,9 +168,6 @@ pub fn get_type_from_type_annotation_kinds(
.iter()
.map(|x| {
if let TypeEnum::TVar { id, .. } = unifier.get_ty(*x).as_ref() {
// this is for the class generic application,
// we only need the information for the copied type var
// associated with the class
*id
} else {
unreachable!()