mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-29 20:34:44 +00:00
Improve error message for referring to a variable before its definition
This commit is contained in:
parent
80c479ecbd
commit
fde5a33d54
6 changed files with 210 additions and 66 deletions
|
@ -13,6 +13,8 @@ use erg_common::{dict, fn_name, option_enum_unwrap, set};
|
||||||
use erg_common::{RcArray, Str};
|
use erg_common::{RcArray, Str};
|
||||||
use OpKind::*;
|
use OpKind::*;
|
||||||
|
|
||||||
|
use erg_parser::ast::Dict as AstDict;
|
||||||
|
use erg_parser::ast::Set as AstSet;
|
||||||
use erg_parser::ast::*;
|
use erg_parser::ast::*;
|
||||||
use erg_parser::token::{Token, TokenKind};
|
use erg_parser::token::{Token, TokenKind};
|
||||||
|
|
||||||
|
@ -227,10 +229,21 @@ impl Context {
|
||||||
let args = self.eval_args(&call.args)?;
|
let args = self.eval_args(&call.args)?;
|
||||||
self.call(subr, args, call.loc())
|
self.call(subr, args, call.loc())
|
||||||
}
|
}
|
||||||
Accessor::Attr(_attr) => todo!(),
|
// TODO: eval attr
|
||||||
Accessor::TupleAttr(_attr) => todo!(),
|
Accessor::Attr(_attr) => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||||
Accessor::Subscr(_subscr) => todo!(),
|
self.cfg.input.clone(),
|
||||||
Accessor::TypeApp(_type_app) => todo!(),
|
line!() as usize,
|
||||||
|
call.loc(),
|
||||||
|
self.caused_by(),
|
||||||
|
))),
|
||||||
|
// TODO: eval type app
|
||||||
|
Accessor::TypeApp(_type_app) => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||||
|
self.cfg.input.clone(),
|
||||||
|
line!() as usize,
|
||||||
|
call.loc(),
|
||||||
|
self.caused_by(),
|
||||||
|
))),
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -306,6 +319,52 @@ impl Context {
|
||||||
Ok(ValueObj::Array(RcArray::from(elems)))
|
Ok(ValueObj::Array(RcArray::from(elems)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eval_const_set(&self, set: &AstSet) -> EvalResult<ValueObj> {
|
||||||
|
let mut elems = vec![];
|
||||||
|
match set {
|
||||||
|
AstSet::Normal(arr) => {
|
||||||
|
for elem in arr.elems.pos_args().iter() {
|
||||||
|
let elem = self.eval_const_expr(&elem.expr)?;
|
||||||
|
elems.push(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ValueObj::Set(Set::from(elems)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_const_dict(&self, dict: &AstDict) -> EvalResult<ValueObj> {
|
||||||
|
let mut elems = dict! {};
|
||||||
|
match dict {
|
||||||
|
AstDict::Normal(dic) => {
|
||||||
|
for elem in dic.kvs.iter() {
|
||||||
|
let key = self.eval_const_expr(&elem.key)?;
|
||||||
|
let value = self.eval_const_expr(&elem.value)?;
|
||||||
|
elems.insert(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ValueObj::Dict(elems))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_const_tuple(&self, tuple: &Tuple) -> EvalResult<ValueObj> {
|
||||||
|
let mut elems = vec![];
|
||||||
|
match tuple {
|
||||||
|
Tuple::Normal(arr) => {
|
||||||
|
for elem in arr.elems.pos_args().iter() {
|
||||||
|
let elem = self.eval_const_expr(&elem.expr)?;
|
||||||
|
elems.push(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ValueObj::Tuple(RcArray::from(elems)))
|
||||||
|
}
|
||||||
|
|
||||||
fn eval_const_record(&self, record: &Record) -> EvalResult<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),
|
||||||
|
@ -456,9 +515,17 @@ impl Context {
|
||||||
Expr::Call(call) => self.eval_const_call(call),
|
Expr::Call(call) => self.eval_const_call(call),
|
||||||
Expr::Def(def) => self.eval_const_def(def),
|
Expr::Def(def) => self.eval_const_def(def),
|
||||||
Expr::Array(arr) => self.eval_const_array(arr),
|
Expr::Array(arr) => self.eval_const_array(arr),
|
||||||
|
Expr::Set(set) => self.eval_const_set(set),
|
||||||
|
Expr::Dict(dict) => self.eval_const_dict(dict),
|
||||||
|
Expr::Tuple(tuple) => self.eval_const_tuple(tuple),
|
||||||
Expr::Record(rec) => self.eval_const_record(rec),
|
Expr::Record(rec) => self.eval_const_record(rec),
|
||||||
Expr::Lambda(lambda) => self.eval_const_lambda(lambda),
|
Expr::Lambda(lambda) => self.eval_const_lambda(lambda),
|
||||||
other => todo!("{other}"),
|
other => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||||
|
self.cfg.input.clone(),
|
||||||
|
line!() as usize,
|
||||||
|
other.loc(),
|
||||||
|
self.caused_by(),
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -351,11 +351,24 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Some((name, _vi)) = self
|
||||||
|
.future_defined_locals
|
||||||
|
.get_key_value(&ident.inspect()[..])
|
||||||
|
{
|
||||||
|
return Err(TyCheckError::access_before_def_error(
|
||||||
|
input.clone(),
|
||||||
|
line!() as usize,
|
||||||
|
ident.loc(),
|
||||||
|
namespace.into(),
|
||||||
|
ident.inspect(),
|
||||||
|
name.ln_begin().unwrap(),
|
||||||
|
self.get_similar_name(ident.inspect()),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
|
if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
|
||||||
if let Ok(vi) = parent.rec_get_var_info(ident, acc_kind, input, namespace) {
|
match parent.rec_get_var_info(ident, acc_kind, input, namespace) {
|
||||||
Ok(vi)
|
Ok(vi) => Ok(vi),
|
||||||
} else {
|
Err(err) if err.core.kind == ErrorKind::DummyError => {
|
||||||
Err(TyCheckError::no_var_error(
|
Err(TyCheckError::no_var_error(
|
||||||
input.clone(),
|
input.clone(),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
|
@ -365,6 +378,8 @@ impl Context {
|
||||||
self.get_similar_name(ident.inspect()),
|
self.get_similar_name(ident.inspect()),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(TyCheckError::dummy(
|
Err(TyCheckError::dummy(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
|
|
|
@ -346,6 +346,8 @@ pub struct Context {
|
||||||
pub(crate) trait_impls: Dict<Str, Set<TypeRelationInstance>>,
|
pub(crate) trait_impls: Dict<Str, Set<TypeRelationInstance>>,
|
||||||
/// stores declared names (not initialized)
|
/// stores declared names (not initialized)
|
||||||
pub(crate) decls: Dict<VarName, VarInfo>,
|
pub(crate) decls: Dict<VarName, VarInfo>,
|
||||||
|
/// for error reporting
|
||||||
|
pub(crate) future_defined_locals: Dict<VarName, VarInfo>,
|
||||||
// stores defined names
|
// stores defined names
|
||||||
// 型の一致はHashMapでは判定できないため、keyはVarNameとして1つずつ見ていく
|
// 型の一致はHashMapでは判定できないため、keyはVarNameとして1つずつ見ていく
|
||||||
/// ```python
|
/// ```python
|
||||||
|
@ -537,6 +539,7 @@ impl Context {
|
||||||
trait_impls: Dict::default(),
|
trait_impls: Dict::default(),
|
||||||
params: params_,
|
params: params_,
|
||||||
decls: Dict::default(),
|
decls: Dict::default(),
|
||||||
|
future_defined_locals: Dict::default(),
|
||||||
locals: Dict::with_capacity(capacity),
|
locals: Dict::with_capacity(capacity),
|
||||||
consts: Dict::default(),
|
consts: Dict::default(),
|
||||||
mono_types: Dict::default(),
|
mono_types: Dict::default(),
|
||||||
|
|
|
@ -37,6 +37,8 @@ use Visibility::*;
|
||||||
|
|
||||||
use super::instantiate::TyVarCache;
|
use super::instantiate::TyVarCache;
|
||||||
|
|
||||||
|
const UBAR: &Str = &Str::ever("_");
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
/// If it is a constant that is defined, there must be no variable of the same name defined across all scopes
|
/// If it is a constant that is defined, there must be no variable of the same name defined across all scopes
|
||||||
pub(crate) fn registered_info(
|
pub(crate) fn registered_info(
|
||||||
|
@ -62,15 +64,20 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _declare_var(
|
fn pre_define_var(
|
||||||
&mut self,
|
&mut self,
|
||||||
sig: &ast::VarSignature,
|
sig: &ast::VarSignature,
|
||||||
opt_t: Option<Type>,
|
opt_t: Option<Type>,
|
||||||
id: Option<DefId>,
|
id: Option<DefId>,
|
||||||
) -> TyCheckResult<()> {
|
) -> TyCheckResult<()> {
|
||||||
let muty = Mutability::from(&sig.inspect().unwrap()[..]);
|
let muty = Mutability::from(&sig.inspect().unwrap_or(UBAR)[..]);
|
||||||
match &sig.pat {
|
let ident = match &sig.pat {
|
||||||
ast::VarPattern::Ident(ident) => {
|
ast::VarPattern::Ident(ident) => ident,
|
||||||
|
ast::VarPattern::Discard(_) => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
let vis = ident.vis();
|
let vis = ident.vis();
|
||||||
let kind = id.map_or(VarKind::Declared, VarKind::Defined);
|
let kind = id.map_or(VarKind::Declared, VarKind::Defined);
|
||||||
let sig_t = self.instantiate_var_sig_t(sig.t_spec.as_ref(), opt_t, PreRegister)?;
|
let sig_t = self.instantiate_var_sig_t(sig.t_spec.as_ref(), opt_t, PreRegister)?;
|
||||||
|
@ -83,16 +90,13 @@ impl Context {
|
||||||
ident.name.inspect(),
|
ident.name.inspect(),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
self.decls.insert(
|
self.future_defined_locals.insert(
|
||||||
ident.name.clone(),
|
ident.name.clone(),
|
||||||
VarInfo::new(sig_t, muty, vis, kind, None, self.impl_of(), None),
|
VarInfo::new(sig_t, muty, vis, kind, None, self.impl_of(), None),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn declare_sub(
|
pub(crate) fn declare_sub(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -555,8 +559,7 @@ impl Context {
|
||||||
|
|
||||||
pub(crate) fn preregister_def(&mut self, def: &ast::Def) -> TyCheckResult<()> {
|
pub(crate) fn preregister_def(&mut self, def: &ast::Def) -> TyCheckResult<()> {
|
||||||
let id = Some(def.body.id);
|
let id = Some(def.body.id);
|
||||||
let ubar = Str::ever("_");
|
let __name__ = def.sig.ident().map(|i| i.inspect()).unwrap_or(UBAR);
|
||||||
let __name__ = def.sig.ident().map(|i| i.inspect()).unwrap_or(&ubar);
|
|
||||||
match &def.sig {
|
match &def.sig {
|
||||||
ast::Signature::Subr(sig) => {
|
ast::Signature::Subr(sig) => {
|
||||||
if sig.is_const() {
|
if sig.is_const() {
|
||||||
|
@ -587,7 +590,8 @@ impl Context {
|
||||||
self.declare_sub(sig, id)?;
|
self.declare_sub(sig, id)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Signature::Var(sig) if sig.is_const() => {
|
ast::Signature::Var(sig) => {
|
||||||
|
if sig.is_const() {
|
||||||
let kind = ContextKind::from(def.def_kind());
|
let kind = ContextKind::from(def.def_kind());
|
||||||
self.grow(__name__, kind, sig.vis(), None);
|
self.grow(__name__, kind, sig.vis(), None);
|
||||||
let (obj, const_t) = match self.eval_const_block(&def.body.block) {
|
let (obj, const_t) = match self.eval_const_block(&def.body.block) {
|
||||||
|
@ -611,8 +615,14 @@ impl Context {
|
||||||
if let Some(ident) = sig.ident() {
|
if let Some(ident) = sig.ident() {
|
||||||
self.register_gen_const(ident, obj)?;
|
self.register_gen_const(ident, obj)?;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let opt_t = self
|
||||||
|
.eval_const_block(&def.body.block)
|
||||||
|
.map(|o| v_enum(set! {o}))
|
||||||
|
.ok();
|
||||||
|
self.pre_define_var(sig, opt_t, id)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1577,6 +1577,44 @@ impl LowerError {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn access_before_def_error(
|
||||||
|
input: Input,
|
||||||
|
errno: usize,
|
||||||
|
loc: Location,
|
||||||
|
caused_by: String,
|
||||||
|
name: &str,
|
||||||
|
defined_line: usize,
|
||||||
|
similar_name: Option<&str>,
|
||||||
|
) -> Self {
|
||||||
|
let name = readable_name(name);
|
||||||
|
let hint = similar_name.map(|n| {
|
||||||
|
let n = StyledStr::new(n, Some(HINT), Some(ATTR));
|
||||||
|
switch_lang!(
|
||||||
|
"japanese" => format!("似た名前の変数があります: {n}"),
|
||||||
|
"simplified_chinese" => format!("存在相同名称变量: {n}"),
|
||||||
|
"traditional_chinese" => format!("存在相同名稱變量: {n}"),
|
||||||
|
"english" => format!("exists a similar name variable: {n}"),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let found = StyledString::new(name, Some(ERR), Some(ATTR));
|
||||||
|
Self::new(
|
||||||
|
ErrorCore::new(
|
||||||
|
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
|
||||||
|
switch_lang!(
|
||||||
|
"japanese" => format!("定義({defined_line}行目)より前で{found}を参照することは出来ません"),
|
||||||
|
"simplified_chinese" => format!("在{found}定义({defined_line}行)之前引用是不允许的"),
|
||||||
|
"traditional_chinese" => format!("在{found}定義({defined_line}行)之前引用是不允許的"),
|
||||||
|
"english" => format!("cannot reference {found} before its definition (line {defined_line})"),
|
||||||
|
),
|
||||||
|
errno,
|
||||||
|
NameError,
|
||||||
|
loc,
|
||||||
|
),
|
||||||
|
input,
|
||||||
|
caused_by,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn no_type_error(
|
pub fn no_type_error(
|
||||||
input: Input,
|
input: Input,
|
||||||
errno: usize,
|
errno: usize,
|
||||||
|
|
|
@ -20,7 +20,7 @@ use erg_common::{dict, fmt_iter, impl_display_from_debug, switch_lang};
|
||||||
use erg_common::{RcArray, Str};
|
use erg_common::{RcArray, Str};
|
||||||
|
|
||||||
use super::codeobj::CodeObj;
|
use super::codeobj::CodeObj;
|
||||||
use super::constructors::{array_t, mono, poly, refinement, set_t, tuple_t};
|
use super::constructors::{array_t, dict_t, mono, poly, refinement, set_t, tuple_t};
|
||||||
use super::free::fresh_varname;
|
use super::free::fresh_varname;
|
||||||
use super::typaram::TyParam;
|
use super::typaram::TyParam;
|
||||||
use super::{ConstSubr, HasType, Predicate, Type};
|
use super::{ConstSubr, HasType, Predicate, Type};
|
||||||
|
@ -743,7 +743,12 @@ impl ValueObj {
|
||||||
arr.iter().next().unwrap().class(),
|
arr.iter().next().unwrap().class(),
|
||||||
TyParam::value(arr.len()),
|
TyParam::value(arr.len()),
|
||||||
),
|
),
|
||||||
Self::Dict(_dict) => todo!(),
|
Self::Dict(dict) => {
|
||||||
|
let tp = dict
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (TyParam::value(k.clone()), TyParam::value(v.clone())));
|
||||||
|
dict_t(TyParam::Dict(tp.collect()))
|
||||||
|
}
|
||||||
Self::Tuple(tup) => tuple_t(tup.iter().map(|v| v.class()).collect()),
|
Self::Tuple(tup) => tuple_t(tup.iter().map(|v| v.class()).collect()),
|
||||||
Self::Set(st) => set_t(st.iter().next().unwrap().class(), TyParam::value(st.len())),
|
Self::Set(st) => set_t(st.iter().next().unwrap().class(), TyParam::value(st.len())),
|
||||||
Self::Code(_) => Type::Code,
|
Self::Code(_) => Type::Code,
|
||||||
|
@ -771,7 +776,13 @@ impl ValueObj {
|
||||||
Self::Array(arr) => poly(
|
Self::Array(arr) => poly(
|
||||||
"Array!",
|
"Array!",
|
||||||
vec![
|
vec![
|
||||||
TyParam::t(arr.iter().next().unwrap().class()),
|
// REVIEW: Never?
|
||||||
|
TyParam::t(
|
||||||
|
arr.iter()
|
||||||
|
.next()
|
||||||
|
.map(|elem| elem.class())
|
||||||
|
.unwrap_or(Type::Never),
|
||||||
|
),
|
||||||
TyParam::value(arr.len()).mutate(),
|
TyParam::value(arr.len()).mutate(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue