forked from M-Labs/nac3
1
0
Fork 0

nac3core: allow class to have no __init__, function/method name with module path added to ensure uniqueness

This commit is contained in:
ychenfo 2021-09-20 14:24:16 +08:00
parent 3c930ae9ab
commit dd1be541b8
5 changed files with 309 additions and 257 deletions

View File

@ -684,7 +684,11 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
unreachable!()
}
};
return self.gen_call(Some((value.custom.unwrap(), val)), (&signature, fun_id), params);
return self.gen_call(
Some((value.custom.unwrap(), val)),
(&signature, fun_id),
params,
);
}
_ => unimplemented!(),
}

View File

@ -17,11 +17,10 @@ pub struct TopLevelComposer {
// keyword list to prevent same user-defined name
pub keyword_list: HashSet<String>,
// to prevent duplicate definition
pub defined_class_name: HashSet<String>,
pub defined_class_method_name: HashSet<String>,
pub defined_function_name: HashSet<String>,
pub defined_names: HashSet<String>,
// get the class def id of a class method
pub method_class: HashMap<DefinitionId, DefinitionId>,
// number of built-in function and classes in the definition list, later skip
pub built_in_num: usize,
}
@ -52,7 +51,7 @@ impl TopLevelComposer {
};
let primitives_ty = primitives.0;
let mut unifier = primitives.1;
let keyword_list: HashSet<String> = HashSet::from_iter(vec![
let mut keyword_list: HashSet<String> = HashSet::from_iter(vec![
"Generic".into(),
"virtual".into(),
"list".into(),
@ -67,9 +66,7 @@ impl TopLevelComposer {
"Kernel".into(),
"KernelImmutable".into(),
]);
let mut defined_class_method_name: HashSet<String> = Default::default();
let mut defined_class_name: HashSet<String> = Default::default();
let mut defined_function_name: HashSet<String> = Default::default();
let defined_names: HashSet<String> = Default::default();
let method_class: HashMap<DefinitionId, DefinitionId> = Default::default();
let mut built_in_id: HashMap<String, DefinitionId> = Default::default();
@ -91,9 +88,7 @@ impl TopLevelComposer {
})),
None,
));
defined_class_method_name.insert(name.clone());
defined_class_name.insert(name.clone());
defined_function_name.insert(name);
keyword_list.insert(name);
}
(
@ -103,9 +98,7 @@ impl TopLevelComposer {
primitives_ty,
unifier,
keyword_list,
defined_class_method_name,
defined_class_name,
defined_function_name,
defined_names,
method_class,
},
built_in_id,
@ -119,7 +112,7 @@ impl TopLevelComposer {
self.definition_ast_list.iter().map(|(x, ..)| x.clone()).collect_vec(),
)
.into(),
// FIXME: all the big unifier or?
// NOTE: only one for now
unifiers: Arc::new(RwLock::new(vec![(
self.unifier.get_shared_unifier(),
self.primitives_ty,
@ -139,23 +132,21 @@ impl TopLevelComposer {
resolver: Option<Arc<Box<dyn SymbolResolver + Send + Sync>>>,
mod_path: String,
) -> Result<(String, DefinitionId, Option<Type>), String> {
let defined_class_name = &mut self.defined_class_name;
let defined_class_method_name = &mut self.defined_class_method_name;
let defined_function_name = &mut self.defined_function_name;
let defined_names = &mut self.defined_names;
match &ast.node {
ast::StmtKind::ClassDef { name, body, .. } => {
if self.keyword_list.contains(name) {
ast::StmtKind::ClassDef { name: class_name, body, .. } => {
if self.keyword_list.contains(class_name) {
return Err("cannot use keyword as a class name".into());
}
if !defined_class_name.insert({
if !defined_names.insert({
let mut n = mod_path.clone();
n.push_str(name.as_str());
n.push_str(class_name.as_str());
n
}) {
return Err("duplicate definition of class".into());
}
let class_name = name.to_string();
let class_name = class_name.clone();
let class_def_id = self.definition_ast_list.len();
// since later when registering class method, ast will still be used,
@ -165,8 +156,8 @@ impl TopLevelComposer {
Arc::new(RwLock::new(Self::make_top_level_class_def(
class_def_id,
resolver.clone(),
name,
Some(constructor_ty)
class_name.as_str(),
Some(constructor_ty),
))),
None,
);
@ -187,23 +178,27 @@ impl TopLevelComposer {
// we do not push anything to the def list, so we keep track of the index
// and then push in the correct order after the for loop
let mut class_method_index_offset = 0;
let mut has_init = false;
for b in body {
if let ast::StmtKind::FunctionDef { name: method_name, .. } = &b.node {
if self.keyword_list.contains(name) {
if self.keyword_list.contains(method_name) {
return Err("cannot use keyword as a method name".into());
}
let global_class_method_name =
Self::make_class_method_name(class_name.clone(), method_name);
if !defined_class_method_name.insert({
let mut n = mod_path.clone();
n.push_str(global_class_method_name.as_str());
n
}) {
return Err("duplicate class method definition".into());
if method_name.ends_with(|x: char| x.is_ascii_digit()) {
return Err(format!(
"function name `{}` must not end with numbers",
method_name
));
}
if method_name == "__init__" {
has_init = true;
let global_class_method_name = {
let mut n = mod_path.clone();
n.push_str(
Self::make_class_method_name(class_name.clone(), method_name)
.as_str(),
);
n
};
if !defined_names.insert(global_class_method_name.clone()) {
return Err("duplicate class method definition".into());
}
let method_def_id = self.definition_ast_list.len() + {
// plus 1 here since we already have the class def
@ -232,9 +227,6 @@ impl TopLevelComposer {
continue;
}
}
if !has_init {
return Err("class def must have __init__ method defined".into());
}
// move the ast to the entry of the class in the ast_list
class_def_ast.1 = Some(ast);
@ -261,12 +253,16 @@ impl TopLevelComposer {
if self.keyword_list.contains(name) {
return Err("cannot use keyword as a top level function name".into());
}
if name.ends_with(|x: char| x.is_ascii_digit()) {
return Err(format!("function name `{}` must not end with numbers", name));
}
let fun_name = name.to_string();
if !defined_function_name.insert({
let global_fun_name = {
let mut n = mod_path;
n.push_str(name.as_str());
n
}) {
};
if !defined_names.insert(global_fun_name.clone()) {
return Err("duplicate top level function define".into());
}
@ -274,8 +270,7 @@ impl TopLevelComposer {
// add to the definition list
self.definition_ast_list.push((
RwLock::new(Self::make_top_level_function_def(
// TODO: is this fun_name or the above name with mod_path?
name.into(),
global_fun_name,
name.into(),
// dummy here, unify with correct type later
ty_to_be_unified,
@ -824,7 +819,8 @@ impl TopLevelComposer {
let mut defined_fields: HashSet<String> = HashSet::new();
for b in class_body_ast {
if let ast::StmtKind::FunctionDef { args, returns, name, .. } = &b.node {
match &b.node {
ast::StmtKind::FunctionDef { args, returns, name, .. } => {
let (method_dummy_ty, method_id) =
Self::get_class_method_def_info(class_methods_def, name)?;
@ -905,7 +901,8 @@ impl TopLevelComposer {
};
// push the dummy type and the type annotation
// into the list for later unification
type_var_to_concrete_def.insert(dummy_func_arg.ty, type_ann.clone());
type_var_to_concrete_def
.insert(dummy_func_arg.ty, type_ann.clone());
result.push(dummy_func_arg)
}
}
@ -961,14 +958,15 @@ impl TopLevelComposer {
);
}
let method_type = unifier.add_ty(TypeEnum::TFunc(
FunSignature { args: arg_types, ret: ret_type, vars: method_var_map }.into(),
FunSignature { args: arg_types, ret: ret_type, vars: method_var_map }
.into(),
));
// unify now since function type is not in type annotation define
// which should be fine since type within method_type will be subst later
unifier.unify(method_dummy_ty, method_type)?;
} else if let ast::StmtKind::AnnAssign { target, annotation, value: None, .. } = &b.node
{
}
ast::StmtKind::AnnAssign { target, annotation, value: None, .. } => {
if let ast::ExprKind::Name { id: attr, .. } = &target.node {
if defined_fields.insert(attr.to_string()) {
let dummy_field_type = unifier.get_fresh_var().0;
@ -1015,11 +1013,14 @@ impl TopLevelComposer {
} else {
return Err("same class fields defined twice".into());
}
}
} else {
return Err("unsupported statement type in class definition body".into());
}
}
ast::StmtKind::Pass => {}
_ => return Err("unsupported statement type in class definition body".into()),
}
}
Ok(())
}
@ -1162,8 +1163,14 @@ impl TopLevelComposer {
for (id, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.built_in_num)
{
let mut function_def = def.write();
if let TopLevelDef::Function { instance_to_stmt, name, simple_name, signature, resolver, .. } =
&mut *function_def
if let TopLevelDef::Function {
instance_to_stmt,
name,
simple_name,
signature,
resolver,
..
} = &mut *function_def
{
if let TypeEnum::TFunc(func_sig) = self.unifier.get_ty(*signature).as_ref() {
let FunSignature { args, ret, vars } = &*func_sig.borrow();
@ -1181,11 +1188,13 @@ impl TopLevelComposer {
&ty_ann,
)?;
if simple_name == "__init__" {
let fn_type = self.unifier.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature {
let fn_type = self.unifier.add_ty(TypeEnum::TFunc(
RefCell::new(FunSignature {
args: args.clone(),
ret: self_ty,
vars: vars.clone()
})));
vars: vars.clone(),
}),
));
self.unifier.unify(fn_type, constructor.unwrap())?;
}
Some(self_ty)

View File

@ -93,7 +93,7 @@ impl TopLevelComposer {
index: usize,
resolver: Option<Arc<Box<dyn SymbolResolver + Send + Sync>>>,
name: &str,
constructor: Option<Type>
constructor: Option<Type>,
) -> TopLevelDef {
TopLevelDef::Class {
name: name.to_string(),

View File

@ -94,7 +94,7 @@ fn test_simple_register(source: Vec<&str>) {
let ast = parse_program(s).unwrap();
let ast = ast[0].clone();
composer.register_top_level(ast, None, "__main__".into()).unwrap();
composer.register_top_level(ast, None, "".into()).unwrap();
}
}
@ -142,7 +142,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
let ast = ast[0].clone();
let (id, def_id, ty) =
composer.register_top_level(ast, Some(resolver.clone()), "__main__".into()).unwrap();
composer.register_top_level(ast, Some(resolver.clone()), "".into()).unwrap();
internal_resolver.add_id_def(id.clone(), def_id);
if let Some(ty) = ty {
internal_resolver.add_id_type(id, ty);
@ -151,7 +151,8 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
composer.start_analysis(true).unwrap();
for (i, (def, _)) in composer.definition_ast_list.iter().skip(composer.built_in_num).enumerate() {
for (i, (def, _)) in composer.definition_ast_list.iter().skip(composer.built_in_num).enumerate()
{
let def = &*def.read();
if let TopLevelDef::Function { signature, name, .. } = def {
let ty_str =
@ -638,12 +639,23 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
"cyclic2"
)]
#[test_case(
vec![indoc! {"
vec![
indoc! {"
class A:
pass
"}
],
vec!["5: Class {\nname: \"A\",\ndef_id: DefinitionId(5),\nancestors: [CustomClassKind { id: DefinitionId(5), params: [] }],\nfields: [],\nmethods: [],\ntype_vars: []\n}"];
"simple pass in class"
)]
#[test_case(
vec![indoc! {"
class A:
def fun3(self):
pass
"}],
vec!["class def must have __init__ method defined"];
"err no __init__"
vec!["function name `fun3` must not end with numbers"];
"err fun end with number"
)]
#[test_case(
vec![indoc! {"
@ -755,7 +767,7 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
("G".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int64]),
],
&mut composer.unifier,
print
print,
);
let resolver = Arc::new(
Box::new(Resolver(internal_resolver.clone())) as Box<dyn SymbolResolver + Send + Sync>
@ -766,7 +778,7 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
let ast = ast[0].clone();
let (id, def_id, ty) = {
match composer.register_top_level(ast, Some(resolver.clone()), "__main__".into()) {
match composer.register_top_level(ast, Some(resolver.clone()), "".into()) {
Ok(x) => x,
Err(msg) => {
if print {
@ -792,7 +804,9 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
}
} else {
// skip 5 to skip primitives
for (i, (def, _)) in composer.definition_ast_list.iter().skip(composer.built_in_num).enumerate() {
for (i, (def, _)) in
composer.definition_ast_list.iter().skip(composer.built_in_num).enumerate()
{
let def = &*def.read();
if print {
@ -923,11 +937,18 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
let internal_resolver = make_internal_resolver_with_tvar(
vec![
("T".into(), vec![]),
("V".into(), vec![composer.primitives_ty.float, composer.primitives_ty.int32, composer.primitives_ty.int64]),
(
"V".into(),
vec![
composer.primitives_ty.float,
composer.primitives_ty.int32,
composer.primitives_ty.int64,
],
),
("G".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int64]),
],
&mut composer.unifier,
print
print,
);
let resolver = Arc::new(
Box::new(Resolver(internal_resolver.clone())) as Box<dyn SymbolResolver + Send + Sync>
@ -938,7 +959,7 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
let ast = ast[0].clone();
let (id, def_id, ty) = {
match composer.register_top_level(ast, Some(resolver.clone()), "__main__".into()) {
match composer.register_top_level(ast, Some(resolver.clone()), "".into()) {
Ok(x) => x,
Err(msg) => {
if print {
@ -965,11 +986,17 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
} else {
// skip 5 to skip primitives
let mut stringify_folder = TypeToStringFolder { unifier: &mut composer.unifier };
for (i, (def, _)) in composer.definition_ast_list.iter().skip(composer.built_in_num).enumerate() {
for (_i, (def, _)) in
composer.definition_ast_list.iter().skip(composer.built_in_num).enumerate()
{
let def = &*def.read();
if let TopLevelDef::Function { instance_to_stmt, name, .. } = def {
println!("=========`{}`: number of instances: {}===========", name, instance_to_stmt.len());
println!(
"=========`{}`: number of instances: {}===========",
name,
instance_to_stmt.len()
);
for inst in instance_to_stmt.iter() {
let ast = &inst.1.body;
for b in ast {
@ -983,25 +1010,29 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
}
}
fn make_internal_resolver_with_tvar(tvars: Vec<(String, Vec<Type>)>, unifier: &mut Unifier, print: bool) -> Arc<ResolverInternal> {
fn make_internal_resolver_with_tvar(
tvars: Vec<(String, Vec<Type>)>,
unifier: &mut Unifier,
print: bool,
) -> Arc<ResolverInternal> {
let res: Arc<ResolverInternal> = ResolverInternal {
id_to_def: Default::default(),
id_to_type: tvars
.into_iter()
.map(|(name, range)| (
name.clone(),
{
.map(|(name, range)| {
(name.clone(), {
let (ty, id) = unifier.get_fresh_var_with_range(range.as_slice());
if print {
println!("{}: {:?}, tvar{}", name, ty, id);
}
ty
}
))
})
})
.collect::<HashMap<_, _>>()
.into(),
class_names: Default::default()
}.into();
class_names: Default::default(),
}
.into();
if print {
println!();
}
@ -1009,7 +1040,7 @@ fn make_internal_resolver_with_tvar(tvars: Vec<(String, Vec<Type>)>, unifier: &m
}
struct TypeToStringFolder<'a> {
unifier: &'a mut Unifier
unifier: &'a mut Unifier,
}
impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
@ -1017,14 +1048,11 @@ impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
type Error = String;
fn map_user(&mut self, user: Option<Type>) -> Result<Self::TargetU, Self::Error> {
Ok(if let Some(ty) = user {
self.unifier.stringify(
ty,
&mut |id| format!("class{}", id.to_string()),
&mut |id| format!("tvar{}", id.to_string()),
)
self.unifier.stringify(ty, &mut |id| format!("class{}", id.to_string()), &mut |id| {
format!("tvar{}", id.to_string())
})
} else {
"None".into()
}
)
})
}
}

View File

@ -797,7 +797,12 @@ impl Unifier {
self.subst_impl(a, mapping, &mut HashMap::new())
}
fn subst_impl(&mut self, a: Type, mapping: &VarMap, cache: &mut HashMap<Type, Option<Type>>) -> Option<Type> {
fn subst_impl(
&mut self,
a: Type,
mapping: &VarMap,
cache: &mut HashMap<Type, Option<Type>>,
) -> Option<Type> {
use TypeVarMeta::*;
let cached = cache.get_mut(&a);
if let Some(cached) = cached {
@ -831,9 +836,9 @@ impl Unifier {
TypeEnum::TList { ty } => {
self.subst_impl(*ty, mapping, cache).map(|t| self.add_ty(TypeEnum::TList { ty: t }))
}
TypeEnum::TVirtual { ty } => {
self.subst_impl(*ty, mapping, cache).map(|t| self.add_ty(TypeEnum::TVirtual { ty: t }))
}
TypeEnum::TVirtual { ty } => self
.subst_impl(*ty, mapping, cache)
.map(|t| self.add_ty(TypeEnum::TVirtual { ty: t })),
TypeEnum::TObj { obj_id, fields, params } => {
// Type variables in field types must be present in the type parameter.
// If the mapping does not contain any type variables in the
@ -851,7 +856,8 @@ impl Unifier {
if need_subst {
cache.insert(a, None);
let obj_id = *obj_id;
let params = self.subst_map(&params, mapping, cache).unwrap_or_else(|| params.clone());
let params =
self.subst_map(&params, mapping, cache).unwrap_or_else(|| params.clone());
let fields = self
.subst_map(&fields.borrow(), mapping, cache)
.unwrap_or_else(|| fields.borrow().clone());
@ -897,7 +903,12 @@ impl Unifier {
}
}
fn subst_map<K>(&mut self, map: &Mapping<K>, mapping: &VarMap, cache: &mut HashMap<Type, Option<Type>>) -> Option<Mapping<K>>
fn subst_map<K>(
&mut self,
map: &Mapping<K>,
mapping: &VarMap,
cache: &mut HashMap<Type, Option<Type>>,
) -> Option<Mapping<K>>
where
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
{