forked from M-Labs/nac3
1
0
Fork 0

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.
This commit is contained in:
pca006132 2022-03-24 21:29:46 +08:00
parent be75fa7368
commit 1084ba2158
5 changed files with 133 additions and 38 deletions

View File

@ -38,7 +38,7 @@ use tempfile::{self, TempDir};
use crate::{ use crate::{
codegen::{rpc_codegen_callback, ArtiqCodeGenerator}, codegen::{rpc_codegen_callback, ArtiqCodeGenerator},
symbol_resolver::{InnerResolver, PythonHelper, Resolver}, symbol_resolver::{InnerResolver, PythonHelper, Resolver, DeferredEvaluationStore},
}; };
mod codegen; mod codegen;
@ -89,6 +89,7 @@ struct Nac3 {
top_levels: Vec<TopLevelComponent>, top_levels: Vec<TopLevelComponent>,
string_store: Arc<RwLock<HashMap<String, i32>>>, string_store: Arc<RwLock<HashMap<String, i32>>>,
exception_ids: Arc<RwLock<HashMap<usize, usize>>>, exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
deferred_eval_store: DeferredEvaluationStore
} }
create_exception!(nac3artiq, CompileError, exceptions::PyException); create_exception!(nac3artiq, CompileError, exceptions::PyException);
@ -400,6 +401,7 @@ impl Nac3 {
working_directory, working_directory,
string_store: Default::default(), string_store: Default::default(),
exception_ids: Default::default(), exception_ids: Default::default(),
deferred_eval_store: DeferredEvaluationStore::new(),
}) })
} }
@ -522,6 +524,7 @@ impl Nac3 {
helper, helper,
string_store: self.string_store.clone(), string_store: self.string_store.clone(),
exception_ids: self.exception_ids.clone(), exception_ids: self.exception_ids.clone(),
deferred_eval_store: self.deferred_eval_store.clone(),
}))) })))
as Arc<dyn SymbolResolver + Send + Sync>; as Arc<dyn SymbolResolver + Send + Sync>;
let name_to_pyid = Rc::new(name_to_pyid); let name_to_pyid = Rc::new(name_to_pyid);
@ -607,6 +610,7 @@ impl Nac3 {
helper, helper,
string_store: self.string_store.clone(), string_store: self.string_store.clone(),
exception_ids: self.exception_ids.clone(), exception_ids: self.exception_ids.clone(),
deferred_eval_store: self.deferred_eval_store.clone(),
}))) as Arc<dyn SymbolResolver + Send + Sync>; }))) as Arc<dyn SymbolResolver + Send + Sync>;
let (_, def_id, _) = composer let (_, def_id, _) = composer
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into()) .register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into())

View File

@ -16,7 +16,10 @@ use pyo3::{
}; };
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
sync::Arc, sync::{
Arc,
atomic::{AtomicBool, Ordering::Relaxed}
}
}; };
use crate::PrimitivePythonId; use crate::PrimitivePythonId;
@ -30,6 +33,21 @@ pub enum PrimitiveValue {
Bool(bool), Bool(bool),
} }
#[derive(Clone)]
pub struct DeferredEvaluationStore {
needs_defer: Arc<AtomicBool>,
store: Arc<RwLock<Vec<(Vec<Type>, 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 struct InnerResolver {
pub id_to_type: RwLock<HashMap<StrRef, Type>>, pub id_to_type: RwLock<HashMap<StrRef, Type>>,
pub id_to_def: RwLock<HashMap<StrRef, DefinitionId>>, pub id_to_def: RwLock<HashMap<StrRef, DefinitionId>>,
@ -44,6 +62,7 @@ pub struct InnerResolver {
pub helper: PythonHelper, pub helper: PythonHelper,
pub string_store: Arc<RwLock<HashMap<String, i32>>>, pub string_store: Arc<RwLock<HashMap<String, i32>>>,
pub exception_ids: Arc<RwLock<HashMap<usize, usize>>>, pub exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
pub deferred_eval_store: DeferredEvaluationStore,
// module specific // module specific
pub name_to_pyid: HashMap<StrRef, u64>, pub name_to_pyid: HashMap<StrRef, u64>,
pub module: PyObject, pub module: PyObject,
@ -298,8 +317,12 @@ impl InnerResolver {
let constraint_types = { let constraint_types = {
let constraints = pyty.getattr("__constraints__").unwrap(); let constraints = pyty.getattr("__constraints__").unwrap();
let mut result: Vec<Type> = vec![]; let mut result: Vec<Type> = vec![];
let needs_defer = self.deferred_eval_store.needs_defer.load(Relaxed);
for i in 0.. { for i in 0.. {
if let Ok(constr) = constraints.get_item(i) { if let Ok(constr) = constraints.get_item(i) {
if needs_defer {
result.push(unifier.get_dummy_var().0);
} else {
result.push({ result.push({
match self.get_pyty_obj_type(py, constr, unifier, defs, primitives)? { match self.get_pyty_obj_type(py, constr, unifier, defs, primitives)? {
Ok((ty, _)) => { Ok((ty, _)) => {
@ -316,10 +339,18 @@ impl InnerResolver {
Err(err) => return Ok(Err(err)), Err(err) => return Ok(Err(err)),
} }
}) })
}
} else { } else {
break; break;
} }
} }
if needs_defer {
self.deferred_eval_store.store.write()
.push((result.clone(),
constraints.extract()?,
pyty.getattr("__name__")?.extract::<String>()?
))
}
result result
}; };
let res = let res =
@ -908,15 +939,14 @@ impl SymbolResolver for Resolver {
} }
} }
if let Ok(t) = sym_ty { if let Ok(t) = sym_ty {
if let TypeEnum::TVar { .. } = &*unifier.get_ty(t) {
self.0.pyid_to_type.write().insert(*id, t); self.0.pyid_to_type.write().insert(*id, t);
} }
}
Ok(sym_ty) Ok(sym_ty)
}) })
.unwrap(), .unwrap(),
}; };
if let Ok(t) = &result {
self.0.id_to_type.write().insert(str, *t);
}
result result
} }
} }
@ -992,6 +1022,45 @@ impl SymbolResolver for Resolver {
} }
} }
fn handle_deferred_eval(
&self,
unifier: &mut Unifier,
defs: &[Arc<RwLock<TopLevelDef>>],
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<Result<(), String>> {
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 { fn get_exception_id(&self, tyid: usize) -> usize {
let exn_ids = self.0.exception_ids.read(); let exn_ids = self.0.exception_ids.read();
exn_ids.get(&tyid).cloned().unwrap_or(0) exn_ids.get(&tyid).cloned().unwrap_or(0)

View File

@ -496,7 +496,13 @@ pub fn gen_func_instance<'ctx, 'a>(
} }
let symbol = format!("{}.{}", name, instance_to_symbol.len()); let symbol = format!("{}.{}", name, instance_to_symbol.len());
instance_to_symbol.insert(key, symbol.clone()); 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 instance = instance_to_stmt.get(&key).unwrap();
let mut store = ConcreteTypeStore::new(); let mut store = ConcreteTypeStore::new();

View File

@ -141,7 +141,15 @@ pub trait SymbolResolver {
fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option<SymbolValue>; fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option<SymbolValue>;
fn get_string_id(&self, s: &str) -> i32; fn get_string_id(&self, s: &str) -> i32;
fn get_exception_id(&self, tyid: usize) -> usize; fn get_exception_id(&self, tyid: usize) -> usize;
// handle function call etc.
fn handle_deferred_eval(
&self,
_unifier: &mut Unifier,
_top_level_defs: &[Arc<RwLock<TopLevelDef>>],
_primitives: &PrimitiveStore
) -> Result<(), String> {
Ok(())
}
} }
thread_local! { thread_local! {

View File

@ -775,6 +775,18 @@ impl TopLevelComposer {
return Err(errors.into_iter().sorted().join("\n----------\n")); 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(()) Ok(())
} }
@ -1057,17 +1069,7 @@ impl TopLevelComposer {
let (method_dummy_ty, method_id) = let (method_dummy_ty, method_id) =
Self::get_class_method_def_info(class_methods_def, *name)?; 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<u32, Type> = HashMap::new();
let mut method_var_map: HashMap<u32, Type> = 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 arg_types: Vec<FuncArg> = { let arg_types: Vec<FuncArg> = {
// check method parameters cannot have same name // check method parameters cannot have same name
@ -1494,8 +1496,8 @@ impl TopLevelComposer {
if new_var_ids != *var_id { if new_var_ids != *var_id {
let new_signature = FunSignature { let new_signature = FunSignature {
args: args.clone(), args: args.clone(),
ret: ret.clone(), ret: *ret,
vars: new_var_ids.iter().zip(vars.values()).map(|(id, v)| (*id, v.clone())).collect(), 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))); unifier.unification_table.set_value(*signature, Rc::new(TypeEnum::TFunc(new_signature)));
*var_id = new_var_ids; *var_id = new_var_ids;
@ -1674,13 +1676,13 @@ impl TopLevelComposer {
simple_name, simple_name,
signature, signature,
resolver, resolver,
var_id: insted_vars,
.. ..
} = &mut *function_def } = &mut *function_def
{ {
if let TypeEnum::TFunc(FunSignature { args, ret, vars }) = if let TypeEnum::TFunc(FunSignature { args, ret, vars }) =
unifier.get_ty(*signature).as_ref() unifier.get_ty(*signature).as_ref()
{ {
let mut vars = vars.clone();
// None if is not class method // None if is not class method
let uninst_self_type = { let uninst_self_type = {
if let Some(class_id) = method_class.get(&DefinitionId(id)) { if let Some(class_id) = method_class.get(&DefinitionId(id)) {
@ -1695,6 +1697,12 @@ impl TopLevelComposer {
&ty_ann, &ty_ann,
&mut None &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())) Some((self_ty, type_vars.clone()))
} else { } else {
unreachable!("must be class def") unreachable!("must be class def")
@ -1725,7 +1733,7 @@ impl TopLevelComposer {
.collect_vec(); .collect_vec();
let mut result: Vec<HashMap<u32, Type>> = Default::default(); let mut result: Vec<HashMap<u32, Type>> = Default::default();
for comb in var_combs { 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? // NOTE: if is empty, means no type var, append a empty subst, ok to do this?
if result.is_empty() { if result.is_empty() {
@ -1913,7 +1921,7 @@ impl TopLevelComposer {
} }
instance_to_stmt.insert( 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 { FunInstance {
body: Arc::new(fun_body), body: Arc::new(fun_body),
unifier_id: 0, unifier_id: 0,