Enhance: empty classes can be generated by Class()

This commit is contained in:
Shunsuke Shibayama 2022-12-23 22:16:22 +09:00
parent 5b0dedd421
commit d8f7e386c8
12 changed files with 127 additions and 78 deletions

View file

@ -1146,7 +1146,7 @@ impl PyCodeGenerator {
log!(info "entered {} ({})", fn_name!(), class_def.sig); log!(info "entered {} ({})", fn_name!(), class_def.sig);
self.emit_push_null(); self.emit_push_null();
let ident = class_def.sig.ident().clone(); let ident = class_def.sig.ident().clone();
let require_or_sup = class_def.require_or_sup.clone(); let require_or_sup = class_def.require_or_sup.clone().map(|x| *x);
let obj = class_def.obj.clone(); let obj = class_def.obj.clone();
self.write_instr(LOAD_BUILD_CLASS); self.write_instr(LOAD_BUILD_CLASS);
self.write_arg(0); self.write_arg(0);
@ -1162,7 +1162,7 @@ impl PyCodeGenerator {
self.write_arg(0); self.write_arg(0);
self.emit_load_const(ident.inspect().clone()); self.emit_load_const(ident.inspect().clone());
// LOAD subclasses // LOAD subclasses
let subclasses_len = self.emit_require_type(obj, *require_or_sup); let subclasses_len = self.emit_require_type(obj, require_or_sup);
self.emit_call_instr(2 + subclasses_len, Name); self.emit_call_instr(2 + subclasses_len, Name);
self.stack_dec_n((1 + 2 + subclasses_len) - 1); self.stack_dec_n((1 + 2 + subclasses_len) - 1);
self.emit_store_instr(ident, Name); self.emit_store_instr(ident, Name);
@ -1194,12 +1194,12 @@ impl PyCodeGenerator {
// fn emit_poly_type_def(&mut self, sig: SubrSignature, body: DefBody) {} // fn emit_poly_type_def(&mut self, sig: SubrSignature, body: DefBody) {}
/// Y = Inherit X => class Y(X): ... /// Y = Inherit X => class Y(X): ...
fn emit_require_type(&mut self, obj: GenTypeObj, require_or_sup: Expr) -> usize { fn emit_require_type(&mut self, obj: GenTypeObj, require_or_sup: Option<Expr>) -> usize {
log!(info "entered {} ({obj}, {require_or_sup})", fn_name!()); log!(info "entered {} ({obj}, {require_or_sup:?})", fn_name!());
match obj { match obj {
GenTypeObj::Class(_) => 0, GenTypeObj::Class(_) => 0,
GenTypeObj::Subclass(_) => { GenTypeObj::Subclass(_) => {
self.emit_expr(require_or_sup); self.emit_expr(require_or_sup.unwrap());
1 // TODO: not always 1 1 // TODO: not always 1
} }
_ => todo!(), _ => todo!(),
@ -2728,25 +2728,38 @@ impl PyCodeGenerator {
fn emit_init_method(&mut self, sig: &Signature, __new__: Type) { fn emit_init_method(&mut self, sig: &Signature, __new__: Type) {
log!(info "entered {}", fn_name!()); log!(info "entered {}", fn_name!());
let new_first_param = __new__.non_default_params().unwrap().first();
let line = sig.ln_begin().unwrap(); let line = sig.ln_begin().unwrap();
let class_name = sig.ident().inspect(); let class_name = sig.ident().inspect();
let mut ident = Identifier::public_with_line(DOT, Str::ever("__init__"), line); let mut ident = Identifier::public_with_line(DOT, Str::ever("__init__"), line);
ident.vi.t = __new__.clone(); ident.vi.t = __new__.clone();
let param_name = fresh_varname();
let param = VarName::from_str_and_line(Str::from(param_name.clone()), line);
let param = NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
let self_param = VarName::from_str_and_line(Str::ever("self"), line); let self_param = VarName::from_str_and_line(Str::ever("self"), line);
let self_param = NonDefaultParamSignature::new(ParamPattern::VarName(self_param), None); let self_param = NonDefaultParamSignature::new(ParamPattern::VarName(self_param), None);
let (param_name, params) = if let Some(new_first_param) = new_first_param {
let param_name = new_first_param
.name()
.map(|s| s.to_string())
.unwrap_or_else(fresh_varname);
let param = VarName::from_str_and_line(Str::from(param_name.clone()), line);
let param = NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
let params = Params::new(vec![self_param, param], None, vec![], None); let params = Params::new(vec![self_param, param], None, vec![], None);
(param_name, params)
} else {
(
"_".into(),
Params::new(vec![self_param], None, vec![], None),
)
};
let subr_sig = SubrSignature::new(ident, params); let subr_sig = SubrSignature::new(ident, params);
let mut attrs = vec![]; let mut attrs = vec![];
match __new__.non_default_params().unwrap()[0].typ() { match new_first_param.map(|pt| pt.typ()) {
// namedtupleは仕様上::xなどの名前を使えない // namedtupleは仕様上::xなどの名前を使えない
// {x = Int; y = Int} // {x = Int; y = Int}
// => self::x = %x.x; self::y = %x.y // => self::x = %x.x; self::y = %x.y
// {.x = Int; .y = Int} // {.x = Int; .y = Int}
// => self.x = %x.x; self.y = %x.y // => self.x = %x.x; self.y = %x.y
Type::Record(rec) => { // () => pass
Some(Type::Record(rec)) => {
for field in rec.keys() { for field in rec.keys() {
let obj = let obj =
Expr::Accessor(Accessor::private_with_line(Str::from(&param_name), line)); Expr::Accessor(Accessor::private_with_line(Str::from(&param_name), line));
@ -2768,9 +2781,14 @@ impl PyCodeGenerator {
attrs.push(Expr::AttrDef(attr_def)); attrs.push(Expr::AttrDef(attr_def));
} }
let none = Token::new(TokenKind::NoneLit, "None", line, 0); let none = Token::new(TokenKind::NoneLit, "None", line, 0);
attrs.push(Expr::Lit(Literal::try_from(none).unwrap())); attrs.push(Expr::Lit(Literal::new(ValueObj::None, none)));
}
Some(other) => todo!("{other}"),
None => {
let none = Token::new(TokenKind::NoneLit, "None", line, 0);
let none = Expr::Lit(Literal::new(ValueObj::None, none));
attrs.push(none);
} }
other => todo!("{other}"),
} }
let block = Block::new(attrs); let block = Block::new(attrs);
let body = DefBody::new(EQUAL, block, DefId(0)); let body = DefBody::new(EQUAL, block, DefId(0));
@ -2787,24 +2805,37 @@ impl PyCodeGenerator {
let class_ident = sig.ident(); let class_ident = sig.ident();
let line = sig.ln_begin().unwrap(); let line = sig.ln_begin().unwrap();
let mut ident = Identifier::public_with_line(DOT, Str::ever("new"), line); let mut ident = Identifier::public_with_line(DOT, Str::ever("new"), line);
ident.vi.t = __new__;
let param_name = fresh_varname();
let param = VarName::from_str_and_line(Str::from(param_name.clone()), line);
let param = NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
let sig = SubrSignature::new(ident, Params::new(vec![param], None, vec![], None));
let arg = PosArg::new(Expr::Accessor(Accessor::private_with_line(
Str::from(param_name),
line,
)));
let class = Expr::Accessor(Accessor::Ident(class_ident.clone())); let class = Expr::Accessor(Accessor::Ident(class_ident.clone()));
let mut new_ident = let mut new_ident =
Identifier::bare(None, VarName::from_str_and_line(Str::ever("__new__"), line)); Identifier::bare(None, VarName::from_str_and_line(Str::ever("__new__"), line));
new_ident.vi.py_name = Some(Str::ever("__call__")); new_ident.vi.py_name = Some(Str::ever("__call__"));
let class_new = class.attr_expr(new_ident); let class_new = class.attr_expr(new_ident);
ident.vi.t = __new__;
if let Some(new_first_param) = ident.vi.t.non_default_params().unwrap().first() {
let param_name = new_first_param
.name()
.map(|s| s.to_string())
.unwrap_or_else(fresh_varname);
let param = VarName::from_str_and_line(Str::from(param_name.clone()), line);
let param = NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
let params = Params::new(vec![param], None, vec![], None);
let sig = SubrSignature::new(ident, params);
let arg = PosArg::new(Expr::Accessor(Accessor::private_with_line(
Str::from(param_name),
line,
)));
let call = class_new.call_expr(Args::new(vec![arg], None, vec![], None)); let call = class_new.call_expr(Args::new(vec![arg], None, vec![], None));
let block = Block::new(vec![call]); let block = Block::new(vec![call]);
let body = DefBody::new(EQUAL, block, DefId(0)); let body = DefBody::new(EQUAL, block, DefId(0));
self.emit_subr_def(Some(class_ident.inspect()), sig, body); self.emit_subr_def(Some(class_ident.inspect()), sig, body);
} else {
let params = Params::new(vec![], None, vec![], None);
let sig = SubrSignature::new(ident, params);
let call = class_new.call_expr(Args::new(vec![], None, vec![], None));
let block = Block::new(vec![call]);
let body = DefBody::new(EQUAL, block, DefId(0));
self.emit_subr_def(Some(class_ident.inspect()), sig, body);
}
} }
fn emit_block(&mut self, block: Block, opt_name: Option<Str>, params: Vec<Str>) -> CodeObj { fn emit_block(&mut self, block: Block, opt_name: Option<Str>, params: Vec<Str>) -> CodeObj {

View file

@ -19,33 +19,30 @@ const CLASS_ERR: StyledStr = StyledStr::new("Class", Some(ERR), None);
const REQ_ERR: StyledStr = StyledStr::new("Requirement", Some(ERR), None); const REQ_ERR: StyledStr = StyledStr::new("Requirement", Some(ERR), None);
const REQ_WARN: StyledStr = StyledStr::new("Requirement", Some(WARN), None); const REQ_WARN: StyledStr = StyledStr::new("Requirement", Some(WARN), None);
/// Requirement: Type, Impl := Type -> ClassType /// Requirement := Type or NoneType, Impl := Type -> ClassType
pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> { pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let require = args.remove_left_or_key("Requirement").ok_or_else(|| { let require = args.remove_left_or_key("Requirement");
ErrorCore::new( let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type().unwrap());
let t = mono(ctx.name.clone());
match require {
Some(value) => {
if let Some(require) = value.as_type() {
Ok(ValueObj::gen_t(GenTypeObj::class(t, Some(require), impls)))
} else {
let require = StyledString::new(format!("{value}"), Some(ERR), None);
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)], vec![SubMessage::only_loc(Location::Unknown)],
format!("{REQ_ERR} is not passed"), format!("non-type object {require} is passed to {REQ_WARN}",),
line!() as usize, line!() as usize,
ErrorKind::TypeError, ErrorKind::TypeError,
Location::Unknown, Location::Unknown,
) )
})?; .into())
let Some(require) = require.as_type() else { }
let require = StyledString::new(format!("{}", require), Some(ERR), None); }
return Err(ErrorCore::new( None => Ok(ValueObj::gen_t(GenTypeObj::class(t, None, impls))),
vec![SubMessage::only_loc(Location::Unknown)], }
format!(
"non-type object {require} is passed to {REQ_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type().unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::class(t, require, impls)))
} }
/// Super: ClassType, Impl := Type, Additional := Type -> ClassType /// Super: ClassType, Impl := Type, Additional := Type -> ClassType

View file

@ -2062,9 +2062,9 @@ impl Context {
Private Private
}; };
let class_t = func( let class_t = func(
vec![kw("Requirement", Type)], vec![],
None, None,
vec![kw("Impl", Type)], vec![kw("Requirement", or(Type, Ellipsis)), kw("Impl", Type)],
ClassType, ClassType,
); );
let class = ConstSubr::Builtin(BuiltinConstSubr::new("Class", class_func, class_t, None)); let class = ConstSubr::Builtin(BuiltinConstSubr::new("Class", class_func, class_t, None));

View file

@ -14,7 +14,7 @@ use erg_common::{fn_name, Str};
use ast::{Decorator, DefId, Identifier, OperationKind, VarName}; use ast::{Decorator, DefId, Identifier, OperationKind, VarName};
use erg_parser::ast; use erg_parser::ast;
use crate::ty::constructors::{free_var, func, func1, proc, ref_, ref_mut, v_enum}; use crate::ty::constructors::{free_var, func, func0, func1, proc, ref_, ref_mut, v_enum};
use crate::ty::free::{Constraint, FreeKind, HasLevel}; use crate::ty::free::{Constraint, FreeKind, HasLevel};
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj}; use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{HasType, ParamTy, SubrType, Type}; use crate::ty::{HasType, ParamTy, SubrType, Type};
@ -882,8 +882,11 @@ impl Context {
2, 2,
self.level, self.level,
); );
let require = gen.require_or_sup().unwrap().typ().clone(); let new_t = if let Some(require) = gen.require_or_sup() {
let new_t = func1(require, gen.typ().clone()); func1(require.typ().clone(), gen.typ().clone())
} else {
func0(gen.typ().clone())
};
methods.register_fixed_auto_impl( methods.register_fixed_auto_impl(
"__new__", "__new__",
new_t.clone(), new_t.clone(),

View file

@ -222,7 +222,7 @@ impl ASTLowerer {
Type::ClassType => { Type::ClassType => {
let ty_obj = GenTypeObj::class( let ty_obj = GenTypeObj::class(
mono(format!("{}{ident}", self.ctx.path())), mono(format!("{}{ident}", self.ctx.path())),
TypeObj::Builtin(Type::Uninited), Some(TypeObj::Builtin(Type::Uninited)),
None, None,
); );
self.ctx.register_gen_type(ident, ty_obj); self.ctx.register_gen_type(ident, ty_obj);

View file

@ -95,7 +95,9 @@ impl SideEffectChecker {
self.check_def(def); self.check_def(def);
} }
Expr::ClassDef(class_def) => { Expr::ClassDef(class_def) => {
self.check_expr(class_def.require_or_sup.as_ref()); if let Some(req_sup) = &class_def.require_or_sup {
self.check_expr(req_sup);
}
// TODO: grow // TODO: grow
for def in class_def.methods.iter() { for def in class_def.methods.iter() {
self.check_expr(def); self.check_expr(def);
@ -322,7 +324,9 @@ impl SideEffectChecker {
self.check_def(def); self.check_def(def);
} }
Expr::ClassDef(class_def) => { Expr::ClassDef(class_def) => {
self.check_expr(class_def.require_or_sup.as_ref()); if let Some(req_sup) = &class_def.require_or_sup {
self.check_expr(req_sup);
}
for def in class_def.methods.iter() { for def in class_def.methods.iter() {
self.check_expr(def); self.check_expr(def);
} }

View file

@ -2055,7 +2055,7 @@ impl Methods {
pub struct ClassDef { pub struct ClassDef {
pub obj: GenTypeObj, pub obj: GenTypeObj,
pub sig: Signature, pub sig: Signature,
pub require_or_sup: Box<Expr>, pub require_or_sup: Option<Box<Expr>>,
/// The type of `new` that is automatically defined if not defined /// The type of `new` that is automatically defined if not defined
pub need_to_gen_new: bool, pub need_to_gen_new: bool,
pub __new__: Type, pub __new__: Type,
@ -2103,7 +2103,7 @@ impl ClassDef {
pub fn new( pub fn new(
obj: GenTypeObj, obj: GenTypeObj,
sig: Signature, sig: Signature,
require_or_sup: Expr, require_or_sup: Option<Expr>,
need_to_gen_new: bool, need_to_gen_new: bool,
__new__: Type, __new__: Type,
methods: Block, methods: Block,
@ -2111,7 +2111,7 @@ impl ClassDef {
Self { Self {
obj, obj,
sig, sig,
require_or_sup: Box::new(require_or_sup), require_or_sup: require_or_sup.map(Box::new),
need_to_gen_new, need_to_gen_new,
__new__, __new__,
methods, methods,

View file

@ -1360,12 +1360,12 @@ impl ASTLowerer {
))); )));
}; };
let type_obj = enum_unwrap!(self.ctx.rec_get_const_obj(hir_def.sig.ident().inspect()).unwrap(), ValueObj::Type:(TypeObj::Generated:(_))); let type_obj = enum_unwrap!(self.ctx.rec_get_const_obj(hir_def.sig.ident().inspect()).unwrap(), ValueObj::Type:(TypeObj::Generated:(_)));
let sup_type = enum_unwrap!(&hir_def.body.block.first().unwrap(), hir::Expr::Call) if let Some(sup_type) = enum_unwrap!(&hir_def.body.block.first().unwrap(), hir::Expr::Call)
.args .args
.get_left_or_key("Super") .get_left_or_key("Super")
.unwrap(); {
Self::check_inheritable(&self.cfg, &mut self.errs, type_obj, sup_type, &hir_def.sig); Self::check_inheritable(&self.cfg, &mut self.errs, type_obj, sup_type, &hir_def.sig);
// vi.t.non_default_params().unwrap()[0].typ().clone() }
let (__new__, need_to_gen_new) = if let (Some(dunder_new_vi), Some(new_vi)) = ( let (__new__, need_to_gen_new) = if let (Some(dunder_new_vi), Some(new_vi)) = (
class_ctx.get_current_scope_var(&VarName::from_static("__new__")), class_ctx.get_current_scope_var(&VarName::from_static("__new__")),
class_ctx.get_current_scope_var(&VarName::from_static("new")), class_ctx.get_current_scope_var(&VarName::from_static("new")),
@ -1404,7 +1404,7 @@ impl ASTLowerer {
)? )?
}; };
let mut hir_def = self.lower_def(class_def.def)?; let mut hir_def = self.lower_def(class_def.def)?;
let base = Self::get_require_or_sup_or_base(hir_def.body.block.remove(0)); let base = Self::get_require_or_sup_or_base(hir_def.body.block.remove(0)).unwrap();
let mut hir_methods = hir::Block::empty(); let mut hir_methods = hir::Block::empty();
for mut methods in class_def.methods_list.into_iter() { for mut methods in class_def.methods_list.into_iter() {
let kind = ContextKind::PatchMethodDefs(base_t.clone()); let kind = ContextKind::PatchMethodDefs(base_t.clone());
@ -1786,16 +1786,16 @@ impl ASTLowerer {
.push((ClassDefType::Simple(base.clone()), methods)); .push((ClassDefType::Simple(base.clone()), methods));
} }
fn get_require_or_sup_or_base(expr: hir::Expr) -> hir::Expr { fn get_require_or_sup_or_base(expr: hir::Expr) -> Option<hir::Expr> {
match expr { match expr {
acc @ hir::Expr::Accessor(_) => acc, acc @ hir::Expr::Accessor(_) => Some(acc),
hir::Expr::Call(mut call) => match call.obj.show_acc().as_ref().map(|s| &s[..]) { hir::Expr::Call(mut call) => match call.obj.show_acc().as_ref().map(|s| &s[..]) {
Some("Class") => call.args.remove_left_or_key("Requirement").unwrap(), Some("Class") => call.args.remove_left_or_key("Requirement"),
Some("Inherit") => call.args.remove_left_or_key("Super").unwrap(), Some("Inherit") => call.args.remove_left_or_key("Super"),
Some("Inheritable") => { Some("Inheritable") => {
Self::get_require_or_sup_or_base(call.args.remove_left_or_key("Class").unwrap()) Self::get_require_or_sup_or_base(call.args.remove_left_or_key("Class").unwrap())
} }
Some("Patch") => call.args.remove_left_or_key("Base").unwrap(), Some("Patch") => call.args.remove_left_or_key("Base"),
_ => todo!(), _ => todo!(),
}, },
other => todo!("{other}"), other => todo!("{other}"),

View file

@ -127,7 +127,9 @@ impl OwnershipChecker {
self.path_stack.pop(); self.path_stack.pop();
} }
Expr::ClassDef(class_def) => { Expr::ClassDef(class_def) => {
self.check_expr(&class_def.require_or_sup, Ownership::Owned, false); if let Some(req_sup) = &class_def.require_or_sup {
self.check_expr(req_sup, Ownership::Owned, false);
}
for def in class_def.methods.iter() { for def in class_def.methods.iter() {
self.check_expr(def, Ownership::Owned, true); self.check_expr(def, Ownership::Owned, true);
} }

View file

@ -62,15 +62,15 @@ pub type EvalValueResult<T> = Result<T, EvalValueError>;
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ClassTypeObj { pub struct ClassTypeObj {
pub t: Type, pub t: Type,
pub require: Box<TypeObj>, pub require: Option<Box<TypeObj>>,
pub impls: Option<Box<TypeObj>>, pub impls: Option<Box<TypeObj>>,
} }
impl ClassTypeObj { impl ClassTypeObj {
pub fn new(t: Type, require: TypeObj, impls: Option<TypeObj>) -> Self { pub fn new(t: Type, require: Option<TypeObj>, impls: Option<TypeObj>) -> Self {
Self { Self {
t, t,
require: Box::new(require), require: require.map(Box::new),
impls: impls.map(Box::new), impls: impls.map(Box::new),
} }
} }
@ -201,7 +201,7 @@ impl fmt::Display for GenTypeObj {
} }
impl GenTypeObj { impl GenTypeObj {
pub fn class(t: Type, require: TypeObj, impls: Option<TypeObj>) -> Self { pub fn class(t: Type, require: Option<TypeObj>, impls: Option<TypeObj>) -> Self {
GenTypeObj::Class(ClassTypeObj::new(t, require, impls)) GenTypeObj::Class(ClassTypeObj::new(t, require, impls))
} }
@ -241,7 +241,7 @@ impl GenTypeObj {
pub fn require_or_sup(&self) -> Option<&TypeObj> { pub fn require_or_sup(&self) -> Option<&TypeObj> {
match self { match self {
Self::Class(class) => Some(class.require.as_ref()), Self::Class(class) => class.require.as_ref().map(AsRef::as_ref),
Self::Subclass(subclass) => Some(subclass.sup.as_ref()), Self::Subclass(subclass) => Some(subclass.sup.as_ref()),
Self::Trait(trait_) => Some(trait_.require.as_ref()), Self::Trait(trait_) => Some(trait_.require.as_ref()),
Self::Subtrait(subtrait) => Some(subtrait.sup.as_ref()), Self::Subtrait(subtrait) => Some(subtrait.sup.as_ref()),

View file

@ -1,3 +1,7 @@
Empty = Class()
empty = Empty.new()
print! empty
# Inheritance is prohibited by default. Remove this decorator and check for errors. # Inheritance is prohibited by default. Remove this decorator and check for errors.
@Inheritable @Inheritable
Point2D = Class {x = Int; y = Int} Point2D = Class {x = Int; y = Int}

8
tests/bug/bug.er Normal file
View file

@ -0,0 +1,8 @@
# z: Zip(Nat, Str)
z = zip [1+1], ["a"+"b"]
for! z, ((i, s),) =>
print! i + 1
print! s + "b"
print! s + 1
print! i + "a"