core/typedef: Add GenericObjectType

This commit is contained in:
David Mak 2024-06-25 18:39:34 +08:00
parent 10a88e1799
commit da4dec08a5
3 changed files with 141 additions and 37 deletions

View File

@ -4,6 +4,7 @@ use inkwell::{
AddressSpace, AddressSpace,
}; };
use itertools::Itertools; use itertools::Itertools;
use nac3core::typecheck::typedef::{GenericObjectType, GenericTypeAdapter};
use nac3core::{ use nac3core::{
codegen::{ codegen::{
classes::{NDArrayType, ProxyType}, classes::{NDArrayType, ProxyType},
@ -17,7 +18,7 @@ use nac3core::{
}, },
typecheck::{ typecheck::{
type_inferencer::PrimitiveStore, type_inferencer::PrimitiveStore,
typedef::{into_var_map, iter_type_vars, Type, TypeEnum, TypeVar, Unifier, VarMap}, typedef::{Type, TypeEnum, TypeVar, Unifier, VarMap},
}, },
}; };
use nac3parser::ast::{self, StrRef}; use nac3parser::ast::{self, StrRef};
@ -767,13 +768,12 @@ impl InnerResolver {
// if is `none` // if is `none`
let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?; let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if zelf_id == self.primitive_ids.none { if zelf_id == self.primitive_ids.none {
let ty_enum = unifier.get_ty_immutable(primitives.option); let extracted_ty = GenericTypeAdapter::create(extracted_ty, unifier);
let TypeEnum::TObj { params, .. } = ty_enum.as_ref() else { let var_map = extracted_ty.iter_var_map(unifier, |tvar_iter, unifier| {
unreachable!("must be tobj") tvar_iter
}; .map(|tvar| {
let TypeEnum::TVar { id, range, name, loc, .. } =
let var_map = into_var_map(iter_type_vars(params).map(|tvar| { &*unifier.get_ty(tvar.ty)
let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(tvar.ty)
else { else {
unreachable!() unreachable!()
}; };
@ -781,7 +781,10 @@ impl InnerResolver {
assert_eq!(*id, tvar.id); assert_eq!(*id, tvar.id);
let ty = unifier.get_fresh_var_with_range(range, *name, *loc).ty; let ty = unifier.get_fresh_var_with_range(range, *name, *loc).ty;
TypeVar { id: *id, ty } TypeVar { id: *id, ty }
})); })
.map(TypeVar::into)
.collect::<VarMap>()
});
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap())); return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()));
} }
@ -797,10 +800,14 @@ impl InnerResolver {
let res = unifier.subst(extracted_ty, &new_var_map).unwrap_or(extracted_ty); let res = unifier.subst(extracted_ty, &new_var_map).unwrap_or(extracted_ty);
Ok(Ok(res)) Ok(Ok(res))
} }
(TypeEnum::TObj { params, fields, .. }, false) => { (TypeEnum::TObj { fields, .. }, false) => {
self.pyid_to_type.write().insert(py_obj_id, extracted_ty); self.pyid_to_type.write().insert(py_obj_id, extracted_ty);
let var_map = into_var_map(iter_type_vars(params).map(|tvar| { let extracted_ty = GenericTypeAdapter::create(extracted_ty, unifier);
let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(tvar.ty) let var_map = extracted_ty.iter_var_map(unifier, |tvar_iter, unifier| {
tvar_iter
.map(|tvar| {
let TypeEnum::TVar { id, range, name, loc, .. } =
&*unifier.get_ty(tvar.ty)
else { else {
unreachable!() unreachable!()
}; };
@ -808,8 +815,11 @@ impl InnerResolver {
assert_eq!(*id, tvar.id); assert_eq!(*id, tvar.id);
let ty = unifier.get_fresh_var_with_range(range, *name, *loc).ty; let ty = unifier.get_fresh_var_with_range(range, *name, *loc).ty;
TypeVar { id: *id, ty } TypeVar { id: *id, ty }
})); })
let mut instantiate_obj = || { .map(TypeVar::into)
.collect::<VarMap>()
});
let instantiate_obj = || {
// loop through non-function fields of the class to get the instantiated value // loop through non-function fields of the class to get the instantiated value
for field in fields { for field in fields {
let name: String = (*field.0).into(); let name: String = (*field.0).into();
@ -844,6 +854,7 @@ impl InnerResolver {
return Ok(Err("object is not of concrete type".into())); return Ok(Err("object is not of concrete type".into()));
} }
} }
let extracted_ty = extracted_ty.into();
let extracted_ty = let extracted_ty =
unifier.subst(extracted_ty, &var_map).unwrap_or(extracted_ty); unifier.subst(extracted_ty, &var_map).unwrap_or(extracted_ty);
Ok(Ok(extracted_ty)) Ok(Ok(extracted_ty))

View File

@ -11,6 +11,7 @@ use inkwell::{
use itertools::Either; use itertools::Either;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::typecheck::typedef::{GenericObjectType, GenericTypeAdapter};
use crate::{ use crate::{
codegen::{ codegen::{
builtin_fns, builtin_fns,
@ -25,7 +26,7 @@ use crate::{
}, },
symbol_resolver::SymbolValue, symbol_resolver::SymbolValue,
toplevel::{helper::PrimDef, numpy::make_ndarray_ty}, toplevel::{helper::PrimDef, numpy::make_ndarray_ty},
typecheck::typedef::{into_var_map, iter_type_vars, TypeVar, VarMap}, typecheck::typedef::{into_var_map, TypeVar, VarMap},
}; };
use super::*; use super::*;
@ -345,23 +346,23 @@ impl<'a> BuiltinBuilder<'a> {
// Option-related // Option-related
let (is_some_ty, unwrap_ty, option_tvar) = let (is_some_ty, unwrap_ty, option_tvar) =
if let TypeEnum::TObj { fields, params, .. } = unifier.get_ty(option).as_ref() { if let TypeEnum::TObj { fields, .. } = &*unifier.get_ty(option) {
let option = GenericTypeAdapter::create(option, unifier);
( (
*fields.get(&PrimDef::OptionIsSome.simple_name().into()).unwrap(), *fields.get(&PrimDef::OptionIsSome.simple_name().into()).unwrap(),
*fields.get(&PrimDef::OptionUnwrap.simple_name().into()).unwrap(), *fields.get(&PrimDef::OptionUnwrap.simple_name().into()).unwrap(),
iter_type_vars(params).next().unwrap(), option.get_var_at(unifier, 0).unwrap(),
) )
} else { } else {
unreachable!() unreachable!()
}; };
let TypeEnum::TObj { fields: ndarray_fields, params: ndarray_params, .. } = let TypeEnum::TObj { fields: ndarray_fields, .. } = &*unifier.get_ty(ndarray) else {
&*unifier.get_ty(ndarray)
else {
unreachable!() unreachable!()
}; };
let ndarray_dtype_tvar = iter_type_vars(ndarray_params).next().unwrap(); let ndarray = GenericTypeAdapter::create(ndarray, unifier);
let ndarray_ndims_tvar = iter_type_vars(ndarray_params).nth(1).unwrap(); let ndarray_dtype_tvar = ndarray.get_var_at(unifier, 0).unwrap();
let ndarray_ndims_tvar = ndarray.get_var_at(unifier, 1).unwrap();
let ndarray_copy_ty = let ndarray_copy_ty =
*ndarray_fields.get(&PrimDef::NDArrayCopy.simple_name().into()).unwrap(); *ndarray_fields.get(&PrimDef::NDArrayCopy.simple_name().into()).unwrap();
let ndarray_fill_ty = let ndarray_fill_ty =

View File

@ -89,6 +89,24 @@ pub struct TypeVar {
pub ty: Type, pub ty: Type,
} }
impl From<(TypeVarId, Type)> for TypeVar {
fn from((id, ty): (TypeVarId, Type)) -> Self {
TypeVar { id, ty }
}
}
impl From<(&TypeVarId, &Type)> for TypeVar {
fn from((id, ty): (&TypeVarId, &Type)) -> Self {
TypeVar { id: *id, ty: *ty }
}
}
impl From<TypeVar> for (TypeVarId, Type) {
fn from(value: TypeVar) -> Self {
(value.id, value.ty)
}
}
/// The mapping between [`TypeVarId`] and [unifier type][`Type`]. /// The mapping between [`TypeVarId`] and [unifier type][`Type`].
pub type VarMap = IndexMapping<TypeVarId>; pub type VarMap = IndexMapping<TypeVarId>;
@ -102,9 +120,83 @@ where
vars.into_iter().map(|var| (var.id, var.ty)).collect() vars.into_iter().map(|var| (var.id, var.ty)).collect()
} }
/// Get an iterator of [`TypeVar`]s from a [`VarMap`] /// A trait representing a possibly generic object type.
pub fn iter_type_vars(var_map: &VarMap) -> impl Iterator<Item = TypeVar> + '_ { pub trait GenericObjectType
var_map.iter().map(|(&id, &ty)| TypeVar { id, ty }) where
Self: Sized,
{
fn try_create(ty: Type, unifier: &mut Unifier) -> Option<Self>;
/// Creates an instance from a [`Type`].
#[must_use]
fn create(ty: Type, unifier: &mut Unifier) -> Self {
Self::try_create(ty, unifier).unwrap()
}
/// Returns the [`Type`] underlying this instance.
#[must_use]
fn get_type(&self) -> Type;
/// See [`Type::obj_id`].
#[must_use]
fn obj_id(&self, unifier: &Unifier) -> DefinitionId {
self.get_type().obj_id(unifier).unwrap()
}
/// Returns a copy of the [`VarMap`] of this object type.
#[must_use]
fn var_map(&self, unifier: &mut Unifier) -> VarMap {
let TypeEnum::TObj { params, .. } = &*unifier.get_ty(self.get_type()) else {
unreachable!()
};
params.clone()
}
/// Creates an iterator over the [`VarMap`] of this object type, applying `iter_fn` on the
/// created [`Iterator`].
#[must_use]
fn iter_var_map<R, IterFn: FnOnce(&mut dyn Iterator<Item = TypeVar>, &mut Unifier) -> R>(
&self,
unifier: &mut Unifier,
iter_fn: IterFn,
) -> R {
let TypeEnum::TObj { params, .. } = &*unifier.get_ty(self.get_type()) else {
unreachable!()
};
let res = iter_fn(&mut params.iter().map(TypeVar::from), unifier);
res
}
/// Returns the [`TypeVar`] instance at the given index.
#[must_use]
fn get_var_at(&self, unifier: &mut Unifier, i: usize) -> Option<TypeVar> {
self.iter_var_map(unifier, |iter, _| iter.nth(i))
}
}
impl<T: GenericObjectType> From<T> for Type {
fn from(value: T) -> Self {
value.get_type()
}
}
/// An adapter that converts [`Type`] into
pub struct GenericTypeAdapter(Type);
impl GenericObjectType for GenericTypeAdapter {
fn try_create(ty: Type, unifier: &mut Unifier) -> Option<Self> {
if let TypeEnum::TObj { .. } = &*unifier.get_ty_immutable(ty) {
Some(GenericTypeAdapter(ty))
} else {
None
}
}
fn get_type(&self) -> Type {
self.0
}
} }
#[derive(Clone)] #[derive(Clone)]