Fix constant evaluation methods to return EvalResult

This commit is contained in:
Shunsuke Shibayama 2022-09-03 11:59:18 +09:00
parent e2bd64ebe5
commit e1eebb252f
7 changed files with 217 additions and 100 deletions

View file

@ -35,6 +35,7 @@ pub enum ErrorKind {
PurityError, PurityError,
HasEffect, HasEffect,
MoveError, MoveError,
NotConstExpr,
/* compile warnings */ /* compile warnings */
AttributeWarning = 60, AttributeWarning = 60,
CastWarning, CastWarning,

View file

@ -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() { if self.consts.get(name).is_some() {
panic!("already registered: {name}"); panic!("already registered: {name}");
} else { } else {
// TODO: visibility (not always private) // TODO: visibility (not always private)
// TODO: kind (not always Builtin) // TODO: kind (not always Builtin)
let vi = VarInfo::new(obj.t(), Const, Private, Builtin); let vi = VarInfo::new(obj.t(), Const, Private, Builtin);
self.consts.insert(VarName::from_static(name), obj); self.consts.insert(VarName::from_str(Str::rc(name)), obj);
self.locals.insert(VarName::from_static(name), vi); self.locals.insert(VarName::from_str(Str::rc(name)), vi);
} }
} }

View file

@ -1,6 +1,6 @@
use std::option::Option; // conflicting to Type::Option 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::vis::Visibility;
use erg_common::Str; use erg_common::Str;
use erg_common::{enum_unwrap, get_hash, log, set}; use erg_common::{enum_unwrap, get_hash, log, set};
@ -315,30 +315,72 @@ impl Context {
} }
} }
// 再帰サブルーチン/型の推論を可能にするため、予め登録しておく // To allow forward references and recursive definitions
pub(crate) fn preregister(&mut self, block: &[ast::Expr]) -> TyCheckResult<()> { pub(crate) fn preregister(&mut self, block: &ast::Block) -> TyCheckResult<()> {
for expr in block.iter() { for expr in block.iter() {
if let ast::Expr::Def(def) = expr { if let ast::Expr::Def(def) = expr {
let id = Some(def.body.id); let id = Some(def.body.id);
let __name__ = def.sig.ident().map(|i| i.inspect()); 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 { match &def.sig {
ast::Signature::Subr(sig) => { ast::Signature::Subr(sig) => {
let opt_ret_t = if let Some(spec) = sig.return_t_spec.as_ref() { let const_t = if sig.is_const() {
Some(self.instantiate_typespec(spec, PreRegister)?) 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 { } 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)?; self.declare_sub(sig, opt_ret_t, id)?;
} }
ast::Signature::Var(sig) if sig.is_const() => { ast::Signature::Var(sig) if sig.is_const() => {
let t = if let Some(spec) = sig.t_spec.as_ref() { let const_t = if sig.is_const() {
Some(self.instantiate_typespec(spec, PreRegister)?) 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 { } 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)?; self.declare_var(sig, t, id)?;
} }

View file

@ -1019,6 +1019,24 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
caused_by, 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)] #[derive(Debug)]

View file

@ -3,9 +3,9 @@ use std::mem;
use erg_common::dict::Dict; use erg_common::dict::Dict;
use erg_common::rccell::RcCell; use erg_common::rccell::RcCell;
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::traits::Stream; use erg_common::traits::{Locational, Stream};
use erg_common::vis::Field; 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 erg_common::{RcArray, Str};
use OpKind::*; use OpKind::*;
@ -13,11 +13,11 @@ use erg_parser::ast::*;
use erg_parser::token::{Token, TokenKind}; use erg_parser::token::{Token, TokenKind};
use erg_type::constructors::{ 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::typaram::{OpKind, TyParam};
use erg_type::value::ValueObj; 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::instantiate::TyVarContext;
use crate::context::Context; 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<OpKind, ()> { fn try_get_op_kind_from_token(kind: TokenKind) -> EvalResult<OpKind> {
match kind { match kind {
TokenKind::Plus => Ok(OpKind::Add), TokenKind::Plus => Ok(OpKind::Add),
TokenKind::Minus => Ok(OpKind::Sub), TokenKind::Minus => Ok(OpKind::Sub),
@ -63,7 +63,7 @@ fn try_get_op_kind_from_token(kind: TokenKind) -> Result<OpKind, ()> {
TokenKind::Shl => Ok(OpKind::Shl), TokenKind::Shl => Ok(OpKind::Shl),
TokenKind::Shr => Ok(OpKind::Shr), TokenKind::Shr => Ok(OpKind::Shr),
TokenKind::Mutate => Ok(OpKind::Mutate), TokenKind::Mutate => Ok(OpKind::Mutate),
_other => Err(()), _other => todo!("{_other}"),
} }
} }
@ -166,44 +166,58 @@ impl SubstContext {
} }
impl Context { impl Context {
fn eval_const_acc(&self, _acc: &Accessor) -> Option<ValueObj> { fn eval_const_acc(&self, acc: &Accessor) -> EvalResult<ValueObj> {
match _acc { match acc {
Accessor::Local(local) => { Accessor::Local(local) => {
if let Some(val) = self.rec_get_const_obj(local.inspect()) { if let Some(val) = self.rec_get_const_obj(local.inspect()) {
Some(val.clone()) Ok(val.clone())
} else { } 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) => { Accessor::Attr(attr) => {
let _obj = self.eval_const_expr(&attr.obj, None)?; let _obj = self.eval_const_expr(&attr.obj, None);
todo!() todo!()
} }
_ => todo!(), _ => todo!(),
} }
} }
fn eval_const_bin(&self, bin: &BinOp) -> Option<ValueObj> { fn eval_const_bin(&self, bin: &BinOp) -> EvalResult<ValueObj> {
match (bin.args[0].as_ref(), bin.args[1].as_ref()) { match (bin.args[0].as_ref(), bin.args[1].as_ref()) {
(Expr::Lit(l), Expr::Lit(r)) => { (Expr::Lit(l), Expr::Lit(r)) => {
let op = try_get_op_kind_from_token(bin.op.kind).ok()?; let op = try_get_op_kind_from_token(bin.op.kind)?;
self.eval_bin_lit(op, eval_lit(l), eval_lit(r)).ok() self.eval_bin_lit(op, eval_lit(l), eval_lit(r))
} }
_ => None, _ => todo!(),
} }
} }
fn eval_const_unary(&self, unary: &UnaryOp) -> Option<ValueObj> { fn eval_const_unary(&self, unary: &UnaryOp) -> EvalResult<ValueObj> {
match unary.args[0].as_ref() { match unary.args[0].as_ref() {
Expr::Lit(lit) => { Expr::Lit(lit) => {
let op = try_get_op_kind_from_token(unary.op.kind).ok()?; let op = try_get_op_kind_from_token(unary.op.kind)?;
self.eval_unary_lit(op, eval_lit(lit)).ok() self.eval_unary_lit(op, eval_lit(lit))
} }
_ => None, _ => todo!(),
} }
} }
fn eval_args(&self, args: &Args, __name__: Option<&Str>) -> Option<ValueArgs> { fn eval_args(&self, args: &Args, __name__: Option<&Str>) -> EvalResult<ValueArgs> {
let mut evaluated_pos_args = vec![]; let mut evaluated_pos_args = vec![];
for arg in args.pos_args().iter() { for arg in args.pos_args().iter() {
let val = self.eval_const_expr(&arg.expr, __name__)?; let val = self.eval_const_expr(&arg.expr, __name__)?;
@ -214,96 +228,116 @@ impl Context {
let val = self.eval_const_expr(&arg.expr, __name__)?; let val = self.eval_const_expr(&arg.expr, __name__)?;
evaluated_kw_args.insert(arg.keyword.inspect().clone(), val); 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<ValueObj> { fn eval_const_call(&self, call: &Call, __name__: Option<&Str>) -> EvalResult<ValueObj> {
if let Expr::Accessor(acc) = call.obj.as_ref() { if let Expr::Accessor(acc) = call.obj.as_ref() {
match acc { match acc {
Accessor::Local(name) if name.is_const() => { Accessor::Local(name) => {
if let Some(ValueObj::Subr(subr)) = self.rec_get_const_obj(&name.inspect()) { let obj =
let subr = subr.clone(); self.rec_get_const_obj(&name.inspect())
let args = self.eval_args(&call.args, __name__)?; .ok_or(EvalError::no_var_error(
Some(subr.call(args, __name__.map(|n| n.clone()))) line!() as usize,
} else { name.loc(),
None 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::Attr(_attr) => todo!(),
Accessor::TupleAttr(_attr) => todo!(), Accessor::TupleAttr(_attr) => todo!(),
Accessor::Public(_name) => todo!(), Accessor::Public(_name) => todo!(),
Accessor::Subscr(_subscr) => todo!(), Accessor::Subscr(_subscr) => todo!(),
} }
} else { } else {
None
}
}
fn eval_const_def(&mut self, def: &Def) -> Option<ValueObj> {
if def.is_const() {
todo!() todo!()
} }
None
} }
fn eval_const_array(&self, arr: &Array) -> Option<ValueObj> { fn eval_const_def(&mut self, def: &Def) -> EvalResult<ValueObj> {
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<ValueObj> {
let mut elems = vec![]; let mut elems = vec![];
match arr { match arr {
Array::Normal(arr) => { Array::Normal(arr) => {
for elem in arr.elems.pos_args().iter() { for elem in arr.elems.pos_args().iter() {
if let Some(elem) = self.eval_const_expr(&elem.expr, None) { let elem = self.eval_const_expr(&elem.expr, None)?;
elems.push(elem); elems.push(elem);
} else {
return None;
}
} }
} }
_ => { _ => {
return None; todo!()
} }
} }
Some(ValueObj::Array(RcArray::from(elems))) Ok(ValueObj::Array(RcArray::from(elems)))
} }
fn eval_const_record(&mut self, record: &Record) -> Option<ValueObj> { fn eval_const_record(&self, record: &Record) -> EvalResult<ValueObj> {
match record { match record {
Record::Normal(rec) => self.eval_const_normal_record(rec), Record::Normal(rec) => self.eval_const_normal_record(rec),
Record::Shortened(_rec) => unreachable!(), // should be desugared Record::Shortened(_rec) => unreachable!(), // should be desugared
} }
} }
fn eval_const_normal_record(&mut self, record: &NormalRecord) -> Option<ValueObj> { fn eval_const_normal_record(&self, record: &NormalRecord) -> EvalResult<ValueObj> {
let mut attrs = vec![]; let mut attrs = vec![];
let mut record_ctx = Context::default(); // TODO: include outer context
for attr in record.attrs.iter() { for attr in record.attrs.iter() {
let name = attr.sig.ident().map(|i| i.inspect()); let name = attr.sig.ident().map(|i| i.inspect());
if let Some(elem) = self.eval_const_block(&attr.body.block, name) { let elem = record_ctx.eval_const_block(&attr.body.block, name)?;
let ident = match &attr.sig { let ident = match &attr.sig {
Signature::Var(var) => match &var.pat { Signature::Var(var) => match &var.pat {
VarPattern::Ident(ident) => { VarPattern::Ident(ident) => Field::new(ident.vis(), ident.inspect().clone()),
Field::new(ident.vis(), ident.inspect().clone())
}
_ => todo!(),
},
_ => todo!(), _ => todo!(),
}; },
attrs.push((ident, elem)); _ => todo!(),
} else { };
return None; 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<ValueObj> { pub(crate) fn eval_const_expr(
&self,
expr: &Expr,
__name__: Option<&Str>,
) -> EvalResult<ValueObj> {
match expr { 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::Accessor(acc) => self.eval_const_acc(acc),
Expr::BinOp(bin) => self.eval_const_bin(bin), Expr::BinOp(bin) => self.eval_const_bin(bin),
Expr::UnaryOp(unary) => self.eval_const_unary(unary), Expr::UnaryOp(unary) => self.eval_const_unary(unary),
Expr::Call(call) => self.eval_const_call(call, __name__), Expr::Call(call) => self.eval_const_call(call, __name__),
Expr::Array(arr) => self.eval_const_array(arr), 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}"), Expr::Lambda(lambda) => todo!("{lambda}"),
other => todo!("{other}"), other => todo!("{other}"),
} }
@ -315,9 +349,9 @@ impl Context {
&mut self, &mut self,
expr: &Expr, expr: &Expr,
__name__: Option<&Str>, __name__: Option<&Str>,
) -> Option<ValueObj> { ) -> EvalResult<ValueObj> {
match expr { 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::Accessor(acc) => self.eval_const_acc(acc),
Expr::BinOp(bin) => self.eval_const_bin(bin), Expr::BinOp(bin) => self.eval_const_bin(bin),
Expr::UnaryOp(unary) => self.eval_const_unary(unary), Expr::UnaryOp(unary) => self.eval_const_unary(unary),
@ -334,7 +368,7 @@ impl Context {
&mut self, &mut self,
block: &Block, block: &Block,
__name__: Option<&Str>, __name__: Option<&Str>,
) -> Option<ValueObj> { ) -> EvalResult<ValueObj> {
for chunk in block.iter().rev().skip(1).rev() { for chunk in block.iter().rev().skip(1).rev() {
self.eval_const_chunk(chunk, __name__)?; self.eval_const_chunk(chunk, __name__)?;
} }

View file

@ -164,7 +164,7 @@ impl ASTLowerer {
fn gen_array_with_length_type(&self, elem: &hir::Expr, len: &ast::Expr) -> Type { fn gen_array_with_length_type(&self, elem: &hir::Expr, len: &ast::Expr) -> Type {
let maybe_len = self.ctx.eval_const_expr(len, None); let maybe_len = self.ctx.eval_const_expr(len, None);
match maybe_len { match maybe_len {
Some(v @ ValueObj::Nat(_)) => { Ok(v @ ValueObj::Nat(_)) => {
if elem.ref_t().is_mut() { if elem.ref_t().is_mut() {
poly_class( poly_class(
"ArrayWithMutType!", "ArrayWithMutType!",
@ -174,7 +174,7 @@ impl ASTLowerer {
array(elem.t(), TyParam::Value(v)) 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() { if elem.ref_t().is_mut() {
poly_class( poly_class(
"ArrayWithMutTypeAndLength!", "ArrayWithMutTypeAndLength!",
@ -184,9 +184,9 @@ impl ASTLowerer {
array_mut(elem.t(), TyParam::Value(v)) array_mut(elem.t(), TyParam::Value(v))
} }
} }
Some(other) => todo!("{other} is not a Nat object"), Ok(other) => todo!("{other} is not a Nat object"),
// TODO: [T; !_] // REVIEW: is it ok to ignore the error?
None => { Err(_e) => {
if elem.ref_t().is_mut() { if elem.ref_t().is_mut() {
poly_class( poly_class(
"ArrayWithMutType!", "ArrayWithMutType!",
@ -366,12 +366,10 @@ impl ASTLowerer {
self.pop_append_errs(); self.pop_append_errs();
e e
})?; })?;
self.ctx self.ctx.preregister(&lambda.body).map_err(|e| {
.preregister(lambda.body.ref_payload()) self.pop_append_errs();
.map_err(|e| { e
self.pop_append_errs(); })?;
e
})?;
let body = self.lower_block(lambda.body).map_err(|e| { let body = self.lower_block(lambda.body).map_err(|e| {
self.pop_append_errs(); self.pop_append_errs();
e e
@ -437,7 +435,7 @@ impl ASTLowerer {
body: ast::DefBody, body: ast::DefBody,
) -> LowerResult<hir::Def> { ) -> LowerResult<hir::Def> {
log!(info "entered {}({sig})", fn_name!()); 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 block = self.lower_block(body.block)?;
let found_body_t = block.ref_t(); let found_body_t = block.ref_t();
let opt_expect_body_t = self let opt_expect_body_t = self
@ -503,7 +501,7 @@ impl ASTLowerer {
.t .t
.clone(); .clone();
self.ctx.assign_params(&sig.params, None)?; 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 block = self.lower_block(body.block)?;
let found_body_t = block.ref_t(); let found_body_t = block.ref_t();
let expect_body_t = t.return_t().unwrap(); 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 AST lowering process has started.");
log!(info "the type-checking process has started."); log!(info "the type-checking process has started.");
let mut module = hir::Module::with_capacity(ast.module.len()); 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() { for expr in ast.module.into_iter() {
match self.lower_expr(expr).and_then(|e| self.use_check(e, mode)) { match self.lower_expr(expr).and_then(|e| self.use_check(e, mode)) {
Ok(expr) => { Ok(expr) => {

View file

@ -2846,7 +2846,7 @@ impl Expr {
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Module(Vec<Expr>); pub struct Module(Block);
impl fmt::Display for Module { impl fmt::Display for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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<Expr> for Module {
fn payload(self) -> Vec<Expr> {
self.0.payload()
}
fn ref_payload(&self) -> &Vec<Expr> {
self.0.ref_payload()
}
fn ref_mut_payload(&mut self) -> &mut Vec<Expr> {
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)] #[derive(Debug)]
pub struct AST { pub struct AST {