feat: support recursive class definition

This commit is contained in:
Shunsuke Shibayama 2023-08-25 21:02:47 +09:00
parent 418f31e6ed
commit f3b188e095
13 changed files with 285 additions and 93 deletions

View file

@ -641,7 +641,11 @@ impl Context {
// ({I: Int | True} :> Int) == true
// {N: Nat | ...} :> Int) == false
// ({I: Int | I >= 0} :> Int) == false
// {U(: Type)} :> { .x = {Int} }(== {{ .x = Int }}) == true
(Refinement(l), r) => {
if let Some(r) = r.to_singleton() {
return self.structural_supertype_of(lhs, &Type::Refinement(r));
}
if l.pred.mentions(&l.var) {
match l.pred.can_be_false() {
Some(true) => {

View file

@ -595,7 +595,13 @@ impl Context {
call.loc(),
self.caused_by(),
))),
_ => unreachable!(),
other => Err(EvalErrors::from(EvalError::feature_error(
self.cfg.input.clone(),
line!() as usize,
other.loc(),
&format!("const call: {other}"),
self.caused_by(),
))),
}
} else {
Err(EvalErrors::from(EvalError::not_const_expr(
@ -1031,26 +1037,8 @@ impl Context {
line!(),
))
}),
Or | BitOr => match (lhs, rhs) {
(ValueObj::Bool(l), ValueObj::Bool(r)) => Ok(ValueObj::Bool(l || r)),
(ValueObj::Int(l), ValueObj::Int(r)) => Ok(ValueObj::Int(l | r)),
(ValueObj::Type(lhs), ValueObj::Type(rhs)) => Ok(self.eval_or_type(lhs, rhs)),
_ => Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
))),
},
And | BitAnd => match (lhs, rhs) {
(ValueObj::Bool(l), ValueObj::Bool(r)) => Ok(ValueObj::Bool(l && r)),
(ValueObj::Int(l), ValueObj::Int(r)) => Ok(ValueObj::Int(l & r)),
(ValueObj::Type(lhs), ValueObj::Type(rhs)) => Ok(self.eval_and_type(lhs, rhs)),
_ => Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
))),
},
Or | BitOr => self.eval_or(lhs, rhs),
And | BitAnd => self.eval_and(lhs, rhs),
BitXor => match (lhs, rhs) {
(ValueObj::Bool(l), ValueObj::Bool(r)) => Ok(ValueObj::Bool(l ^ r)),
(ValueObj::Int(l), ValueObj::Int(r)) => Ok(ValueObj::Int(l ^ r)),
@ -1068,6 +1056,27 @@ impl Context {
}
}
fn eval_or(&self, lhs: ValueObj, rhs: ValueObj) -> EvalResult<ValueObj> {
match (lhs, rhs) {
(ValueObj::Bool(l), ValueObj::Bool(r)) => Ok(ValueObj::Bool(l || r)),
(ValueObj::Int(l), ValueObj::Int(r)) => Ok(ValueObj::Int(l | r)),
(ValueObj::Type(lhs), ValueObj::Type(rhs)) => Ok(self.eval_or_type(lhs, rhs)),
(lhs, rhs) => {
let lhs = self.convert_value_into_type(lhs).ok();
let rhs = self.convert_value_into_type(rhs).ok();
if let Some((l, r)) = lhs.zip(rhs) {
self.eval_or(ValueObj::builtin_type(l), ValueObj::builtin_type(r))
} else {
Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
)))
}
}
}
}
fn eval_or_type(&self, lhs: TypeObj, rhs: TypeObj) -> ValueObj {
match (lhs, rhs) {
(
@ -1101,6 +1110,27 @@ impl Context {
}
}
fn eval_and(&self, lhs: ValueObj, rhs: ValueObj) -> EvalResult<ValueObj> {
match (lhs, rhs) {
(ValueObj::Bool(l), ValueObj::Bool(r)) => Ok(ValueObj::Bool(l && r)),
(ValueObj::Int(l), ValueObj::Int(r)) => Ok(ValueObj::Int(l & r)),
(ValueObj::Type(lhs), ValueObj::Type(rhs)) => Ok(self.eval_and_type(lhs, rhs)),
(lhs, rhs) => {
let lhs = self.convert_value_into_type(lhs).ok();
let rhs = self.convert_value_into_type(rhs).ok();
if let Some((l, r)) = lhs.zip(rhs) {
self.eval_and(ValueObj::builtin_type(l), ValueObj::builtin_type(r))
} else {
Err(EvalErrors::from(EvalError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
)))
}
}
}
}
fn eval_and_type(&self, lhs: TypeObj, rhs: TypeObj) -> ValueObj {
match (lhs, rhs) {
(

View file

@ -65,12 +65,12 @@ pub(crate) fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
match base {
Some(value) => {
if let Some(base) = value.as_type(ctx) {
Ok(ValueObj::gen_t(GenTypeObj::class(t, Some(base), impls)).into())
Ok(ValueObj::gen_t(GenTypeObj::class(t, Some(base), impls, true)).into())
} else {
Err(type_mismatch("type", value, "Base"))
}
}
None => Ok(ValueObj::gen_t(GenTypeObj::class(t, None, impls)).into()),
None => Ok(ValueObj::gen_t(GenTypeObj::class(t, None, impls, true)).into()),
}
}
@ -133,7 +133,7 @@ pub(crate) fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type(ctx).unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::trait_(t, req, impls)).into())
Ok(ValueObj::gen_t(GenTypeObj::trait_(t, req, impls, true)).into())
}
/// Base: Type, Impl := Type -> Patch

View file

@ -907,13 +907,46 @@ impl Context {
Ok(())
}
// To allow forward references and recursive definitions
pub(crate) fn preregister(&mut self, block: &ast::Block) -> TyCheckResult<()> {
pub(crate) fn preregister_const(&mut self, block: &ast::Block) -> TyCheckResult<()> {
let mut total_errs = TyCheckErrors::empty();
for expr in block.iter() {
match expr {
ast::Expr::Def(def) => {
if let Err(errs) = self.preregister_def(def) {
if let Err(errs) = self.preregister_const_def(def) {
total_errs.extend(errs);
}
}
ast::Expr::ClassDef(class_def) => {
if let Err(errs) = self.preregister_const_def(&class_def.def) {
total_errs.extend(errs);
}
}
ast::Expr::PatchDef(patch_def) => {
if let Err(errs) = self.preregister_const_def(&patch_def.def) {
total_errs.extend(errs);
}
}
ast::Expr::Dummy(dummy) => {
if let Err(errs) = self.preregister_const(&dummy.exprs) {
total_errs.extend(errs);
}
}
_ => {}
}
}
if total_errs.is_empty() {
Ok(())
} else {
Err(total_errs)
}
}
pub(crate) fn register_const(&mut self, block: &ast::Block) -> TyCheckResult<()> {
let mut total_errs = TyCheckErrors::empty();
for expr in block.iter() {
match expr {
ast::Expr::Def(def) => {
if let Err(errs) = self.register_const_def(def) {
total_errs.extend(errs);
}
if def.def_kind().is_import() {
@ -923,17 +956,17 @@ impl Context {
}
}
ast::Expr::ClassDef(class_def) => {
if let Err(errs) = self.preregister_def(&class_def.def) {
if let Err(errs) = self.register_const_def(&class_def.def) {
total_errs.extend(errs);
}
}
ast::Expr::PatchDef(patch_def) => {
if let Err(errs) = self.preregister_def(&patch_def.def) {
if let Err(errs) = self.register_const_def(&patch_def.def) {
total_errs.extend(errs);
}
}
ast::Expr::Dummy(dummy) => {
if let Err(errs) = self.preregister(&dummy.exprs) {
if let Err(errs) = self.register_const(&dummy.exprs) {
total_errs.extend(errs);
}
}
@ -988,7 +1021,43 @@ impl Context {
res
}
pub(crate) fn preregister_def(&mut self, def: &ast::Def) -> TyCheckResult<()> {
fn preregister_const_def(&mut self, def: &ast::Def) -> TyCheckResult<()> {
match &def.sig {
ast::Signature::Var(var) if var.is_const() => {
let Some(ast::Expr::Call(call)) = def.body.block.first() else {
return Ok(());
};
self.preregister_type(var, call)
}
_ => Ok(()),
}
}
fn preregister_type(&mut self, var: &ast::VarSignature, call: &ast::Call) -> TyCheckResult<()> {
match call.obj.as_ref() {
ast::Expr::Accessor(ast::Accessor::Ident(ident)) => match &ident.inspect()[..] {
"Class" => {
let ident = var.ident().unwrap();
let t = Type::Mono(format!("{}{ident}", self.name).into());
let class = GenTypeObj::class(t, None, None, false);
let class = ValueObj::Type(TypeObj::Generated(class));
self.register_gen_const(ident, class, false)
}
"Trait" => {
let ident = var.ident().unwrap();
let t = Type::Mono(format!("{}{ident}", self.name).into());
let trait_ =
GenTypeObj::trait_(t, TypeObj::builtin_type(Type::Failure), None, false);
let trait_ = ValueObj::Type(TypeObj::Generated(trait_));
self.register_gen_const(ident, trait_, false)
}
_ => Ok(()),
},
_ => Ok(()),
}
}
pub(crate) fn register_const_def(&mut self, def: &ast::Def) -> TyCheckResult<()> {
let id = Some(def.body.id);
let __name__ = def.sig.ident().map(|i| i.inspect()).unwrap_or(UBAR);
match &def.sig {
@ -1278,7 +1347,10 @@ impl Context {
alias: bool,
) -> CompileResult<()> {
let vis = self.instantiate_vis_modifier(&ident.vis)?;
if self.rec_get_const_obj(ident.inspect()).is_some() && vis.is_private() {
let inited = self
.rec_get_const_obj(ident.inspect())
.is_some_and(|v| v.is_inited());
if inited && vis.is_private() {
Err(CompileErrors::from(CompileError::reassign_error(
self.cfg.input.clone(),
line!() as usize,
@ -1457,14 +1529,13 @@ impl Context {
2,
self.level,
);
let Some(TypeObj::Builtin {
if let Some(TypeObj::Builtin {
t: Type::Record(req),
..
}) = gen.base_or_sup()
else {
todo!("{gen}")
};
self.register_instance_attrs(&mut ctx, req)?;
{
self.register_instance_attrs(&mut ctx, req)?;
}
self.register_gen_mono_type(ident, gen, ctx, Const)
} else {
feature_error!(
@ -1635,15 +1706,10 @@ impl Context {
meta_t: Type,
) -> CompileResult<()> {
let vis = self.instantiate_vis_modifier(&ident.vis)?;
if self.mono_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() {
let inited = self
.rec_get_const_obj(ident.inspect())
.is_some_and(|v| v.is_inited());
if inited && vis.is_private() {
// TODO: display where defined
Err(CompileErrors::from(CompileError::reassign_error(
self.cfg.input.clone(),
@ -1682,16 +1748,10 @@ impl Context {
muty: Mutability,
) -> CompileResult<()> {
let vis = self.instantiate_vis_modifier(&ident.vis)?;
// FIXME: recursive search
if self.mono_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() {
let inited = self
.rec_get_const_obj(ident.inspect())
.is_some_and(|v| v.is_inited());
if inited && vis.is_private() {
Err(CompileErrors::from(CompileError::reassign_error(
self.cfg.input.clone(),
line!() as usize,
@ -1732,16 +1792,10 @@ impl Context {
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() {
let inited = self
.rec_get_const_obj(ident.inspect())
.is_some_and(|v| v.is_inited());
if inited && vis.is_private() {
Err(CompileErrors::from(CompileError::reassign_error(
self.cfg.input.clone(),
line!() as usize,

View file

@ -1323,8 +1323,12 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
self.sub_unify(maybe_sub, &Type::Refinement(sup))?;
}
(sub, Refinement(_)) => {
let sub = sub.clone().into_refinement();
self.sub_unify(&Type::Refinement(sub), maybe_sup)?;
if let Some(sub) = sub.to_singleton() {
self.sub_unify(&Type::Refinement(sub), maybe_sup)?;
} else {
let sub = sub.clone().into_refinement();
self.sub_unify(&Type::Refinement(sub), maybe_sup)?;
}
}
(Subr(_) | Record(_), Type) => {}
// REVIEW: correct?

View file

@ -772,14 +772,18 @@ impl ASTLowerer {
let (t, ty_obj) = match t {
Type::ClassType => {
let t = mono(format!("{}{ident}", self.module.context.path()));
let ty_obj = GenTypeObj::class(t.clone(), None, None);
let ty_obj = GenTypeObj::class(t.clone(), None, None, true);
let t = v_enum(set! { ValueObj::builtin_class(t) });
(t, Some(ty_obj))
}
Type::TraitType => {
let t = mono(format!("{}{ident}", self.module.context.path()));
let ty_obj =
GenTypeObj::trait_(t.clone(), TypeObj::builtin_type(Type::Uninited), None);
let ty_obj = GenTypeObj::trait_(
t.clone(),
TypeObj::builtin_type(Type::Uninited),
None,
true,
);
let t = v_enum(set! { ValueObj::builtin_trait(t) });
(t, Some(ty_obj))
}
@ -793,7 +797,7 @@ impl ASTLowerer {
})
.collect();
let t = poly(format!("{}{ident}", self.module.context.path()), params);
let ty_obj = GenTypeObj::class(t.clone(), None, None);
let ty_obj = GenTypeObj::class(t.clone(), None, None, true);
let t = v_enum(set! { ValueObj::builtin_class(t) });
(t, Some(ty_obj))
}
@ -875,7 +879,7 @@ impl ASTLowerer {
pub(crate) fn declare_module(&mut self, ast: AST) -> HIR {
let mut module = hir::Module::with_capacity(ast.module.len());
let _ = self.module.context.preregister(ast.module.block());
let _ = self.module.context.register_const(ast.module.block());
for chunk in ast.module.into_iter() {
match self.declare_chunk(chunk, false) {
Ok(chunk) => {

View file

@ -256,7 +256,7 @@ impl ASTLowerer {
None,
);
let mut module = hir::Module::with_capacity(ast.module.len());
if let Err(errs) = self.module.context.preregister(ast.module.block()) {
if let Err(errs) = self.module.context.register_const(ast.module.block()) {
self.errs.extend(errs);
}
for chunk in ast.module.into_iter() {

View file

@ -1230,7 +1230,7 @@ impl ASTLowerer {
}
overwritten
};
if let Err(errs) = self.module.context.preregister(&lambda.body) {
if let Err(errs) = self.module.context.register_const(&lambda.body) {
self.errs.extend(errs);
}
let body = self.lower_block(lambda.body).map_err(|errs| {
@ -1453,7 +1453,7 @@ impl ASTLowerer {
body: ast::DefBody,
) -> LowerResult<hir::Def> {
log!(info "entered {}({sig})", fn_name!());
if let Err(errs) = self.module.context.preregister(&body.block) {
if let Err(errs) = self.module.context.register_const(&body.block) {
self.errs.extend(errs);
}
match self.lower_block(body.block) {
@ -1549,7 +1549,7 @@ impl ASTLowerer {
if let Err(errs) = self.module.context.assign_params(&mut params, Some(subr_t)) {
self.errs.extend(errs);
}
if let Err(errs) = self.module.context.preregister(&body.block) {
if let Err(errs) = self.module.context.register_const(&body.block) {
self.errs.extend(errs);
}
match self.lower_block(body.block) {
@ -1614,7 +1614,7 @@ impl ASTLowerer {
if let Err(errs) = self.module.context.assign_params(&mut params, None) {
self.errs.extend(errs);
}
if let Err(errs) = self.module.context.preregister(&body.block) {
if let Err(errs) = self.module.context.register_const(&body.block) {
self.errs.extend(errs);
}
self.module
@ -1678,10 +1678,13 @@ impl ASTLowerer {
for attr in methods.attrs.iter_mut() {
match attr {
ast::ClassAttr::Def(def) => {
self.module.context.preregister_def(def).map_err(|errs| {
self.pop_append_errs();
errs
})?;
self.module
.context
.register_const_def(def)
.map_err(|errs| {
self.pop_append_errs();
errs
})?;
if let Some(ident) = def.sig.ident() {
if self
.module
@ -1899,10 +1902,13 @@ impl ASTLowerer {
def.sig.col_begin().unwrap(),
));
}
self.module.context.preregister_def(def).map_err(|errs| {
self.pop_append_errs();
errs
})?;
self.module
.context
.register_const_def(def)
.map_err(|errs| {
self.pop_append_errs();
errs
})?;
}
ast::ClassAttr::Decl(_) | ast::ClassAttr::Doc(_) => {}
}
@ -2536,7 +2542,10 @@ impl ASTLowerer {
}
}
let mut module = hir::Module::with_capacity(ast.module.len());
if let Err(errs) = self.module.context.preregister(ast.module.block()) {
if let Err(errs) = self.module.context.preregister_const(ast.module.block()) {
self.errs.extend(errs);
}
if let Err(errs) = self.module.context.register_const(ast.module.block()) {
self.errs.extend(errs);
}
for chunk in ast.module.into_iter() {

View file

@ -22,6 +22,7 @@ use std::fmt;
use std::ops::{BitAnd, BitOr, Deref, Not, Range, RangeInclusive};
use std::path::PathBuf;
use erg_common::consts::DEBUG_MODE;
use erg_common::dict::Dict;
use erg_common::error::Location;
use erg_common::fresh::FRESH_GEN;
@ -1925,6 +1926,14 @@ impl Type {
}
}
pub fn is_singleton_refinement(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_singleton_refinement(),
Self::Refinement(refine) => matches!(refine.pred.as_ref(), Predicate::Equal { .. }),
_ => false,
}
}
pub fn is_record(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_record(),
@ -2531,6 +2540,35 @@ impl Type {
}
}
/// ```erg
/// { .x = {Int} } == {{ .x = Int }}
/// K({Int}) == {K(Int)} # TODO
/// ```
pub fn to_singleton(&self) -> Option<RefinementType> {
match self {
Type::Record(rec) if rec.values().all(|t| t.is_singleton_refinement()) => {
let mut new_rec = Dict::new();
for (k, t) in rec.iter() {
if let Some(t) = t
.singleton_value()
.and_then(|tp| <&Type>::try_from(tp).ok())
{
new_rec.insert(k.clone(), t.clone());
} else if DEBUG_MODE {
todo!("{t}");
}
}
let t = Type::Record(new_rec);
Some(RefinementType::new(
Str::ever("_"),
Type::Type,
Predicate::eq(Str::ever("_"), TyParam::t(t)),
))
}
_ => None,
}
}
pub fn deconstruct_refinement(self) -> Result<(Str, Type, Predicate), Type> {
match self {
Type::FreeVar(fv) if fv.is_linked() => fv.crack().clone().deconstruct_refinement(),

View file

@ -63,14 +63,16 @@ pub struct ClassTypeObj {
pub t: Type,
pub base: Option<Box<TypeObj>>,
pub impls: Option<Box<TypeObj>>,
pub inited: bool,
}
impl ClassTypeObj {
pub fn new(t: Type, base: Option<TypeObj>, impls: Option<TypeObj>) -> Self {
pub fn new(t: Type, base: Option<TypeObj>, impls: Option<TypeObj>, inited: bool) -> Self {
Self {
t,
base: base.map(Box::new),
impls: impls.map(Box::new),
inited,
}
}
}
@ -99,14 +101,16 @@ pub struct TraitTypeObj {
pub t: Type,
pub requires: Box<TypeObj>,
pub impls: Option<Box<TypeObj>>,
pub inited: bool,
}
impl TraitTypeObj {
pub fn new(t: Type, requires: TypeObj, impls: Option<TypeObj>) -> Self {
pub fn new(t: Type, requires: TypeObj, impls: Option<TypeObj>, inited: bool) -> Self {
Self {
t,
requires: Box::new(requires),
impls: impls.map(Box::new),
inited,
}
}
}
@ -223,8 +227,8 @@ impl LimitedDisplay for GenTypeObj {
}
impl GenTypeObj {
pub fn class(t: Type, require: Option<TypeObj>, impls: Option<TypeObj>) -> Self {
GenTypeObj::Class(ClassTypeObj::new(t, require, impls))
pub fn class(t: Type, require: Option<TypeObj>, impls: Option<TypeObj>, inited: bool) -> Self {
GenTypeObj::Class(ClassTypeObj::new(t, require, impls, inited))
}
pub fn inherited(
@ -236,8 +240,8 @@ impl GenTypeObj {
GenTypeObj::Subclass(InheritedTypeObj::new(t, sup, impls, additional))
}
pub fn trait_(t: Type, require: TypeObj, impls: Option<TypeObj>) -> Self {
GenTypeObj::Trait(TraitTypeObj::new(t, require, impls))
pub fn trait_(t: Type, require: TypeObj, impls: Option<TypeObj>, inited: bool) -> Self {
GenTypeObj::Trait(TraitTypeObj::new(t, require, impls, inited))
}
pub fn patch(t: Type, base: TypeObj, impls: Option<TypeObj>) -> Self {
@ -265,6 +269,14 @@ impl GenTypeObj {
GenTypeObj::Structural(StructuralTypeObj::new(t, type_))
}
pub const fn is_inited(&self) -> bool {
match self {
Self::Class(class) => class.inited,
Self::Trait(trait_) => trait_.inited,
_ => true,
}
}
pub fn base_or_sup(&self) -> Option<&TypeObj> {
match self {
Self::Class(class) => class.base.as_ref().map(AsRef::as_ref),
@ -428,6 +440,13 @@ impl TypeObj {
}
}
pub const fn is_inited(&self) -> bool {
match self {
Self::Builtin { .. } => true,
Self::Generated(gen) => gen.is_inited(),
}
}
pub fn typ(&self) -> &Type {
match self {
TypeObj::Builtin { t, .. } => t,
@ -970,6 +989,13 @@ impl ValueObj {
matches!(self, Self::Type(_))
}
pub const fn is_inited(&self) -> bool {
match self {
Self::Type(t) => t.is_inited(),
_ => true,
}
}
pub fn from_str(t: Type, mut content: Str) -> Option<Self> {
match t {
Type::Int => content.replace('_', "").parse::<i32>().ok().map(Self::Int),