From f3b188e095c4f99361d32b3f7420daeddb929b71 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Fri, 25 Aug 2023 21:02:47 +0900 Subject: [PATCH] feat: support recursive class definition --- crates/erg_compiler/context/compare.rs | 4 + crates/erg_compiler/context/eval.rs | 72 ++++++--- .../context/initialize/const_func.rs | 6 +- crates/erg_compiler/context/register.rs | 138 ++++++++++++------ crates/erg_compiler/context/unify.rs | 8 +- crates/erg_compiler/declare.rs | 14 +- crates/erg_compiler/lint.rs | 2 +- crates/erg_compiler/lower.rs | 35 +++-- crates/erg_compiler/ty/mod.rs | 38 +++++ crates/erg_compiler/ty/value.rs | 38 ++++- examples/list.er | 11 ++ tests/should_ok/class.er | 7 + tests/test.rs | 5 + 13 files changed, 285 insertions(+), 93 deletions(-) create mode 100644 examples/list.er diff --git a/crates/erg_compiler/context/compare.rs b/crates/erg_compiler/context/compare.rs index ad028ed81..4bbe09041 100644 --- a/crates/erg_compiler/context/compare.rs +++ b/crates/erg_compiler/context/compare.rs @@ -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) => { diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index 6ca2cfed0..ba16d8a54 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -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 { + 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 { + 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) { ( diff --git a/crates/erg_compiler/context/initialize/const_func.rs b/crates/erg_compiler/context/initialize/const_func.rs index 80c9790d1..69062bbc8 100644 --- a/crates/erg_compiler/context/initialize/const_func.rs +++ b/crates/erg_compiler/context/initialize/const_func.rs @@ -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 diff --git a/crates/erg_compiler/context/register.rs b/crates/erg_compiler/context/register.rs index 572a757da..82a7cfb6d 100644 --- a/crates/erg_compiler/context/register.rs +++ b/crates/erg_compiler/context/register.rs @@ -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, diff --git a/crates/erg_compiler/context/unify.rs b/crates/erg_compiler/context/unify.rs index fac835a69..ac4012695 100644 --- a/crates/erg_compiler/context/unify.rs +++ b/crates/erg_compiler/context/unify.rs @@ -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? diff --git a/crates/erg_compiler/declare.rs b/crates/erg_compiler/declare.rs index c90e9755d..bf7408a5a 100644 --- a/crates/erg_compiler/declare.rs +++ b/crates/erg_compiler/declare.rs @@ -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) => { diff --git a/crates/erg_compiler/lint.rs b/crates/erg_compiler/lint.rs index 8100b0b1c..8fae9c435 100644 --- a/crates/erg_compiler/lint.rs +++ b/crates/erg_compiler/lint.rs @@ -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() { diff --git a/crates/erg_compiler/lower.rs b/crates/erg_compiler/lower.rs index a73b7ce0a..ccf148337 100644 --- a/crates/erg_compiler/lower.rs +++ b/crates/erg_compiler/lower.rs @@ -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 { 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() { diff --git a/crates/erg_compiler/ty/mod.rs b/crates/erg_compiler/ty/mod.rs index 592d3a9ce..a69be605e 100644 --- a/crates/erg_compiler/ty/mod.rs +++ b/crates/erg_compiler/ty/mod.rs @@ -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 { + 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(), diff --git a/crates/erg_compiler/ty/value.rs b/crates/erg_compiler/ty/value.rs index f12706ef8..bbfda60a9 100644 --- a/crates/erg_compiler/ty/value.rs +++ b/crates/erg_compiler/ty/value.rs @@ -63,14 +63,16 @@ pub struct ClassTypeObj { pub t: Type, pub base: Option>, pub impls: Option>, + pub inited: bool, } impl ClassTypeObj { - pub fn new(t: Type, base: Option, impls: Option) -> Self { + pub fn new(t: Type, base: Option, impls: Option, 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, pub impls: Option>, + pub inited: bool, } impl TraitTypeObj { - pub fn new(t: Type, requires: TypeObj, impls: Option) -> Self { + pub fn new(t: Type, requires: TypeObj, impls: Option, 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, impls: Option) -> Self { - GenTypeObj::Class(ClassTypeObj::new(t, require, impls)) + pub fn class(t: Type, require: Option, impls: Option, 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) -> Self { - GenTypeObj::Trait(TraitTypeObj::new(t, require, impls)) + pub fn trait_(t: Type, require: TypeObj, impls: Option, inited: bool) -> Self { + GenTypeObj::Trait(TraitTypeObj::new(t, require, impls, inited)) } pub fn patch(t: Type, base: TypeObj, impls: Option) -> 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 { match t { Type::Int => content.replace('_', "").parse::().ok().map(Self::Int), diff --git a/examples/list.er b/examples/list.er new file mode 100644 index 000000000..2255ea83e --- /dev/null +++ b/examples/list.er @@ -0,0 +1,11 @@ +IntList = Class NoneType or { .node = Int; .next = IntList } +IntList. + null = IntList::__new__ None + insert self, node = IntList::__new__ { .node; .next = self } + fst self = + match self::base: + { node; next = _ } => node + None => None + +l = IntList.null.insert 1 +assert l.fst() == 1 diff --git a/tests/should_ok/class.er b/tests/should_ok/class.er index b21e54bdb..1b4e998a0 100644 --- a/tests/should_ok/class.er +++ b/tests/should_ok/class.er @@ -2,3 +2,10 @@ unittest = pyimport "unittest" Test! = Inherit unittest.TestCase! _ = Test! + +# forward reference +C = Class { .x = D } +D = Class { .y = Int } + +c = C.new { .x = D.new { .y = 1 } } +assert c.x.y == 1 diff --git a/tests/test.rs b/tests/test.rs index eab03b61b..ea9525439 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -158,6 +158,11 @@ fn exec_interpolation() -> Result<(), ()> { expect_success("tests/should_ok/interpolation.er", 0) } +#[test] +fn exec_list() -> Result<(), ()> { + expect_success("examples/list.er", 0) +} + #[test] fn exec_long() -> Result<(), ()> { expect_success("tests/should_ok/long.er", 257)