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!() 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!(), _ => unimplemented!(),
} }

View File

@ -17,11 +17,10 @@ pub struct TopLevelComposer {
// keyword list to prevent same user-defined name // keyword list to prevent same user-defined name
pub keyword_list: HashSet<String>, pub keyword_list: HashSet<String>,
// to prevent duplicate definition // to prevent duplicate definition
pub defined_class_name: HashSet<String>, pub defined_names: HashSet<String>,
pub defined_class_method_name: HashSet<String>,
pub defined_function_name: HashSet<String>,
// get the class def id of a class method // get the class def id of a class method
pub method_class: HashMap<DefinitionId, DefinitionId>, pub method_class: HashMap<DefinitionId, DefinitionId>,
// number of built-in function and classes in the definition list, later skip
pub built_in_num: usize, pub built_in_num: usize,
} }
@ -52,7 +51,7 @@ impl TopLevelComposer {
}; };
let primitives_ty = primitives.0; let primitives_ty = primitives.0;
let mut unifier = primitives.1; 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(), "Generic".into(),
"virtual".into(), "virtual".into(),
"list".into(), "list".into(),
@ -67,9 +66,7 @@ impl TopLevelComposer {
"Kernel".into(), "Kernel".into(),
"KernelImmutable".into(), "KernelImmutable".into(),
]); ]);
let mut defined_class_method_name: HashSet<String> = Default::default(); let defined_names: HashSet<String> = Default::default();
let mut defined_class_name: HashSet<String> = Default::default();
let mut defined_function_name: HashSet<String> = Default::default();
let method_class: HashMap<DefinitionId, DefinitionId> = Default::default(); let method_class: HashMap<DefinitionId, DefinitionId> = Default::default();
let mut built_in_id: HashMap<String, DefinitionId> = Default::default(); let mut built_in_id: HashMap<String, DefinitionId> = Default::default();
@ -91,9 +88,7 @@ impl TopLevelComposer {
})), })),
None, None,
)); ));
defined_class_method_name.insert(name.clone()); keyword_list.insert(name);
defined_class_name.insert(name.clone());
defined_function_name.insert(name);
} }
( (
@ -103,9 +98,7 @@ impl TopLevelComposer {
primitives_ty, primitives_ty,
unifier, unifier,
keyword_list, keyword_list,
defined_class_method_name, defined_names,
defined_class_name,
defined_function_name,
method_class, method_class,
}, },
built_in_id, built_in_id,
@ -119,7 +112,7 @@ impl TopLevelComposer {
self.definition_ast_list.iter().map(|(x, ..)| x.clone()).collect_vec(), self.definition_ast_list.iter().map(|(x, ..)| x.clone()).collect_vec(),
) )
.into(), .into(),
// FIXME: all the big unifier or? // NOTE: only one for now
unifiers: Arc::new(RwLock::new(vec![( unifiers: Arc::new(RwLock::new(vec![(
self.unifier.get_shared_unifier(), self.unifier.get_shared_unifier(),
self.primitives_ty, self.primitives_ty,
@ -139,23 +132,21 @@ impl TopLevelComposer {
resolver: Option<Arc<Box<dyn SymbolResolver + Send + Sync>>>, resolver: Option<Arc<Box<dyn SymbolResolver + Send + Sync>>>,
mod_path: String, mod_path: String,
) -> Result<(String, DefinitionId, Option<Type>), String> { ) -> Result<(String, DefinitionId, Option<Type>), String> {
let defined_class_name = &mut self.defined_class_name; let defined_names = &mut self.defined_names;
let defined_class_method_name = &mut self.defined_class_method_name;
let defined_function_name = &mut self.defined_function_name;
match &ast.node { match &ast.node {
ast::StmtKind::ClassDef { name, body, .. } => { ast::StmtKind::ClassDef { name: class_name, body, .. } => {
if self.keyword_list.contains(name) { if self.keyword_list.contains(class_name) {
return Err("cannot use keyword as a class name".into()); 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(); let mut n = mod_path.clone();
n.push_str(name.as_str()); n.push_str(class_name.as_str());
n n
}) { }) {
return Err("duplicate definition of class".into()); 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(); let class_def_id = self.definition_ast_list.len();
// since later when registering class method, ast will still be used, // 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( Arc::new(RwLock::new(Self::make_top_level_class_def(
class_def_id, class_def_id,
resolver.clone(), resolver.clone(),
name, class_name.as_str(),
Some(constructor_ty) Some(constructor_ty),
))), ))),
None, None,
); );
@ -187,23 +178,27 @@ impl TopLevelComposer {
// we do not push anything to the def list, so we keep track of the index // 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 // and then push in the correct order after the for loop
let mut class_method_index_offset = 0; let mut class_method_index_offset = 0;
let mut has_init = false;
for b in body { for b in body {
if let ast::StmtKind::FunctionDef { name: method_name, .. } = &b.node { 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()); return Err("cannot use keyword as a method name".into());
} }
let global_class_method_name = if method_name.ends_with(|x: char| x.is_ascii_digit()) {
Self::make_class_method_name(class_name.clone(), method_name); return Err(format!(
if !defined_class_method_name.insert({ "function name `{}` must not end with numbers",
let mut n = mod_path.clone(); method_name
n.push_str(global_class_method_name.as_str()); ));
n
}) {
return Err("duplicate class method definition".into());
} }
if method_name == "__init__" { let global_class_method_name = {
has_init = true; 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() + { let method_def_id = self.definition_ast_list.len() + {
// plus 1 here since we already have the class def // plus 1 here since we already have the class def
@ -232,9 +227,6 @@ impl TopLevelComposer {
continue; 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 // move the ast to the entry of the class in the ast_list
class_def_ast.1 = Some(ast); class_def_ast.1 = Some(ast);
@ -261,12 +253,16 @@ impl TopLevelComposer {
if self.keyword_list.contains(name) { if self.keyword_list.contains(name) {
return Err("cannot use keyword as a top level function name".into()); 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(); let fun_name = name.to_string();
if !defined_function_name.insert({ let global_fun_name = {
let mut n = mod_path; let mut n = mod_path;
n.push_str(name.as_str()); n.push_str(name.as_str());
n n
}) { };
if !defined_names.insert(global_fun_name.clone()) {
return Err("duplicate top level function define".into()); return Err("duplicate top level function define".into());
} }
@ -274,8 +270,7 @@ impl TopLevelComposer {
// add to the definition list // add to the definition list
self.definition_ast_list.push(( self.definition_ast_list.push((
RwLock::new(Self::make_top_level_function_def( RwLock::new(Self::make_top_level_function_def(
// TODO: is this fun_name or the above name with mod_path? global_fun_name,
name.into(),
name.into(), name.into(),
// dummy here, unify with correct type later // dummy here, unify with correct type later
ty_to_be_unified, ty_to_be_unified,
@ -824,66 +819,110 @@ impl TopLevelComposer {
let mut defined_fields: HashSet<String> = HashSet::new(); let mut defined_fields: HashSet<String> = HashSet::new();
for b in class_body_ast { for b in class_body_ast {
if let ast::StmtKind::FunctionDef { args, returns, name, .. } = &b.node { match &b.node {
let (method_dummy_ty, method_id) = ast::StmtKind::FunctionDef { args, returns, name, .. } => {
Self::get_class_method_def_info(class_methods_def, name)?; let (method_dummy_ty, method_id) =
Self::get_class_method_def_info(class_methods_def, name)?;
// the method var map can surely include the class's generic parameters // the method var map can surely include the class's generic parameters
let mut method_var_map: HashMap<u32, Type> = class_type_vars_def let mut method_var_map: HashMap<u32, Type> = class_type_vars_def
.iter() .iter()
.map(|ty| { .map(|ty| {
if let TypeEnum::TVar { id, .. } = unifier.get_ty(*ty).as_ref() { if let TypeEnum::TVar { id, .. } = unifier.get_ty(*ty).as_ref() {
(*id, *ty) (*id, *ty)
} else { } else {
unreachable!("must be type var here") unreachable!("must be type var here")
}
})
.collect();
let arg_types: Vec<FuncArg> = {
// check method parameters cannot have same name
let mut defined_paramter_name: HashSet<String> = HashSet::new();
let have_unique_fuction_parameter_name = args.args.iter().all(|x| {
defined_paramter_name.insert(x.node.arg.clone())
&& (!keyword_list.contains(&x.node.arg) || x.node.arg == "self")
});
if !have_unique_fuction_parameter_name {
return Err("class method must have unique parameter names \
and names thould not be the same as the keywords"
.into());
}
if name == "__init__" && !defined_paramter_name.contains("self") {
return Err("__init__ function must have a `self` parameter".into());
}
if !defined_paramter_name.contains("self") {
return Err("currently does not support static method".into());
} }
})
.collect();
let arg_types: Vec<FuncArg> = { let mut result = Vec::new();
// check method parameters cannot have same name for x in &args.args {
let mut defined_paramter_name: HashSet<String> = HashSet::new(); let name = x.node.arg.clone();
let have_unique_fuction_parameter_name = args.args.iter().all(|x| { if name != "self" {
defined_paramter_name.insert(x.node.arg.clone()) let type_ann = {
&& (!keyword_list.contains(&x.node.arg) || x.node.arg == "self") let annotation_expr = x
}); .node
if !have_unique_fuction_parameter_name { .annotation
return Err("class method must have unique parameter names \ .as_ref()
and names thould not be the same as the keywords" .ok_or_else(|| "type annotation needed".to_string())?
.into()); .as_ref();
} parse_ast_to_type_annotation_kinds(
if name == "__init__" && !defined_paramter_name.contains("self") { class_resolver.as_ref(),
return Err("__init__ function must have a `self` parameter".into()); temp_def_list,
} unifier,
if !defined_paramter_name.contains("self") { primitives,
return Err("currently does not support static method".into()); annotation_expr,
} vec![(class_id, class_type_vars_def.clone())]
.into_iter()
.collect(),
)?
};
// find type vars within this method parameter type annotation
let type_vars_within =
get_type_var_contained_in_type_annotation(&type_ann);
// handle the class type var and the method type var
for type_var_within in type_vars_within {
if let TypeAnnotation::TypeVarKind(ty) = type_var_within {
let id = Self::get_var_id(ty, unifier)?;
if let Some(prev_ty) = method_var_map.insert(id, ty) {
// if already in the list, make sure they are the same?
assert_eq!(prev_ty, ty);
}
} else {
unreachable!("must be type var annotation");
}
}
// finish handling type vars
let dummy_func_arg = FuncArg {
name,
ty: unifier.get_fresh_var().0,
// TODO: default value?
default_value: None,
};
// 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());
result.push(dummy_func_arg)
}
}
result
};
let mut result = Vec::new(); let ret_type = {
for x in &args.args { if let Some(result) = returns {
let name = x.node.arg.clone(); let result = result.as_ref();
if name != "self" { let annotation = parse_ast_to_type_annotation_kinds(
let type_ann = { class_resolver.as_ref(),
let annotation_expr = x temp_def_list,
.node unifier,
.annotation primitives,
.as_ref() result,
.ok_or_else(|| "type annotation needed".to_string())? vec![(class_id, class_type_vars_def.clone())].into_iter().collect(),
.as_ref(); )?;
parse_ast_to_type_annotation_kinds( // find type vars within this return type annotation
class_resolver.as_ref(),
temp_def_list,
unifier,
primitives,
annotation_expr,
vec![(class_id, class_type_vars_def.clone())]
.into_iter()
.collect(),
)?
};
// find type vars within this method parameter type annotation
let type_vars_within = let type_vars_within =
get_type_var_contained_in_type_annotation(&type_ann); get_type_var_contained_in_type_annotation(&annotation);
// handle the class type var and the method type var // handle the class type var and the method type var
for type_var_within in type_vars_within { for type_var_within in type_vars_within {
if let TypeAnnotation::TypeVarKind(ty) = type_var_within { if let TypeAnnotation::TypeVarKind(ty) = type_var_within {
@ -896,128 +935,90 @@ impl TopLevelComposer {
unreachable!("must be type var annotation"); unreachable!("must be type var annotation");
} }
} }
// finish handling type vars let dummy_return_type = unifier.get_fresh_var().0;
let dummy_func_arg = FuncArg { type_var_to_concrete_def.insert(dummy_return_type, annotation.clone());
name, dummy_return_type
ty: unifier.get_fresh_var().0, } else {
// TODO: default value? // if do not have return annotation, return none
default_value: None, // for uniform handling, still use type annoatation
}; let dummy_return_type = unifier.get_fresh_var().0;
// push the dummy type and the type annotation type_var_to_concrete_def.insert(
// into the list for later unification dummy_return_type,
type_var_to_concrete_def.insert(dummy_func_arg.ty, type_ann.clone()); TypeAnnotation::PrimitiveKind(primitives.none),
result.push(dummy_func_arg) );
dummy_return_type
} }
} };
result
};
let ret_type = { if let TopLevelDef::Function { var_id, .. } =
if let Some(result) = returns { temp_def_list.get(method_id.0).unwrap().write().deref_mut()
let result = result.as_ref(); {
let annotation = parse_ast_to_type_annotation_kinds( var_id.extend_from_slice(
class_resolver.as_ref(), method_var_map.keys().into_iter().copied().collect_vec().as_slice(),
temp_def_list,
unifier,
primitives,
result,
vec![(class_id, class_type_vars_def.clone())].into_iter().collect(),
)?;
// find type vars within this return type annotation
let type_vars_within =
get_type_var_contained_in_type_annotation(&annotation);
// handle the class type var and the method type var
for type_var_within in type_vars_within {
if let TypeAnnotation::TypeVarKind(ty) = type_var_within {
let id = Self::get_var_id(ty, unifier)?;
if let Some(prev_ty) = method_var_map.insert(id, ty) {
// if already in the list, make sure they are the same?
assert_eq!(prev_ty, ty);
}
} else {
unreachable!("must be type var annotation");
}
}
let dummy_return_type = unifier.get_fresh_var().0;
type_var_to_concrete_def.insert(dummy_return_type, annotation.clone());
dummy_return_type
} else {
// if do not have return annotation, return none
// for uniform handling, still use type annoatation
let dummy_return_type = unifier.get_fresh_var().0;
type_var_to_concrete_def.insert(
dummy_return_type,
TypeAnnotation::PrimitiveKind(primitives.none),
); );
dummy_return_type
} }
}; let method_type = unifier.add_ty(TypeEnum::TFunc(
FunSignature { args: arg_types, ret: ret_type, vars: method_var_map }
.into(),
));
if let TopLevelDef::Function { var_id, .. } = // unify now since function type is not in type annotation define
temp_def_list.get(method_id.0).unwrap().write().deref_mut() // which should be fine since type within method_type will be subst later
{ unifier.unify(method_dummy_ty, method_type)?;
var_id.extend_from_slice(
method_var_map.keys().into_iter().copied().collect_vec().as_slice(),
);
} }
let method_type = unifier.add_ty(TypeEnum::TFunc( ast::StmtKind::AnnAssign { target, annotation, value: None, .. } => {
FunSignature { args: arg_types, ret: ret_type, vars: method_var_map }.into(), 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;
class_fields_def.push((attr.to_string(), dummy_field_type));
// unify now since function type is not in type annotation define // handle Kernel[T], KernelImmutable[T]
// which should be fine since type within method_type will be subst later let annotation = {
unifier.unify(method_dummy_ty, method_type)?; match &annotation.as_ref().node {
} else if let ast::StmtKind::AnnAssign { target, annotation, value: None, .. } = &b.node ast::ExprKind::Subscript { value, slice, .. }
{ if {
if let ast::ExprKind::Name { id: attr, .. } = &target.node { matches!(&value.node, ast::ExprKind::Name { id, .. } if id == "Kernel" || id == "KernelImmutable")
if defined_fields.insert(attr.to_string()) { } =>
let dummy_field_type = unifier.get_fresh_var().0; {
class_fields_def.push((attr.to_string(), dummy_field_type)); slice
}
// handle Kernel[T], KernelImmutable[T] _ => annotation,
let annotation = {
match &annotation.as_ref().node {
ast::ExprKind::Subscript { value, slice, .. }
if {
matches!(&value.node, ast::ExprKind::Name { id, .. } if id == "Kernel" || id == "KernelImmutable")
} =>
{
slice
} }
_ => annotation, };
}
};
let annotation = parse_ast_to_type_annotation_kinds( let annotation = parse_ast_to_type_annotation_kinds(
class_resolver.as_ref(), class_resolver.as_ref(),
&temp_def_list, &temp_def_list,
unifier, unifier,
primitives, primitives,
annotation.as_ref(), annotation.as_ref(),
vec![(class_id, class_type_vars_def.clone())].into_iter().collect(), vec![(class_id, class_type_vars_def.clone())].into_iter().collect(),
)?; )?;
// find type vars within this return type annotation // find type vars within this return type annotation
let type_vars_within = let type_vars_within =
get_type_var_contained_in_type_annotation(&annotation); get_type_var_contained_in_type_annotation(&annotation);
// handle the class type var and the method type var // handle the class type var and the method type var
for type_var_within in type_vars_within { for type_var_within in type_vars_within {
if let TypeAnnotation::TypeVarKind(t) = type_var_within { if let TypeAnnotation::TypeVarKind(t) = type_var_within {
if !class_type_vars_def.contains(&t) { if !class_type_vars_def.contains(&t) {
return Err("class fields can only use type \ return Err("class fields can only use type \
vars declared as class generic type vars" vars declared as class generic type vars"
.into()); .into());
}
} else {
unreachable!("must be type var annotation");
} }
} else {
unreachable!("must be type var annotation");
} }
type_var_to_concrete_def.insert(dummy_field_type, annotation);
} else {
return Err("same class fields defined twice".into());
} }
type_var_to_concrete_def.insert(dummy_field_type, annotation);
} else { } else {
return Err("same class fields defined twice".into()); return Err("unsupported statement type in class definition body".into());
} }
} }
} else { ast::StmtKind::Pass => {}
return Err("unsupported statement type in class definition body".into()); _ => return Err("unsupported statement type in class definition body".into()),
} }
} }
Ok(()) Ok(())
@ -1162,10 +1163,16 @@ impl TopLevelComposer {
for (id, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.built_in_num) for (id, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.built_in_num)
{ {
let mut function_def = def.write(); let mut function_def = def.write();
if let TopLevelDef::Function { instance_to_stmt, name, simple_name, signature, resolver, .. } = if let TopLevelDef::Function {
&mut *function_def instance_to_stmt,
name,
simple_name,
signature,
resolver,
..
} = &mut *function_def
{ {
if let TypeEnum::TFunc(func_sig) = self.unifier.get_ty(*signature).as_ref() { if let TypeEnum::TFunc(func_sig) = self.unifier.get_ty(*signature).as_ref() {
let FunSignature { args, ret, vars } = &*func_sig.borrow(); let FunSignature { args, ret, vars } = &*func_sig.borrow();
// None if is not class method // None if is not class method
let self_type = { let self_type = {
@ -1181,11 +1188,13 @@ impl TopLevelComposer {
&ty_ann, &ty_ann,
)?; )?;
if simple_name == "__init__" { 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(
args: args.clone(), RefCell::new(FunSignature {
ret: self_ty, args: args.clone(),
vars: vars.clone() ret: self_ty,
}))); vars: vars.clone(),
}),
));
self.unifier.unify(fn_type, constructor.unwrap())?; self.unifier.unify(fn_type, constructor.unwrap())?;
} }
Some(self_ty) Some(self_ty)

View File

@ -93,7 +93,7 @@ impl TopLevelComposer {
index: usize, index: usize,
resolver: Option<Arc<Box<dyn SymbolResolver + Send + Sync>>>, resolver: Option<Arc<Box<dyn SymbolResolver + Send + Sync>>>,
name: &str, name: &str,
constructor: Option<Type> constructor: Option<Type>,
) -> TopLevelDef { ) -> TopLevelDef {
TopLevelDef::Class { TopLevelDef::Class {
name: name.to_string(), 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 = parse_program(s).unwrap();
let ast = ast[0].clone(); 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 ast = ast[0].clone();
let (id, def_id, ty) = 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); internal_resolver.add_id_def(id.clone(), def_id);
if let Some(ty) = ty { if let Some(ty) = ty {
internal_resolver.add_id_type(id, 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(); 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(); let def = &*def.read();
if let TopLevelDef::Function { signature, name, .. } = def { if let TopLevelDef::Function { signature, name, .. } = def {
let ty_str = let ty_str =
@ -637,13 +638,24 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
vec!["cyclic inheritance detected"]; vec!["cyclic inheritance detected"];
"cyclic2" "cyclic2"
)] )]
#[test_case(
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( #[test_case(
vec![indoc! {" vec![indoc! {"
class A: class A:
pass def fun3(self):
pass
"}], "}],
vec!["class def must have __init__ method defined"]; vec!["function name `fun3` must not end with numbers"];
"err no __init__" "err fun end with number"
)] )]
#[test_case( #[test_case(
vec![indoc! {" vec![indoc! {"
@ -749,13 +761,13 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
let mut composer: TopLevelComposer = Default::default(); let mut composer: TopLevelComposer = Default::default();
let internal_resolver = make_internal_resolver_with_tvar( let internal_resolver = make_internal_resolver_with_tvar(
vec![ vec![
("T".into(), vec![]), ("T".into(), vec![]),
("V".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int32]), ("V".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int32]),
("G".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int64]), ("G".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int64]),
], ],
&mut composer.unifier, &mut composer.unifier,
print print,
); );
let resolver = Arc::new( let resolver = Arc::new(
Box::new(Resolver(internal_resolver.clone())) as Box<dyn SymbolResolver + Send + Sync> 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 ast = ast[0].clone();
let (id, def_id, ty) = { 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, Ok(x) => x,
Err(msg) => { Err(msg) => {
if print { if print {
@ -792,7 +804,9 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
} }
} else { } else {
// skip 5 to skip primitives // 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(); let def = &*def.read();
if print { if print {
@ -921,13 +935,20 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
let mut composer: TopLevelComposer = Default::default(); let mut composer: TopLevelComposer = Default::default();
let internal_resolver = make_internal_resolver_with_tvar( let internal_resolver = make_internal_resolver_with_tvar(
vec![ vec![
("T".into(), 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]), ("G".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int64]),
], ],
&mut composer.unifier, &mut composer.unifier,
print print,
); );
let resolver = Arc::new( let resolver = Arc::new(
Box::new(Resolver(internal_resolver.clone())) as Box<dyn SymbolResolver + Send + Sync> 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 ast = ast[0].clone();
let (id, def_id, ty) = { 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, Ok(x) => x,
Err(msg) => { Err(msg) => {
if print { if print {
@ -964,12 +985,18 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
} }
} else { } else {
// skip 5 to skip primitives // skip 5 to skip primitives
let mut stringify_folder = TypeToStringFolder { unifier: &mut composer.unifier}; 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(); let def = &*def.read();
if let TopLevelDef::Function { instance_to_stmt, name, .. } = def { 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() { for inst in instance_to_stmt.iter() {
let ast = &inst.1.body; let ast = &inst.1.body;
for b in ast { 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 { let res: Arc<ResolverInternal> = ResolverInternal {
id_to_def: Default::default(), id_to_def: Default::default(),
id_to_type: tvars id_to_type: tvars
.into_iter() .into_iter()
.map(|(name, range)| ( .map(|(name, range)| {
name.clone(), (name.clone(), {
{
let (ty, id) = unifier.get_fresh_var_with_range(range.as_slice()); let (ty, id) = unifier.get_fresh_var_with_range(range.as_slice());
if print { if print {
println!("{}: {:?}, tvar{}", name, ty, id); println!("{}: {:?}, tvar{}", name, ty, id);
} }
ty ty
} })
)) })
.collect::<HashMap<_, _>>() .collect::<HashMap<_, _>>()
.into(), .into(),
class_names: Default::default() class_names: Default::default(),
}.into(); }
.into();
if print { if print {
println!(); println!();
} }
@ -1009,7 +1040,7 @@ fn make_internal_resolver_with_tvar(tvars: Vec<(String, Vec<Type>)>, unifier: &m
} }
struct TypeToStringFolder<'a> { struct TypeToStringFolder<'a> {
unifier: &'a mut Unifier unifier: &'a mut Unifier,
} }
impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> { impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
@ -1017,14 +1048,11 @@ impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
type Error = String; type Error = String;
fn map_user(&mut self, user: Option<Type>) -> Result<Self::TargetU, Self::Error> { fn map_user(&mut self, user: Option<Type>) -> Result<Self::TargetU, Self::Error> {
Ok(if let Some(ty) = user { Ok(if let Some(ty) = user {
self.unifier.stringify( self.unifier.stringify(ty, &mut |id| format!("class{}", id.to_string()), &mut |id| {
ty, format!("tvar{}", id.to_string())
&mut |id| format!("class{}", id.to_string()), })
&mut |id| format!("tvar{}", id.to_string()), } else {
) "None".into()
} else { })
"None".into()
}
)
} }
} }

View File

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