mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 04:44: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 OpKind::*;
|
||||
|
||||
use erg_parser::ast::Dict as AstDict;
|
||||
use erg_parser::ast::Set as AstSet;
|
||||
use erg_parser::ast::*;
|
||||
use erg_parser::token::{Token, TokenKind};
|
||||
|
||||
|
@ -227,10 +229,21 @@ impl Context {
|
|||
let args = self.eval_args(&call.args)?;
|
||||
self.call(subr, args, call.loc())
|
||||
}
|
||||
Accessor::Attr(_attr) => todo!(),
|
||||
Accessor::TupleAttr(_attr) => todo!(),
|
||||
Accessor::Subscr(_subscr) => todo!(),
|
||||
Accessor::TypeApp(_type_app) => todo!(),
|
||||
// TODO: eval attr
|
||||
Accessor::Attr(_attr) => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
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 {
|
||||
todo!()
|
||||
|
@ -306,6 +319,52 @@ impl Context {
|
|||
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> {
|
||||
match record {
|
||||
Record::Normal(rec) => self.eval_const_normal_record(rec),
|
||||
|
@ -456,9 +515,17 @@ impl Context {
|
|||
Expr::Call(call) => self.eval_const_call(call),
|
||||
Expr::Def(def) => self.eval_const_def(def),
|
||||
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::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,19 +351,34 @@ 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 Ok(vi) = parent.rec_get_var_info(ident, acc_kind, input, namespace) {
|
||||
Ok(vi)
|
||||
} else {
|
||||
Err(TyCheckError::no_var_error(
|
||||
input.clone(),
|
||||
line!() as usize,
|
||||
ident.loc(),
|
||||
namespace.into(),
|
||||
ident.inspect(),
|
||||
self.get_similar_name(ident.inspect()),
|
||||
))
|
||||
match parent.rec_get_var_info(ident, acc_kind, input, namespace) {
|
||||
Ok(vi) => Ok(vi),
|
||||
Err(err) if err.core.kind == ErrorKind::DummyError => {
|
||||
Err(TyCheckError::no_var_error(
|
||||
input.clone(),
|
||||
line!() as usize,
|
||||
ident.loc(),
|
||||
namespace.into(),
|
||||
ident.inspect(),
|
||||
self.get_similar_name(ident.inspect()),
|
||||
))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
} else {
|
||||
Err(TyCheckError::dummy(
|
||||
|
|
|
@ -346,6 +346,8 @@ pub struct Context {
|
|||
pub(crate) trait_impls: Dict<Str, Set<TypeRelationInstance>>,
|
||||
/// stores declared names (not initialized)
|
||||
pub(crate) decls: Dict<VarName, VarInfo>,
|
||||
/// for error reporting
|
||||
pub(crate) future_defined_locals: Dict<VarName, VarInfo>,
|
||||
// stores defined names
|
||||
// 型の一致はHashMapでは判定できないため、keyはVarNameとして1つずつ見ていく
|
||||
/// ```python
|
||||
|
@ -537,6 +539,7 @@ impl Context {
|
|||
trait_impls: Dict::default(),
|
||||
params: params_,
|
||||
decls: Dict::default(),
|
||||
future_defined_locals: Dict::default(),
|
||||
locals: Dict::with_capacity(capacity),
|
||||
consts: Dict::default(),
|
||||
mono_types: Dict::default(),
|
||||
|
|
|
@ -37,6 +37,8 @@ use Visibility::*;
|
|||
|
||||
use super::instantiate::TyVarCache;
|
||||
|
||||
const UBAR: &Str = &Str::ever("_");
|
||||
|
||||
impl Context {
|
||||
/// 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(
|
||||
|
@ -62,35 +64,37 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
fn _declare_var(
|
||||
fn pre_define_var(
|
||||
&mut self,
|
||||
sig: &ast::VarSignature,
|
||||
opt_t: Option<Type>,
|
||||
id: Option<DefId>,
|
||||
) -> TyCheckResult<()> {
|
||||
let muty = Mutability::from(&sig.inspect().unwrap()[..]);
|
||||
match &sig.pat {
|
||||
ast::VarPattern::Ident(ident) => {
|
||||
let vis = ident.vis();
|
||||
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)?;
|
||||
if let Some(_decl) = self.decls.remove(&ident.name) {
|
||||
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
sig.loc(),
|
||||
self.caused_by(),
|
||||
ident.name.inspect(),
|
||||
)))
|
||||
} else {
|
||||
self.decls.insert(
|
||||
ident.name.clone(),
|
||||
VarInfo::new(sig_t, muty, vis, kind, None, self.impl_of(), None),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
let muty = Mutability::from(&sig.inspect().unwrap_or(UBAR)[..]);
|
||||
let ident = match &sig.pat {
|
||||
ast::VarPattern::Ident(ident) => ident,
|
||||
ast::VarPattern::Discard(_) => {
|
||||
return Ok(());
|
||||
}
|
||||
_ => todo!(),
|
||||
};
|
||||
let vis = ident.vis();
|
||||
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)?;
|
||||
if let Some(_decl) = self.decls.remove(&ident.name) {
|
||||
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
sig.loc(),
|
||||
self.caused_by(),
|
||||
ident.name.inspect(),
|
||||
)))
|
||||
} else {
|
||||
self.future_defined_locals.insert(
|
||||
ident.name.clone(),
|
||||
VarInfo::new(sig_t, muty, vis, kind, None, self.impl_of(), None),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,8 +559,7 @@ impl Context {
|
|||
|
||||
pub(crate) fn preregister_def(&mut self, def: &ast::Def) -> TyCheckResult<()> {
|
||||
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 {
|
||||
ast::Signature::Subr(sig) => {
|
||||
if sig.is_const() {
|
||||
|
@ -587,32 +590,39 @@ impl Context {
|
|||
self.declare_sub(sig, id)?;
|
||||
}
|
||||
}
|
||||
ast::Signature::Var(sig) if sig.is_const() => {
|
||||
let kind = ContextKind::from(def.def_kind());
|
||||
self.grow(__name__, kind, sig.vis(), None);
|
||||
let (obj, const_t) = match self.eval_const_block(&def.body.block) {
|
||||
Ok(obj) => (obj.clone(), v_enum(set! {obj})),
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
ast::Signature::Var(sig) => {
|
||||
if sig.is_const() {
|
||||
let kind = ContextKind::from(def.def_kind());
|
||||
self.grow(__name__, kind, sig.vis(), None);
|
||||
let (obj, const_t) = match self.eval_const_block(&def.body.block) {
|
||||
Ok(obj) => (obj.clone(), v_enum(set! {obj})),
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
if let Some(spec) = sig.t_spec.as_ref() {
|
||||
let mut dummy_tv_cache = TyVarCache::new(self.level, self);
|
||||
let spec_t = self.instantiate_typespec(
|
||||
spec,
|
||||
None,
|
||||
&mut dummy_tv_cache,
|
||||
PreRegister,
|
||||
false,
|
||||
)?;
|
||||
self.sub_unify(&const_t, &spec_t, def.body.loc(), None)?;
|
||||
}
|
||||
};
|
||||
if let Some(spec) = sig.t_spec.as_ref() {
|
||||
let mut dummy_tv_cache = TyVarCache::new(self.level, self);
|
||||
let spec_t = self.instantiate_typespec(
|
||||
spec,
|
||||
None,
|
||||
&mut dummy_tv_cache,
|
||||
PreRegister,
|
||||
false,
|
||||
)?;
|
||||
self.sub_unify(&const_t, &spec_t, def.body.loc(), None)?;
|
||||
}
|
||||
self.pop();
|
||||
if let Some(ident) = sig.ident() {
|
||||
self.register_gen_const(ident, obj)?;
|
||||
self.pop();
|
||||
if let Some(ident) = sig.ident() {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
input: Input,
|
||||
errno: usize,
|
||||
|
|
|
@ -20,7 +20,7 @@ use erg_common::{dict, fmt_iter, impl_display_from_debug, switch_lang};
|
|||
use erg_common::{RcArray, Str};
|
||||
|
||||
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::typaram::TyParam;
|
||||
use super::{ConstSubr, HasType, Predicate, Type};
|
||||
|
@ -743,7 +743,12 @@ impl ValueObj {
|
|||
arr.iter().next().unwrap().class(),
|
||||
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::Set(st) => set_t(st.iter().next().unwrap().class(), TyParam::value(st.len())),
|
||||
Self::Code(_) => Type::Code,
|
||||
|
@ -771,7 +776,13 @@ impl ValueObj {
|
|||
Self::Array(arr) => poly(
|
||||
"Array!",
|
||||
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(),
|
||||
],
|
||||
),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue