feat: impl polymorphic type declaration

This commit is contained in:
Shunsuke Shibayama 2023-06-04 01:34:20 +09:00
parent ea132e2345
commit 5052ebb077
10 changed files with 289 additions and 85 deletions

View file

@ -496,7 +496,7 @@ impl<'a> HIRVisitor<'a> {
Expr::Record(record) => self.get_record_info(record, token),
Expr::Set(set) => self.get_set_info(set, token),
Expr::Tuple(tuple) => self.get_tuple_info(tuple, token),
Expr::TypeAsc(type_asc) => self.get_expr_info(&type_asc.expr, token),
Expr::TypeAsc(type_asc) => self.get_tasc_info(type_asc, token),
Expr::Dummy(dummy) => self.get_dummy_info(dummy, token),
Expr::Compound(block) | Expr::Code(block) => self.get_block_info(block, token),
Expr::ReDef(redef) => self.get_redef_info(redef, token),
@ -702,4 +702,9 @@ impl<'a> HIRVisitor<'a> {
// _ => None, // todo!(),
}
}
fn get_tasc_info(&self, tasc: &TypeAscription, token: &Token) -> Option<VarInfo> {
self.get_expr_info(&tasc.expr, token)
.or_else(|| self.get_expr_info(&tasc.spec.expr, token))
}
}

View file

@ -782,7 +782,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
if self.ctx.supertype_of(&sub_t, &super_t) {
Ok(sub_t)
} else {
Err(TyCheckErrors::from(TyCheckError::subtyping_error(
Err(TyCheckErrors::from(TyCheckError::invariant_error(
self.ctx.cfg.input.clone(),
line!() as usize,
&sub_t,

View file

@ -1530,13 +1530,15 @@ impl Context {
pub(crate) fn instantiate_typespec(&self, t_spec: &ast::TypeSpec) -> TyCheckResult<Type> {
let mut dummy_tv_cache = TyVarCache::new(self.level, self);
self.instantiate_typespec_full(
t_spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)
self.instantiate_typespec_with_tv_cache(t_spec, &mut dummy_tv_cache)
}
pub(crate) fn instantiate_typespec_with_tv_cache(
&self,
t_spec: &ast::TypeSpec,
tv_cache: &mut TyVarCache,
) -> TyCheckResult<Type> {
self.instantiate_typespec_full(t_spec, None, tv_cache, RegistrationMode::Normal, false)
}
pub(crate) fn instantiate_field(&self, ident: &Identifier) -> TyCheckResult<Field> {

View file

@ -182,7 +182,7 @@ impl std::ops::Mul for Variance {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ParamSpec {
pub(crate) name: Option<&'static str>,
pub(crate) name: Option<Str>,
// TODO: `:` or `<:`
pub(crate) t: Type,
pub is_var_params: bool,
@ -191,15 +191,15 @@ pub struct ParamSpec {
}
impl ParamSpec {
pub const fn new(
name: Option<&'static str>,
pub fn new<S: Into<Str>>(
name: Option<S>,
t: Type,
is_var_params: bool,
default: DefaultInfo,
loc: AbsLocation,
) -> Self {
Self {
name,
name: name.map(|s| s.into()),
t,
is_var_params,
default_info: default,
@ -207,8 +207,8 @@ impl ParamSpec {
}
}
pub const fn named(
name: &'static str,
pub fn named<S: Into<Str>>(
name: S,
t: Type,
is_var_params: bool,
default: DefaultInfo,
@ -222,7 +222,7 @@ impl ParamSpec {
)
}
pub const fn named_nd(name: &'static str, t: Type) -> Self {
pub fn named_nd<S: Into<Str>>(name: S, t: Type) -> Self {
Self::new(
Some(name),
t,
@ -232,7 +232,7 @@ impl ParamSpec {
)
}
pub const fn t(name: &'static str, is_var_params: bool, default: DefaultInfo) -> Self {
pub fn t<S: Into<Str>>(name: S, is_var_params: bool, default: DefaultInfo) -> Self {
Self::new(
Some(name),
Type,
@ -242,7 +242,7 @@ impl ParamSpec {
)
}
pub const fn t_nd(name: &'static str) -> Self {
pub fn t_nd<S: Into<Str>>(name: S) -> Self {
Self::new(
Some(name),
Type,
@ -574,18 +574,18 @@ impl Context {
let id = DefId(get_hash(&(&name, &param)));
if let Some(name) = param.name {
let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
let muty = Mutability::from(name);
let muty = Mutability::from(&name[..]);
let vi = VarInfo::new(
param.t,
muty,
Visibility::private(name),
Visibility::private(&name),
kind,
None,
None,
None,
param.loc,
);
params_.push((Some(VarName::new(Token::static_symbol(name))), vi));
params_.push((Some(VarName::new(Token::symbol(&name))), vi));
} else {
let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
let muty = Mutability::Immutable;

View file

@ -26,7 +26,7 @@ use ast::{
use erg_parser::ast;
use crate::ty::constructors::{
free_var, func, func0, func1, proc, ref_, ref_mut, unknown_len_array_t, v_enum,
free_var, func, func0, func1, proc, ref_, ref_mut, tp_enum, unknown_len_array_t, v_enum,
};
use crate::ty::free::{Constraint, HasLevel};
use crate::ty::typaram::TyParam;
@ -49,6 +49,7 @@ use RegistrationMode::*;
use super::instantiate::TyVarCache;
use super::instantiate_spec::ParamKind;
use super::ParamSpec;
pub fn valid_mod_name(name: &str) -> bool {
!name.is_empty() && !name.starts_with('/') && name.trim() == name
@ -1286,66 +1287,30 @@ impl Context {
2,
self.level,
);
let mut methods =
Self::methods(None, self.cfg.clone(), self.shared.clone(), 2, self.level);
let new_t = if let Some(base) = gen.base_or_sup() {
match base {
TypeObj::Builtin {
t: Type::Record(rec),
..
} => {
for (field, t) in rec.iter() {
let varname = VarName::from_str(field.symbol.clone());
let vi = VarInfo::instance_attr(
field.clone(),
t.clone(),
self.impl_of(),
ctx.name.clone(),
);
ctx.decls.insert(varname, vi);
if ERG_MODE {
self.gen_class_new_method(&gen, &mut ctx)?;
}
}
other => {
methods.register_fixed_auto_impl(
"base",
other.typ().clone(),
Immutable,
Visibility::BUILTIN_PRIVATE,
None,
)?;
}
}
func1(base.typ().clone(), gen.typ().clone())
} else {
func0(gen.typ().clone())
};
methods.register_fixed_auto_impl(
"__new__",
new_t.clone(),
Immutable,
Visibility::BUILTIN_PRIVATE,
Some("__call__".into()),
)?;
// 必要なら、ユーザーが独自に上書きする
// users can override this if necessary
methods.register_auto_impl(
"new",
new_t,
Immutable,
Visibility::BUILTIN_PUBLIC,
None,
)?;
ctx.methods_list
.push((ClassDefType::Simple(gen.typ().clone()), methods));
self.register_gen_mono_type(ident, gen, ctx, Const)
} else {
feature_error!(
CompileErrors,
CompileError,
self,
ident.loc(),
"polymorphic class definition"
)
log!(err "{:?}", gen.typ().typarams());
let params = gen
.typ()
.typarams()
.into_iter()
.map(|tp| ParamSpec::t_nd(tp.qual_name().unwrap_or(Str::ever("_"))))
.collect();
let mut ctx = Self::poly_class(
gen.typ().qual_name(),
params,
self.cfg.clone(),
self.shared.clone(),
2,
self.level,
);
if ERG_MODE {
self.gen_class_new_method(&gen, &mut ctx)?;
}
self.register_gen_poly_type(ident, gen, ctx, Const)
}
}
GenTypeObj::Subclass(_) => {
@ -1560,6 +1525,54 @@ impl Context {
}
}
fn gen_class_new_method(&self, gen: &GenTypeObj, ctx: &mut Context) -> CompileResult<()> {
let mut methods = Self::methods(None, self.cfg.clone(), self.shared.clone(), 2, self.level);
let new_t = if let Some(base) = gen.base_or_sup() {
match base {
TypeObj::Builtin {
t: Type::Record(rec),
..
} => {
for (field, t) in rec.iter() {
let varname = VarName::from_str(field.symbol.clone());
let vi = VarInfo::instance_attr(
field.clone(),
t.clone(),
self.impl_of(),
ctx.name.clone(),
);
ctx.decls.insert(varname, vi);
}
}
other => {
methods.register_fixed_auto_impl(
"base",
other.typ().clone(),
Immutable,
Visibility::BUILTIN_PRIVATE,
None,
)?;
}
}
func1(base.typ().clone(), gen.typ().clone())
} else {
func0(gen.typ().clone())
};
methods.register_fixed_auto_impl(
"__new__",
new_t.clone(),
Immutable,
Visibility::BUILTIN_PRIVATE,
Some("__call__".into()),
)?;
// 必要なら、ユーザーが独自に上書きする
// users can override this if necessary
methods.register_auto_impl("new", new_t, Immutable, Visibility::BUILTIN_PUBLIC, None)?;
ctx.methods_list
.push((ClassDefType::Simple(gen.typ().clone()), methods));
Ok(())
}
pub(crate) fn register_type_alias(
&mut self,
ident: &Identifier,
@ -1656,6 +1669,66 @@ impl Context {
}
}
fn register_gen_poly_type(
&mut self,
ident: &Identifier,
gen: GenTypeObj,
ctx: Self,
muty: Mutability,
) -> CompileResult<()> {
let vis = self.instantiate_vis_modifier(&ident.vis)?;
// FIXME: recursive search
if self.poly_types.contains_key(ident.inspect()) {
Err(CompileErrors::from(CompileError::reassign_error(
self.cfg.input.clone(),
line!() as usize,
ident.loc(),
self.caused_by(),
ident.inspect(),
)))
} else if self.rec_get_const_obj(ident.inspect()).is_some() && vis.is_private() {
Err(CompileErrors::from(CompileError::reassign_error(
self.cfg.input.clone(),
line!() as usize,
ident.loc(),
self.caused_by(),
ident.inspect(),
)))
} else {
let t = gen.typ().clone();
let val = ValueObj::Type(TypeObj::Generated(gen));
let params = t
.typarams()
.into_iter()
.map(|tp| {
ParamTy::Pos(tp_enum(
self.get_tp_t(&tp).unwrap_or(Type::Obj),
set! { tp },
))
})
.collect();
let meta_t = func(params, None, vec![], v_enum(set! { val.clone() })).quantify();
let name = &ident.name;
let id = DefId(get_hash(&(&self.name, &name)));
let vi = VarInfo::new(
meta_t,
muty,
Visibility::new(vis, self.name.clone()),
VarKind::Defined(id),
None,
self.impl_of(),
None,
self.absolutize(name.loc()),
);
self.index().register(&vi);
self.decls.insert(name.clone(), vi);
self.consts.insert(name.clone(), val);
self.register_methods(&t, &ctx);
self.poly_types.insert(name.clone(), (t, ctx));
Ok(())
}
}
fn register_gen_mono_patch(
&mut self,
ident: &Identifier,

View file

@ -6,17 +6,18 @@ use erg_common::{enum_unwrap, fn_name, log, set, Str, Triple};
use erg_parser::ast::{self, AscriptionKind, Identifier, VarName, AST};
use crate::context::instantiate::TyVarCache;
use crate::lower::ASTLowerer;
use crate::ty::constructors::{mono, v_enum};
use crate::ty::constructors::{mono, poly, ty_tp, type_q, v_enum};
use crate::ty::free::HasLevel;
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{HasType, Type, Visibility};
use crate::compile::AccessKind;
use crate::error::{LowerError, LowerErrors, LowerResult};
use crate::hir;
use crate::hir::HIR;
use crate::varinfo::{Mutability, VarInfo, VarKind};
use crate::{feature_error, hir};
impl ASTLowerer {
fn declare_var(
@ -576,6 +577,48 @@ impl ASTLowerer {
let t_spec = hir::TypeSpecWithOp::new(tasc.t_spec, t_spec_expr, Type::Failure);
Ok(attr.type_asc(t_spec))
}
ast::Expr::Call(call) => {
let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = *call.obj else {
return feature_error!(
LowerErrors,
LowerError,
&self.module.context,
call.obj.loc(),
"complex polymorphic type declaration"
);
};
let py_name = Str::rc(ident.inspect().trim_end_matches('!'));
let mut tv_cache = TyVarCache::new(self.module.context.level, &self.module.context);
if let Some((t, _)) = self.module.context.get_type(ident.inspect()) {
for (tp, arg) in t.typarams().iter().zip(call.args.pos_args()) {
if let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = &arg.expr {
tv_cache.push_or_init_typaram(&ident.name, tp, &self.module.context);
}
}
}
let t = self
.module
.context
.instantiate_typespec_with_tv_cache(&tasc.t_spec.t_spec, &mut tv_cache)?;
t.lift();
let t = self.module.context.generalize_t(t);
match kind {
AscriptionKind::TypeOf | AscriptionKind::AsCast => {
self.declare_instance(&ident, &t, py_name)?;
}
AscriptionKind::SubtypeOf => {
self.declare_subtype(&ident, &t)?;
}
_ => {
log!(err "supertype ascription is not supported yet");
}
}
let acc = self.fake_lower_acc(ast::Accessor::Ident(ident))?;
let args = self.fake_lower_args(call.args)?;
let t_spec_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr.clone())?;
let t_spec = hir::TypeSpecWithOp::new(tasc.t_spec, t_spec_expr, Type::Failure);
Ok(hir::Expr::Accessor(acc).call_expr(args).type_asc(t_spec))
}
other => Err(LowerErrors::from(LowerError::declare_error(
self.cfg().input.clone(),
line!() as usize,
@ -616,6 +659,17 @@ impl ASTLowerer {
let t = v_enum(set! { ValueObj::builtin_trait(t) });
(t, Some(ty_obj))
}
Type::Subr(subr) if subr.return_t.is_class_type() => {
let params = subr
.non_default_params
.iter()
.map(|p| ty_tp(type_q(p.name().unwrap_or(&Str::ever("_")))))
.collect();
let t = poly(format!("{}{ident}", self.module.context.path()), params);
let ty_obj = GenTypeObj::class(t.clone(), None, None);
let t = v_enum(set! { ValueObj::builtin_class(t) });
(t, Some(ty_obj))
}
_ => (t.clone(), None),
};
self.module.context.assign_var_sig(

View file

@ -770,6 +770,53 @@ passed keyword args: {kw_args_len}"
)
}
pub fn invariant_error(
input: Input,
errno: usize,
sub_t: &Type,
sup_t: &Type,
loc: Location,
caused_by: String,
) -> Self {
let mut sub_type = StyledStrings::default();
switch_lang!(
"japanese" => sub_type.push_str("部分型: "),
"simplified_chinese" => sub_type.push_str("子类型: "),
"simplified_chinese" =>sub_type.push_str("子類型:"),
"english" => sub_type.push_str("subtype: "),
);
sub_type.push_str_with_color_and_attr(format!("{sub_t}"), HINT, ATTR);
let mut sup_type = StyledStrings::default();
switch_lang!(
"japanese" => sup_type.push_str("汎化型: "),
"simplified_chinese" => sup_type.push_str("父类型: "),
"simplified_chinese" => sup_type.push_str("父類型: "),
"english" =>sup_type.push_str("supertype: "),
);
sup_type.push_str_with_color_and_attr(format!("{sup_t}"), ERR, ATTR);
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(
loc,
vec![sub_type.to_string(), sup_type.to_string()],
None,
)],
switch_lang!(
"japanese" => "不変な型パラメータを一意に決定できません",
"simplified_chinese" => "无法唯一确定不变型的类型参数",
"traditional_chinese" => "無法唯一確定不變型的類型參數",
"english" => "cannot uniquely determine the type parameter of the invariant type",
),
errno,
TypeError,
loc,
),
input,
caused_by,
)
}
pub fn pred_unification_error(
input: Input,
errno: usize,

View file

@ -1,6 +1,7 @@
# TODO: dependent (static shaped)
.NDArray = 'ndarray': ClassType
.NDArray <: Num
.NDArray = 'ndarray': (T: Type) -> ClassType
.NDArray(T) <: Output T
.NDArray(_) <: Num
.NDArray.
shape: [Nat; _]
ndim: Nat

View file

@ -145,7 +145,11 @@ impl OwnershipChecker {
Expr::Accessor(acc) => self.check_acc(acc, ownership, chunk),
// TODO: referenced
Expr::Call(call) => {
let args_owns = call.signature_t().unwrap().args_ownership();
let sig_t = call.signature_t().unwrap();
if sig_t.is_failure() || sig_t.is_class_type() {
return;
}
let args_owns = sig_t.args_ownership();
let non_defaults_len = if call.is_method_call() {
args_owns.non_defaults.len() - 1
} else {

View file

@ -1847,6 +1847,24 @@ impl Type {
}
}
pub fn is_failure(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_failure(),
Self::Refinement(refine) => refine.t.is_failure(),
Self::Failure => true,
_ => false,
}
}
pub fn is_class_type(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_class_type(),
Self::Refinement(refine) => refine.t.is_class_type(),
Self::ClassType => true,
_ => false,
}
}
pub fn as_free(&self) -> Option<&FreeTyVar> {
<&FreeTyVar>::try_from(self).ok()
}