forked from M-Labs/nac3
1
0
Fork 0

nac3core: fix polymorphic class method partial instantiation

This commit is contained in:
ychenfo 2021-11-09 01:15:41 +08:00
parent 439cef636f
commit c2706fa720
4 changed files with 48 additions and 19 deletions

View File

@ -147,8 +147,14 @@ impl ConcreteTypeStore {
fields: fields fields: fields
.borrow() .borrow()
.iter() .iter()
.map(|(name, ty)| { .filter_map(|(name, ty)| {
(*name, (self.from_unifier_type(unifier, primitives, ty.0, cache), ty.1)) // filter out functions as they can have type vars and
// will not affect codegen
if let TypeEnum::TFunc( .. ) = &*unifier.get_ty(ty.0) {
None
} else {
Some((*name, (self.from_unifier_type(unifier, primitives, ty.0, cache), ty.1)))
}
}) })
.collect(), .collect(),
params: params params: params

View File

@ -1654,7 +1654,7 @@ impl TopLevelComposer {
if let TypeEnum::TFunc(func_sig) = self.unifier.get_ty(*signature).as_ref() { if let TypeEnum::TFunc(func_sig) = self.unifier.get_ty(*signature).as_ref() {
let FunSignature { args, ret, vars } = &*func_sig.borrow(); let FunSignature { args, ret, vars } = &*func_sig.borrow();
// None if is not class method // None if is not class method
let self_type = { let uninst_self_type = {
if let Some(class_id) = self.method_class.get(&DefinitionId(id)) { if let Some(class_id) = self.method_class.get(&DefinitionId(id)) {
let class_def = self.definition_ast_list.get(class_id.0).unwrap(); let class_def = self.definition_ast_list.get(class_id.0).unwrap();
let class_def = class_def.0.read(); let class_def = class_def.0.read();
@ -1666,7 +1666,7 @@ impl TopLevelComposer {
&self.primitives_ty, &self.primitives_ty,
&ty_ann, &ty_ann,
)?; )?;
Some(self_ty) Some((self_ty, type_vars.clone()))
} else { } else {
unreachable!("must be class def") unreachable!("must be class def")
} }
@ -1717,9 +1717,34 @@ impl TopLevelComposer {
}; };
let self_type = { let self_type = {
let unifier = &mut self.unifier; let unifier = &mut self.unifier;
self_type.map(|x| unifier.subst(x, &subst).unwrap_or(x)) uninst_self_type
.clone()
.map(|(self_type, type_vars)| {
let subst_for_self = {
let class_ty_var_ids = type_vars
.iter()
.map(|x| {
if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*x) {
*id
} else {
unreachable!("must be type var here");
}
})
.collect::<HashSet<_>>();
subst
.iter()
.filter_map(|(ty_var_id, ty_var_target)| {
if class_ty_var_ids.contains(ty_var_id) {
Some((*ty_var_id, *ty_var_target))
} else {
None
}
})
.collect::<HashMap<_, _>>()
};
unifier.subst(self_type, &subst_for_self).unwrap_or(self_type)
})
}; };
let mut identifiers = { let mut identifiers = {
// NOTE: none and function args? // NOTE: none and function args?
let mut result: HashSet<_> = HashSet::new(); let mut result: HashSet<_> = HashSet::new();

View File

@ -280,9 +280,11 @@ pub fn get_type_from_type_annotation_kinds(
{ {
let ok: bool = { let ok: bool = {
// create a temp type var and unify to check compatibility // create a temp type var and unify to check compatibility
let temp = p == *tvar || {
unifier.get_fresh_var_with_range(range.borrow().as_slice()); let temp =
unifier.unify(temp.0, p).is_ok() unifier.get_fresh_var_with_range(range.borrow().as_slice());
unifier.unify(temp.0, p).is_ok()
}
}; };
if ok { if ok {
result.insert(*id, p); result.insert(*id, p);

View File

@ -719,22 +719,18 @@ impl Unifier {
/// Returns Some(T) where T is the instantiated type. /// Returns Some(T) where T is the instantiated type.
/// Returns None if the function is already instantiated. /// Returns None if the function is already instantiated.
fn instantiate_fun(&mut self, ty: Type, fun: &FunSignature) -> Type { fn instantiate_fun(&mut self, ty: Type, fun: &FunSignature) -> Type {
let mut instantiated = false; let mut instantiated = true;
let mut vars = Vec::new(); let mut vars = Vec::new();
for (k, v) in fun.vars.iter() { for (k, v) in fun.vars.iter() {
if let TypeEnum::TVar { id, range, .. } = if let TypeEnum::TVar { id, range, .. } =
self.unification_table.probe_value(*v).as_ref() self.unification_table.probe_value(*v).as_ref()
{ {
if k != id { // need to do this for partial instantiated function
instantiated = true; // (in class methods that contains type vars not in class)
break; if k == id {
instantiated = false;
vars.push((*k, range.clone()));
} }
// actually, if the first check succeeded, the function should be uninstatiated.
// The cloned values must be used and would not be wasted.
vars.push((*k, range.clone()));
} else {
instantiated = true;
break;
} }
} }
if instantiated { if instantiated {