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:
parent
be75fa7368
commit
1084ba2158
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue