diff --git a/flake.nix b/flake.nix index 07cea776..7987b259 100644 --- a/flake.nix +++ b/flake.nix @@ -180,7 +180,9 @@ clippy pre-commit rustfmt + rust-analyzer ]; + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; shellHook = '' export DEMO_LINALG_STUB=${packages.x86_64-linux.demo-linalg-stub}/lib/liblinalg.a diff --git a/nac3artiq/demo/dataset_db.mdb b/nac3artiq/demo/dataset_db.mdb new file mode 100644 index 00000000..5511ad47 Binary files /dev/null and b/nac3artiq/demo/dataset_db.mdb differ diff --git a/nac3artiq/demo/dataset_db.mdb-lock b/nac3artiq/demo/dataset_db.mdb-lock new file mode 100644 index 00000000..82238114 Binary files /dev/null and b/nac3artiq/demo/dataset_db.mdb-lock differ diff --git a/nac3artiq/demo/demo.py b/nac3artiq/demo/demo.py index aa135757..8e4251b6 100644 --- a/nac3artiq/demo/demo.py +++ b/nac3artiq/demo/demo.py @@ -1,26 +1,87 @@ from min_artiq import * +from numpy import int32 +# @nac3 +# class A: +# a: int32 +# core: KernelInvariant[Core] + +# def __init__(self, a: int32): +# self.core = Core() +# self.a = a + +# @kernel +# def output_all_fields(self): +# #print(self.a) +# pass + +# @kernel +# def set_a(self, a: int32): +# self.a = a + +# @nac3 +# class B(A): +# b: int32 + +# def __init__(self, b: int32): +# # A.__init__(self, b + 1) +# self.core = Core() +# self.a = b +# self.b = b +# self.set_b(b) + +# @kernel +# def output_parent_fields(self): +# # A.output_all_fields(self) +# pass + +# @kernel +# def output_all_fields(self): +# # A.output_all_fields(self) +# pass +# #print(self.b) + +# @kernel +# def set_b(self, b: int32): +# self.b = b + @nac3 -class Demo: +class C: + c: Kernel[int32] + a: Kernel[int32] + b: Kernel[int32] core: KernelInvariant[Core] - led0: KernelInvariant[TTLOut] - led1: KernelInvariant[TTLOut] - def __init__(self): + def __init__(self, c: int32): + # B.__init__(self, c + 1) self.core = Core() - self.led0 = TTLOut(self.core, 18) - self.led1 = TTLOut(self.core, 19) + self.a = c + self.b = c + self.c = c + + @kernel + def output_parent_fields(self): + # B.output_all_fields(self) + pass @kernel - def run(self): - self.core.reset() - while True: - with parallel: - self.led0.pulse(100.*ms) - self.led1.pulse(100.*ms) - self.core.delay(100.*ms) + def output_all_fields(self): + # B.output_all_fields(self) + #print(self.c) + pass + @kernel + def set_c(self, c: int32): + self.c = c + + @kernel + def run(self): + self.output_all_fields() + # self.set_a(1) + # self.set_b(2) + self.set_c(3) + self.output_all_fields() if __name__ == "__main__": - Demo().run() + C(10).run() diff --git a/nac3artiq/demo/module.elf b/nac3artiq/demo/module.elf new file mode 100644 index 00000000..41377d06 Binary files /dev/null and b/nac3artiq/demo/module.elf differ diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 603a508e..c3b36909 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -1,3 +1,4 @@ +use indexmap::IndexMap; use nac3parser::ast::fold::Fold; use std::rc::Rc; @@ -5,7 +6,7 @@ use crate::{ codegen::{expr::get_subst_key, stmt::exn_constructor}, symbol_resolver::SymbolValue, typecheck::{ - type_inferencer::{FunctionData, Inferencer}, + type_inferencer::{report_error, FunctionData, Inferencer}, typedef::{TypeVar, VarMap}, }, }; @@ -389,8 +390,15 @@ impl TopLevelComposer { } pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet> { - self.analyze_top_level_class_type_var()?; - self.analyze_top_level_class_bases()?; + let unifier = self.unifier.borrow_mut(); + let primitives_store = &self.primitives_ty; + let def_list = &self.definition_ast_list; + + // Step 1. Analyze type variables within class definitions + Self::analyze_top_level_class_type_var2(def_list, unifier, primitives_store, (&self.keyword_list, &self.core_config))?; + + // self.analyze_top_level_class_type_var()?; + // self.analyze_top_level_class_bases()?; self.analyze_top_level_class_fields_methods()?; self.analyze_top_level_function()?; if inference { @@ -399,178 +407,70 @@ impl TopLevelComposer { Ok(()) } - /// step 1, analyze the type vars associated with top level class - fn analyze_top_level_class_type_var(&mut self) -> Result<(), HashSet> { - let def_list = &self.definition_ast_list; - let temp_def_list = self.extract_def_list(); - let unifier = self.unifier.borrow_mut(); - let primitives_store = &self.primitives_ty; - - let mut analyze = |class_def: &Arc>, class_ast: &Option| { - // only deal with class def here - let mut class_def = class_def.write(); - let (class_bases_ast, class_def_type_vars, class_resolver) = { - if let TopLevelDef::Class { type_vars, resolver, .. } = &mut *class_def { - let Some(ast::Located { node: ast::StmtKind::ClassDef { bases, .. }, .. }) = - class_ast - else { - unreachable!() - }; - - (bases, type_vars, resolver) - } else { - return Ok(()); - } + fn analyze_bases( + class_def: &Arc>, + class_ast: &Option, + temp_def_list: &[Arc>], + unifier: &mut Unifier, + primitives_store: &PrimitiveStore, + ) -> Result<(), HashSet> { + let mut class_def = class_def.write(); + let (class_def_id, class_ancestors, class_bases_ast, class_type_vars, class_resolver) = { + let TopLevelDef::Class { object_id, ancestors, type_vars, resolver, .. } = + &mut *class_def + else { + unreachable!() }; - let class_resolver = class_resolver.as_ref().unwrap(); - let class_resolver = &**class_resolver; - - let mut is_generic = false; - for b in class_bases_ast { - match &b.node { - // analyze typevars bounded to the class, - // only support things like `class A(Generic[T, V])`, - // things like `class A(Generic[T, V, ImportedModule.T])` is not supported - // i.e. only simple names are allowed in the subscript - // should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params - ast::ExprKind::Subscript { value, slice, .. } - if { - matches!( - &value.node, - ast::ExprKind::Name { id, .. } if id == &"Generic".into() - ) - } => - { - if is_generic { - return Err(HashSet::from([format!( - "only single Generic[...] is allowed (at {})", - b.location - )])); - } - is_generic = true; - - let type_var_list: Vec<&ast::Expr<()>>; - // if `class A(Generic[T, V, G])` - if let ast::ExprKind::Tuple { elts, .. } = &slice.node { - type_var_list = elts.iter().collect_vec(); - // `class A(Generic[T])` - } else { - type_var_list = vec![&**slice]; - } - - // parse the type vars - let type_vars = type_var_list - .into_iter() - .map(|e| { - class_resolver.parse_type_annotation( - &temp_def_list, - unifier, - primitives_store, - e, - ) - }) - .collect::, _>>()?; - - // check if all are unique type vars - let all_unique_type_var = { - let mut occurred_type_var_id: HashSet = HashSet::new(); - type_vars.iter().all(|x| { - let ty = unifier.get_ty(*x); - if let TypeEnum::TVar { id, .. } = ty.as_ref() { - occurred_type_var_id.insert(*id) - } else { - false - } - }) - }; - if !all_unique_type_var { - return Err(HashSet::from([format!( - "duplicate type variable occurs (at {})", - slice.location - )])); - } - - // add to TopLevelDef - class_def_type_vars.extend(type_vars); - } - - // if others, do nothing in this function - _ => continue, - } - } - Ok(()) + let Some(ast::Located { node: ast::StmtKind::ClassDef { bases, .. }, .. }) = class_ast + else { + unreachable!() + }; + (object_id, ancestors, bases, type_vars, resolver.as_ref().unwrap().as_ref()) }; - let mut errors = HashSet::new(); - for (class_def, class_ast) in def_list.iter().skip(self.builtin_num) { - if class_ast.is_none() { - continue; - } - if let Err(e) = analyze(class_def, class_ast) { - errors.extend(e); - } - } - if !errors.is_empty() { - return Err(errors); - } - Ok(()) - } - /// step 2, base classes. - /// now that the type vars of all classes are done, handle base classes and - /// put Self class into the ancestors list. We only allow single inheritance - fn analyze_top_level_class_bases(&mut self) -> Result<(), HashSet> { - if self.unifier.top_level.is_none() { - let ctx = Arc::new(self.make_top_level_context()); - self.unifier.top_level = Some(ctx); - } + let mut is_generic = false; + let mut has_base = false; + // Check class bases for typevars + for b in class_bases_ast { + match &b.node { + // analyze typevars bounded to the class, + // only support things like `class A(Generic[T, V])`, + // things like `class A(Generic[T, V, ImportedModule.T])` is not supported + // i.e. only simple names are allowed in the subscript + // should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params + ast::ExprKind::Subscript { value, slice, .. } if matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"Generic".into()) => + { + if is_generic { + return report_error("only single Generic[...] is allowed", b.location); + } + is_generic = true; - let temp_def_list = self.extract_def_list(); - let unifier = self.unifier.borrow_mut(); - let primitive_types = self.primitives_ty; - - let mut get_direct_parents = - |class_def: &Arc>, class_ast: &Option| { - let mut class_def = class_def.write(); - let (class_def_id, class_bases, class_ancestors, class_resolver, class_type_vars) = { - if let TopLevelDef::Class { - ancestors, resolver, object_id, type_vars, .. - } = &mut *class_def - { - let Some(ast::Located { - node: ast::StmtKind::ClassDef { bases, .. }, .. - }) = class_ast - else { - unreachable!() - }; - - (object_id, bases, ancestors, resolver, type_vars) + let type_var_list: Vec<&ast::Expr<()>>; + // if `class A(Generic[T, V, G])` + if let ast::ExprKind::Tuple { elts, .. } = &slice.node { + type_var_list = elts.iter().collect_vec(); + // `class A(Generic[T])` } else { - return Ok(()); + type_var_list = vec![&**slice]; } - }; - let class_resolver = class_resolver.as_ref().unwrap(); - let class_resolver = &**class_resolver; - let mut has_base = false; - for b in class_bases { - // type vars have already been handled, so skip on `Generic[...]` - if matches!( - &b.node, - ast::ExprKind::Subscript { value, .. } - if matches!( - &value.node, - ast::ExprKind::Name { id, .. } if id == &"Generic".into() + let type_vars = type_var_list + .into_iter() + .map(|e| { + class_resolver.parse_type_annotation( + temp_def_list, + unifier, + primitives_store, + e, ) - ) { - continue; - } + }) + .collect::, _>>()?; + class_type_vars.extend(type_vars); + } + ast::ExprKind::Name { .. } => { if has_base { - return Err(HashSet::from([format!( - "a class definition can only have at most one base class \ - declaration and one generic declaration (at {})", - b.location - )])); + return report_error("a class definition can only have at most one base class declaration and one generic declaration", b.location); } has_base = true; @@ -578,9 +478,9 @@ impl TopLevelComposer { // bast_ty if it is a CustomClassKind let base_ty = parse_ast_to_type_annotation_kinds( class_resolver, - &temp_def_list, + temp_def_list, unifier, - &primitive_types, + primitives_store, b, vec![(*class_def_id, class_type_vars.clone())] .into_iter() @@ -590,123 +490,127 @@ impl TopLevelComposer { if let TypeAnnotation::CustomClass { .. } = &base_ty { class_ancestors.push(base_ty); } else { - return Err(HashSet::from([format!( - "class base declaration can only be custom class (at {})", + return report_error( + "class base declaration can only be custom class", b.location, - )])); + ); } } - Ok(()) - }; + // TODO: Report Error here + _ => { + println!("Type was => {}", b.node.name()); + } + } + } - // first, only push direct parent into the list + Ok(()) + } + + fn analyze_ancestors( + class_def: &Arc>, + temp_def_list: &[Arc>], + ) { + // Check if class has a direct parent + let mut class_def = class_def.write(); + let TopLevelDef::Class { ancestors, type_vars, object_id, .. } = &mut *class_def else { + unreachable!() + }; + let mut anc_set = HashMap::new(); + + if let Some(ancestor) = ancestors.first() { + let TypeAnnotation::CustomClass { id, .. } = ancestor else { unreachable!() }; + let TopLevelDef::Class { ancestors: parent_ancestors, .. } = + &*temp_def_list[id.0].read() + else { + unreachable!() + }; + for anc in parent_ancestors.iter().skip(1) { + let TypeAnnotation::CustomClass { id, .. } = anc else { unreachable!() }; + anc_set.insert(id, anc.clone()); + } + ancestors.extend(anc_set.into_iter().map(|f| f.1).collect::>()); + } + + ancestors.insert(0, make_self_type_annotation(type_vars.as_slice(), *object_id)); + } + + /// step 1, analyze the type vars associated with top level class + fn analyze_top_level_class_type_var2( + def_list: &[DefAst], + unifier: &mut Unifier, + primitives_store: &PrimitiveStore, + core_info: (&HashSet, &ComposerConfig), + ) -> Result<(), HashSet> { let mut errors = HashSet::new(); - for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) { - if class_ast.is_none() { - continue; - } - if let Err(e) = get_direct_parents(class_def, class_ast) { - errors.extend(e); - } - } - if !errors.is_empty() { - return Err(errors); - } + let mut temp_def_list: Vec>> = Vec::default(); - // second, get all ancestors - let mut ancestors_store: HashMap> = HashMap::default(); - let mut get_all_ancestors = - |class_def: &Arc>| -> Result<(), HashSet> { - let class_def = class_def.read(); - let (class_ancestors, class_id) = { - if let TopLevelDef::Class { ancestors, object_id, .. } = &*class_def { - (ancestors, *object_id) - } else { - return Ok(()); - } - }; - ancestors_store.insert( - class_id, - // if class has direct parents, get all ancestors of its parents. Else just empty - if class_ancestors.is_empty() { - vec![] - } else { - Self::get_all_ancestors_helper( - &class_ancestors[0], - temp_def_list.as_slice(), - )? - }, - ); - Ok(()) - }; - for (class_def, ast) in self.definition_ast_list.iter().skip(self.builtin_num) { - if ast.is_none() { - continue; - } - if let Err(e) = get_all_ancestors(class_def) { - errors.extend(e); - } - } - if !errors.is_empty() { - return Err(errors); - } - - // insert the ancestors to the def list - for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) { - if class_ast.is_none() { - continue; - } - let mut class_def = class_def.write(); - let (class_ancestors, class_id, class_type_vars) = { - if let TopLevelDef::Class { ancestors, object_id, type_vars, .. } = &mut *class_def - { - (ancestors, *object_id, type_vars) - } else { - continue; + for (class_def, class_ast) in def_list { + if class_ast.is_some() && matches!(&*class_def.read(), TopLevelDef::Class { .. }) { + // Add type vars and direct parents + if let Err(e) = Self::analyze_bases( + class_def, + class_ast, + &temp_def_list, + unifier, + primitives_store, + ) { + errors.extend(e); } - }; + // Get class ancestors order matters here. Like python we will only consider classes to be correct if they are in same order + Self::analyze_ancestors(class_def, &temp_def_list); - let ans = ancestors_store.get_mut(&class_id).unwrap(); - class_ancestors.append(ans); + let mut type_var_to_concrete_def: HashMap = HashMap::new(); + if let Err(e) = Self::analyze_single_class_methods_fields( + class_def, + &class_ast.as_ref().unwrap().node, + &temp_def_list, + unifier, + primitives_store, + &mut type_var_to_concrete_def, + core_info, + ) { + errors.extend(e); + } - // insert self type annotation to the front of the vector to maintain the order - class_ancestors - .insert(0, make_self_type_annotation(class_type_vars.as_slice(), class_id)); - - // special case classes that inherit from Exception - if class_ancestors - .iter() - .any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) - { - // if inherited from Exception, the body should be a pass - let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node else { + // special case classes that inherit from Exception + let TopLevelDef::Class { ancestors: class_ancestors, loc, .. } = &*class_def.read() + else { unreachable!() }; - - for stmt in body { - if matches!( - stmt.node, - ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. } - ) { - return Err(HashSet::from([ - "Classes inherited from exception should have no custom fields/methods" - .into(), - ])); + if class_ancestors + .iter() + .any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) + { + // if inherited from Exception, the body should be a pass + let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node + else { + unreachable!() + }; + for stmt in body { + if matches!( + stmt.node, + ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. } + ) { + errors.extend(report_error("Classes inherited from exception should have no custom fields/methods", loc.unwrap())); + } } } } + // println!("Adding class_def of {name} to the temp_def_list with ID {}", object_id.0); + temp_def_list.push(class_def.clone()); } // deal with ancestor of Exception object - let TopLevelDef::Class { name, ancestors, object_id, .. } = - &mut *self.definition_ast_list[7].0.write() + let TopLevelDef::Class { name, ancestors, object_id, .. } = &mut *def_list[7].0.write() else { unreachable!() }; - assert_eq!(*name, "Exception".into()); ancestors.push(make_self_type_annotation(&[], *object_id)); + if !errors.is_empty() { + return Err(errors); + } Ok(()) } @@ -1199,126 +1103,81 @@ impl TopLevelComposer { let mut method_var_map = VarMap::new(); let arg_types: Vec = { - // check method parameters cannot have same name + // Function arguments must have: + // 1) `self` as first argument + // 2) unique names + // 3) names different than keywords + match args.args.first() { + Some(id) if id.node.arg == "self".into() => {}, + _ => return report_error("class method must have a `self` parameter", b.location), + } let mut defined_parameter_name: HashSet<_> = HashSet::new(); - let zelf: StrRef = "self".into(); - for x in &args.args { - if !defined_parameter_name.insert(x.node.arg) - || (keyword_list.contains(&x.node.arg) && x.node.arg != zelf) - { - return Err(HashSet::from([ - format!("top level function must have unique parameter names \ - and names should not be the same as the keywords (at {})", - x.location), - ])) + for arg in args.args.iter().skip(1) { + if !defined_parameter_name.insert(arg.node.arg) { + return report_error("class method must have a unique parameter names", b.location) + } + if keyword_list.contains(&arg.node.arg) { + return report_error("parameter names should not be the same as the keywords", b.location) } } - if name == &"__init__".into() && !defined_parameter_name.contains(&zelf) { - return Err(HashSet::from([ - format!("__init__ method must have a `self` parameter (at {})", b.location), - ])) + // `self` must not be provided type annotation or default value + if args.args.len() == args.defaults.len() { + return report_error("`self` cannot have a default value", b.location) } - if !defined_parameter_name.contains(&zelf) { - return Err(HashSet::from([ - format!("class method must have a `self` parameter (at {})", b.location), - ])) + if args.args[0].node.annotation.is_some() { + return report_error("`self` cannot have a type annotation", b.location) } - let mut result = Vec::new(); - - 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 = { - let annotation_expr = x - .node - .annotation - .as_ref() - .ok_or_else(|| HashSet::from([ - format!( - "type annotation needed for `{}` at {}", - x.node.arg, x.location - ), - ]))? - .as_ref(); - parse_ast_to_type_annotation_kinds( - class_resolver, - temp_def_list, - unifier, - primitives, - annotation_expr, - vec![(class_id, class_type_vars_def.clone())] - .into_iter() - .collect::>(), - )? + let no_defaults = args.args.len() - args.defaults.len() - 1; + for (idx, x) in itertools::enumerate(args.args.iter().skip(1)) { + let type_ann = { + let Some(annotation_expr) = x.node.annotation.as_ref() else {return report_error(format!("type annotation needed for `{}`", x.node.arg).as_str(), x.location)}; + parse_ast_to_type_annotation_kinds( + class_resolver, + 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 = 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 { + let TypeAnnotation::TypeVar(ty) = type_var_within else { + unreachable!("must be type var annotation") }; - // 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 { - let TypeAnnotation::TypeVar(ty) = type_var_within else { - unreachable!("must be type var annotation") - }; - 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); - } + 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); } - // finish handling type vars - let dummy_func_arg = FuncArg { - name, - ty: unifier.get_dummy_var().ty, - default_value: match default { - None => None, - Some(default) => { - if name == "self".into() { - return Err(HashSet::from([ - 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| HashSet::from([ - format!("{} (at {})", err, x.location), - ]))?; - v - }) - } - }, - is_vararg: false, - }; - // 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); } + // finish handling type vars + let dummy_func_arg = FuncArg { + name: x.node.arg, + ty: unifier.get_dummy_var().ty, + default_value: if idx < no_defaults { None } else { + let default_idx = idx - no_defaults; + + Some({ + let v = Self::parse_parameter_default_value(&args.defaults[default_idx], class_resolver)?; + Self::check_default_param_type(&v, &type_ann, primitives, unifier).map_err(|err| report_error::<()>(err.as_str(), x.location).unwrap_err())?; + v + }) + }, + is_vararg: false, + }; + // 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 }; @@ -1440,23 +1299,13 @@ impl TopLevelComposer { match v { ast::Constant::Bool(_) | ast::Constant::Str(_) | ast::Constant::Int(_) | ast::Constant::Float(_) => {} _ => { - return Err(HashSet::from([ - format!( - "unsupported statement in class definition body (at {})", - b.location - ), - ])) + return report_error("unsupported statement in class definition body", b.location) } } class_attributes_def.push((*attr, dummy_field_type, v.clone())); } _ => { - return Err(HashSet::from([ - format!( - "unsupported statement in class definition body (at {})", - b.location - ), - ])) + return report_error("unsupported statement in class definition body", b.location) } } annotation @@ -1482,43 +1331,22 @@ impl TopLevelComposer { }; if !class_type_vars_def.contains(&t) { - return Err(HashSet::from([ - format!( - "class fields can only use type \ - vars over which the class is generic (at {})", - annotation.location - ), - ])) + return report_error("class fields can only use type vars over which the class is generic", b.location) } } type_var_to_concrete_def.insert(dummy_field_type, parsed_annotation); } else { - return Err(HashSet::from([ - format!( - "same class fields `{}` defined twice (at {})", - attr, target.location - ), - ])) + return report_error(format!("same class fields `{}` defined twice", attr).as_str(), target.location) } } else { - return Err(HashSet::from([ - format!( - "unsupported statement type in class definition body (at {})", - target.location - ), - ])) + return report_error("unsupported statement in class definition body", target.location) } } ast::StmtKind::Assign { .. } // we don't class attributes | ast::StmtKind::Expr { value: _, .. } // typically a docstring; ignoring all expressions matches CPython behavior | ast::StmtKind::Pass { .. } => {} _ => { - return Err(HashSet::from([ - format!( - "unsupported statement in class definition body (at {})", - b.location - ), - ])) + return report_error("unsupported statement in class definition body", b.location) } } } @@ -1570,44 +1398,27 @@ impl TopLevelComposer { // handle methods override // since we need to maintain the order, create a new list - let mut new_child_methods: Vec<(StrRef, Type, DefinitionId)> = Vec::new(); - let mut is_override: HashSet = HashSet::new(); - for (anc_method_name, anc_method_ty, anc_method_def_id) in methods { - // find if there is a method with same name in the child class - let mut to_be_added = (*anc_method_name, *anc_method_ty, *anc_method_def_id); - for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def { - if class_method_name == anc_method_name { - // ignore and handle self - // if is __init__ method, no need to check return type - let ok = class_method_name == &"__init__".into() - || Self::check_overload_function_type( - *class_method_ty, - *anc_method_ty, - unifier, - type_var_to_concrete_def, - ); - if !ok { - return Err(HashSet::from([format!( - "method {class_method_name} has same name as ancestors' method, but incompatible type"), - ])); - } - // mark it as added - is_override.insert(*class_method_name); - to_be_added = (*class_method_name, *class_method_ty, *class_method_defid); - break; + let mut new_child_methods: IndexMap = methods.iter().map(|m| (m.0.clone(), (m.1.clone(), m.2.clone()))).collect(); + // let mut new_child_methods: Vec<(StrRef, Type, DefinitionId)> = methods.clone(); + for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def { + if let Some((ty, _ ) ) = new_child_methods.insert(*class_method_name, (*class_method_ty, *class_method_defid)) { + let ok = class_method_name == &"__init__".into() + || Self::check_overload_function_type( + *class_method_ty, + ty, + unifier, + type_var_to_concrete_def, + ); + if !ok { + return Err(HashSet::from([format!( + "method {class_method_name} has same name as ancestors' method, but incompatible type"), + ])); } } - new_child_methods.push(to_be_added); } - // add those that are not overriding method to the new_child_methods - for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def { - if !is_override.contains(class_method_name) { - new_child_methods.push((*class_method_name, *class_method_ty, *class_method_defid)); - } - } - // use the new_child_methods to replace all the elements in `class_methods_def` class_methods_def.clear(); - class_methods_def.extend(new_child_methods); + class_methods_def.extend(new_child_methods.iter().map(|f| (f.0.clone(), f.1.0, f.1.1)).collect_vec()); + let is_override: HashSet = HashSet::new(); // handle class fields let mut new_child_fields: Vec<(StrRef, Type, bool)> = Vec::new(); diff --git a/nac3core/src/toplevel/type_annotation.rs b/nac3core/src/toplevel/type_annotation.rs index d997f176..3603e465 100644 --- a/nac3core/src/toplevel/type_annotation.rs +++ b/nac3core/src/toplevel/type_annotation.rs @@ -1,6 +1,7 @@ use super::*; use crate::symbol_resolver::SymbolValue; use crate::toplevel::helper::{PrimDef, PrimDefDetails}; +use crate::typecheck::type_inferencer::report_error; use crate::typecheck::typedef::VarMap; use nac3parser::ast::Constant; use strum::IntoEnumIterator; @@ -97,7 +98,13 @@ pub fn parse_ast_to_type_annotation_kinds( Ok(TypeAnnotation::CustomClass { id: PrimDef::Exception.id(), params: Vec::default() }) } else if let Ok(obj_id) = resolver.get_identifier_def(*id) { let type_vars = { - let def_read = top_level_defs[obj_id.0].try_read(); + let Some(top_level_def) = top_level_defs.get(obj_id.0) else { + return report_error( + format!("Name Error undefined name {id}").as_str(), + expr.location, + ); + }; + let def_read = top_level_def.try_read(); if let Some(def_read) = def_read { if let TopLevelDef::Class { type_vars, .. } = &*def_read { type_vars.clone() diff --git a/nac3core/src/typecheck/type_inferencer/mod.rs b/nac3core/src/typecheck/type_inferencer/mod.rs index a5b8cd49..fa1352b5 100644 --- a/nac3core/src/typecheck/type_inferencer/mod.rs +++ b/nac3core/src/typecheck/type_inferencer/mod.rs @@ -114,7 +114,7 @@ impl Fold<()> for NaiveFolder { } } -fn report_error(msg: &str, location: Location) -> Result { +pub fn report_error(msg: &str, location: Location) -> Result { Err(HashSet::from([format!("{msg} at {location}")])) } diff --git a/pyo3/nac3artiq.so b/pyo3/nac3artiq.so new file mode 100755 index 00000000..e73ca32b Binary files /dev/null and b/pyo3/nac3artiq.so differ