mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 12:14:43 +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 {
|
StoreLoadKind::Local | StoreLoadKind::LocalConst => match acc_kind {
|
||||||
Name => LOAD_NAME as u8,
|
Name => LOAD_NAME as u8,
|
||||||
Attr => LOAD_ATTR as u8,
|
UnboundAttr => LOAD_ATTR as u8,
|
||||||
Method => LOAD_METHOD as u8,
|
BoundAttr => LOAD_METHOD as u8,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -669,9 +669,9 @@ impl PyCodeGenerator {
|
||||||
StoreLoadKind::Local | StoreLoadKind::LocalConst => {
|
StoreLoadKind::Local | StoreLoadKind::LocalConst => {
|
||||||
match acc_kind {
|
match acc_kind {
|
||||||
Name => STORE_NAME as u8,
|
Name => STORE_NAME as u8,
|
||||||
Attr => STORE_ATTR as u8,
|
UnboundAttr => STORE_ATTR as u8,
|
||||||
// cannot overwrite methods directly
|
// 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!());
|
log!(info "entered {} ({ident})", fn_name!());
|
||||||
let escaped = escape_ident(ident);
|
let escaped = escape_ident(ident);
|
||||||
let name = self
|
let name = self
|
||||||
.local_search(&escaped, Attr)
|
.local_search(&escaped, UnboundAttr)
|
||||||
.unwrap_or_else(|| self.register_attr(escaped));
|
.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_instr(instr);
|
||||||
self.write_arg(name.idx);
|
self.write_arg(name.idx);
|
||||||
if self.py_version.minor >= Some(11) {
|
if self.py_version.minor >= Some(11) {
|
||||||
|
@ -809,9 +809,9 @@ impl PyCodeGenerator {
|
||||||
}
|
}
|
||||||
let escaped = escape_ident(ident);
|
let escaped = escape_ident(ident);
|
||||||
let name = self
|
let name = self
|
||||||
.local_search(&escaped, Method)
|
.local_search(&escaped, BoundAttr)
|
||||||
.unwrap_or_else(|| self.register_method(escaped));
|
.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_instr(instr);
|
||||||
self.write_arg(name.idx);
|
self.write_arg(name.idx);
|
||||||
if self.py_version.minor >= Some(11) {
|
if self.py_version.minor >= Some(11) {
|
||||||
|
@ -866,7 +866,7 @@ impl PyCodeGenerator {
|
||||||
}
|
}
|
||||||
Accessor::Attr(attr) => {
|
Accessor::Attr(attr) => {
|
||||||
self.emit_expr(*attr.obj);
|
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);
|
self.emit_precall_and_call(argc);
|
||||||
} else {
|
} else {
|
||||||
match kind {
|
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_instr(Opcode310::CALL_FUNCTION),
|
||||||
}
|
}
|
||||||
self.write_arg(argc);
|
self.write_arg(argc);
|
||||||
|
@ -2188,7 +2188,7 @@ impl PyCodeGenerator {
|
||||||
let is_py_api = method_name.is_py_api();
|
let is_py_api = method_name.is_py_api();
|
||||||
self.emit_expr(obj);
|
self.emit_expr(obj);
|
||||||
self.emit_load_method_instr(method_name);
|
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) {
|
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_name_instr(Identifier::private("#path"));
|
||||||
self.emit_load_method_instr(Identifier::public("append"));
|
self.emit_load_method_instr(Identifier::public("append"));
|
||||||
self.emit_load_const(erg_std_path().to_str().unwrap());
|
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.stack_dec();
|
||||||
self.emit_pop_top();
|
self.emit_pop_top();
|
||||||
let erg_std_mod = Identifier::public("_erg_std_prelude");
|
let erg_std_mod = Identifier::public("_erg_std_prelude");
|
||||||
|
|
|
@ -80,19 +80,31 @@ impl Name {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum AccessKind {
|
pub enum AccessKind {
|
||||||
Name,
|
Name,
|
||||||
Attr,
|
/// class/module attr
|
||||||
Method,
|
/// e.g. `Str.center`
|
||||||
|
UnboundAttr,
|
||||||
|
/// method/instance attr
|
||||||
|
/// e.g. `"aaa".center`
|
||||||
|
///
|
||||||
|
/// can also access class/module attrs
|
||||||
|
BoundAttr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccessKind {
|
impl AccessKind {
|
||||||
pub const fn is_local(&self) -> bool {
|
pub const fn is_local(&self) -> bool {
|
||||||
matches!(self, Self::Name)
|
matches!(self, Self::Name)
|
||||||
}
|
}
|
||||||
pub const fn is_attr(&self) -> bool {
|
pub const fn is_unbound_attr(&self) -> bool {
|
||||||
matches!(self, Self::Attr)
|
matches!(self, Self::UnboundAttr)
|
||||||
|
}
|
||||||
|
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(),
|
||||||
}
|
}
|
||||||
pub const fn is_method(&self) -> bool {
|
|
||||||
matches!(self, Self::Method)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -420,7 +420,7 @@ impl Context {
|
||||||
) -> Triple<VarInfo, TyCheckError> {
|
) -> Triple<VarInfo, TyCheckError> {
|
||||||
if let Some(vi) = self.get_current_scope_var(&ident.name) {
|
if let Some(vi) = self.get_current_scope_var(&ident.name) {
|
||||||
match self.validate_visibility(ident, vi, input, namespace) {
|
match self.validate_visibility(ident, vi, input, namespace) {
|
||||||
Ok(()) => {
|
Ok(()) if acc_kind.matches(vi) => {
|
||||||
return Triple::Ok(vi.clone());
|
return Triple::Ok(vi.clone());
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -428,6 +428,7 @@ impl Context {
|
||||||
return Triple::Err(err);
|
return Triple::Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
} else if let Some((name, _vi)) = self
|
} else if let Some((name, _vi)) = self
|
||||||
.future_defined_locals
|
.future_defined_locals
|
||||||
|
@ -453,6 +454,17 @@ impl Context {
|
||||||
self.get_similar_name(ident.inspect()),
|
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 acc_kind.is_local() {
|
||||||
if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
|
if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
|
||||||
return parent.rec_get_var_info(ident, acc_kind, input, namespace);
|
return parent.rec_get_var_info(ident, acc_kind, input, namespace);
|
||||||
|
@ -468,7 +480,7 @@ impl Context {
|
||||||
) -> Option<&mut VarInfo> {
|
) -> Option<&mut VarInfo> {
|
||||||
if let Some(vi) = self.get_current_scope_var(&ident.name) {
|
if let Some(vi) = self.get_current_scope_var(&ident.name) {
|
||||||
match self.validate_visibility(ident, vi, &self.cfg.input, self) {
|
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();
|
let vi = self.get_mut_current_scope_var(&ident.name).unwrap();
|
||||||
return Some(vi);
|
return Some(vi);
|
||||||
}
|
}
|
||||||
|
@ -477,6 +489,7 @@ impl Context {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if acc_kind.is_local() {
|
if acc_kind.is_local() {
|
||||||
|
@ -500,7 +513,7 @@ impl Context {
|
||||||
.or_else(|| self.future_defined_locals.get(&ident.inspect()[..]))
|
.or_else(|| self.future_defined_locals.get(&ident.inspect()[..]))
|
||||||
{
|
{
|
||||||
match self.validate_visibility(ident, vi, input, namespace) {
|
match self.validate_visibility(ident, vi, input, namespace) {
|
||||||
Ok(()) => {
|
Ok(()) if acc_kind.matches(vi) => {
|
||||||
return Triple::Ok(vi.clone());
|
return Triple::Ok(vi.clone());
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -508,6 +521,7 @@ impl Context {
|
||||||
return Triple::Err(err);
|
return Triple::Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if acc_kind.is_local() {
|
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) {
|
if let Ok(singular_ctxs) = self.get_singular_ctxs_by_hir_expr(obj, namespace) {
|
||||||
for ctx in singular_ctxs {
|
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) => {
|
Triple::Ok(vi) => {
|
||||||
return 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) => {
|
Triple::Ok(vi) => {
|
||||||
if let Some(self_t) = vi.t.self_t() {
|
if let Some(self_t) = vi.t.self_t() {
|
||||||
match self
|
match self
|
||||||
|
@ -636,7 +652,7 @@ impl Context {
|
||||||
Triple::None
|
Triple::None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_attr_from_nominal_t(
|
fn get_bound_attr_from_nominal_t(
|
||||||
&self,
|
&self,
|
||||||
obj: &hir::Expr,
|
obj: &hir::Expr,
|
||||||
ident: &Identifier,
|
ident: &Identifier,
|
||||||
|
@ -646,7 +662,7 @@ impl Context {
|
||||||
let self_t = obj.t();
|
let self_t = obj.t();
|
||||||
if let Some(sups) = self.get_nominal_super_type_ctxs(&self_t) {
|
if let Some(sups) = self.get_nominal_super_type_ctxs(&self_t) {
|
||||||
for ctx in sups {
|
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) => {
|
Triple::Ok(vi) => {
|
||||||
return Triple::Ok(vi);
|
return Triple::Ok(vi);
|
||||||
}
|
}
|
||||||
|
@ -657,7 +673,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
// if self is a methods context
|
// if self is a methods context
|
||||||
if let Some(ctx) = self.get_same_name_context(&ctx.name) {
|
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) => {
|
Triple::Ok(vi) => {
|
||||||
return Triple::Ok(vi);
|
return Triple::Ok(vi);
|
||||||
}
|
}
|
||||||
|
@ -691,7 +707,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for ctx in ctxs {
|
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) => {
|
Triple::Ok(vi) => {
|
||||||
obj.ref_t().coerce();
|
obj.ref_t().coerce();
|
||||||
return Triple::Ok(vi);
|
return Triple::Ok(vi);
|
||||||
|
@ -702,7 +718,7 @@ impl Context {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
if let Some(ctx) = self.get_same_name_context(&ctx.name) {
|
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) => {
|
Triple::Ok(vi) => {
|
||||||
return Triple::Ok(vi);
|
return Triple::Ok(vi);
|
||||||
}
|
}
|
||||||
|
@ -946,7 +962,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ctx) = self.get_same_name_context(&ctx.name) {
|
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) => {
|
Triple::Ok(t) => {
|
||||||
return 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(_))
|
matches!(self, Self::MethodDefs(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn is_trait_impl(&self) -> bool {
|
||||||
|
matches!(self, Self::MethodDefs(Some(_)))
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn is_type(&self) -> bool {
|
pub const fn is_type(&self) -> bool {
|
||||||
matches!(self, Self::Class | Self::Trait | Self::StructuralTrait)
|
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::config::ErgMode;
|
||||||
use erg_common::consts::{ERG_MODE, PYTHON_MODE};
|
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::env::{is_pystd_main_module, is_std_decl_path};
|
||||||
use erg_common::erg_util::BUILTIN_ERG_MODS;
|
use erg_common::erg_util::BUILTIN_ERG_MODS;
|
||||||
use erg_common::levenshtein::get_similar_name;
|
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::typaram::TyParam;
|
||||||
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
|
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
|
||||||
use crate::ty::{
|
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;
|
use crate::build_hir::HIRBuilder;
|
||||||
|
@ -200,7 +201,10 @@ impl Context {
|
||||||
} else {
|
} else {
|
||||||
None
|
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(
|
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
|
@ -266,7 +270,10 @@ impl Context {
|
||||||
self.absolutize(sig.ident.name.loc()),
|
self.absolutize(sig.ident.name.loc()),
|
||||||
);
|
);
|
||||||
self.index().register(&vi);
|
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(
|
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
|
@ -1347,16 +1354,7 @@ impl Context {
|
||||||
..
|
..
|
||||||
} = additional
|
} = additional
|
||||||
{
|
{
|
||||||
for (field, t) in rec.iter() {
|
self.register_instance_attrs(&mut ctx, rec)?;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
param_t
|
param_t
|
||||||
.map(|t| self.intersection(t, additional.typ()))
|
.map(|t| self.intersection(t, additional.typ()))
|
||||||
|
@ -1418,16 +1416,7 @@ impl Context {
|
||||||
self.level,
|
self.level,
|
||||||
);
|
);
|
||||||
let Some(TypeObj::Builtin{ t: Type::Record(req), .. }) = gen.base_or_sup() else { todo!("{gen}") };
|
let Some(TypeObj::Builtin{ t: Type::Record(req), .. }) = gen.base_or_sup() else { todo!("{gen}") };
|
||||||
for (field, t) in req.iter() {
|
self.register_instance_attrs(&mut ctx, req)?;
|
||||||
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_gen_mono_type(ident, gen, ctx, Const)
|
self.register_gen_mono_type(ident, gen, ctx, Const)
|
||||||
} else {
|
} else {
|
||||||
feature_error!(
|
feature_error!(
|
||||||
|
@ -1460,16 +1449,7 @@ impl Context {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
if let Some(additional) = additional {
|
if let Some(additional) = additional {
|
||||||
for (field, t) in additional.iter() {
|
self.register_instance_attrs(&mut ctx, additional)?;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for sup in super_classes.into_iter() {
|
for sup in super_classes.into_iter() {
|
||||||
if let Some((_, sup_ctx)) = self.get_nominal_type_ctx(&sup) {
|
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<()> {
|
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 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() {
|
let new_t = if let Some(base) = gen.base_or_sup() {
|
||||||
|
@ -1529,16 +1532,7 @@ impl Context {
|
||||||
t: Type::Record(rec),
|
t: Type::Record(rec),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for (field, t) in rec.iter() {
|
self.register_instance_attrs(ctx, rec)?;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
methods.register_fixed_auto_impl(
|
methods.register_fixed_auto_impl(
|
||||||
|
|
|
@ -177,7 +177,7 @@ impl ASTLowerer {
|
||||||
for ctx in ctxs {
|
for ctx in ctxs {
|
||||||
if let Triple::Ok(vi) = ctx.rec_get_var_info(
|
if let Triple::Ok(vi) = ctx.rec_get_var_info(
|
||||||
&ident.raw,
|
&ident.raw,
|
||||||
AccessKind::Attr,
|
AccessKind::UnboundAttr,
|
||||||
self.input(),
|
self.input(),
|
||||||
&self.module.context,
|
&self.module.context,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -485,11 +485,12 @@ impl LowerError {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let hint = similar_name.map(|n| {
|
let hint = similar_name.map(|n| {
|
||||||
let vis = similar_info.map_or("".into(), |vi| vi.vis.modifier.display());
|
let vis = similar_info.map_or("".into(), |vi| vi.vis.modifier.display());
|
||||||
|
let kind = similar_info.map_or("", |vi| vi.kind.display());
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => format!("似た名前の{vis}属性があります: {n}"),
|
"japanese" => format!("似た名前の{vis}{kind}属性があります: {n}"),
|
||||||
"simplified_chinese" => format!("具有相同名称的{vis}属性: {n}"),
|
"simplified_chinese" => format!("具有相同名称的{vis}{kind}属性: {n}"),
|
||||||
"traditional_chinese" => format!("具有相同名稱的{vis}屬性: {n}"),
|
"traditional_chinese" => format!("具有相同名稱的{vis}{kind}屬性: {n}"),
|
||||||
"english" => format!("has a similar name {vis} attribute: {n}"),
|
"english" => format!("has a similar name {vis} {kind} attribute: {n}"),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let found = StyledString::new(name, Some(ERR), Some(ATTR));
|
let found = StyledString::new(name, Some(ERR), Some(ATTR));
|
||||||
|
@ -1157,4 +1158,30 @@ impl LowerWarning {
|
||||||
caused_by,
|
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::error::Location;
|
||||||
use erg_common::set::Set;
|
use erg_common::set::Set;
|
||||||
use erg_common::Str;
|
use erg_common::{switch_lang, Str};
|
||||||
|
|
||||||
use erg_parser::ast::DefId;
|
use erg_parser::ast::DefId;
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ use Mutability::*;
|
||||||
pub enum VarKind {
|
pub enum VarKind {
|
||||||
Defined(DefId),
|
Defined(DefId),
|
||||||
Declared,
|
Declared,
|
||||||
|
InstanceAttr,
|
||||||
Parameter {
|
Parameter {
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
var: bool,
|
var: bool,
|
||||||
|
@ -88,6 +89,38 @@ impl VarKind {
|
||||||
pub const fn is_builtin(&self) -> bool {
|
pub const fn is_builtin(&self) -> bool {
|
||||||
matches!(self, Self::Builtin)
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -285,12 +318,11 @@ impl VarInfo {
|
||||||
} else {
|
} else {
|
||||||
Mutability::Immutable
|
Mutability::Immutable
|
||||||
};
|
};
|
||||||
let kind = VarKind::Declared;
|
|
||||||
Self::new(
|
Self::new(
|
||||||
t,
|
t,
|
||||||
muty,
|
muty,
|
||||||
Visibility::new(field.vis, namespace),
|
Visibility::new(field.vis, namespace),
|
||||||
kind,
|
VarKind::InstanceAttr,
|
||||||
None,
|
None,
|
||||||
impl_of,
|
impl_of,
|
||||||
None,
|
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)
|
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]
|
#[test]
|
||||||
fn exec_collection_err() -> Result<(), ()> {
|
fn exec_collection_err() -> Result<(), ()> {
|
||||||
expect_failure("tests/should_err/collection.er", 0, 4)
|
expect_failure("tests/should_err/collection.er", 0, 4)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue