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,
HasEffect,
MoveError,
NotConstExpr,
/* compile warnings */
AttributeWarning = 60,
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() {
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);
}
}

View file

@ -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)?;
}

View file

@ -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)]

View file

@ -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<OpKind, ()> {
fn try_get_op_kind_from_token(kind: TokenKind) -> EvalResult<OpKind> {
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<OpKind, ()> {
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<ValueObj> {
match _acc {
fn eval_const_acc(&self, acc: &Accessor) -> EvalResult<ValueObj> {
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<ValueObj> {
fn eval_const_bin(&self, bin: &BinOp) -> EvalResult<ValueObj> {
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<ValueObj> {
fn eval_const_unary(&self, unary: &UnaryOp) -> EvalResult<ValueObj> {
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<ValueArgs> {
fn eval_args(&self, args: &Args, __name__: Option<&Str>) -> EvalResult<ValueArgs> {
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<ValueObj> {
fn eval_const_call(&self, call: &Call, __name__: Option<&Str>) -> EvalResult<ValueObj> {
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();
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__)?;
Some(subr.call(args, __name__.map(|n| n.clone())))
} else {
None
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<ValueObj> {
if def.is_const() {
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![];
match arr {
Array::Normal(arr) => {
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);
} 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 {
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<ValueObj> {
fn eval_const_normal_record(&self, record: &NormalRecord) -> EvalResult<ValueObj> {
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 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())
}
VarPattern::Ident(ident) => Field::new(ident.vis(), ident.inspect().clone()),
_ => todo!(),
},
_ => todo!(),
};
attrs.push((ident, elem));
} else {
return None;
}
}
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 {
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<ValueObj> {
) -> EvalResult<ValueObj> {
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<ValueObj> {
) -> EvalResult<ValueObj> {
for chunk in block.iter().rev().skip(1).rev() {
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 {
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,9 +366,7 @@ impl ASTLowerer {
self.pop_append_errs();
e
})?;
self.ctx
.preregister(lambda.body.ref_payload())
.map_err(|e| {
self.ctx.preregister(&lambda.body).map_err(|e| {
self.pop_append_errs();
e
})?;
@ -437,7 +435,7 @@ impl ASTLowerer {
body: ast::DefBody,
) -> LowerResult<hir::Def> {
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) => {

View file

@ -2846,7 +2846,7 @@ impl Expr {
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Module(Vec<Expr>);
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<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)]
pub struct AST {