forked from M-Labs/nac3
1
0
Fork 0

nac3core: top level fields inheritance check, more tests

This commit is contained in:
ychenfo 2021-09-12 04:34:30 +08:00
parent 147298ff40
commit b419634f8a
2 changed files with 171 additions and 16 deletions

View File

@ -1114,25 +1114,26 @@ impl TopLevelComposer {
// handle class fields
let mut new_child_fields: Vec<(String, Type)> = Vec::new();
let mut is_override: HashSet<String> = HashSet::new();
// let mut is_override: HashSet<String> = HashSet::new();
for (anc_field_name, anc_field_ty) in fields {
let mut to_be_added = (anc_field_name.to_string(), *anc_field_ty);
let to_be_added = (anc_field_name.to_string(), *anc_field_ty);
// find if there is a fields with the same name in the child class
for (class_field_name, class_field_ty) in class_fields_def.iter() {
for (class_field_name, ..) in class_fields_def.iter() {
if class_field_name == anc_field_name {
let ok = Self::check_overload_field_type(
*class_field_ty,
*anc_field_ty,
unifier,
type_var_to_concrete_def,
);
if !ok {
return Err("fields has same name as ancestors' field, but incompatible type".into());
}
// mark it as added
is_override.insert(class_field_name.to_string());
to_be_added = (class_field_name.to_string(), *class_field_ty);
break;
// let ok = Self::check_overload_field_type(
// *class_field_ty,
// *anc_field_ty,
// unifier,
// type_var_to_concrete_def,
// );
// if !ok {
// return Err("fields has same name as ancestors' field, but incompatible type".into());
// }
// // mark it as added
// is_override.insert(class_field_name.to_string());
// to_be_added = (class_field_name.to_string(), *class_field_ty);
// break;
return Err(format!("field `{}` has already declared in the ancestor classes", class_field_name))
}
}
new_child_fields.push(to_be_added);

View File

@ -496,6 +496,109 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
];
"self1"
)]
#[test_case(
vec![
indoc! {"
class A(Generic[T]):
a: int32
b: T
c: A[int64]
def __init__(self, t: T):
self.a = 3
self.b = T
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
pass
def foo(self, c: C):
pass
"},
indoc! {"
class B(Generic[V], A[float]):
d: C
def __init__(self):
pass
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
# override
pass
"},
indoc! {"
class C(B[bool]):
e: int64
def __init__(self):
pass
"}
],
vec![
indoc! {"5: Class {
name: \"A\",
def_id: DefinitionId(5),
ancestors: [CustomClassKind { id: DefinitionId(5), params: [TypeVarKind(UnificationKey(100))] }],
fields: [(\"a\", \"class0\"), (\"b\", \"tvar2\"), (\"c\", \"class5[2->class1]\")],
methods: [(\"__init__\", \"fn[[t=tvar2], class4]\", DefinitionId(6)), (\"fun\", \"fn[[a=class0, b=tvar2], list[virtual[class10[3->class3]]]]\", DefinitionId(7)), (\"foo\", \"fn[[c=class14], class4]\", DefinitionId(8))],
type_vars: [UnificationKey(100)]
}"},
indoc! {"6: Function {
name: \"A__init__\",
sig: \"fn[[t=tvar2], class4]\",
var_id: [2]
}"},
indoc! {"7: Function {
name: \"Afun\",
sig: \"fn[[a=class0, b=tvar2], list[virtual[class10[3->class3]]]]\",
var_id: [2]
}"},
indoc! {"8: Function {
name: \"Afoo\",
sig: \"fn[[c=class14], class4]\",
var_id: [2]
}"},
indoc! {"9: Initializer { DefinitionId(5) }"},
indoc! {"10: Class {
name: \"B\",
def_id: DefinitionId(10),
ancestors: [CustomClassKind { id: DefinitionId(10), params: [TypeVarKind(UnificationKey(101))] }, CustomClassKind { id: DefinitionId(5), params: [PrimitiveKind(UnificationKey(2))] }],
fields: [(\"a\", \"class0\"), (\"b\", \"tvar2\"), (\"c\", \"class5[2->class1]\"), (\"d\", \"class14\")],
methods: [(\"__init__\", \"fn[[], class4]\", DefinitionId(11)), (\"fun\", \"fn[[a=class0, b=tvar2], list[virtual[class10[3->class3]]]]\", DefinitionId(12)), (\"foo\", \"fn[[c=class14], class4]\", DefinitionId(8))],
type_vars: [UnificationKey(101)]
}"},
indoc! {"11: Function {
name: \"B__init__\",
sig: \"fn[[], class4]\",
var_id: [3]
}"},
indoc! {"12: Function {
name: \"Bfun\",
sig: \"fn[[a=class0, b=tvar2], list[virtual[class10[3->class3]]]]\",
var_id: [2, 3]
}"},
indoc! {"13: Initializer { DefinitionId(10) }"},
indoc! {"14: Class {
name: \"C\",
def_id: DefinitionId(14),
ancestors: [CustomClassKind { id: DefinitionId(14), params: [] }, CustomClassKind { id: DefinitionId(10), params: [PrimitiveKind(UnificationKey(3))] }, CustomClassKind { id: DefinitionId(5), params: [PrimitiveKind(UnificationKey(2))] }],
fields: [(\"a\", \"class0\"), (\"b\", \"tvar2\"), (\"c\", \"class5[2->class1]\"), (\"d\", \"class14\"), (\"e\", \"class1\")],
methods: [(\"__init__\", \"fn[[], class4]\", DefinitionId(15)), (\"fun\", \"fn[[a=class0, b=tvar2], list[virtual[class10[3->class3]]]]\", DefinitionId(12)), (\"foo\", \"fn[[c=class14], class4]\", DefinitionId(8))],
type_vars: []
}"},
indoc! {"15: Function {
name: \"C__init__\",
sig: \"fn[[], class4]\",
var_id: []
}"},
indoc! {"16: Initializer { DefinitionId(14) }"},
];
"inheritance_override"
)]
#[test_case(
vec![
indoc! {"
@ -585,6 +688,57 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
vec!["a class def can only have at most one base class declaration and one generic declaration"];
"err multiple inheritance"
)]
#[test_case(
vec![
indoc! {"
class A(Generic[T]):
a: int32
b: T
c: A[int64]
def __init__(self, t: T):
self.a = 3
self.b = T
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
pass
"},
indoc! {"
class B(Generic[V], A[float]):
def __init__(self):
pass
def fun(self, a: int32, b: T) -> list[virtual[B[int32]]]:
# override
pass
"}
],
vec!["method has same name as ancestors' method, but incompatible type"];
"err_incompatible_inheritance_method"
)]
#[test_case(
vec![
indoc! {"
class A(Generic[T]):
a: int32
b: T
c: A[int64]
def __init__(self, t: T):
self.a = 3
self.b = T
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
pass
"},
indoc! {"
class B(Generic[V], A[float]):
a: int32
def __init__(self):
pass
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
# override
pass
"}
],
vec!["field `a` has already declared in the ancestor classes"];
"err_incompatible_inheritance_field"
)]
fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
let print = false;
let mut composer = TopLevelComposer::new();