From c2706fa720245dcde82eb25c9dfde8466e416dc3 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Tue, 9 Nov 2021 01:15:41 +0800 Subject: [PATCH 01/14] nac3core: fix polymorphic class method partial instantiation --- nac3core/src/codegen/concrete_type.rs | 10 +++++-- nac3core/src/toplevel/composer.rs | 33 +++++++++++++++++++++--- nac3core/src/toplevel/type_annotation.rs | 8 +++--- nac3core/src/typecheck/typedef/mod.rs | 16 +++++------- 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/nac3core/src/codegen/concrete_type.rs b/nac3core/src/codegen/concrete_type.rs index f4f4518b9..422e4dfc1 100644 --- a/nac3core/src/codegen/concrete_type.rs +++ b/nac3core/src/codegen/concrete_type.rs @@ -147,8 +147,14 @@ impl ConcreteTypeStore { fields: fields .borrow() .iter() - .map(|(name, ty)| { - (*name, (self.from_unifier_type(unifier, primitives, ty.0, cache), ty.1)) + .filter_map(|(name, ty)| { + // filter out functions as they can have type vars and + // will not affect codegen + if let TypeEnum::TFunc( .. ) = &*unifier.get_ty(ty.0) { + None + } else { + Some((*name, (self.from_unifier_type(unifier, primitives, ty.0, cache), ty.1))) + } }) .collect(), params: params diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index bf9f15612..57ca26434 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -1654,7 +1654,7 @@ impl TopLevelComposer { if let TypeEnum::TFunc(func_sig) = self.unifier.get_ty(*signature).as_ref() { let FunSignature { args, ret, vars } = &*func_sig.borrow(); // None if is not class method - let self_type = { + let uninst_self_type = { if let Some(class_id) = self.method_class.get(&DefinitionId(id)) { let class_def = self.definition_ast_list.get(class_id.0).unwrap(); let class_def = class_def.0.read(); @@ -1666,7 +1666,7 @@ impl TopLevelComposer { &self.primitives_ty, &ty_ann, )?; - Some(self_ty) + Some((self_ty, type_vars.clone())) } else { unreachable!("must be class def") } @@ -1717,9 +1717,34 @@ impl TopLevelComposer { }; let self_type = { let unifier = &mut self.unifier; - self_type.map(|x| unifier.subst(x, &subst).unwrap_or(x)) + uninst_self_type + .clone() + .map(|(self_type, type_vars)| { + let subst_for_self = { + let class_ty_var_ids = type_vars + .iter() + .map(|x| { + if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*x) { + *id + } else { + unreachable!("must be type var here"); + } + }) + .collect::>(); + subst + .iter() + .filter_map(|(ty_var_id, ty_var_target)| { + if class_ty_var_ids.contains(ty_var_id) { + Some((*ty_var_id, *ty_var_target)) + } else { + None + } + }) + .collect::>() + }; + unifier.subst(self_type, &subst_for_self).unwrap_or(self_type) + }) }; - let mut identifiers = { // NOTE: none and function args? let mut result: HashSet<_> = HashSet::new(); diff --git a/nac3core/src/toplevel/type_annotation.rs b/nac3core/src/toplevel/type_annotation.rs index 257d582e2..307d87944 100644 --- a/nac3core/src/toplevel/type_annotation.rs +++ b/nac3core/src/toplevel/type_annotation.rs @@ -280,9 +280,11 @@ pub fn get_type_from_type_annotation_kinds( { let ok: bool = { // 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() + p == *tvar || { + 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); diff --git a/nac3core/src/typecheck/typedef/mod.rs b/nac3core/src/typecheck/typedef/mod.rs index eb0cf4bc8..85e619c8b 100644 --- a/nac3core/src/typecheck/typedef/mod.rs +++ b/nac3core/src/typecheck/typedef/mod.rs @@ -719,22 +719,18 @@ impl Unifier { /// Returns Some(T) where T is the instantiated type. /// Returns None if the function is already instantiated. fn instantiate_fun(&mut self, ty: Type, fun: &FunSignature) -> Type { - let mut instantiated = false; + let mut instantiated = true; let mut vars = Vec::new(); for (k, v) in fun.vars.iter() { if let TypeEnum::TVar { id, range, .. } = self.unification_table.probe_value(*v).as_ref() { - if k != id { - instantiated = true; - break; + // need to do this for partial instantiated function + // (in class methods that contains type vars not in class) + if k == id { + instantiated = false; + vars.push((*k, range.clone())); } - // actually, if the first check succeeded, the function should be uninstatiated. - // The cloned values must be used and would not be wasted. - vars.push((*k, range.clone())); - } else { - instantiated = true; - break; } } if instantiated { From a9635f0979ace19f0e3ea7d67ffd9c2be93a517c Mon Sep 17 00:00:00 2001 From: ychenfo Date: Wed, 10 Nov 2021 23:38:47 +0800 Subject: [PATCH 02/14] nac3core: top level use codegen official get_subst_key --- nac3core/src/codegen/expr.rs | 45 ++++++++++++++++++------------- nac3core/src/toplevel/composer.rs | 23 ++++++---------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 4ad363c7f..b267d3308 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -7,7 +7,7 @@ use crate::{ }, symbol_resolver::SymbolValue, toplevel::{DefinitionId, TopLevelDef}, - typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum}, + typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier}, }; use inkwell::{ types::{BasicType, BasicTypeEnum}, @@ -21,6 +21,31 @@ use nac3parser::ast::{ use super::CodeGenerator; +pub fn get_subst_key( + unifier: &mut Unifier, + obj: Option, + fun_vars: &HashMap, + filter: Option<&Vec>, +) -> String { + let mut vars = obj + .map(|ty| { + if let TypeEnum::TObj { params, .. } = &*unifier.get_ty(ty) { + params.borrow().clone() + } else { + unreachable!() + } + }) + .unwrap_or_default(); + vars.extend(fun_vars.iter()); + let sorted = + vars.keys().filter(|id| filter.map(|v| v.contains(id)).unwrap_or(true)).sorted(); + sorted + .map(|id| { + unifier.stringify(vars[id], &mut |id| id.to_string(), &mut |id| id.to_string()) + }) + .join(", ") +} + impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { pub fn build_gep_and_load( &mut self, @@ -36,23 +61,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { fun: &FunSignature, filter: Option<&Vec>, ) -> String { - let mut vars = obj - .map(|ty| { - if let TypeEnum::TObj { params, .. } = &*self.unifier.get_ty(ty) { - params.borrow().clone() - } else { - unreachable!() - } - }) - .unwrap_or_default(); - vars.extend(fun.vars.iter()); - let sorted = - vars.keys().filter(|id| filter.map(|v| v.contains(id)).unwrap_or(true)).sorted(); - sorted - .map(|id| { - self.unifier.stringify(vars[id], &mut |id| id.to_string(), &mut |id| id.to_string()) - }) - .join(", ") + get_subst_key(&mut self.unifier, obj, &fun.vars, filter) } pub fn get_attr_index(&mut self, ty: Type, attr: StrRef) -> usize { diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 57ca26434..8dd5404c9 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -6,6 +6,7 @@ use inkwell::FloatPredicate; use crate::{ symbol_resolver::SymbolValue, typecheck::type_inferencer::{FunctionData, Inferencer}, + codegen::expr::get_subst_key, }; use super::*; @@ -1835,21 +1836,13 @@ impl TopLevelComposer { instance_to_stmt.insert( // NOTE: refer to codegen/expr/get_subst_key function - { - let unifier = &mut self.unifier; - subst - .keys() - .sorted() - .map(|id| { - let ty = subst.get(id).unwrap(); - unifier.stringify( - *ty, - &mut |id| id.to_string(), - &mut |id| id.to_string(), - ) - }) - .join(", ") - }, + + get_subst_key( + &mut self.unifier, + self_type, + &subst, + None + ), FunInstance { body: Arc::new(fun_body), unifier_id: 0, From a50df6560e05cb512e18830d4e26ee7ab9a98e57 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Thu, 11 Nov 2021 20:25:33 +0800 Subject: [PATCH 03/14] nac3core: fix handling on rigid typevar --- nac3core/src/codegen/mod.rs | 8 ++++- nac3core/src/toplevel/composer.rs | 49 +++++++++++++++++++++---------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index d64212b68..68dc804ac 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -280,7 +280,13 @@ pub fn gen_func<'ctx, G: CodeGenerator + ?Sized>( // this should be unification between variables and concrete types // and should not cause any problem... let b = task.store.to_unifier_type(&mut unifier, &primitives, *b, &mut cache); - unifier.unify(*a, b).unwrap(); + unifier.unify(*a, b).or_else(|err| { + if matches!(&*unifier.get_ty(*a), TypeEnum::TRigidVar { .. }) { + Ok(unifier.replace_rigid_var(*a, b)) + } else { + Err(err) + } + }).unwrap() } // rebuild primitive store with unique representatives diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 8dd5404c9..9c23930e6 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -1171,8 +1171,17 @@ impl TopLevelComposer { primitives_store.none } }; - var_id.extend_from_slice( - function_var_map.keys().into_iter().copied().collect_vec().as_slice(), + var_id.extend_from_slice(function_var_map + .iter() + .filter_map(|(id, ty)| { + if matches!(&*unifier.get_ty(*ty), TypeEnum::TVar { range, .. } if range.borrow().is_empty()) { + None + } else { + Some(*id) + } + }) + .collect_vec() + .as_slice() ); let function_ty = unifier.add_ty(TypeEnum::TFunc( FunSignature { args: arg_types, ret: return_ty, vars: function_var_map } @@ -1375,9 +1384,20 @@ impl TopLevelComposer { if let TopLevelDef::Function { var_id, .. } = temp_def_list.get(method_id.0).unwrap().write().deref_mut() { - var_id.extend_from_slice( - method_var_map.keys().into_iter().copied().collect_vec().as_slice(), + var_id.extend_from_slice(method_var_map + .iter() + .filter_map(|(id, ty)| { + if matches!(&*unifier.get_ty(*ty), TypeEnum::TVar { range, .. } if range.borrow().is_empty()) { + None + } else { + Some(*id) + } + }) + .collect_vec() + .as_slice() ); + } else { + unreachable!() } let method_type = unifier.add_ty(TypeEnum::TFunc( FunSignature { args: arg_types, ret: ret_type, vars: method_var_map } @@ -1649,6 +1669,7 @@ impl TopLevelComposer { simple_name, signature, resolver, + var_id: insted_vars, .. } = &mut *function_def { @@ -1675,20 +1696,20 @@ impl TopLevelComposer { None } }; + // carefully handle those with bounds, without bounds and no typevars + // if class methods, `vars` also contains all class typevars here let (type_var_subst_comb, no_range_vars) = { let unifier = &mut self.unifier; let mut no_ranges: Vec = Vec::new(); - let var_ids = vars.iter().map(|(id, ty)| { - if matches!(unifier.get_ty(*ty).as_ref(), TypeEnum::TVar { range, .. } if range.borrow().is_empty()) { - no_ranges.push(*ty); - } - *id - }) - .collect_vec(); + let var_ids = vars.keys().copied().collect_vec(); let var_combs = vars .iter() .map(|(_, ty)| { - unifier.get_instantiations(*ty).unwrap_or_else(|| vec![*ty]) + unifier.get_instantiations(*ty).unwrap_or_else(|| { + let rigid = unifier.get_fresh_rigid_var().0; + no_ranges.push(rigid); + vec![rigid] + }) }) .multi_cartesian_product() .collect_vec(); @@ -1835,13 +1856,11 @@ impl TopLevelComposer { } instance_to_stmt.insert( - // NOTE: refer to codegen/expr/get_subst_key function - get_subst_key( &mut self.unifier, self_type, &subst, - None + Some(insted_vars), ), FunInstance { body: Arc::new(fun_body), From d336200bf4dbaedff748b5c2efc43cf0d6240d36 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Thu, 11 Nov 2021 21:13:07 +0800 Subject: [PATCH 04/14] nac3core: fix broken tests due to the fix of rigid typevar handling --- ...core__toplevel__test__test_analyze__generic_class.snap | 2 +- ...oplevel__test__test_analyze__inheritance_override.snap | 8 ++++---- ..._toplevel__test__test_analyze__list_tuple_generic.snap | 6 +++--- .../nac3core__toplevel__test__test_analyze__self1.snap | 4 ++-- ...oplevel__test__test_analyze__simple_class_compose.snap | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__generic_class.snap b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__generic_class.snap index 869c50784..42e382766 100644 --- a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__generic_class.snap +++ b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__generic_class.snap @@ -9,5 +9,5 @@ expression: res_vec "Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a=int32], var4]\",\nvar_id: [4]\n}\n", "Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b=var3], none]\")],\ntype_vars: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", - "Function {\nname: \"B.foo\",\nsig: \"fn[[b=var3], none]\",\nvar_id: [3]\n}\n", + "Function {\nname: \"B.foo\",\nsig: \"fn[[b=var3], none]\",\nvar_id: []\n}\n", ] diff --git a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__inheritance_override.snap b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__inheritance_override.snap index 2de87dabf..b08e93527 100644 --- a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__inheritance_override.snap +++ b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__inheritance_override.snap @@ -5,12 +5,12 @@ expression: res_vec --- [ "Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var3\\\"]}\"],\nfields: [\"a\", \"b\", \"c\"],\nmethods: [(\"__init__\", \"fn[[t=var3], none]\"), (\"fun\", \"fn[[a=int32, b=var3], list[virtual[B[4->bool]]]]\"), (\"foo\", \"fn[[c=C], none]\")],\ntype_vars: [\"var3\"]\n}\n", - "Function {\nname: \"A.__init__\",\nsig: \"fn[[t=var3], none]\",\nvar_id: [3]\n}\n", - "Function {\nname: \"A.fun\",\nsig: \"fn[[a=int32, b=var3], list[virtual[B[4->bool]]]]\",\nvar_id: [3]\n}\n", - "Function {\nname: \"A.foo\",\nsig: \"fn[[c=C], none]\",\nvar_id: [3]\n}\n", + "Function {\nname: \"A.__init__\",\nsig: \"fn[[t=var3], none]\",\nvar_id: []\n}\n", + "Function {\nname: \"A.fun\",\nsig: \"fn[[a=int32, b=var3], list[virtual[B[4->bool]]]]\",\nvar_id: []\n}\n", + "Function {\nname: \"A.foo\",\nsig: \"fn[[c=C], none]\",\nvar_id: []\n}\n", "Class {\nname: \"B\",\nancestors: [\"{class: B, params: [\\\"var4\\\"]}\", \"{class: A, params: [\\\"float\\\"]}\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a=int32, b=var3], list[virtual[B[4->bool]]]]\"), (\"foo\", \"fn[[c=C], none]\")],\ntype_vars: [\"var4\"]\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: [4]\n}\n", - "Function {\nname: \"B.fun\",\nsig: \"fn[[a=int32, b=var3], list[virtual[B[4->bool]]]]\",\nvar_id: [3, 4]\n}\n", + "Function {\nname: \"B.fun\",\nsig: \"fn[[a=int32, b=var3], list[virtual[B[4->bool]]]]\",\nvar_id: [4]\n}\n", "Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: B, params: [\\\"bool\\\"]}\", \"{class: A, params: [\\\"float\\\"]}\"],\nfields: [\"a\", \"b\", \"c\", \"d\", \"e\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a=int32, b=var3], list[virtual[B[4->bool]]]]\"), (\"foo\", \"fn[[c=C], none]\")],\ntype_vars: []\n}\n", "Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", ] diff --git a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__list_tuple_generic.snap b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__list_tuple_generic.snap index 58d1e0880..2083a398b 100644 --- a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__list_tuple_generic.snap +++ b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__list_tuple_generic.snap @@ -4,10 +4,10 @@ expression: res_vec --- [ - "Function {\nname: \"foo\",\nsig: \"fn[[a=list[int32], b=tuple[var3, float]], A[3->B, 4->bool]]\",\nvar_id: [3]\n}\n", + "Function {\nname: \"foo\",\nsig: \"fn[[a=list[int32], b=tuple[var3, float]], A[3->B, 4->bool]]\",\nvar_id: []\n}\n", "Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var3\\\", \\\"var4\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v=var4], none]\"), (\"fun\", \"fn[[a=var3], var4]\")],\ntype_vars: [\"var3\", \"var4\"]\n}\n", - "Function {\nname: \"A.__init__\",\nsig: \"fn[[v=var4], none]\",\nvar_id: [3, 4]\n}\n", - "Function {\nname: \"A.fun\",\nsig: \"fn[[a=var3], var4]\",\nvar_id: [3, 4]\n}\n", + "Function {\nname: \"A.__init__\",\nsig: \"fn[[v=var4], none]\",\nvar_id: [4]\n}\n", + "Function {\nname: \"A.fun\",\nsig: \"fn[[a=var3], var4]\",\nvar_id: [4]\n}\n", "Function {\nname: \"gfun\",\nsig: \"fn[[a=A[3->list[float], 4->int32]], none]\",\nvar_id: []\n}\n", "Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], none]\")],\ntype_vars: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", diff --git a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__self1.snap b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__self1.snap index a482fafdd..2ded39602 100644 --- a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__self1.snap +++ b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__self1.snap @@ -5,8 +5,8 @@ expression: res_vec --- [ "Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var3\\\", \\\"var4\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a=A[3->float, 4->bool], b=B], none]\"), (\"fun\", \"fn[[a=A[3->float, 4->bool]], A[3->bool, 4->int32]]\")],\ntype_vars: [\"var3\", \"var4\"]\n}\n", - "Function {\nname: \"A.__init__\",\nsig: \"fn[[a=A[3->float, 4->bool], b=B], none]\",\nvar_id: [3, 4]\n}\n", - "Function {\nname: \"A.fun\",\nsig: \"fn[[a=A[3->float, 4->bool]], A[3->bool, 4->int32]]\",\nvar_id: [3, 4]\n}\n", + "Function {\nname: \"A.__init__\",\nsig: \"fn[[a=A[3->float, 4->bool], b=B], none]\",\nvar_id: [4]\n}\n", + "Function {\nname: \"A.fun\",\nsig: \"fn[[a=A[3->float, 4->bool]], A[3->bool, 4->int32]]\",\nvar_id: [4]\n}\n", "Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: A, params: [\\\"int64\\\", \\\"bool\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a=A[3->float, 4->bool]], A[3->bool, 4->int32]]\"), (\"foo\", \"fn[[b=B], B]\"), (\"bar\", \"fn[[a=A[3->list[B], 4->int32]], tuple[A[3->virtual[A[3->B, 4->int32]], 4->bool], B]]\")],\ntype_vars: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.foo\",\nsig: \"fn[[b=B], B]\",\nvar_id: []\n}\n", diff --git a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__simple_class_compose.snap b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__simple_class_compose.snap index 42569f36d..d6f269ac0 100644 --- a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__simple_class_compose.snap +++ b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__simple_class_compose.snap @@ -7,12 +7,12 @@ expression: res_vec "Class {\nname: \"A\",\nancestors: [\"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b=B], none]\"), (\"foo\", \"fn[[a=var3, b=var4], none]\")],\ntype_vars: []\n}\n", "Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"A.fun\",\nsig: \"fn[[b=B], none]\",\nvar_id: []\n}\n", - "Function {\nname: \"A.foo\",\nsig: \"fn[[a=var3, b=var4], none]\",\nvar_id: [3, 4]\n}\n", + "Function {\nname: \"A.foo\",\nsig: \"fn[[a=var3, b=var4], none]\",\nvar_id: [4]\n}\n", "Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: C, params: []}\", \"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b=B], none]\"), (\"foo\", \"fn[[a=var3, b=var4], none]\")],\ntype_vars: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b=B], none]\"), (\"foo\", \"fn[[a=var3, b=var4], none]\")],\ntype_vars: []\n}\n", "Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"C.fun\",\nsig: \"fn[[b=B], none]\",\nvar_id: []\n}\n", "Function {\nname: \"foo\",\nsig: \"fn[[a=A], none]\",\nvar_id: []\n}\n", - "Function {\nname: \"ff\",\nsig: \"fn[[a=var3], var4]\",\nvar_id: [3, 4]\n}\n", + "Function {\nname: \"ff\",\nsig: \"fn[[a=var3], var4]\",\nvar_id: [4]\n}\n", ] From aae9925014a8618adf9315aba448b62ae41d6ef5 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Thu, 11 Nov 2021 23:44:57 +0800 Subject: [PATCH 05/14] nac3standalone: report when entry point run function cannot be found --- nac3standalone/src/main.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nac3standalone/src/main.rs b/nac3standalone/src/main.rs index 5b4e31dd0..1b09e9b0e 100644 --- a/nac3standalone/src/main.rs +++ b/nac3standalone/src/main.rs @@ -100,7 +100,11 @@ fn main() { let instance = { let defs = top_level.definitions.read(); - let mut instance = defs[resolver.get_identifier_def("run".into()).unwrap().0].write(); + let mut instance = + defs[resolver + .get_identifier_def("run".into()) + .unwrap_or_else(|| panic!("cannot find `run() -> int32 entry point`")).0 + ].write(); if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, From 01b51b62eeed92f89f31359e6b00871df5741729 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Fri, 12 Nov 2021 02:25:53 +0800 Subject: [PATCH 06/14] nac3core: composer better error msg in for uninit field --- nac3core/src/toplevel/composer.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 9c23930e6..e7c17cd05 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -1646,11 +1646,14 @@ impl TopLevelComposer { unreachable!("must be init function here") } let all_inited = Self::get_all_assigned_field(body.as_slice())?; - if fields.iter().any(|x| !all_inited.contains(&x.0)) { - return Err(format!( - "fields of class {} not fully initialized", - class_name - )); + for (f, _, _) in fields { + if !all_inited.contains(f) { + return Err(format!( + "fields `{}` of class `{}` not fully initialized", + f, + class_name + )); + } } } } From c6f75c8bde0dff462fab0d75987804ddb5e3d782 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Mon, 22 Nov 2021 14:52:52 +0800 Subject: [PATCH 07/14] nac3standalone: fix error message when no entry point is found --- nac3standalone/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nac3standalone/src/main.rs b/nac3standalone/src/main.rs index 1b09e9b0e..17b61b2bb 100644 --- a/nac3standalone/src/main.rs +++ b/nac3standalone/src/main.rs @@ -103,7 +103,7 @@ fn main() { let mut instance = defs[resolver .get_identifier_def("run".into()) - .unwrap_or_else(|| panic!("cannot find `run() -> int32 entry point`")).0 + .unwrap_or_else(|| panic!("cannot find run() entry point")).0 ].write(); if let TopLevelDef::Function { instance_to_stmt, From 664e02cec4c5e1b1fed3b2d11fdb73f11ab0747e Mon Sep 17 00:00:00 2001 From: ychenfo Date: Mon, 22 Nov 2021 14:55:39 +0800 Subject: [PATCH 08/14] nac3core: fix clippy warning --- nac3core/src/codegen/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index 68dc804ac..b6c94f939 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -282,7 +282,8 @@ pub fn gen_func<'ctx, G: CodeGenerator + ?Sized>( let b = task.store.to_unifier_type(&mut unifier, &primitives, *b, &mut cache); unifier.unify(*a, b).or_else(|err| { if matches!(&*unifier.get_ty(*a), TypeEnum::TRigidVar { .. }) { - Ok(unifier.replace_rigid_var(*a, b)) + unifier.replace_rigid_var(*a, b); + Ok(()) } else { Err(err) } From 49476d06e1eeba59ffbf025af134d600ca317827 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Mon, 22 Nov 2021 15:06:16 +0800 Subject: [PATCH 09/14] nac3core: clearer comments --- nac3core/src/codegen/concrete_type.rs | 5 +++-- nac3core/src/typecheck/typedef/mod.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nac3core/src/codegen/concrete_type.rs b/nac3core/src/codegen/concrete_type.rs index 422e4dfc1..140142ef3 100644 --- a/nac3core/src/codegen/concrete_type.rs +++ b/nac3core/src/codegen/concrete_type.rs @@ -148,8 +148,9 @@ impl ConcreteTypeStore { .borrow() .iter() .filter_map(|(name, ty)| { - // filter out functions as they can have type vars and - // will not affect codegen + // here we should not have type vars, but some partial instantiated + // class methods can still have uninstantiated type vars, so + // filter out all the methods, as this will not affect codegen if let TypeEnum::TFunc( .. ) = &*unifier.get_ty(ty.0) { None } else { diff --git a/nac3core/src/typecheck/typedef/mod.rs b/nac3core/src/typecheck/typedef/mod.rs index 85e619c8b..0d194b26b 100644 --- a/nac3core/src/typecheck/typedef/mod.rs +++ b/nac3core/src/typecheck/typedef/mod.rs @@ -725,8 +725,9 @@ impl Unifier { if let TypeEnum::TVar { id, range, .. } = self.unification_table.probe_value(*v).as_ref() { - // need to do this for partial instantiated function - // (in class methods that contains type vars not in class) + // for class methods that contain type vars not in class declaration, + // as long as there exits one uninstantiated type var, the function is not instantiated, + // and need to do substitution on those type vars if k == id { instantiated = false; vars.push((*k, range.clone())); From 45870888356b870fa6f4f91aaa463b29d41d94b2 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Tue, 23 Nov 2021 07:32:09 +0800 Subject: [PATCH 10/14] Constant Default Parameter Support (#98) Add support for constant default parameter Reviewed-on: https://git.m-labs.hk/M-Labs/nac3/pulls/98 Co-authored-by: ychenfo Co-committed-by: ychenfo --- nac3artiq/src/symbol_resolver.rs | 81 +++++++++++- nac3core/src/codegen/test.rs | 4 + nac3core/src/symbol_resolver.rs | 1 + nac3core/src/toplevel/composer.rs | 64 ++++++++- nac3core/src/toplevel/helper.rs | 122 ++++++++++++++++++ nac3core/src/toplevel/mod.rs | 2 +- nac3core/src/toplevel/test.rs | 4 + .../src/typecheck/type_inferencer/test.rs | 4 + nac3standalone/src/basic_symbol_resolver.rs | 18 ++- nac3standalone/src/main.rs | 62 ++++++++- 10 files changed, 347 insertions(+), 15 deletions(-) diff --git a/nac3artiq/src/symbol_resolver.rs b/nac3artiq/src/symbol_resolver.rs index e420bd808..165250969 100644 --- a/nac3artiq/src/symbol_resolver.rs +++ b/nac3artiq/src/symbol_resolver.rs @@ -2,7 +2,7 @@ use inkwell::{types::BasicType, values::BasicValueEnum, AddressSpace}; use nac3core::{ codegen::CodeGenContext, location::Location, - symbol_resolver::SymbolResolver, + symbol_resolver::{SymbolResolver, SymbolValue}, toplevel::{DefinitionId, TopLevelDef}, typecheck::{ type_inferencer::PrimitiveStore, @@ -14,7 +14,7 @@ use pyo3::{ types::{PyList, PyModule, PyTuple}, PyAny, PyObject, PyResult, Python, }; -use nac3parser::ast::StrRef; +use nac3parser::ast::{self, StrRef}; use std::{ cell::RefCell, collections::{HashMap, HashSet}, @@ -399,9 +399,86 @@ impl Resolver { } } } + + fn get_default_param_obj_value(&self, obj: &PyAny, helper: &PythonHelper) -> PyResult> { + let ty_id: u64 = helper + .id_fn + .call1((helper.type_fn.call1((obj,))?,))? + .extract()?; + Ok( + if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 { + let val: i32 = obj.extract()?; + Ok(SymbolValue::I32(val)) + } else if ty_id == self.primitive_ids.int64 { + let val: i64 = obj.extract()?; + Ok(SymbolValue::I64(val)) + } else if ty_id == self.primitive_ids.bool { + let val: bool = obj.extract()?; + Ok(SymbolValue::Bool(val)) + } else if ty_id == self.primitive_ids.float { + let val: f64 = obj.extract()?; + Ok(SymbolValue::Double(val)) + } else if ty_id == self.primitive_ids.tuple { + let elements: &PyTuple = obj.cast_as()?; + let elements: Result, String>, _> = elements + .iter() + .map(|elem| { + self.get_default_param_obj_value( + elem, + helper + ) + }) + .collect(); + let elements = match elements? { + Ok(el) => el, + Err(err) => return Ok(Err(err)) + }; + Ok(SymbolValue::Tuple(elements)) + } else { + Err("only primitives values and tuple can be default parameter value".into()) + } + ) + } } impl SymbolResolver for Resolver { + fn get_default_param_value( + &self, + expr: &ast::Expr + ) -> Option { + match &expr.node { + ast::ExprKind::Name { id, .. } => { + Python::with_gil( + |py| -> PyResult> { + let obj: &PyAny = self.module.extract(py)?; + let members: &PyList = PyModule::import(py, "inspect")? + .getattr("getmembers")? + .call1((obj,))? + .cast_as()?; + let mut sym_value = None; + for member in members.iter() { + let key: &str = member.get_item(0)?.extract()?; + let val = member.get_item(1)?; + if key == id.to_string() { + let builtins = PyModule::import(py, "builtins")?; + let helper = PythonHelper { + id_fn: builtins.getattr("id").unwrap(), + len_fn: builtins.getattr("len").unwrap(), + type_fn: builtins.getattr("type").unwrap(), + }; + sym_value = Some(self.get_default_param_obj_value(val, &helper).unwrap().unwrap()); + break; + } + } + Ok(sym_value) + } + ) + .unwrap() + } + _ => unimplemented!("other type of expr not supported at {}", expr.location) + } + } + fn get_symbol_type( &self, unifier: &mut Unifier, diff --git a/nac3core/src/codegen/test.rs b/nac3core/src/codegen/test.rs index ce958ff32..f9e2810ff 100644 --- a/nac3core/src/codegen/test.rs +++ b/nac3core/src/codegen/test.rs @@ -37,6 +37,10 @@ impl Resolver { } impl SymbolResolver for Resolver { + fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option { + unimplemented!() + } + fn get_symbol_type( &self, _: &mut Unifier, diff --git a/nac3core/src/symbol_resolver.rs b/nac3core/src/symbol_resolver.rs index 161d78a92..e8d594da6 100644 --- a/nac3core/src/symbol_resolver.rs +++ b/nac3core/src/symbol_resolver.rs @@ -44,6 +44,7 @@ pub trait SymbolResolver { ctx: &mut CodeGenContext<'ctx, 'a>, ) -> Option>; fn get_symbol_location(&self, str: StrRef) -> Option; + fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option; // handle function call etc. } diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index e7c17cd05..b0afaf263 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -1066,10 +1066,23 @@ impl TopLevelComposer { and names thould not be the same as the keywords" .into()); } - - args.args + + let arg_with_default: Vec<(&ast::Located>, Option<&ast::Expr>)> = args + .args .iter() - .map(|x| -> Result { + .rev() + .zip(args + .defaults + .iter() + .rev() + .map(|x| -> Option<&ast::Expr> { Some(x) }) + .chain(std::iter::repeat(None)) + ).collect_vec(); + + arg_with_default + .iter() + .rev() + .map(|(x, default)| -> Result { let annotation = x .node .annotation @@ -1121,7 +1134,19 @@ impl TopLevelComposer { Ok(FuncArg { name: x.node.arg, ty, - default_value: Default::default(), + default_value: match default { + None => None, + Some(default) => Some({ + let v = Self::parse_parameter_default_value(default, resolver)?; + Self::check_default_param_type( + &v, + &type_annotation, + primitives_store, + unifier + ).map_err(|err| format!("{} at {}", err, x.location))?; + v + }) + } }) }) .collect::, _>>()? @@ -1282,7 +1307,20 @@ impl TopLevelComposer { } let mut result = Vec::new(); - for x in &args.args { + + let arg_with_default: Vec<(&ast::Located>, Option<&ast::Expr>)> = args + .args + .iter() + .rev() + .zip(args + .defaults + .iter() + .rev() + .map(|x| -> Option<&ast::Expr> { Some(x) }) + .chain(std::iter::repeat(None)) + ).collect_vec(); + + for (x, default) in arg_with_default.into_iter().rev() { let name = x.node.arg; if name != zelf { let type_ann = { @@ -1327,8 +1365,20 @@ impl TopLevelComposer { let dummy_func_arg = FuncArg { name, ty: unifier.get_fresh_var().0, - // TODO: default value? - default_value: None, + default_value: match default { + None => None, + Some(default) => { + if name == "self".into() { + return Err(format!("`self` parameter cannot take default value at {}", x.location)); + } + Some({ + let v = Self::parse_parameter_default_value(default, class_resolver)?; + Self::check_default_param_type(&v, &type_ann, primitives, unifier) + .map_err(|err| format!("{} at {}", err, x.location))?; + v + }) + } + } }; // push the dummy type and the type annotation // into the list for later unification diff --git a/nac3core/src/toplevel/helper.rs b/nac3core/src/toplevel/helper.rs index 35da97c9b..5c8b85e75 100644 --- a/nac3core/src/toplevel/helper.rs +++ b/nac3core/src/toplevel/helper.rs @@ -1,3 +1,8 @@ +use std::convert::TryInto; + +use nac3parser::ast::{Constant, Location}; +use crate::symbol_resolver::SymbolValue; + use super::*; impl TopLevelDef { @@ -341,4 +346,121 @@ impl TopLevelComposer { } Ok(result) } + + pub fn parse_parameter_default_value(default: &ast::Expr, resolver: &(dyn SymbolResolver + Send + Sync)) -> Result { + parse_parameter_default_value(default, resolver) + } + + pub fn check_default_param_type(val: &SymbolValue, ty: &TypeAnnotation, primitive: &PrimitiveStore, unifier: &mut Unifier) -> Result<(), String> { + let res = match val { + SymbolValue::Bool(..) => { + if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.bool) { + None + } else { + Some("bool".to_string()) + } + } + SymbolValue::Double(..) => { + if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.float) { + None + } else { + Some("float".to_string()) + } + } + SymbolValue::I32(..) => { + if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.int32) { + None + } else { + Some("int32".to_string()) + } + } + SymbolValue::I64(..) => { + if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.int64) { + None + } else { + Some("int64".to_string()) + } + } + SymbolValue::Tuple(elts) => { + if let TypeAnnotation::Tuple(elts_ty) = ty { + for (e, t) in elts.iter().zip(elts_ty.iter()) { + Self::check_default_param_type(e, t, primitive, unifier)? + } + if elts.len() != elts_ty.len() { + Some(format!("tuple of length {}", elts.len())) + } else { + None + } + } else { + Some("tuple".to_string()) + } + } + }; + if let Some(found) = res { + Err(format!( + "incompatible default parameter type, expect {}, found {}", + ty.stringify(unifier), + found + )) + } else { + Ok(()) + } + } +} + +pub fn parse_parameter_default_value(default: &ast::Expr, resolver: &(dyn SymbolResolver + Send + Sync)) -> Result { + fn handle_constant(val: &Constant, loc: &Location) -> Result { + match val { + Constant::Int(v) => { + if let Ok(v) = v.try_into() { + Ok(SymbolValue::I32(v)) + } else { + Err(format!( + "integer value out of range at {}", + loc + )) + } + } + Constant::Float(v) => Ok(SymbolValue::Double(*v)), + Constant::Bool(v) => Ok(SymbolValue::Bool(*v)), + Constant::Tuple(tuple) => Ok(SymbolValue::Tuple( + tuple.iter().map(|x| handle_constant(x, loc)).collect::, _>>()? + )), + _ => unimplemented!("this constant is not supported at {}", loc), + } + } + match &default.node { + ast::ExprKind::Constant { value, .. } => handle_constant(value, &default.location), + ast::ExprKind::Call { func, args, .. } if { + match &func.node { + ast::ExprKind::Name { id, .. } => *id == "int64".into(), + _ => false, + } + } => { + if args.len() == 1 { + match &args[0].node { + ast::ExprKind::Constant { value: Constant::Int(v), .. } => + Ok(SymbolValue::I64(v.try_into().unwrap())), + _ => Err(format!("only allow constant integer here at {}", default.location)) + } + } else { + Err(format!("only allow constant integer here at {}", default.location)) + } + } + ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts + .iter() + .map(|x| parse_parameter_default_value(x, resolver)) + .collect::, _>>()? + )), + ast::ExprKind::Name { id, .. } => { + resolver.get_default_param_value(default).ok_or_else( + || format!( + "`{}` cannot be used as a default parameter at {} (not primitive type or tuple / not defined?)", + id, + default.location + ) + ) + } + _ => unimplemented!("only constant default is supported now at {}", default.location), + } } diff --git a/nac3core/src/toplevel/mod.rs b/nac3core/src/toplevel/mod.rs index c96a7cac6..f6fc15b06 100644 --- a/nac3core/src/toplevel/mod.rs +++ b/nac3core/src/toplevel/mod.rs @@ -23,7 +23,7 @@ use inkwell::values::BasicValueEnum; pub struct DefinitionId(pub usize); pub mod composer; -mod helper; +pub mod helper; mod type_annotation; use composer::*; use type_annotation::*; diff --git a/nac3core/src/toplevel/test.rs b/nac3core/src/toplevel/test.rs index e2795474e..0f4fd83fb 100644 --- a/nac3core/src/toplevel/test.rs +++ b/nac3core/src/toplevel/test.rs @@ -35,6 +35,10 @@ impl ResolverInternal { struct Resolver(Arc); impl SymbolResolver for Resolver { + fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option { + unimplemented!() + } + fn get_symbol_type( &self, _: &mut Unifier, diff --git a/nac3core/src/typecheck/type_inferencer/test.rs b/nac3core/src/typecheck/type_inferencer/test.rs index d3aa09fa9..da51aa9db 100644 --- a/nac3core/src/typecheck/type_inferencer/test.rs +++ b/nac3core/src/typecheck/type_inferencer/test.rs @@ -19,6 +19,10 @@ struct Resolver { } impl SymbolResolver for Resolver { + fn get_default_param_value(&self, _: &nac3parser::ast::Expr) -> Option { + unimplemented!() + } + fn get_symbol_type( &self, _: &mut Unifier, diff --git a/nac3standalone/src/basic_symbol_resolver.rs b/nac3standalone/src/basic_symbol_resolver.rs index 82c1ac312..9983d5079 100644 --- a/nac3standalone/src/basic_symbol_resolver.rs +++ b/nac3standalone/src/basic_symbol_resolver.rs @@ -2,7 +2,7 @@ use inkwell::values::BasicValueEnum; use nac3core::{ codegen::CodeGenContext, location::Location, - symbol_resolver::SymbolResolver, + symbol_resolver::{SymbolResolver, SymbolValue}, toplevel::{DefinitionId, TopLevelDef}, typecheck::{ type_inferencer::PrimitiveStore, @@ -10,13 +10,14 @@ use nac3core::{ }, }; use parking_lot::{Mutex, RwLock}; -use nac3parser::ast::StrRef; +use nac3parser::ast::{self, StrRef}; use std::{collections::HashMap, sync::Arc}; pub struct ResolverInternal { pub id_to_type: Mutex>, pub id_to_def: Mutex>, pub class_names: Mutex>, + pub module_globals: Mutex>, } impl ResolverInternal { @@ -27,11 +28,24 @@ impl ResolverInternal { pub fn add_id_type(&self, id: StrRef, ty: Type) { self.id_to_type.lock().insert(id, ty); } + + pub fn add_module_global(&self, id: StrRef, val: SymbolValue) { + self.module_globals.lock().insert(id, val); + } } pub struct Resolver(pub Arc); impl SymbolResolver for Resolver { + fn get_default_param_value(&self, expr: &ast::Expr) -> Option { + match &expr.node { + ast::ExprKind::Name { id, .. } => { + self.0.module_globals.lock().get(id).cloned() + } + _ => unimplemented!("other type of expr not supported at {}", expr.location) + } + } + fn get_symbol_type( &self, _: &mut Unifier, diff --git a/nac3standalone/src/main.rs b/nac3standalone/src/main.rs index 17b61b2bb..f6c4be68f 100644 --- a/nac3standalone/src/main.rs +++ b/nac3standalone/src/main.rs @@ -4,8 +4,8 @@ use inkwell::{ OptimizationLevel, }; use nac3core::typecheck::type_inferencer::PrimitiveStore; -use nac3parser::parser; -use std::env; +use nac3parser::{ast::{Expr, ExprKind, StmtKind}, parser}; +use std::{borrow::Borrow, env}; use std::fs; use std::{collections::HashMap, path::Path, sync::Arc, time::SystemTime}; @@ -15,7 +15,7 @@ use nac3core::{ WorkerRegistry, }, symbol_resolver::SymbolResolver, - toplevel::{composer::TopLevelComposer, TopLevelDef}, + toplevel::{composer::TopLevelComposer, TopLevelDef, helper::parse_parameter_default_value}, typecheck::typedef::FunSignature, }; @@ -48,6 +48,7 @@ fn main() { id_to_type: builtins_ty.into(), id_to_def: builtins_def.into(), class_names: Default::default(), + module_globals: Default::default(), } .into(); let resolver = @@ -66,6 +67,61 @@ fn main() { ); for stmt in parser_result.into_iter() { + if let StmtKind::Assign { targets, value, .. } = &stmt.node { + fn handle_assignment_pattern( + targets: &[Expr], + value: &Expr, + resolver: &(dyn SymbolResolver + Send + Sync), + internal_resolver: &ResolverInternal, + ) -> Result<(), String> { + if targets.len() == 1 { + match &targets[0].node { + ExprKind::Name { id, .. } => { + let val = parse_parameter_default_value(value.borrow(), resolver)?; + internal_resolver.add_module_global(*id, val); + Ok(()) + } + ExprKind::List { elts, .. } + | ExprKind::Tuple { elts, .. } => { + handle_assignment_pattern(elts, value, resolver, internal_resolver)?; + Ok(()) + } + _ => unreachable!("cannot be assigned") + } + } else { + match &value.node { + ExprKind::List { elts, .. } + | ExprKind::Tuple { elts, .. } => { + if elts.len() != targets.len() { + Err(format!( + "number of elements to unpack does not match (expect {}, found {}) at {}", + targets.len(), + elts.len(), + value.location + )) + } else { + for (tar, val) in targets.iter().zip(elts) { + handle_assignment_pattern( + std::slice::from_ref(tar), + val, + resolver, + internal_resolver + )?; + } + Ok(()) + } + }, + _ => Err(format!("unpack of this expression is not supported at {}", value.location)) + } + } + } + if let Err(err) = handle_assignment_pattern(targets, value, resolver.as_ref(), internal_resolver.as_ref()) { + eprintln!("{}", err); + return; + } + continue; + } + let (name, def_id, ty) = composer .register_top_level(stmt, Some(resolver.clone()), "__main__".into()) .unwrap(); From 970f075490aa71404d94000f934a85b3008e4087 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 23 Nov 2021 11:22:08 +0800 Subject: [PATCH 11/14] flake: switch to nixpkgs 21.11 release --- flake.lock | 8 ++++---- flake.nix | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index 50d000ce3..b7922587b 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1636698608, - "narHash": "sha256-sxLLeQmH3UrP3UANqXzMLE0bPDgY5aIt04iBoPffG2E=", + "lastModified": 1637636156, + "narHash": "sha256-E2ym4Vcpqu9JYoQDXJZR48gVD+LPPbaCoYveIk7Xu3Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "92c881b6a72abce5bb2f5db3f903b4871d13aaa9", + "rev": "b026e1cf87a108dd06fe521f224fdc72fd0b013d", "type": "github" }, "original": { "owner": "NixOS", - "ref": "master", + "ref": "release-21.11", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index bcaa89708..0e89187fc 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ { description = "The third-generation ARTIQ compiler"; - inputs.nixpkgs.url = github:NixOS/nixpkgs/master; + inputs.nixpkgs.url = github:NixOS/nixpkgs/release-21.11; outputs = { self, nixpkgs }: let From de8fc264d78847c407975ad6d4a3c687b6a9732f Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 23 Nov 2021 15:34:44 +0800 Subject: [PATCH 12/14] fix unsupported default parameter error message --- nac3core/src/toplevel/helper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nac3core/src/toplevel/helper.rs b/nac3core/src/toplevel/helper.rs index 5c8b85e75..b9259854f 100644 --- a/nac3core/src/toplevel/helper.rs +++ b/nac3core/src/toplevel/helper.rs @@ -461,6 +461,6 @@ pub fn parse_parameter_default_value(default: &ast::Expr, resolver: &(dyn Symbol ) ) } - _ => unimplemented!("only constant default is supported now at {}", default.location), + _ => Err(format!("unsupported default parameter at {}", default.location)) } } From 0ae1fe82b4628f71e46918ea8815d5e697efb129 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 23 Nov 2021 15:35:33 +0800 Subject: [PATCH 13/14] remove unnecessary cargo config extra-link-arg has been stabilized --- .cargo/config | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index ac01e73cc..000000000 --- a/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[unstable] -extra-link-arg = true From 7ee82de3123864dd2429771ccea9d6a4127a0f4d Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sat, 27 Nov 2021 20:27:46 +0800 Subject: [PATCH 14/14] nac3core: fixed weird type inference error --- nac3core/src/toplevel/composer.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index b0afaf263..a8faf130b 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -195,7 +195,7 @@ impl TopLevelComposer { signature: primitives.1.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature { args: vec![FuncArg { name: "_".into(), ty: num_ty.0, default_value: None }], ret: float, - vars: var_map, + vars: var_map.clone(), }))), var_id: Default::default(), instance_to_symbol: Default::default(), @@ -398,7 +398,7 @@ impl TopLevelComposer { signature: primitives.1.add_ty(TypeEnum::TFunc(RefCell::new(FunSignature { args: vec![FuncArg { name: "_".into(), ty: num_ty.0, default_value: None }], ret: primitives.0.bool, - vars: Default::default(), + vars: var_map, }))), var_id: Default::default(), instance_to_symbol: Default::default(), @@ -1066,7 +1066,7 @@ impl TopLevelComposer { and names thould not be the same as the keywords" .into()); } - + let arg_with_default: Vec<(&ast::Located>, Option<&ast::Expr>)> = args .args .iter() @@ -1307,7 +1307,7 @@ impl TopLevelComposer { } let mut result = Vec::new(); - + let arg_with_default: Vec<(&ast::Located>, Option<&ast::Expr>)> = args .args .iter() @@ -1319,7 +1319,7 @@ impl TopLevelComposer { .map(|x| -> Option<&ast::Expr> { Some(x) }) .chain(std::iter::repeat(None)) ).collect_vec(); - + for (x, default) in arg_with_default.into_iter().rev() { let name = x.node.arg; if name != zelf {