1
0
forked from M-Labs/nac3

nac3core: top level check inheritance method overload

This commit is contained in:
ychenfo 2021-08-30 17:38:07 +08:00
parent 82c2edcf8d
commit 098bd1e6e6
3 changed files with 182 additions and 14 deletions

View File

@ -1,4 +1,5 @@
use super::*;
use crate::typecheck::typedef::TypeVarMeta;
impl TopLevelComposer {
pub fn make_primitives() -> (PrimitiveStore, Unifier) {
@ -116,4 +117,49 @@ impl TopLevelComposer {
None
}
}
pub fn check_overload_type_compatible(unifier: &mut Unifier, ty: Type, other: Type) -> bool {
let ty = unifier.get_ty(ty);
let ty = ty.as_ref();
let other = unifier.get_ty(other);
let other = other.as_ref();
match (ty, other) {
(TypeEnum::TList { ty }, TypeEnum::TList { ty: other })
| (TypeEnum::TVirtual { ty }, TypeEnum::TVirtual { ty: other }) => {
Self::check_overload_type_compatible(unifier, *ty, *other)
}
(TypeEnum::TTuple { ty }, TypeEnum::TTuple { ty: other }) => ty
.iter()
.zip(other)
.all(|(ty, other)| Self::check_overload_type_compatible(unifier, *ty, *other)),
(
TypeEnum::TObj { obj_id, params, .. },
TypeEnum::TObj { obj_id: other_obj_id, params: other_params, .. },
) => {
let params = &*params.borrow();
let other_params = &*other_params.borrow();
obj_id.0 == other_obj_id.0
&& (params.iter().all(|(var_id, ty)| {
if let Some(other_ty) = other_params.get(var_id) {
Self::check_overload_type_compatible(unifier, *ty, *other_ty)
} else {
false
}
}))
}
(
TypeEnum::TVar { id, meta: TypeVarMeta::Generic, .. },
TypeEnum::TVar { id: other_id, meta: TypeVarMeta::Generic, .. },
) => {
// NOTE: directly compare var_id?
*id == *other_id
}
_ => false,
}
}
}

View File

@ -452,7 +452,7 @@ impl TopLevelComposer {
}
}
// second get all ancestors
// 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();
@ -513,7 +513,7 @@ impl TopLevelComposer {
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new();
for (class_def, class_ast) in def_ast_list {
Self::analyze_single_class(
Self::analyze_single_class_methods_fields(
class_def.clone(),
&class_ast.as_ref().unwrap().node,
&temp_def_list,
@ -531,10 +531,20 @@ impl TopLevelComposer {
unifier.unify(ty, target_ty)?;
}
// handle the inheritanced methods and fields
for (class_def, _) in def_ast_list {
Self::analyze_single_class_ancestors(
class_def.clone(),
&temp_def_list,
unifier,
primitives,
)?;
}
Ok(())
}
/// step 4, after class methods are done
/// step 4, after class methods are done, top level functions have nothing unknown
fn analyze_top_level_function(&mut self) -> Result<(), String> {
let def_list = &self.definition_ast_list;
let keyword_list = &self.keyword_list;
@ -682,13 +692,7 @@ impl TopLevelComposer {
Ok(())
}
/// step 5, field instantiation?
fn analyze_top_level_field_instantiation(&mut self) -> Result<(), String> {
// TODO:
unimplemented!()
}
fn analyze_single_class(
fn analyze_single_class_methods_fields(
class_def: Arc<RwLock<TopLevelDef>>,
class_ast: &ast::StmtKind<()>,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
@ -720,7 +724,7 @@ impl TopLevelComposer {
{
if let ast::StmtKind::ClassDef { name, bases, body, .. } = &class_ast {
(
object_id,
*object_id,
name.clone(),
bases,
body,
@ -828,7 +832,7 @@ impl TopLevelComposer {
};
type_var_to_concrete_def.insert(
dummy_func_arg.ty,
make_self_type_annotation(temp_def_list, *class_id)?,
make_self_type_annotation(temp_def_list, class_id)?,
);
result.push(dummy_func_arg);
}
@ -874,7 +878,7 @@ impl TopLevelComposer {
let dummy_return_type = unifier.get_fresh_var().0;
type_var_to_concrete_def.insert(
dummy_return_type,
make_self_type_annotation(temp_def_list, *class_id)?,
make_self_type_annotation(temp_def_list, class_id)?,
);
dummy_return_type
}
@ -943,4 +947,122 @@ impl TopLevelComposer {
}
Ok(())
}
fn analyze_single_class_ancestors(
class_def: Arc<RwLock<TopLevelDef>>,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> Result<(), String> {
let mut class_def = class_def.write();
let (
_class_id,
class_ancestor_def,
_class_fields_def,
class_methods_def,
_class_type_vars_def,
_class_resolver,
) = if let TopLevelDef::Class {
object_id,
ancestors,
fields,
methods,
resolver,
type_vars,
..
} = class_def.deref_mut()
{
(*object_id, ancestors, fields, methods, type_vars, resolver)
} else {
unreachable!("here must be class def ast");
};
for (method_name, method_ty, ..) in class_methods_def {
if method_name == "__init__" {
continue;
}
// search the ancestors from the nearest to the deepest to find overload and check
'search_for_overload: for anc in class_ancestor_def.iter().skip(1) {
if let TypeAnnotation::CustomClassKind { id, params } = anc {
let anc_class_def = temp_def_list.get(id.0).unwrap().read();
let anc_class_def = anc_class_def.deref();
if let TopLevelDef::Class { methods, type_vars, .. } = anc_class_def {
for (anc_method_name, anc_method_ty, ..) in methods {
// if same name, then is overload, needs check
if anc_method_name == method_name {
let param_ty = params
.iter()
.map(|x| {
get_type_from_type_annotation_kinds(
temp_def_list,
unifier,
primitives,
x,
)
})
.collect::<Result<Vec<_>, _>>()?;
let subst = type_vars
.iter()
.map(|x| {
if let TypeEnum::TVar { id, .. } =
unifier.get_ty(*x).as_ref()
{
*id
} else {
unreachable!()
}
})
.zip(param_ty.into_iter())
.collect::<HashMap<u32, Type>>();
let anc_method_ty = unifier.subst(*anc_method_ty, &subst).unwrap();
if let (
TypeEnum::TFunc(child_method_sig),
TypeEnum::TFunc(parent_method_sig),
) = (
unifier.get_ty(*method_ty).as_ref(),
unifier.get_ty(anc_method_ty).as_ref(),
) {
let (
FunSignature { args: c_as, ret: c_r, .. },
FunSignature { args: p_as, ret: p_r, .. },
) = (&*child_method_sig.borrow(), &*parent_method_sig.borrow());
// arguments
for (
FuncArg { name: c_name, ty: c_ty, .. },
FuncArg { name: p_name, ty: p_ty, .. },
) in c_as.iter().zip(p_as)
{
if c_name == "self" {
continue;
}
if c_name != p_name
|| !Self::check_overload_type_compatible(
unifier, *c_ty, *p_ty,
)
{
return Err("incompatible parameter".into());
}
}
// check the compatibility of c_r and p_r
if !Self::check_overload_type_compatible(unifier, *c_r, *p_r) {
return Err("incompatible parameter".into());
}
} else {
unreachable!("must be function type")
}
break 'search_for_overload;
}
}
}
}
}
}
Ok(())
}
}

View File

@ -156,7 +156,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
result
} else {
return Err("application of type vars to generic class \
not currently supported"
is not currently supported"
.into());
}
};