mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 10:49:54 +00:00
fix: distinguish between access to class/instance attr
This commit is contained in:
parent
1cbe2de706
commit
d4b78eb020
10 changed files with 233 additions and 80 deletions
|
@ -646,8 +646,8 @@ impl PyCodeGenerator {
|
|||
}
|
||||
StoreLoadKind::Local | StoreLoadKind::LocalConst => match acc_kind {
|
||||
Name => LOAD_NAME as u8,
|
||||
Attr => LOAD_ATTR as u8,
|
||||
Method => LOAD_METHOD as u8,
|
||||
UnboundAttr => LOAD_ATTR as u8,
|
||||
BoundAttr => LOAD_METHOD as u8,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -669,9 +669,9 @@ impl PyCodeGenerator {
|
|||
StoreLoadKind::Local | StoreLoadKind::LocalConst => {
|
||||
match acc_kind {
|
||||
Name => STORE_NAME as u8,
|
||||
Attr => STORE_ATTR as u8,
|
||||
UnboundAttr => STORE_ATTR as u8,
|
||||
// cannot overwrite methods directly
|
||||
Method => STORE_ATTR as u8,
|
||||
BoundAttr => STORE_ATTR as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -792,9 +792,9 @@ impl PyCodeGenerator {
|
|||
log!(info "entered {} ({ident})", fn_name!());
|
||||
let escaped = escape_ident(ident);
|
||||
let name = self
|
||||
.local_search(&escaped, Attr)
|
||||
.local_search(&escaped, UnboundAttr)
|
||||
.unwrap_or_else(|| self.register_attr(escaped));
|
||||
let instr = self.select_load_instr(name.kind, Attr);
|
||||
let instr = self.select_load_instr(name.kind, UnboundAttr);
|
||||
self.write_instr(instr);
|
||||
self.write_arg(name.idx);
|
||||
if self.py_version.minor >= Some(11) {
|
||||
|
@ -809,9 +809,9 @@ impl PyCodeGenerator {
|
|||
}
|
||||
let escaped = escape_ident(ident);
|
||||
let name = self
|
||||
.local_search(&escaped, Method)
|
||||
.local_search(&escaped, BoundAttr)
|
||||
.unwrap_or_else(|| self.register_method(escaped));
|
||||
let instr = self.select_load_instr(name.kind, Method);
|
||||
let instr = self.select_load_instr(name.kind, BoundAttr);
|
||||
self.write_instr(instr);
|
||||
self.write_arg(name.idx);
|
||||
if self.py_version.minor >= Some(11) {
|
||||
|
@ -866,7 +866,7 @@ impl PyCodeGenerator {
|
|||
}
|
||||
Accessor::Attr(attr) => {
|
||||
self.emit_expr(*attr.obj);
|
||||
self.emit_store_instr(attr.ident, Attr);
|
||||
self.emit_store_instr(attr.ident, UnboundAttr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1010,7 +1010,7 @@ impl PyCodeGenerator {
|
|||
self.emit_precall_and_call(argc);
|
||||
} else {
|
||||
match kind {
|
||||
AccessKind::Method => self.write_instr(Opcode310::CALL_METHOD),
|
||||
AccessKind::BoundAttr => self.write_instr(Opcode310::CALL_METHOD),
|
||||
_ => self.write_instr(Opcode310::CALL_FUNCTION),
|
||||
}
|
||||
self.write_arg(argc);
|
||||
|
@ -2188,7 +2188,7 @@ impl PyCodeGenerator {
|
|||
let is_py_api = method_name.is_py_api();
|
||||
self.emit_expr(obj);
|
||||
self.emit_load_method_instr(method_name);
|
||||
self.emit_args_311(args, Method, is_py_api);
|
||||
self.emit_args_311(args, BoundAttr, is_py_api);
|
||||
}
|
||||
|
||||
fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) {
|
||||
|
@ -3121,7 +3121,7 @@ impl PyCodeGenerator {
|
|||
self.emit_load_name_instr(Identifier::private("#path"));
|
||||
self.emit_load_method_instr(Identifier::public("append"));
|
||||
self.emit_load_const(erg_std_path().to_str().unwrap());
|
||||
self.emit_call_instr(1, Method);
|
||||
self.emit_call_instr(1, BoundAttr);
|
||||
self.stack_dec();
|
||||
self.emit_pop_top();
|
||||
let erg_std_mod = Identifier::public("_erg_std_prelude");
|
||||
|
|
|
@ -80,19 +80,31 @@ impl Name {
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AccessKind {
|
||||
Name,
|
||||
Attr,
|
||||
Method,
|
||||
/// class/module attr
|
||||
/// e.g. `Str.center`
|
||||
UnboundAttr,
|
||||
/// method/instance attr
|
||||
/// e.g. `"aaa".center`
|
||||
///
|
||||
/// can also access class/module attrs
|
||||
BoundAttr,
|
||||
}
|
||||
|
||||
impl AccessKind {
|
||||
pub const fn is_local(&self) -> bool {
|
||||
matches!(self, Self::Name)
|
||||
}
|
||||
pub const fn is_attr(&self) -> bool {
|
||||
matches!(self, Self::Attr)
|
||||
pub const fn is_unbound_attr(&self) -> bool {
|
||||
matches!(self, Self::UnboundAttr)
|
||||
}
|
||||
pub const fn is_method(&self) -> bool {
|
||||
matches!(self, Self::Method)
|
||||
pub const fn is_bound_attr(&self) -> bool {
|
||||
matches!(self, Self::BoundAttr)
|
||||
}
|
||||
pub fn matches(&self, vi: &VarInfo) -> bool {
|
||||
match self {
|
||||
Self::Name | Self::BoundAttr => true,
|
||||
Self::UnboundAttr => !vi.kind.is_instance_attr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -420,7 +420,7 @@ impl Context {
|
|||
) -> Triple<VarInfo, TyCheckError> {
|
||||
if let Some(vi) = self.get_current_scope_var(&ident.name) {
|
||||
match self.validate_visibility(ident, vi, input, namespace) {
|
||||
Ok(()) => {
|
||||
Ok(()) if acc_kind.matches(vi) => {
|
||||
return Triple::Ok(vi.clone());
|
||||
}
|
||||
Err(err) => {
|
||||
|
@ -428,6 +428,7 @@ impl Context {
|
|||
return Triple::Err(err);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if let Some((name, _vi)) = self
|
||||
.future_defined_locals
|
||||
|
@ -453,6 +454,17 @@ impl Context {
|
|||
self.get_similar_name(ident.inspect()),
|
||||
));
|
||||
}
|
||||
for (_, method_ctx) in self.methods_list.iter() {
|
||||
match method_ctx.rec_get_var_info(ident, acc_kind, input, namespace) {
|
||||
Triple::Ok(vi) => {
|
||||
return Triple::Ok(vi);
|
||||
}
|
||||
Triple::Err(e) => {
|
||||
return Triple::Err(e);
|
||||
}
|
||||
Triple::None => {}
|
||||
}
|
||||
}
|
||||
if acc_kind.is_local() {
|
||||
if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
|
||||
return parent.rec_get_var_info(ident, acc_kind, input, namespace);
|
||||
|
@ -468,7 +480,7 @@ impl Context {
|
|||
) -> Option<&mut VarInfo> {
|
||||
if let Some(vi) = self.get_current_scope_var(&ident.name) {
|
||||
match self.validate_visibility(ident, vi, &self.cfg.input, self) {
|
||||
Ok(()) => {
|
||||
Ok(()) if acc_kind.matches(vi) => {
|
||||
let vi = self.get_mut_current_scope_var(&ident.name).unwrap();
|
||||
return Some(vi);
|
||||
}
|
||||
|
@ -477,6 +489,7 @@ impl Context {
|
|||
return None;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if acc_kind.is_local() {
|
||||
|
@ -500,7 +513,7 @@ impl Context {
|
|||
.or_else(|| self.future_defined_locals.get(&ident.inspect()[..]))
|
||||
{
|
||||
match self.validate_visibility(ident, vi, input, namespace) {
|
||||
Ok(()) => {
|
||||
Ok(()) if acc_kind.matches(vi) => {
|
||||
return Triple::Ok(vi.clone());
|
||||
}
|
||||
Err(err) => {
|
||||
|
@ -508,6 +521,7 @@ impl Context {
|
|||
return Triple::Err(err);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if acc_kind.is_local() {
|
||||
|
@ -562,9 +576,10 @@ impl Context {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
// class/module attr
|
||||
if let Ok(singular_ctxs) = self.get_singular_ctxs_by_hir_expr(obj, namespace) {
|
||||
for ctx in singular_ctxs {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::Attr, input, namespace) {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::UnboundAttr, input, namespace) {
|
||||
Triple::Ok(vi) => {
|
||||
return Triple::Ok(vi);
|
||||
}
|
||||
|
@ -575,7 +590,8 @@ impl Context {
|
|||
}
|
||||
}
|
||||
}
|
||||
match self.get_attr_from_nominal_t(obj, ident, input, namespace) {
|
||||
// bound method/instance attr
|
||||
match self.get_bound_attr_from_nominal_t(obj, ident, input, namespace) {
|
||||
Triple::Ok(vi) => {
|
||||
if let Some(self_t) = vi.t.self_t() {
|
||||
match self
|
||||
|
@ -636,7 +652,7 @@ impl Context {
|
|||
Triple::None
|
||||
}
|
||||
|
||||
fn get_attr_from_nominal_t(
|
||||
fn get_bound_attr_from_nominal_t(
|
||||
&self,
|
||||
obj: &hir::Expr,
|
||||
ident: &Identifier,
|
||||
|
@ -646,7 +662,7 @@ impl Context {
|
|||
let self_t = obj.t();
|
||||
if let Some(sups) = self.get_nominal_super_type_ctxs(&self_t) {
|
||||
for ctx in sups {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::Attr, input, namespace) {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) {
|
||||
Triple::Ok(vi) => {
|
||||
return Triple::Ok(vi);
|
||||
}
|
||||
|
@ -657,7 +673,7 @@ impl Context {
|
|||
}
|
||||
// if self is a methods context
|
||||
if let Some(ctx) = self.get_same_name_context(&ctx.name) {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::Method, input, namespace) {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) {
|
||||
Triple::Ok(vi) => {
|
||||
return Triple::Ok(vi);
|
||||
}
|
||||
|
@ -691,7 +707,7 @@ impl Context {
|
|||
}
|
||||
};
|
||||
for ctx in ctxs {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::Attr, input, namespace) {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) {
|
||||
Triple::Ok(vi) => {
|
||||
obj.ref_t().coerce();
|
||||
return Triple::Ok(vi);
|
||||
|
@ -702,7 +718,7 @@ impl Context {
|
|||
_ => {}
|
||||
}
|
||||
if let Some(ctx) = self.get_same_name_context(&ctx.name) {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::Method, input, namespace) {
|
||||
match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) {
|
||||
Triple::Ok(vi) => {
|
||||
return Triple::Ok(vi);
|
||||
}
|
||||
|
@ -946,7 +962,7 @@ impl Context {
|
|||
}
|
||||
}
|
||||
if let Some(ctx) = self.get_same_name_context(&ctx.name) {
|
||||
match ctx.rec_get_var_info(attr_name, AccessKind::Method, input, namespace) {
|
||||
match ctx.rec_get_var_info(attr_name, AccessKind::BoundAttr, input, namespace) {
|
||||
Triple::Ok(t) => {
|
||||
return Ok(t);
|
||||
}
|
||||
|
@ -3072,4 +3088,58 @@ impl Context {
|
|||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_instance_attr(&self, name: &str) -> Option<&VarInfo> {
|
||||
if let Some(vi) = self.locals.get(name) {
|
||||
if vi.kind.is_instance_attr() {
|
||||
return Some(vi);
|
||||
}
|
||||
}
|
||||
if let Some(vi) = self.decls.get(name) {
|
||||
if vi.kind.is_instance_attr() {
|
||||
return Some(vi);
|
||||
}
|
||||
}
|
||||
if self.kind.is_method_def() {
|
||||
self.get_nominal_type_ctx(&mono(&self.name))
|
||||
.and_then(|(_, ctx)| ctx.get_instance_attr(name))
|
||||
} else {
|
||||
self.methods_list.iter().find_map(|(_, ctx)| {
|
||||
if ctx.kind.is_trait_impl() {
|
||||
None
|
||||
} else {
|
||||
ctx.get_instance_attr(name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// does not remove instance attribute declarations
|
||||
pub(crate) fn remove_class_attr(&mut self, name: &str) -> Option<(VarName, VarInfo)> {
|
||||
if let Some((k, v)) = self.locals.remove_entry(name) {
|
||||
if v.kind.is_instance_attr() {
|
||||
self.locals.insert(k, v);
|
||||
} else {
|
||||
return Some((k, v));
|
||||
}
|
||||
} else if let Some((k, v)) = self.decls.remove_entry(name) {
|
||||
if v.kind.is_instance_attr() {
|
||||
self.decls.insert(k, v);
|
||||
} else {
|
||||
return Some((k, v));
|
||||
}
|
||||
}
|
||||
if self.kind.is_method_def() {
|
||||
self.get_mut_nominal_type_ctx(&mono(&self.name))
|
||||
.and_then(|(_, ctx)| ctx.remove_class_attr(name))
|
||||
} else {
|
||||
self.methods_list.iter_mut().find_map(|(_, ctx)| {
|
||||
if ctx.kind.is_trait_impl() {
|
||||
None
|
||||
} else {
|
||||
ctx.remove_class_attr(name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -319,6 +319,10 @@ impl ContextKind {
|
|||
matches!(self, Self::MethodDefs(_))
|
||||
}
|
||||
|
||||
pub const fn is_trait_impl(&self) -> bool {
|
||||
matches!(self, Self::MethodDefs(Some(_)))
|
||||
}
|
||||
|
||||
pub const fn is_type(&self) -> bool {
|
||||
matches!(self, Self::Class | Self::Trait | Self::StructuralTrait)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::time::{Duration, SystemTime};
|
|||
|
||||
use erg_common::config::ErgMode;
|
||||
use erg_common::consts::{ERG_MODE, PYTHON_MODE};
|
||||
use erg_common::dict::Dict;
|
||||
use erg_common::env::{is_pystd_main_module, is_std_decl_path};
|
||||
use erg_common::erg_util::BUILTIN_ERG_MODS;
|
||||
use erg_common::levenshtein::get_similar_name;
|
||||
|
@ -32,7 +33,7 @@ use crate::ty::free::{Constraint, HasLevel};
|
|||
use crate::ty::typaram::TyParam;
|
||||
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
|
||||
use crate::ty::{
|
||||
GuardType, HasType, ParamTy, SubrType, Type, Variable, Visibility, VisibilityModifier,
|
||||
Field, GuardType, HasType, ParamTy, SubrType, Type, Variable, Visibility, VisibilityModifier,
|
||||
};
|
||||
|
||||
use crate::build_hir::HIRBuilder;
|
||||
|
@ -200,7 +201,10 @@ impl Context {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(_decl) = self.decls.remove(&ident.name) {
|
||||
if self
|
||||
.remove_class_attr(ident.name.inspect())
|
||||
.is_some_and(|(_, decl)| !decl.kind.is_auto())
|
||||
{
|
||||
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
|
@ -266,7 +270,10 @@ impl Context {
|
|||
self.absolutize(sig.ident.name.loc()),
|
||||
);
|
||||
self.index().register(&vi);
|
||||
if let Some(_decl) = self.decls.remove(name) {
|
||||
if self
|
||||
.remove_class_attr(name)
|
||||
.is_some_and(|(_, decl)| !decl.kind.is_auto())
|
||||
{
|
||||
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
|
@ -1347,16 +1354,7 @@ impl Context {
|
|||
..
|
||||
} = additional
|
||||
{
|
||||
for (field, t) in rec.iter() {
|
||||
let varname = VarName::from_str(field.symbol.clone());
|
||||
let vi = VarInfo::instance_attr(
|
||||
field.clone(),
|
||||
t.clone(),
|
||||
self.impl_of(),
|
||||
ctx.name.clone(),
|
||||
);
|
||||
ctx.decls.insert(varname, vi);
|
||||
}
|
||||
self.register_instance_attrs(&mut ctx, rec)?;
|
||||
}
|
||||
param_t
|
||||
.map(|t| self.intersection(t, additional.typ()))
|
||||
|
@ -1418,16 +1416,7 @@ impl Context {
|
|||
self.level,
|
||||
);
|
||||
let Some(TypeObj::Builtin{ t: Type::Record(req), .. }) = gen.base_or_sup() else { todo!("{gen}") };
|
||||
for (field, t) in req.iter() {
|
||||
let vi = VarInfo::instance_attr(
|
||||
field.clone(),
|
||||
t.clone(),
|
||||
self.impl_of(),
|
||||
ctx.name.clone(),
|
||||
);
|
||||
ctx.decls
|
||||
.insert(VarName::from_str(field.symbol.clone()), vi);
|
||||
}
|
||||
self.register_instance_attrs(&mut ctx, req)?;
|
||||
self.register_gen_mono_type(ident, gen, ctx, Const)
|
||||
} else {
|
||||
feature_error!(
|
||||
|
@ -1460,16 +1449,7 @@ impl Context {
|
|||
None
|
||||
};
|
||||
if let Some(additional) = additional {
|
||||
for (field, t) in additional.iter() {
|
||||
let vi = VarInfo::instance_attr(
|
||||
field.clone(),
|
||||
t.clone(),
|
||||
self.impl_of(),
|
||||
ctx.name.clone(),
|
||||
);
|
||||
ctx.decls
|
||||
.insert(VarName::from_str(field.symbol.clone()), vi);
|
||||
}
|
||||
self.register_instance_attrs(&mut ctx, additional)?;
|
||||
}
|
||||
for sup in super_classes.into_iter() {
|
||||
if let Some((_, sup_ctx)) = self.get_nominal_type_ctx(&sup) {
|
||||
|
@ -1521,6 +1501,29 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
fn register_instance_attrs(
|
||||
&self,
|
||||
ctx: &mut Context,
|
||||
rec: &Dict<Field, Type>,
|
||||
) -> CompileResult<()> {
|
||||
for (field, t) in rec.iter() {
|
||||
let varname = VarName::from_str(field.symbol.clone());
|
||||
let vi =
|
||||
VarInfo::instance_attr(field.clone(), t.clone(), self.impl_of(), ctx.name.clone());
|
||||
// self.index().register(&vi);
|
||||
if let Some(_ent) = ctx.decls.insert(varname.clone(), vi) {
|
||||
return Err(CompileErrors::from(CompileError::duplicate_decl_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
varname.loc(),
|
||||
self.caused_by(),
|
||||
varname.inspect(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_class_new_method(&self, gen: &GenTypeObj, ctx: &mut Context) -> CompileResult<()> {
|
||||
let mut methods = Self::methods(None, self.cfg.clone(), self.shared.clone(), 2, self.level);
|
||||
let new_t = if let Some(base) = gen.base_or_sup() {
|
||||
|
@ -1529,16 +1532,7 @@ impl Context {
|
|||
t: Type::Record(rec),
|
||||
..
|
||||
} => {
|
||||
for (field, t) in rec.iter() {
|
||||
let varname = VarName::from_str(field.symbol.clone());
|
||||
let vi = VarInfo::instance_attr(
|
||||
field.clone(),
|
||||
t.clone(),
|
||||
self.impl_of(),
|
||||
ctx.name.clone(),
|
||||
);
|
||||
ctx.decls.insert(varname, vi);
|
||||
}
|
||||
self.register_instance_attrs(ctx, rec)?;
|
||||
}
|
||||
other => {
|
||||
methods.register_fixed_auto_impl(
|
||||
|
|
|
@ -177,7 +177,7 @@ impl ASTLowerer {
|
|||
for ctx in ctxs {
|
||||
if let Triple::Ok(vi) = ctx.rec_get_var_info(
|
||||
&ident.raw,
|
||||
AccessKind::Attr,
|
||||
AccessKind::UnboundAttr,
|
||||
self.input(),
|
||||
&self.module.context,
|
||||
) {
|
||||
|
|
|
@ -485,11 +485,12 @@ impl LowerError {
|
|||
) -> Self {
|
||||
let hint = similar_name.map(|n| {
|
||||
let vis = similar_info.map_or("".into(), |vi| vi.vis.modifier.display());
|
||||
let kind = similar_info.map_or("", |vi| vi.kind.display());
|
||||
switch_lang!(
|
||||
"japanese" => format!("似た名前の{vis}属性があります: {n}"),
|
||||
"simplified_chinese" => format!("具有相同名称的{vis}属性: {n}"),
|
||||
"traditional_chinese" => format!("具有相同名稱的{vis}屬性: {n}"),
|
||||
"english" => format!("has a similar name {vis} attribute: {n}"),
|
||||
"japanese" => format!("似た名前の{vis}{kind}属性があります: {n}"),
|
||||
"simplified_chinese" => format!("具有相同名称的{vis}{kind}属性: {n}"),
|
||||
"traditional_chinese" => format!("具有相同名稱的{vis}{kind}屬性: {n}"),
|
||||
"english" => format!("has a similar name {vis} {kind} attribute: {n}"),
|
||||
)
|
||||
});
|
||||
let found = StyledString::new(name, Some(ERR), Some(ATTR));
|
||||
|
@ -1157,4 +1158,30 @@ impl LowerWarning {
|
|||
caused_by,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn same_name_instance_attr_warning(
|
||||
input: Input,
|
||||
errno: usize,
|
||||
loc: Location,
|
||||
caused_by: String,
|
||||
name: &str,
|
||||
) -> Self {
|
||||
let name = StyledStr::new(readable_name(name), Some(WARN), Some(ATTR));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
vec![SubMessage::only_loc(loc)],
|
||||
switch_lang!(
|
||||
"japanese" => format!("同名のインスタンス属性{name}が存在します"),
|
||||
"simplified_chinese" => format!("同名的实例属性{name}已存在"),
|
||||
"traditional_chinese" => format!("同名的實例屬性{name}已存在"),
|
||||
"english" => format!("an instance attribute named {name} already exists"),
|
||||
),
|
||||
errno,
|
||||
NameWarning,
|
||||
loc,
|
||||
),
|
||||
input,
|
||||
caused_by,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
|||
|
||||
use erg_common::error::Location;
|
||||
use erg_common::set::Set;
|
||||
use erg_common::Str;
|
||||
use erg_common::{switch_lang, Str};
|
||||
|
||||
use erg_parser::ast::DefId;
|
||||
|
||||
|
@ -39,6 +39,7 @@ use Mutability::*;
|
|||
pub enum VarKind {
|
||||
Defined(DefId),
|
||||
Declared,
|
||||
InstanceAttr,
|
||||
Parameter {
|
||||
def_id: DefId,
|
||||
var: bool,
|
||||
|
@ -88,6 +89,38 @@ impl VarKind {
|
|||
pub const fn is_builtin(&self) -> bool {
|
||||
matches!(self, Self::Builtin)
|
||||
}
|
||||
|
||||
pub const fn is_auto(&self) -> bool {
|
||||
matches!(self, Self::Auto)
|
||||
}
|
||||
|
||||
pub const fn is_instance_attr(&self) -> bool {
|
||||
matches!(self, Self::InstanceAttr)
|
||||
}
|
||||
|
||||
pub const fn display(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Auto | Self::FixedAuto => switch_lang!(
|
||||
"japanese" => "自動",
|
||||
"simplified_chinese" => "自动",
|
||||
"traditional_chinese" => "自動",
|
||||
"english" => "auto",
|
||||
),
|
||||
Self::Builtin => switch_lang!(
|
||||
"japanese" => "組み込み",
|
||||
"simplified_chinese" => "内置",
|
||||
"traditional_chinese" => "內置",
|
||||
"english" => "builtin",
|
||||
),
|
||||
Self::InstanceAttr => switch_lang!(
|
||||
"japanese" => "インスタンス",
|
||||
"simplified_chinese" => "实例",
|
||||
"traditional_chinese" => "實例",
|
||||
"english" => "instance",
|
||||
),
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -285,12 +318,11 @@ impl VarInfo {
|
|||
} else {
|
||||
Mutability::Immutable
|
||||
};
|
||||
let kind = VarKind::Declared;
|
||||
Self::new(
|
||||
t,
|
||||
muty,
|
||||
Visibility::new(field.vis, namespace),
|
||||
kind,
|
||||
VarKind::InstanceAttr,
|
||||
None,
|
||||
impl_of,
|
||||
None,
|
||||
|
|
9
tests/should_err/class_attr.er
Normal file
9
tests/should_err/class_attr.er
Normal file
|
@ -0,0 +1,9 @@
|
|||
C = Class { .x = Int; .y = Int }
|
||||
C.
|
||||
x = "aa"
|
||||
|
||||
_: Str = C.x
|
||||
_ = C.y # ERR
|
||||
c = C.new({.x = 1; .y = 2})
|
||||
_: Int = c.x
|
||||
_: Int = c.y
|
|
@ -273,6 +273,11 @@ fn exec_assert_cast() -> Result<(), ()> {
|
|||
expect_failure("examples/assert_cast.er", 0, 3)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_class_attr_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/class_attr.er", 1, 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_collection_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/collection.er", 0, 4)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue