diff --git a/compiler/erg_common/error.rs b/compiler/erg_common/error.rs index 7d54df97..c09ac28d 100644 --- a/compiler/erg_common/error.rs +++ b/compiler/erg_common/error.rs @@ -35,6 +35,7 @@ pub enum ErrorKind { PurityError, HasEffect, MoveError, + NotConstExpr, /* compile warnings */ AttributeWarning = 60, CastWarning, diff --git a/compiler/erg_compiler/context/initialize/mod.rs b/compiler/erg_compiler/context/initialize/mod.rs index 83856b66..eaad99c0 100644 --- a/compiler/erg_compiler/context/initialize/mod.rs +++ b/compiler/erg_compiler/context/initialize/mod.rs @@ -47,15 +47,15 @@ impl Context { } } - fn register_const(&mut self, name: &'static str, obj: ValueObj) { + pub(crate) fn register_const(&mut self, name: &str, obj: ValueObj) { if self.consts.get(name).is_some() { panic!("already registered: {name}"); } else { // TODO: visibility (not always private) // TODO: kind (not always Builtin) let vi = VarInfo::new(obj.t(), Const, Private, Builtin); - self.consts.insert(VarName::from_static(name), obj); - self.locals.insert(VarName::from_static(name), vi); + self.consts.insert(VarName::from_str(Str::rc(name)), obj); + self.locals.insert(VarName::from_str(Str::rc(name)), vi); } } diff --git a/compiler/erg_compiler/context/register.rs b/compiler/erg_compiler/context/register.rs index df880afc..1ac1bf5a 100644 --- a/compiler/erg_compiler/context/register.rs +++ b/compiler/erg_compiler/context/register.rs @@ -1,6 +1,6 @@ use std::option::Option; // conflicting to Type::Option -use erg_common::traits::Locational; +use erg_common::traits::{Locational, Stream}; use erg_common::vis::Visibility; use erg_common::Str; use erg_common::{enum_unwrap, get_hash, log, set}; @@ -315,30 +315,72 @@ impl Context { } } - // 再帰サブルーチン/型の推論を可能にするため、予め登録しておく - pub(crate) fn preregister(&mut self, block: &[ast::Expr]) -> TyCheckResult<()> { + // To allow forward references and recursive definitions + pub(crate) fn preregister(&mut self, block: &ast::Block) -> TyCheckResult<()> { for expr in block.iter() { if let ast::Expr::Def(def) = expr { let id = Some(def.body.id); let __name__ = def.sig.ident().map(|i| i.inspect()); - let mut eval_body_t = || { - self.eval_const_block(&def.body.block, __name__) - .map(|c| enum_t(set![c])) - }; match &def.sig { ast::Signature::Subr(sig) => { - let opt_ret_t = if let Some(spec) = sig.return_t_spec.as_ref() { - Some(self.instantiate_typespec(spec, PreRegister)?) + let const_t = if sig.is_const() { + match self.eval_const_block(&def.body.block, __name__) { + Ok(obj) => { + self.register_const(__name__.unwrap(), obj.clone()); + Some(enum_t(set![obj])) + } + Err(e) => { + return Err(e); + } + } } else { - eval_body_t() + None + }; + let opt_ret_t = if let Some(spec) = sig.return_t_spec.as_ref() { + let spec_t = self.instantiate_typespec(spec, PreRegister)?; + if let Some(const_t) = const_t { + self.sub_unify( + &const_t, + &spec_t, + Some(def.body.loc()), + None, + None, + )?; + } + Some(spec_t) + } else { + const_t }; self.declare_sub(sig, opt_ret_t, id)?; } ast::Signature::Var(sig) if sig.is_const() => { - let t = if let Some(spec) = sig.t_spec.as_ref() { - Some(self.instantiate_typespec(spec, PreRegister)?) + let const_t = if sig.is_const() { + match self.eval_const_block(&def.body.block, __name__) { + Ok(obj) => { + self.register_const(__name__.unwrap(), obj.clone()); + Some(enum_t(set![obj])) + } + Err(e) => { + return Err(e); + } + } } else { - eval_body_t() + None + }; + let t = if let Some(spec) = sig.t_spec.as_ref() { + let spec_t = self.instantiate_typespec(spec, PreRegister)?; + if let Some(const_t) = const_t { + self.sub_unify( + &const_t, + &spec_t, + Some(def.body.loc()), + None, + None, + )?; + } + Some(spec_t) + } else { + const_t }; self.declare_var(sig, t, id)?; } diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index b6081d61..c38f02e8 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -1019,6 +1019,24 @@ passed keyword args: {RED}{kw_args_len}{RESET}" caused_by, ) } + + pub fn not_const_expr(errno: usize, loc: Location, caused_by: Str) -> Self { + Self::new( + ErrorCore::new( + errno, + NotConstExpr, + loc, + switch_lang!( + "japanese" => "定数式ではありません", + "simplified_chinese" => "不是常量表达式", + "traditional_chinese" => "不是常量表達式", + "english" => "not a constant expression", + ), + None, + ), + caused_by, + ) + } } #[derive(Debug)] diff --git a/compiler/erg_compiler/eval.rs b/compiler/erg_compiler/eval.rs index 864e16cc..310c9c11 100644 --- a/compiler/erg_compiler/eval.rs +++ b/compiler/erg_compiler/eval.rs @@ -3,9 +3,9 @@ use std::mem; use erg_common::dict::Dict; use erg_common::rccell::RcCell; use erg_common::set::Set; -use erg_common::traits::Stream; +use erg_common::traits::{Locational, Stream}; use erg_common::vis::Field; -use erg_common::{dict, fn_name, set}; +use erg_common::{dict, fn_name, option_enum_unwrap, set}; use erg_common::{RcArray, Str}; use OpKind::*; @@ -13,11 +13,11 @@ use erg_parser::ast::*; use erg_parser::token::{Token, TokenKind}; use erg_type::constructors::{ - enum_t, mono_proj, poly_class, poly_trait, ref_, ref_mut, refinement, subr_t, + class, enum_t, mono_proj, poly_class, poly_trait, ref_, ref_mut, refinement, subr_t, }; use erg_type::typaram::{OpKind, TyParam}; use erg_type::value::ValueObj; -use erg_type::{Predicate, SubrKind, TyBound, Type, ValueArgs}; +use erg_type::{HasType, Predicate, SubrKind, TyBound, Type, ValueArgs}; use crate::context::instantiate::TyVarContext; use crate::context::Context; @@ -41,7 +41,7 @@ pub fn type_from_token_kind(kind: TokenKind) -> Type { } } -fn try_get_op_kind_from_token(kind: TokenKind) -> Result { +fn try_get_op_kind_from_token(kind: TokenKind) -> EvalResult { match kind { TokenKind::Plus => Ok(OpKind::Add), TokenKind::Minus => Ok(OpKind::Sub), @@ -63,7 +63,7 @@ fn try_get_op_kind_from_token(kind: TokenKind) -> Result { TokenKind::Shl => Ok(OpKind::Shl), TokenKind::Shr => Ok(OpKind::Shr), TokenKind::Mutate => Ok(OpKind::Mutate), - _other => Err(()), + _other => todo!("{_other}"), } } @@ -166,44 +166,58 @@ impl SubstContext { } impl Context { - fn eval_const_acc(&self, _acc: &Accessor) -> Option { - match _acc { + fn eval_const_acc(&self, acc: &Accessor) -> EvalResult { + match acc { Accessor::Local(local) => { if let Some(val) = self.rec_get_const_obj(local.inspect()) { - Some(val.clone()) + Ok(val.clone()) } else { - None + if local.is_const() { + Err(EvalError::no_var_error( + line!() as usize, + local.loc(), + self.caused_by(), + local.inspect(), + self.get_similar_name(local.inspect()), + )) + } else { + Err(EvalError::not_const_expr( + line!() as usize, + acc.loc(), + self.caused_by(), + )) + } } } Accessor::Attr(attr) => { - let _obj = self.eval_const_expr(&attr.obj, None)?; + let _obj = self.eval_const_expr(&attr.obj, None); todo!() } _ => todo!(), } } - fn eval_const_bin(&self, bin: &BinOp) -> Option { + fn eval_const_bin(&self, bin: &BinOp) -> EvalResult { match (bin.args[0].as_ref(), bin.args[1].as_ref()) { (Expr::Lit(l), Expr::Lit(r)) => { - let op = try_get_op_kind_from_token(bin.op.kind).ok()?; - self.eval_bin_lit(op, eval_lit(l), eval_lit(r)).ok() + let op = try_get_op_kind_from_token(bin.op.kind)?; + self.eval_bin_lit(op, eval_lit(l), eval_lit(r)) } - _ => None, + _ => todo!(), } } - fn eval_const_unary(&self, unary: &UnaryOp) -> Option { + fn eval_const_unary(&self, unary: &UnaryOp) -> EvalResult { match unary.args[0].as_ref() { Expr::Lit(lit) => { - let op = try_get_op_kind_from_token(unary.op.kind).ok()?; - self.eval_unary_lit(op, eval_lit(lit)).ok() + let op = try_get_op_kind_from_token(unary.op.kind)?; + self.eval_unary_lit(op, eval_lit(lit)) } - _ => None, + _ => todo!(), } } - fn eval_args(&self, args: &Args, __name__: Option<&Str>) -> Option { + fn eval_args(&self, args: &Args, __name__: Option<&Str>) -> EvalResult { let mut evaluated_pos_args = vec![]; for arg in args.pos_args().iter() { let val = self.eval_const_expr(&arg.expr, __name__)?; @@ -214,96 +228,116 @@ impl Context { let val = self.eval_const_expr(&arg.expr, __name__)?; evaluated_kw_args.insert(arg.keyword.inspect().clone(), val); } - Some(ValueArgs::new(evaluated_pos_args, evaluated_kw_args)) + Ok(ValueArgs::new(evaluated_pos_args, evaluated_kw_args)) } - fn eval_const_call(&self, call: &Call, __name__: Option<&Str>) -> Option { + fn eval_const_call(&self, call: &Call, __name__: Option<&Str>) -> EvalResult { if let Expr::Accessor(acc) = call.obj.as_ref() { match acc { - Accessor::Local(name) if name.is_const() => { - if let Some(ValueObj::Subr(subr)) = self.rec_get_const_obj(&name.inspect()) { - let subr = subr.clone(); - let args = self.eval_args(&call.args, __name__)?; - Some(subr.call(args, __name__.map(|n| n.clone()))) - } else { - None - } + Accessor::Local(name) => { + let obj = + self.rec_get_const_obj(&name.inspect()) + .ok_or(EvalError::no_var_error( + line!() as usize, + name.loc(), + self.caused_by(), + name.inspect(), + self.get_similar_name(name.inspect()), + ))?; + let subr = option_enum_unwrap!(obj, ValueObj::Subr) + .ok_or(EvalError::type_mismatch_error( + line!() as usize, + name.loc(), + self.caused_by(), + name.inspect(), + &class("Subroutine"), + &obj.t(), + None, + ))? + .clone(); + let args = self.eval_args(&call.args, __name__)?; + Ok(subr.call(args, __name__.map(|n| n.clone()))) } - Accessor::Local(_) => None, Accessor::Attr(_attr) => todo!(), Accessor::TupleAttr(_attr) => todo!(), Accessor::Public(_name) => todo!(), Accessor::Subscr(_subscr) => todo!(), } } else { - None - } - } - - fn eval_const_def(&mut self, def: &Def) -> Option { - if def.is_const() { todo!() } - None } - fn eval_const_array(&self, arr: &Array) -> Option { + fn eval_const_def(&mut self, def: &Def) -> EvalResult { + if def.is_const() { + let __name__ = def.sig.ident().map(|i| i.inspect()).unwrap(); + let obj = self.eval_const_block(&def.body.block, Some(__name__))?; + erg_common::log!(); + self.register_const(__name__, obj); + Ok(ValueObj::None) + } else { + Err(EvalError::not_const_expr( + line!() as usize, + def.body.block.loc(), + self.caused_by(), + )) + } + } + + fn eval_const_array(&self, arr: &Array) -> EvalResult { let mut elems = vec![]; match arr { Array::Normal(arr) => { for elem in arr.elems.pos_args().iter() { - if let Some(elem) = self.eval_const_expr(&elem.expr, None) { - elems.push(elem); - } else { - return None; - } + let elem = self.eval_const_expr(&elem.expr, None)?; + elems.push(elem); } } _ => { - return None; + todo!() } } - Some(ValueObj::Array(RcArray::from(elems))) + Ok(ValueObj::Array(RcArray::from(elems))) } - fn eval_const_record(&mut self, record: &Record) -> Option { + fn eval_const_record(&self, record: &Record) -> EvalResult { match record { Record::Normal(rec) => self.eval_const_normal_record(rec), Record::Shortened(_rec) => unreachable!(), // should be desugared } } - fn eval_const_normal_record(&mut self, record: &NormalRecord) -> Option { + fn eval_const_normal_record(&self, record: &NormalRecord) -> EvalResult { let mut attrs = vec![]; + let mut record_ctx = Context::default(); // TODO: include outer context for attr in record.attrs.iter() { let name = attr.sig.ident().map(|i| i.inspect()); - if let Some(elem) = self.eval_const_block(&attr.body.block, name) { - let ident = match &attr.sig { - Signature::Var(var) => match &var.pat { - VarPattern::Ident(ident) => { - Field::new(ident.vis(), ident.inspect().clone()) - } - _ => todo!(), - }, + let elem = record_ctx.eval_const_block(&attr.body.block, name)?; + let ident = match &attr.sig { + Signature::Var(var) => match &var.pat { + VarPattern::Ident(ident) => Field::new(ident.vis(), ident.inspect().clone()), _ => todo!(), - }; - attrs.push((ident, elem)); - } else { - return None; - } + }, + _ => todo!(), + }; + attrs.push((ident, elem)); } - Some(ValueObj::Record(attrs.into_iter().collect())) + Ok(ValueObj::Record(attrs.into_iter().collect())) } - pub(crate) fn eval_const_expr(&self, expr: &Expr, __name__: Option<&Str>) -> Option { + pub(crate) fn eval_const_expr( + &self, + expr: &Expr, + __name__: Option<&Str>, + ) -> EvalResult { match expr { - Expr::Lit(lit) => Some(eval_lit(lit)), + Expr::Lit(lit) => Ok(eval_lit(lit)), Expr::Accessor(acc) => self.eval_const_acc(acc), Expr::BinOp(bin) => self.eval_const_bin(bin), Expr::UnaryOp(unary) => self.eval_const_unary(unary), Expr::Call(call) => self.eval_const_call(call, __name__), Expr::Array(arr) => self.eval_const_array(arr), - Expr::Record(rec) => todo!("{rec}"), // self.eval_const_record(rec), + Expr::Record(rec) => self.eval_const_record(rec), Expr::Lambda(lambda) => todo!("{lambda}"), other => todo!("{other}"), } @@ -315,9 +349,9 @@ impl Context { &mut self, expr: &Expr, __name__: Option<&Str>, - ) -> Option { + ) -> EvalResult { match expr { - Expr::Lit(lit) => Some(eval_lit(lit)), + Expr::Lit(lit) => Ok(eval_lit(lit)), Expr::Accessor(acc) => self.eval_const_acc(acc), Expr::BinOp(bin) => self.eval_const_bin(bin), Expr::UnaryOp(unary) => self.eval_const_unary(unary), @@ -334,7 +368,7 @@ impl Context { &mut self, block: &Block, __name__: Option<&Str>, - ) -> Option { + ) -> EvalResult { for chunk in block.iter().rev().skip(1).rev() { self.eval_const_chunk(chunk, __name__)?; } diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index 42326d95..b054c163 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -164,7 +164,7 @@ impl ASTLowerer { fn gen_array_with_length_type(&self, elem: &hir::Expr, len: &ast::Expr) -> Type { let maybe_len = self.ctx.eval_const_expr(len, None); match maybe_len { - Some(v @ ValueObj::Nat(_)) => { + Ok(v @ ValueObj::Nat(_)) => { if elem.ref_t().is_mut() { poly_class( "ArrayWithMutType!", @@ -174,7 +174,7 @@ impl ASTLowerer { array(elem.t(), TyParam::Value(v)) } } - Some(v @ ValueObj::Mut(_)) if v.class() == class("Nat!") => { + Ok(v @ ValueObj::Mut(_)) if v.class() == class("Nat!") => { if elem.ref_t().is_mut() { poly_class( "ArrayWithMutTypeAndLength!", @@ -184,9 +184,9 @@ impl ASTLowerer { array_mut(elem.t(), TyParam::Value(v)) } } - Some(other) => todo!("{other} is not a Nat object"), - // TODO: [T; !_] - None => { + Ok(other) => todo!("{other} is not a Nat object"), + // REVIEW: is it ok to ignore the error? + Err(_e) => { if elem.ref_t().is_mut() { poly_class( "ArrayWithMutType!", @@ -366,12 +366,10 @@ impl ASTLowerer { self.pop_append_errs(); e })?; - self.ctx - .preregister(lambda.body.ref_payload()) - .map_err(|e| { - self.pop_append_errs(); - e - })?; + self.ctx.preregister(&lambda.body).map_err(|e| { + self.pop_append_errs(); + e + })?; let body = self.lower_block(lambda.body).map_err(|e| { self.pop_append_errs(); e @@ -437,7 +435,7 @@ impl ASTLowerer { body: ast::DefBody, ) -> LowerResult { log!(info "entered {}({sig})", fn_name!()); - self.ctx.preregister(body.block.ref_payload())?; + self.ctx.preregister(&body.block)?; let block = self.lower_block(body.block)?; let found_body_t = block.ref_t(); let opt_expect_body_t = self @@ -503,7 +501,7 @@ impl ASTLowerer { .t .clone(); self.ctx.assign_params(&sig.params, None)?; - self.ctx.preregister(body.block.ref_payload())?; + self.ctx.preregister(&body.block)?; let block = self.lower_block(body.block)?; let found_body_t = block.ref_t(); let expect_body_t = t.return_t().unwrap(); @@ -556,7 +554,7 @@ impl ASTLowerer { log!(info "the AST lowering process has started."); log!(info "the type-checking process has started."); let mut module = hir::Module::with_capacity(ast.module.len()); - self.ctx.preregister(ast.module.ref_payload())?; + self.ctx.preregister(ast.module.block())?; for expr in ast.module.into_iter() { match self.lower_expr(expr).and_then(|e| self.use_check(e, mode)) { Ok(expr) => { diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index b74b3298..02cbcd99 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -2846,7 +2846,7 @@ impl Expr { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Module(Vec); +pub struct Module(Block); impl fmt::Display for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -2860,7 +2860,31 @@ impl Locational for Module { } } -impl_stream_for_wrapper!(Module, Expr); +impl Stream for Module { + fn payload(self) -> Vec { + self.0.payload() + } + fn ref_payload(&self) -> &Vec { + self.0.ref_payload() + } + fn ref_mut_payload(&mut self) -> &mut Vec { + self.0.ref_mut_payload() + } +} + +impl Module { + pub const fn empty() -> Self { + Self(Block::empty()) + } + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self(Block::with_capacity(capacity)) + } + + pub fn block(&self) -> &Block { + &self.0 + } +} #[derive(Debug)] pub struct AST {