From f1013d9a17326b1a9acbf7670182e14bc2f9cc78 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Thu, 9 Sep 2021 02:03:44 +0800 Subject: [PATCH] nac3core: top level fix type var within list tuple, test of type var application compatibility --- nac3core/src/toplevel/test.rs | 79 ++++++++++++++++++++++++ nac3core/src/toplevel/type_annotation.rs | 49 +++++++++++---- nac3core/src/typecheck/typedef/mod.rs | 8 ++- 3 files changed, 121 insertions(+), 15 deletions(-) diff --git a/nac3core/src/toplevel/test.rs b/nac3core/src/toplevel/test.rs index a3bdf1a43..e210d002d 100644 --- a/nac3core/src/toplevel/test.rs +++ b/nac3core/src/toplevel/test.rs @@ -335,6 +335,85 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s ]; "generic class" )] +#[test_case( + vec![ + indoc! {" + def foo(a: list[int32], b: tuple[T, float]) -> A[B, bool]: + pass + "}, + indoc! {" + class A(Generic[T, V]): + def __init__(v: V): + self.a: T = 1 + self.b: V = v + def fun(a: T) -> V: + pass + "}, + indoc! {" + def gfun(a: A[list[float], int32]): + pass + "}, + indoc! {" + class B: + def __init__(): + pass + "} + ], + vec![ + indoc! {"5: Function { + name: \"foo\", + sig: \"fn[[a=list[class0], b=tuple[tvar2, class2]], class6[2->class11, 3->class3]]\", + var_id: [2] + }"}, + + indoc! {"6: Class { + name: \"A\", + def_id: DefinitionId(6), + ancestors: [CustomClassKind { id: DefinitionId(6), params: [TypeVarKind(UnificationKey(100)), TypeVarKind(UnificationKey(101))] }], + fields: [(\"a\", \"tvar2\"), (\"b\", \"tvar3\")], + methods: [(\"__init__\", \"fn[[v=tvar3], class4]\", DefinitionId(7)), (\"fun\", \"fn[[a=tvar2], tvar3]\", DefinitionId(8))], + type_vars: [UnificationKey(100), UnificationKey(101)] + }"}, + + indoc! {"7: Function { + name: \"A__init__\", + sig: \"fn[[v=tvar3], class4]\", + var_id: [2, 3] + }"}, + + indoc! {"8: Function { + name: \"Afun\", + sig: \"fn[[a=tvar2], tvar3]\", + var_id: [2, 3] + }"}, + + indoc! {"9: Initializer { DefinitionId(6) }"}, + + indoc! {"10: Function { + name: \"gfun\", + sig: \"fn[[a=class6[2->list[class2], 3->class0]], class4]\", + var_id: [] + }"}, + + indoc! {"11: Class { + name: \"B\", + def_id: DefinitionId(11), + ancestors: [CustomClassKind { id: DefinitionId(11), params: [] }], + fields: [], + methods: [(\"__init__\", \"fn[[], class4]\", DefinitionId(12))], + type_vars: [] + }"}, + + indoc! {"12: Function { + name: \"B__init__\", + sig: \"fn[[], class4]\", + var_id: [] + }"}, + + indoc! {"13: Initializer { DefinitionId(11) }"}, + ]; + "list tuple generic" +)] fn test_simple_class_analyze(source: Vec<&str>, res: Vec<&str>) { let mut composer = TopLevelComposer::new(); diff --git a/nac3core/src/toplevel/type_annotation.rs b/nac3core/src/toplevel/type_annotation.rs index 7bb02f0b7..3f8ddb594 100644 --- a/nac3core/src/toplevel/type_annotation.rs +++ b/nac3core/src/toplevel/type_annotation.rs @@ -126,6 +126,7 @@ pub fn parse_ast_to_type_annotation_kinds( .ok_or_else(|| "unknown class name".to_string())?; let def = top_level_defs[obj_id.0].read(); if let TopLevelDef::Class { type_vars, .. } = &*def { + // we do not check whether the application of type variables are compatible here let param_type_infos = { let params_ast = if let ast::ExprKind::Tuple { elts, .. } = &slice.node { elts.iter().collect_vec() @@ -208,17 +209,35 @@ pub fn get_type_from_type_annotation_kinds( }) .collect::, _>>()?; - let subst = type_vars - .iter() - .map(|x| { - if let TypeEnum::TVar { id, .. } = unifier.get_ty(*x).as_ref() { - *id + let subst = { + // NOTE: check for compatible range here + let mut result: HashMap = HashMap::new(); + for (tvar, p) in type_vars.iter().zip(param_ty) { + if let TypeEnum::TVar { id, range, meta: TypeVarMeta::Generic } = unifier.get_ty(*tvar).as_ref() { + let ok: bool = { + // NOTE: create a temp type var and unify to check compatibility + let temp = unifier.get_fresh_var_with_range(range.borrow().as_slice()); + unifier.unify(temp.0, p).is_ok() + }; + if ok { + result.insert(*id, p); + } else { + return Err(format!( + "cannot apply type {} to type variable with id {:?}", + unifier.stringify( + p, + &mut |id| format!("class{}", id), + &mut |id| format!("tvar{}", id) + ), + *id + )) + } } else { - unreachable!() + unreachable!("must be generic type var") } - }) - .zip(param_ty.into_iter()) - .collect::>(); + } + result + }; let mut tobj_fields = methods .iter() .map(|(name, ty, _)| { @@ -244,9 +263,7 @@ pub fn get_type_from_type_annotation_kinds( Ok(unifier.add_ty(TypeEnum::TObj { obj_id: *id, - //fields: RefCell::new(tobj_fields), - fields: RefCell::new(HashMap::new()), - // fields: Default::default(), + fields: RefCell::new(tobj_fields), params: subst.into(), })) } @@ -325,7 +342,13 @@ pub fn get_type_var_contained_in_type_annotation(ann: &TypeAnnotation) -> Vec {} + TypeAnnotation::ListKind(ann) => result.extend(get_type_var_contained_in_type_annotation(ann.as_ref())), + TypeAnnotation::TupleKind(anns) => { + for a in anns { + result.extend(get_type_var_contained_in_type_annotation(a)); + } + } + TypeAnnotation::PrimitiveKind( .. ) => {} } result } diff --git a/nac3core/src/typecheck/typedef/mod.rs b/nac3core/src/typecheck/typedef/mod.rs index e4599945b..1017d28f6 100644 --- a/nac3core/src/typecheck/typedef/mod.rs +++ b/nac3core/src/typecheck/typedef/mod.rs @@ -714,8 +714,12 @@ impl Unifier { let name = obj_to_name(obj_id.0); let params = params.borrow(); if !params.is_empty() { - let mut params = - params.values().map(|v| self.stringify(*v, obj_to_name, var_to_name)); + let params = + params.iter().map(|(id, v)| + format!("{}->{}", *id, self.stringify(*v, obj_to_name, var_to_name)) + ); + // NOTE: sort to preserve order + let mut params = params.sorted(); format!("{}[{}]", name, params.join(", ")) } else { name