diff --git a/nac3artiq/src/lib.rs b/nac3artiq/src/lib.rs index 7148255..13bc850 100644 --- a/nac3artiq/src/lib.rs +++ b/nac3artiq/src/lib.rs @@ -38,7 +38,7 @@ use tempfile::{self, TempDir}; use crate::{ codegen::{rpc_codegen_callback, ArtiqCodeGenerator}, - symbol_resolver::{InnerResolver, PythonHelper, Resolver}, + symbol_resolver::{InnerResolver, PythonHelper, Resolver, DeferredEvaluationStore}, }; mod codegen; @@ -89,6 +89,7 @@ struct Nac3 { top_levels: Vec, string_store: Arc>>, exception_ids: Arc>>, + deferred_eval_store: DeferredEvaluationStore } create_exception!(nac3artiq, CompileError, exceptions::PyException); @@ -400,6 +401,7 @@ impl Nac3 { working_directory, string_store: Default::default(), exception_ids: Default::default(), + deferred_eval_store: DeferredEvaluationStore::new(), }) } @@ -522,6 +524,7 @@ impl Nac3 { helper, string_store: self.string_store.clone(), exception_ids: self.exception_ids.clone(), + deferred_eval_store: self.deferred_eval_store.clone(), }))) as Arc; let name_to_pyid = Rc::new(name_to_pyid); @@ -607,6 +610,7 @@ impl Nac3 { helper, string_store: self.string_store.clone(), exception_ids: self.exception_ids.clone(), + deferred_eval_store: self.deferred_eval_store.clone(), }))) as Arc; let (_, def_id, _) = composer .register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into()) diff --git a/nac3artiq/src/symbol_resolver.rs b/nac3artiq/src/symbol_resolver.rs index 66380a3..e26435c 100644 --- a/nac3artiq/src/symbol_resolver.rs +++ b/nac3artiq/src/symbol_resolver.rs @@ -16,7 +16,10 @@ use pyo3::{ }; use std::{ collections::{HashMap, HashSet}, - sync::Arc, + sync::{ + Arc, + atomic::{AtomicBool, Ordering::Relaxed} + } }; use crate::PrimitivePythonId; @@ -30,6 +33,21 @@ pub enum PrimitiveValue { Bool(bool), } +#[derive(Clone)] +pub struct DeferredEvaluationStore { + needs_defer: Arc, + store: Arc, PyObject, String)>>>, +} + +impl DeferredEvaluationStore { + pub fn new() -> Self { + DeferredEvaluationStore { + needs_defer: Arc::new(AtomicBool::new(true)), + store: Arc::new(RwLock::new(Vec::new())), + } + } +} + pub struct InnerResolver { pub id_to_type: RwLock>, pub id_to_def: RwLock>, @@ -44,6 +62,7 @@ pub struct InnerResolver { pub helper: PythonHelper, pub string_store: Arc>>, pub exception_ids: Arc>>, + pub deferred_eval_store: DeferredEvaluationStore, // module specific pub name_to_pyid: HashMap, pub module: PyObject, @@ -298,28 +317,40 @@ impl InnerResolver { let constraint_types = { let constraints = pyty.getattr("__constraints__").unwrap(); let mut result: Vec = vec![]; + let needs_defer = self.deferred_eval_store.needs_defer.load(Relaxed); for i in 0.. { if let Ok(constr) = constraints.get_item(i) { - result.push({ - match self.get_pyty_obj_type(py, constr, unifier, defs, primitives)? { - Ok((ty, _)) => { - if unifier.is_concrete(ty, &[]) { - ty - } else { - return Ok(Err(format!( - "the {}th constraint of TypeVar `{}` is not concrete", - i + 1, - pyty.getattr("__name__")?.extract::()? - ))); + if needs_defer { + result.push(unifier.get_dummy_var().0); + } else { + result.push({ + match self.get_pyty_obj_type(py, constr, unifier, defs, primitives)? { + Ok((ty, _)) => { + if unifier.is_concrete(ty, &[]) { + ty + } else { + return Ok(Err(format!( + "the {}th constraint of TypeVar `{}` is not concrete", + i + 1, + pyty.getattr("__name__")?.extract::()? + ))); + } } + Err(err) => return Ok(Err(err)), } - Err(err) => return Ok(Err(err)), - } - }) + }) + } } else { break; } } + if needs_defer { + self.deferred_eval_store.store.write() + .push((result.clone(), + constraints.extract()?, + pyty.getattr("__name__")?.extract::()? + )) + } result }; let res = @@ -908,15 +939,14 @@ impl SymbolResolver for Resolver { } } if let Ok(t) = sym_ty { - self.0.pyid_to_type.write().insert(*id, t); + if let TypeEnum::TVar { .. } = &*unifier.get_ty(t) { + self.0.pyid_to_type.write().insert(*id, t); + } } Ok(sym_ty) }) .unwrap(), }; - if let Ok(t) = &result { - self.0.id_to_type.write().insert(str, *t); - } result } } @@ -992,6 +1022,45 @@ impl SymbolResolver for Resolver { } } + fn handle_deferred_eval( + &self, + unifier: &mut Unifier, + defs: &[Arc>], + primitives: &PrimitiveStore + ) -> Result<(), String> { + // we don't need a lock because this will only be run in a single thread + if self.0.deferred_eval_store.needs_defer.load(Relaxed) { + self.0.deferred_eval_store.needs_defer.store(false, Relaxed); + let store = self.0.deferred_eval_store.store.read(); + Python::with_gil(|py| -> PyResult> { + for (variables, constraints, name) in store.iter() { + let constraints: &PyAny = constraints.as_ref(py); + for (i, var) in variables.iter().enumerate() { + if let Ok(constr) = constraints.get_item(i) { + match self.0.get_pyty_obj_type(py, constr, unifier, defs, primitives)? { + Ok((ty, _)) => { + if !unifier.is_concrete(ty, &[]) { + return Ok(Err(format!( + "the {}th constraint of TypeVar `{}` is not concrete", + i + 1, + name, + ))); + } + unifier.unify(ty, *var).unwrap() + } + Err(err) => return Ok(Err(err)), + } + } else { + break; + } + } + } + Ok(Ok(())) + }).unwrap()? + } + Ok(()) + } + fn get_exception_id(&self, tyid: usize) -> usize { let exn_ids = self.0.exception_ids.read(); exn_ids.get(&tyid).cloned().unwrap_or(0) diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index bfebffb..5afeba8 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -496,7 +496,13 @@ pub fn gen_func_instance<'ctx, 'a>( } let symbol = format!("{}.{}", name, instance_to_symbol.len()); instance_to_symbol.insert(key, symbol.clone()); - let key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), sign, Some(var_id)); + let mut filter = var_id.clone(); + if let Some((obj_ty, _)) = &obj { + if let TypeEnum::TObj { params, .. } = &*ctx.unifier.get_ty(*obj_ty) { + filter.extend(params.keys()); + } + } + let key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), sign, Some(&filter)); let instance = instance_to_stmt.get(&key).unwrap(); let mut store = ConcreteTypeStore::new(); diff --git a/nac3core/src/symbol_resolver.rs b/nac3core/src/symbol_resolver.rs index d5df9f2..96f941d 100644 --- a/nac3core/src/symbol_resolver.rs +++ b/nac3core/src/symbol_resolver.rs @@ -141,7 +141,15 @@ pub trait SymbolResolver { fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option; fn get_string_id(&self, s: &str) -> i32; fn get_exception_id(&self, tyid: usize) -> usize; - // handle function call etc. + + fn handle_deferred_eval( + &self, + _unifier: &mut Unifier, + _top_level_defs: &[Arc>], + _primitives: &PrimitiveStore + ) -> Result<(), String> { + Ok(()) + } } thread_local! { diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 6350f69..991926a 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -775,6 +775,18 @@ impl TopLevelComposer { return Err(errors.into_iter().sorted().join("\n----------\n")); } + for (def, _) in def_ast_list.iter().skip(self.builtin_num) { + match &*def.read() { + TopLevelDef::Class { resolver: Some(resolver), .. } + | TopLevelDef::Function { resolver: Some(resolver), .. } => { + if let Err(e) = resolver.handle_deferred_eval(unifier, &temp_def_list, primitives) { + errors.insert(e); + } + } + _ => {} + } + } + Ok(()) } @@ -1057,17 +1069,7 @@ impl TopLevelComposer { let (method_dummy_ty, method_id) = Self::get_class_method_def_info(class_methods_def, *name)?; - // the method var map can surely include the class's generic parameters - let mut method_var_map: HashMap = class_type_vars_def - .iter() - .map(|ty| { - if let TypeEnum::TVar { id, .. } = unifier.get_ty(*ty).as_ref() { - (*id, *ty) - } else { - unreachable!("must be type var here") - } - }) - .collect(); + let mut method_var_map: HashMap = HashMap::new(); let arg_types: Vec = { // check method parameters cannot have same name @@ -1494,8 +1496,8 @@ impl TopLevelComposer { if new_var_ids != *var_id { let new_signature = FunSignature { args: args.clone(), - ret: ret.clone(), - vars: new_var_ids.iter().zip(vars.values()).map(|(id, v)| (*id, v.clone())).collect(), + ret: *ret, + vars: new_var_ids.iter().zip(vars.values()).map(|(id, v)| (*id, *v)).collect(), }; unifier.unification_table.set_value(*signature, Rc::new(TypeEnum::TFunc(new_signature))); *var_id = new_var_ids; @@ -1674,13 +1676,13 @@ impl TopLevelComposer { simple_name, signature, resolver, - var_id: insted_vars, .. } = &mut *function_def { if let TypeEnum::TFunc(FunSignature { args, ret, vars }) = unifier.get_ty(*signature).as_ref() { + let mut vars = vars.clone(); // None if is not class method let uninst_self_type = { if let Some(class_id) = method_class.get(&DefinitionId(id)) { @@ -1695,6 +1697,12 @@ impl TopLevelComposer { &ty_ann, &mut None )?; + vars.extend(type_vars.iter().map(|ty| + if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*ty) { + (*id, *ty) + } else { + unreachable!() + })); Some((self_ty, type_vars.clone())) } else { unreachable!("must be class def") @@ -1725,7 +1733,7 @@ impl TopLevelComposer { .collect_vec(); let mut result: Vec> = Default::default(); for comb in var_combs { - result.push(insted_vars.clone().into_iter().zip(comb).collect()); + result.push(vars.keys().cloned().zip(comb).collect()); } // NOTE: if is empty, means no type var, append a empty subst, ok to do this? if result.is_empty() { @@ -1913,7 +1921,7 @@ impl TopLevelComposer { } instance_to_stmt.insert( - get_subst_key(unifier, self_type, &subst, Some(insted_vars)), + get_subst_key(unifier, self_type, &subst, Some(&vars.keys().cloned().collect())), FunInstance { body: Arc::new(fun_body), unifier_id: 0, 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 793af15..5a9c4fe 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 @@ -1,13 +1,13 @@ --- source: nac3core/src/toplevel/test.rs -assertion_line: 540 +assertion_line: 549 expression: res_vec --- [ "Class {\nname: \"Generic_A\",\nancestors: [\"{class: Generic_A, params: [\\\"V\\\"]}\", \"{class: B, params: []}\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n", - "Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: [6]\n}\n", - "Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [6, 17]\n}\n", + "Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", + "Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [17]\n}\n", "Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], 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 4227911..7879cf3 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 @@ -1,6 +1,6 @@ --- source: nac3core/src/toplevel/test.rs -assertion_line: 540 +assertion_line: 549 expression: res_vec --- @@ -10,8 +10,8 @@ expression: res_vec "Function {\nname: \"A.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[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: [\\\"var6\\\"]}\", \"{class: A, params: [\\\"float\\\"]}\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"var6\"]\n}\n", - "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: [6]\n}\n", - "Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: [6]\n}\n", + "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", + "Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\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:T], list[virtual[B[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 6dc65e9..1a33c0f 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 @@ -1,14 +1,14 @@ --- source: nac3core/src/toplevel/test.rs -assertion_line: 540 +assertion_line: 549 expression: res_vec --- [ "Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n", "Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"T\\\", \\\"V\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v:V], none]\"), (\"fun\", \"fn[[a:T], V]\")],\ntype_vars: [\"T\", \"V\"]\n}\n", - "Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [18, 19]\n}\n", - "Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [19, 24]\n}\n", + "Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [19]\n}\n", + "Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [24]\n}\n", "Function {\nname: \"gfun\",\nsig: \"fn[[a:A[int32, list[float]]], 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 7ccdd44..50a7a66 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 @@ -1,13 +1,13 @@ --- source: nac3core/src/toplevel/test.rs -assertion_line: 540 +assertion_line: 549 expression: res_vec --- [ "Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var5\\\", \\\"var6\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"var5\", \"var6\"]\n}\n", - "Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[bool, float], b:B], none]\",\nvar_id: [6]\n}\n", - "Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[bool, float]], A[bool, int32]]\",\nvar_id: [6]\n}\n", + "Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[bool, float], b:B], none]\",\nvar_id: []\n}\n", + "Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[bool, float]], A[bool, int32]]\",\nvar_id: []\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[bool, float]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], 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",