WIP: impl type checker

This commit is contained in:
Shunsuke Shibayama 2022-09-05 01:13:14 +09:00
parent cf0858ddde
commit e860da2a15
9 changed files with 274 additions and 26 deletions

View file

@ -399,8 +399,6 @@ impl Context {
&& kw_check() && kw_check()
// contravariant // contravariant
} }
// RefMut, OptionMut are invariant
(Ref(lhs), Ref(rhs)) => self.supertype_of(lhs, rhs),
// ?T(<: Nat) !:> ?U(:> Int) // ?T(<: Nat) !:> ?U(:> Int)
// ?T(<: Nat) :> ?U(<: Int) (?U can be smaller than ?T) // ?T(<: Nat) :> ?U(<: Int) (?U can be smaller than ?T)
(FreeVar(lfv), FreeVar(rfv)) => { (FreeVar(lfv), FreeVar(rfv)) => {
@ -490,7 +488,18 @@ impl Context {
} }
true true
} }
// (MonoQuantVar(_), _) | (_, MonoQuantVar(_)) => true, (Type::Record(lhs), Type::Record(rhs)) => {
for (k, l) in lhs.iter() {
if let Some(r) = rhs.get(k) {
if !self.supertype_of(l, r) {
return false;
}
} else {
return false;
}
}
true
}
// REVIEW: maybe this is incomplete // REVIEW: maybe this is incomplete
// ({I: Int | I >= 0} :> {N: Int | N >= 0}) == true, // ({I: Int | I >= 0} :> {N: Int | N >= 0}) == true,
// ({I: Int | I >= 0} :> {I: Int | I >= 1}) == true, // ({I: Int | I >= 0} :> {I: Int | I >= 1}) == true,
@ -565,6 +574,8 @@ impl Context {
} }
(_lhs, Not(_, _)) => todo!(), (_lhs, Not(_, _)) => todo!(),
(Not(_, _), _rhs) => todo!(), (Not(_, _), _rhs) => todo!(),
// RefMut, OptionMut are invariant
(Ref(lhs), Ref(rhs)) => self.supertype_of(lhs, rhs),
// TはすべてのRef(T)のメソッドを持つので、Ref(T)のサブタイプ // TはすべてのRef(T)のメソッドを持つので、Ref(T)のサブタイプ
// REVIEW: RefMut is invariant, maybe // REVIEW: RefMut is invariant, maybe
(Ref(lhs), rhs) | (RefMut(lhs), rhs) => self.supertype_of(lhs, rhs), (Ref(lhs), rhs) | (RefMut(lhs), rhs) => self.supertype_of(lhs, rhs),

View file

@ -9,7 +9,7 @@ use erg_common::vis::Visibility;
use erg_common::Str; use erg_common::Str;
use erg_type::typaram::TyParam; use erg_type::typaram::TyParam;
use erg_type::value::ValueObj; use erg_type::value::{TypeKind, TypeObj, ValueObj};
use erg_type::Type; use erg_type::Type;
use erg_type::{constructors::*, BuiltinConstSubr, ConstSubr}; use erg_type::{constructors::*, BuiltinConstSubr, ConstSubr};
use ParamSpec as PS; use ParamSpec as PS;
@ -51,11 +51,54 @@ impl Context {
if self.rec_get_const_obj(name).is_some() { if self.rec_get_const_obj(name).is_some() {
panic!("already registered: {name}"); panic!("already registered: {name}");
} else { } else {
// TODO: visibility (not always private) match obj {
// TODO: kind (not always Builtin) /*ValueObj::Type(TypeObj::Builtin(_builtin)) => {
let vi = VarInfo::new(enum_t(set! {obj.clone()}), Const, Private, Builtin); todo!()
self.consts.insert(VarName::from_str(Str::rc(name)), obj); }*/
self.locals.insert(VarName::from_str(Str::rc(name)), vi); ValueObj::Type(TypeObj::Gen(gen)) => match gen.kind {
TypeKind::Class => {
if gen.t.is_monomorphic() {
let super_traits =
gen.impls.into_iter().map(|to| to.typ().clone()).collect();
let mut ctx =
Self::mono_class(gen.t.name(), vec![], super_traits, self.level);
let require = gen.require_or_sup.typ().clone();
let new_t = func1(require, gen.t.clone());
ctx.register_impl("__new__", new_t, Immutable, Private);
self.register_mono_type(gen.t, ctx, Const);
} else {
todo!()
}
}
TypeKind::InheritedClass => {
if gen.t.is_monomorphic() {
let super_classes = vec![gen.require_or_sup.typ().clone()];
let super_traits =
gen.impls.into_iter().map(|to| to.typ().clone()).collect();
let mut ctx = Self::mono_class(
gen.t.name(),
super_classes,
super_traits,
self.level,
);
let sup = gen.require_or_sup.typ().clone();
let new_t = func1(sup, gen.t.clone());
ctx.register_impl("__new__", new_t, Immutable, Private);
self.register_mono_type(gen.t, ctx, Const);
} else {
todo!()
}
}
other => todo!("{other:?}"),
},
other => {
// TODO: visibility (not always private)
// TODO: kind (not always Builtin)
let vi = VarInfo::new(enum_t(set! {other.clone()}), Const, Private, Builtin);
self.consts.insert(VarName::from_str(Str::rc(name)), other);
self.locals.insert(VarName::from_str(Str::rc(name)), vi);
}
}
} }
} }

View file

@ -1020,6 +1020,13 @@ impl Context {
typ: &Type, typ: &Type,
) -> Option<(&'a Type, &'a Context)> { ) -> Option<(&'a Type, &'a Context)> {
match typ { match typ {
Type::FreeVar(fv) if fv.is_linked() => {
return self.rec_get_nominal_type_ctx(&fv.crack());
}
Type::FreeVar(fv) => {
let sup = fv.crack_sup()?;
return self.rec_get_nominal_type_ctx(&sup);
}
Type::Refinement(refine) => { Type::Refinement(refine) => {
return self.rec_get_nominal_type_ctx(&refine.t); return self.rec_get_nominal_type_ctx(&refine.t);
} }
@ -1040,6 +1047,7 @@ impl Context {
return Some((t, ctx)); return Some((t, ctx));
} }
} }
Type::Ref(t) | Type::RefMut(t) => return self.rec_get_nominal_type_ctx(t),
other => todo!("{other}"), other => todo!("{other}"),
} }
if let Some(outer) = &self.outer { if let Some(outer) = &self.outer {
@ -1053,8 +1061,14 @@ impl Context {
match obj.ref_t() { match obj.ref_t() {
// TODO: attr // TODO: attr
Type::Module => self.rec_get_mod(&obj.var_full_name()?), Type::Module => self.rec_get_mod(&obj.var_full_name()?),
Type::Type => self
.rec_get_nominal_type_ctx(&Type::Mono(Str::from(obj.var_full_name().unwrap())))
.map(|(_, ctx)| ctx),
Type::Class => todo!(), Type::Class => todo!(),
Type::Trait => todo!(), Type::Trait => todo!(),
Type::Refinement(refine) => {
self.rec_get_nominal_type_ctx(&refine.t).map(|(_, ctx)| ctx)
}
_ => None, _ => None,
} }
} }
@ -1113,4 +1127,24 @@ impl Context {
None None
} }
} }
pub(crate) fn get_self_t(&self) -> Type {
if self.kind.is_method_def() || self.kind.is_type() {
// TODO: poly type
let name = self.name.split("::").last().unwrap();
let name = name.split(".").last().unwrap();
let mono_t = mono(Str::rc(name));
if let Some((t, _)) = self.rec_get_nominal_type_ctx(&mono_t) {
t.clone()
} else {
todo!("{mono_t}")
}
} else {
if let Some(outer) = &self.outer {
outer.get_self_t()
} else {
todo!()
}
}
}
} }

View file

@ -203,6 +203,16 @@ pub enum ContextKind {
Dummy, Dummy,
} }
impl ContextKind {
pub const fn is_method_def(&self) -> bool {
matches!(self, Self::MethodDefs)
}
pub const fn is_type(&self) -> bool {
matches!(self, Self::Class | Self::Trait | Self::StructuralTrait)
}
}
/// 記号表に登録されているモードを表す /// 記号表に登録されているモードを表す
/// Preregister: サブルーチンまたは定数式、前方参照できる /// Preregister: サブルーチンまたは定数式、前方参照できる
/// Normal: 前方参照できない /// Normal: 前方参照できない

View file

@ -9,7 +9,7 @@ use erg_type::free::HasLevel;
use ast::{DefId, VarName}; use ast::{DefId, VarName};
use erg_parser::ast; use erg_parser::ast;
use erg_type::constructors::{enum_t, func, proc}; use erg_type::constructors::{enum_t, func, proc, ref_, ref_mut};
use erg_type::value::ValueObj; use erg_type::value::ValueObj;
use erg_type::{HasType, ParamTy, SubrType, TyBound, Type}; use erg_type::{HasType, ParamTy, SubrType, TyBound, Type};
use Type::*; use Type::*;
@ -161,18 +161,29 @@ impl Context {
opt_decl_t: Option<&ParamTy>, opt_decl_t: Option<&ParamTy>,
) -> TyCheckResult<()> { ) -> TyCheckResult<()> {
match &sig.pat { match &sig.pat {
ast::ParamPattern::Lit(_) => Ok(()),
ast::ParamPattern::Discard(_token) => Ok(()), ast::ParamPattern::Discard(_token) => Ok(()),
ast::ParamPattern::VarName(v) => { ast::ParamPattern::VarName(name) => {
if self.registered(v.inspect(), v.inspect().is_uppercase()) { if self.registered(name.inspect(), name.is_const()) {
Err(TyCheckError::reassign_error( Err(TyCheckError::reassign_error(
line!() as usize, line!() as usize,
v.loc(), name.loc(),
self.caused_by(), self.caused_by(),
v.inspect(), name.inspect(),
)) ))
} else { } else {
// ok, not defined // ok, not defined
let spec_t = self.instantiate_param_sig_t(sig, opt_decl_t, Normal)?; let spec_t = self.instantiate_param_sig_t(sig, opt_decl_t, Normal)?;
if &name.inspect()[..] == "self" {
let self_t = self.get_self_t();
self.sub_unify(
&spec_t,
&self_t,
Some(name.loc()),
None,
Some(name.inspect()),
)?;
}
let idx = if let Some(outer) = outer { let idx = if let Some(outer) = outer {
ParamIdx::nested(outer, nth) ParamIdx::nested(outer, nth)
} else { } else {
@ -183,16 +194,101 @@ impl Context {
} else { } else {
DefaultInfo::NonDefault DefaultInfo::NonDefault
}; };
let kind = VarKind::parameter(DefId(get_hash(&(&self.name, v))), idx, default); let kind =
VarKind::parameter(DefId(get_hash(&(&self.name, name))), idx, default);
self.params.push(( self.params.push((
Some(v.clone()), Some(name.clone()),
VarInfo::new(spec_t, Immutable, Private, kind), VarInfo::new(spec_t, Immutable, Private, kind),
)); ));
Ok(()) Ok(())
} }
} }
ast::ParamPattern::Lit(_) => Ok(()), ast::ParamPattern::Ref(name) => {
_ => unreachable!(), if self.registered(name.inspect(), name.is_const()) {
Err(TyCheckError::reassign_error(
line!() as usize,
name.loc(),
self.caused_by(),
name.inspect(),
))
} else {
// ok, not defined
let spec_t = self.instantiate_param_sig_t(sig, opt_decl_t, Normal)?;
if &name.inspect()[..] == "self" {
let self_t = self.get_self_t();
self.sub_unify(
&spec_t,
&self_t,
Some(name.loc()),
None,
Some(name.inspect()),
)?;
}
let spec_t = ref_(spec_t);
let idx = if let Some(outer) = outer {
ParamIdx::nested(outer, nth)
} else {
ParamIdx::Nth(nth)
};
let default = if sig.opt_default_val.is_some() {
DefaultInfo::WithDefault
} else {
DefaultInfo::NonDefault
};
let kind =
VarKind::parameter(DefId(get_hash(&(&self.name, name))), idx, default);
self.params.push((
Some(name.clone()),
VarInfo::new(spec_t, Immutable, Private, kind),
));
Ok(())
}
}
ast::ParamPattern::RefMut(name) => {
if self.registered(name.inspect(), name.is_const()) {
Err(TyCheckError::reassign_error(
line!() as usize,
name.loc(),
self.caused_by(),
name.inspect(),
))
} else {
// ok, not defined
let spec_t = self.instantiate_param_sig_t(sig, opt_decl_t, Normal)?;
if &name.inspect()[..] == "self" {
let self_t = self.get_self_t();
self.sub_unify(
&spec_t,
&self_t,
Some(name.loc()),
None,
Some(name.inspect()),
)?;
}
let spec_t = ref_mut(spec_t);
let idx = if let Some(outer) = outer {
ParamIdx::nested(outer, nth)
} else {
ParamIdx::Nth(nth)
};
let default = if sig.opt_default_val.is_some() {
DefaultInfo::WithDefault
} else {
DefaultInfo::NonDefault
};
let kind =
VarKind::parameter(DefId(get_hash(&(&self.name, name))), idx, default);
self.params.push((
Some(name.clone()),
VarInfo::new(spec_t, Immutable, Private, kind),
));
Ok(())
}
}
other => {
log!(err "{other}");
unreachable!()
}
} }
} }
@ -272,7 +368,7 @@ impl Context {
) )
})?; })?;
} }
if self.registered(name.inspect(), name.inspect().is_uppercase()) { if self.registered(name.inspect(), name.is_const()) {
Err(TyCheckError::reassign_error( Err(TyCheckError::reassign_error(
line!() as usize, line!() as usize,
name.loc(), name.loc(),

View file

@ -1111,6 +1111,23 @@ impl Context {
return Ok(()); return Ok(());
} }
(Type::FreeVar(_fv), _r) => todo!(), (Type::FreeVar(_fv), _r) => todo!(),
(Type::Record(lrec), Type::Record(rrec)) => {
for (k, l) in lrec.iter() {
if let Some(r) = rrec.get(k) {
self.sub_unify(l, r, sub_loc, sup_loc, param_name)?;
} else {
return Err(TyCheckError::subtyping_error(
line!() as usize,
maybe_sub,
maybe_sup,
sub_loc,
sup_loc,
self.caused_by(),
));
}
}
return Ok(());
}
(Type::Subr(lsub), Type::Subr(rsub)) => { (Type::Subr(lsub), Type::Subr(rsub)) => {
for lpt in lsub.default_params.iter() { for lpt in lsub.default_params.iter() {
if let Some(rpt) = rsub.default_params.iter().find(|rpt| rpt.name() == lpt.name()) { if let Some(rpt) = rsub.default_params.iter().find(|rpt| rpt.name() == lpt.name()) {

View file

@ -2291,7 +2291,23 @@ pub enum ParamPattern {
VarArgs(VarName), VarArgs(VarName),
} }
impl_display_for_enum!(ParamPattern; Discard, VarName, Lit, Array, Tuple, Record, Ref, RefMut, VarArgs); impl NestedDisplay for ParamPattern {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
match self {
Self::Discard(tok) => write!(f, "{}", tok),
Self::VarName(var_name) => write!(f, "{}", var_name),
Self::Lit(lit) => write!(f, "{}", lit),
Self::Array(array) => write!(f, "{}", array),
Self::Tuple(tuple) => write!(f, "{}", tuple),
Self::Record(record) => write!(f, "{}", record),
Self::Ref(var_name) => write!(f, "ref {}", var_name),
Self::RefMut(var_name) => write!(f, "ref! {}", var_name),
Self::VarArgs(var_name) => write!(f, "...{}", var_name),
}
}
}
impl_display_from_nested!(ParamPattern);
impl_locational_for_enum!(ParamPattern; Discard, VarName, Lit, Array, Tuple, Record, Ref, RefMut, VarArgs); impl_locational_for_enum!(ParamPattern; Discard, VarName, Lit, Array, Tuple, Record, Ref, RefMut, VarArgs);
impl ParamPattern { impl ParamPattern {

View file

@ -111,19 +111,28 @@ impl LimitedDisplay for Constraint {
(true, false) => { (true, false) => {
write!(f, "<: ")?; write!(f, "<: ")?;
sup.limited_fmt(f, limit - 1)?; sup.limited_fmt(f, limit - 1)?;
write!(f, "(cyclicity: {cyclicity:?})") if cfg!(feature = "debug") {
write!(f, "(cyclicity: {cyclicity:?})")?;
}
Ok(())
} }
(false, true) => { (false, true) => {
write!(f, ":> ")?; write!(f, ":> ")?;
sub.limited_fmt(f, limit - 1)?; sub.limited_fmt(f, limit - 1)?;
write!(f, "(cyclicity: {cyclicity:?})") if cfg!(feature = "debug") {
write!(f, "(cyclicity: {cyclicity:?})")?;
}
Ok(())
} }
(false, false) => { (false, false) => {
write!(f, ":> ")?; write!(f, ":> ")?;
sub.limited_fmt(f, limit - 1)?; sub.limited_fmt(f, limit - 1)?;
write!(f, ", <: ")?; write!(f, ", <: ")?;
sup.limited_fmt(f, limit - 1)?; sup.limited_fmt(f, limit - 1)?;
write!(f, "(cyclicity: {cyclicity:?})") if cfg!(feature = "debug") {
write!(f, "(cyclicity: {cyclicity:?})")?;
}
Ok(())
} }
}, },
Self::TypeOf(t) => { Self::TypeOf(t) => {
@ -258,7 +267,11 @@ impl<T: LimitedDisplay> LimitedDisplay for FreeKind<T> {
} => { } => {
write!(f, "?{name}(")?; write!(f, "?{name}(")?;
constraint.limited_fmt(f, limit - 1)?; constraint.limited_fmt(f, limit - 1)?;
write!(f, ")[{lev}]") write!(f, ")")?;
if cfg!(feature = "debug") {
write!(f, "[{lev}]")?;
}
Ok(())
} }
Self::Unbound { Self::Unbound {
id, id,
@ -267,7 +280,11 @@ impl<T: LimitedDisplay> LimitedDisplay for FreeKind<T> {
} => { } => {
write!(f, "?{id}(")?; write!(f, "?{id}(")?;
constraint.limited_fmt(f, limit - 1)?; constraint.limited_fmt(f, limit - 1)?;
write!(f, ")[{lev}]") write!(f, ")")?;
if cfg!(feature = "debug") {
write!(f, "[{lev}]")?;
}
Ok(())
} }
} }
} }
@ -501,7 +518,7 @@ impl<T: Clone + HasLevel> Free<T> {
.and_then(|c| c.get_type().cloned()) .and_then(|c| c.get_type().cloned())
} }
pub fn crack_subtype(&self) -> Option<Type> { pub fn crack_sup(&self) -> Option<Type> {
self.borrow() self.borrow()
.constraint() .constraint()
.and_then(|c| c.get_super_type().cloned()) .and_then(|c| c.get_super_type().cloned())

View file

@ -357,6 +357,10 @@ impl ValueObj {
} }
} }
pub const fn is_type(&self) -> bool {
matches!(self, Self::Type(_))
}
pub const fn is_mut(&self) -> bool { pub const fn is_mut(&self) -> bool {
matches!(self, Self::Mut(_)) matches!(self, Self::Mut(_))
} }