1
0
forked from M-Labs/nac3

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::{
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<TopLevelComponent>,
string_store: Arc<RwLock<HashMap<String, i32>>>,
exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
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<dyn SymbolResolver + Send + Sync>;
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<dyn SymbolResolver + Send + Sync>;
let (_, def_id, _) = composer
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into())

View File

@ -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<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 id_to_type: RwLock<HashMap<StrRef, Type>>,
pub id_to_def: RwLock<HashMap<StrRef, DefinitionId>>,
@ -44,6 +62,7 @@ pub struct InnerResolver {
pub helper: PythonHelper,
pub string_store: Arc<RwLock<HashMap<String, i32>>>,
pub exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
pub deferred_eval_store: DeferredEvaluationStore,
// module specific
pub name_to_pyid: HashMap<StrRef, u64>,
pub module: PyObject,
@ -298,28 +317,40 @@ impl InnerResolver {
let constraint_types = {
let constraints = pyty.getattr("__constraints__").unwrap();
let mut result: Vec<Type> = 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::<String>()?
)));
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::<String>()?
)));
}
}
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::<String>()?
))
}
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<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 {
let exn_ids = self.0.exception_ids.read();
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());
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();

View File

@ -141,7 +141,15 @@ pub trait SymbolResolver {
fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option<SymbolValue>;
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<RwLock<TopLevelDef>>],
_primitives: &PrimitiveStore
) -> Result<(), String> {
Ok(())
}
}
thread_local! {

View File

@ -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<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 mut method_var_map: HashMap<u32, Type> = HashMap::new();
let arg_types: Vec<FuncArg> = {
// 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<HashMap<u32, Type>> = 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,