From 1084ba2158106231cb6decab35673dbf7ba23990 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Thu, 24 Mar 2022 21:29:46 +0800 Subject: [PATCH] nac3core: fixed typevar with finite range 1. Function type variables should not include class type variables, because they are not bound to the function. 2. Defer type variable constraint evaluation until we get all fields definition. --- nac3artiq/src/lib.rs | 6 +- nac3artiq/src/symbol_resolver.rs | 107 ++++++++++++++++++++++++------ nac3core/src/codegen/expr.rs | 8 ++- nac3core/src/symbol_resolver.rs | 10 ++- nac3core/src/toplevel/composer.rs | 40 ++++++----- 5 files changed, 133 insertions(+), 38 deletions(-) diff --git a/nac3artiq/src/lib.rs b/nac3artiq/src/lib.rs index 7148255eb..13bc850d7 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 66380a3cb..e26435c0f 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 bfebffb61..5afeba8c1 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 d5df9f2ab..96f941d2b 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 6350f6989..991926a62 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,