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

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_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> {
let require = args.remove_left_or_key("Requirement").ok_or_else(|| {
ErrorCore::new(
let require = args.remove_left_or_key("Requirement");
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)],
format!("{REQ_ERR} is not passed"),
format!("non-type object {require} is passed to {REQ_WARN}",),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
)
})?;
let Some(require) = require.as_type() else {
let require = StyledString::new(format!("{}", require), Some(ERR), None);
return Err(ErrorCore::new(
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)))
.into())
}
}
None => Ok(ValueObj::gen_t(GenTypeObj::class(t, None, impls))),
}
}
/// Super: ClassType, Impl := Type, Additional := Type -> ClassType

View file

@ -2062,9 +2062,9 @@ impl Context {
Private
};
let class_t = func(
vec![kw("Requirement", Type)],
vec![],
None,
vec![kw("Impl", Type)],
vec![kw("Requirement", or(Type, Ellipsis)), kw("Impl", Type)],
ClassType,
);
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 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::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{HasType, ParamTy, SubrType, Type};
@ -882,8 +882,11 @@ impl Context {
2,
self.level,
);
let require = gen.require_or_sup().unwrap().typ().clone();
let new_t = func1(require, gen.typ().clone());
let new_t = if let Some(require) = gen.require_or_sup() {
func1(require.typ().clone(), gen.typ().clone())
} else {
func0(gen.typ().clone())
};
methods.register_fixed_auto_impl(
"__new__",
new_t.clone(),

View file

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

View file

@ -95,7 +95,9 @@ impl SideEffectChecker {
self.check_def(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
for def in class_def.methods.iter() {
self.check_expr(def);
@ -322,7 +324,9 @@ impl SideEffectChecker {
self.check_def(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() {
self.check_expr(def);
}

View file

@ -2055,7 +2055,7 @@ impl Methods {
pub struct ClassDef {
pub obj: GenTypeObj,
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
pub need_to_gen_new: bool,
pub __new__: Type,
@ -2103,7 +2103,7 @@ impl ClassDef {
pub fn new(
obj: GenTypeObj,
sig: Signature,
require_or_sup: Expr,
require_or_sup: Option<Expr>,
need_to_gen_new: bool,
__new__: Type,
methods: Block,
@ -2111,7 +2111,7 @@ impl ClassDef {
Self {
obj,
sig,
require_or_sup: Box::new(require_or_sup),
require_or_sup: require_or_sup.map(Box::new),
need_to_gen_new,
__new__,
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 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
.get_left_or_key("Super")
.unwrap();
{
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)) = (
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 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();
for mut methods in class_def.methods_list.into_iter() {
let kind = ContextKind::PatchMethodDefs(base_t.clone());
@ -1786,16 +1786,16 @@ impl ASTLowerer {
.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 {
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[..]) {
Some("Class") => call.args.remove_left_or_key("Requirement").unwrap(),
Some("Inherit") => call.args.remove_left_or_key("Super").unwrap(),
Some("Class") => call.args.remove_left_or_key("Requirement"),
Some("Inherit") => call.args.remove_left_or_key("Super"),
Some("Inheritable") => {
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!(),
},
other => todo!("{other}"),

View file

@ -127,7 +127,9 @@ impl OwnershipChecker {
self.path_stack.pop();
}
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() {
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)]
pub struct ClassTypeObj {
pub t: Type,
pub require: Box<TypeObj>,
pub require: Option<Box<TypeObj>>,
pub impls: Option<Box<TypeObj>>,
}
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 {
t,
require: Box::new(require),
require: require.map(Box::new),
impls: impls.map(Box::new),
}
}
@ -201,7 +201,7 @@ impl fmt::Display for 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))
}
@ -241,7 +241,7 @@ impl GenTypeObj {
pub fn require_or_sup(&self) -> Option<&TypeObj> {
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::Trait(trait_) => Some(trait_.require.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.
@Inheritable
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"