Implement Patch

This commit is contained in:
Shunsuke Shibayama 2022-12-02 20:03:02 +09:00
parent 7078f95cec
commit bade70ef91
22 changed files with 741 additions and 116 deletions

View file

@ -40,7 +40,7 @@ use crate::context::eval::type_from_token_kind;
use crate::error::CompileError; use crate::error::CompileError;
use crate::hir::{ use crate::hir::{
Accessor, Args, Array, AttrDef, BinOp, Block, Call, ClassDef, Def, DefBody, Expr, Identifier, Accessor, Args, Array, AttrDef, BinOp, Block, Call, ClassDef, Def, DefBody, Expr, Identifier,
Lambda, Literal, Params, PosArg, Record, Signature, SubrSignature, Tuple, UnaryOp, Lambda, Literal, Params, PatchDef, PosArg, Record, Signature, SubrSignature, Tuple, UnaryOp,
VarSignature, HIR, VarSignature, HIR,
}; };
use crate::ty::value::ValueObj; use crate::ty::value::ValueObj;
@ -48,11 +48,16 @@ use crate::ty::{HasType, Type, TypeCode, TypePair};
use erg_common::fresh::fresh_varname; use erg_common::fresh::fresh_varname;
use AccessKind::*; use AccessKind::*;
fn fake_method_to_func(class: &str, name: &str) -> Option<&'static str> { /// patch method -> function
match (class, name) { /// patch attr -> variable
(_, "abs") => Some("abs"), fn debind(name: Option<&str>) -> Option<Str> {
(_, "iter") => Some("iter"), match name {
(_, "map") => Some("map"), Some(name) if name.starts_with("Function::") => {
Some(Str::from(name.replace("Function::", "")))
}
Some(patch_method) if patch_method.contains("::") || patch_method.contains('.') => {
Some(Str::rc(patch_method))
}
_ => None, _ => None,
} }
} }
@ -873,8 +878,14 @@ impl PyCodeGenerator {
if is_record { if is_record {
a.ident.dot = Some(DOT); a.ident.dot = Some(DOT);
} }
self.emit_expr(*a.obj); if let Some(varname) = debind(a.ident.vi.py_name.as_ref().map(|s| &s[..])) {
self.emit_load_attr_instr(a.ident); a.ident.dot = None;
a.ident.name = VarName::from_str(varname);
self.emit_load_name_instr(a.ident);
} else {
self.emit_expr(*a.obj);
self.emit_load_attr_instr(a.ident);
}
} }
} }
} }
@ -1135,6 +1146,27 @@ impl PyCodeGenerator {
self.stack_dec(); self.stack_dec();
} }
fn emit_patch_def(&mut self, patch_def: PatchDef) {
log!(info "entered {} ({})", fn_name!(), patch_def.sig);
for def in patch_def.methods {
// Invert.
// invert self = ...
// ↓
// def Invert::invert(self): ...
let Expr::Def(mut def) = def else { todo!() };
let namespace = self.cur_block_codeobj().name.trim_start_matches("::");
let name = format!(
"{}{}{}",
namespace,
patch_def.sig.ident().to_string_without_type(),
def.sig.ident().to_string_without_type()
);
def.sig.ident_mut().name = VarName::from_str(Str::from(name));
def.sig.ident_mut().dot = None;
self.emit_def(def);
}
}
// NOTE: use `TypeVar`, `Generic` in `typing` module // NOTE: use `TypeVar`, `Generic` in `typing` module
// fn emit_poly_type_def(&mut self, sig: SubrSignature, body: DefBody) {} // fn emit_poly_type_def(&mut self, sig: SubrSignature, body: DefBody) {}
@ -1986,14 +2018,13 @@ impl PyCodeGenerator {
fn emit_call_method(&mut self, obj: Expr, method_name: Identifier, args: Args) { fn emit_call_method(&mut self, obj: Expr, method_name: Identifier, args: Args) {
log!(info "entered {}", fn_name!()); log!(info "entered {}", fn_name!());
let class = obj.ref_t().qual_name(); // これは必ずmethodのあるクラスになっている
if &method_name.inspect()[..] == "update!" { if &method_name.inspect()[..] == "update!" {
if self.py_version.minor >= Some(11) { if self.py_version.minor >= Some(11) {
return self.emit_call_update_311(obj, args); return self.emit_call_update_311(obj, args);
} else { } else {
return self.emit_call_update_310(obj, args); return self.emit_call_update_310(obj, args);
} }
} else if let Some(func_name) = fake_method_to_func(&class, method_name.inspect()) { } else if let Some(func_name) = debind(method_name.vi.py_name.as_ref().map(|s| &s[..])) {
return self.emit_call_fake_method(obj, func_name, method_name, args); return self.emit_call_fake_method(obj, func_name, method_name, args);
} }
let is_py_api = obj.is_py_api(); let is_py_api = obj.is_py_api();
@ -2114,13 +2145,13 @@ impl PyCodeGenerator {
fn emit_call_fake_method( fn emit_call_fake_method(
&mut self, &mut self,
obj: Expr, obj: Expr,
func_name: &'static str, func_name: Str,
mut method_name: Identifier, mut method_name: Identifier,
mut args: Args, mut args: Args,
) { ) {
log!(info "entered {}", fn_name!()); log!(info "entered {}", fn_name!());
method_name.dot = None; method_name.dot = None;
method_name.vi.py_name = Some(Str::ever(func_name)); method_name.vi.py_name = Some(func_name);
self.emit_push_null(); self.emit_push_null();
self.emit_load_name_instr(method_name); self.emit_load_name_instr(method_name);
args.insert_pos(0, PosArg::new(obj)); args.insert_pos(0, PosArg::new(obj));
@ -2314,6 +2345,7 @@ impl PyCodeGenerator {
Expr::Accessor(acc) => self.emit_acc(acc), Expr::Accessor(acc) => self.emit_acc(acc),
Expr::Def(def) => self.emit_def(def), Expr::Def(def) => self.emit_def(def),
Expr::ClassDef(class) => self.emit_class_def(class), Expr::ClassDef(class) => self.emit_class_def(class),
Expr::PatchDef(patch) => self.emit_patch_def(patch),
Expr::AttrDef(attr) => self.emit_attr_def(attr), Expr::AttrDef(attr) => self.emit_attr_def(attr),
Expr::Lambda(lambda) => self.emit_lambda(lambda), Expr::Lambda(lambda) => self.emit_lambda(lambda),
Expr::UnaryOp(unary) => self.emit_unaryop(unary), Expr::UnaryOp(unary) => self.emit_unaryop(unary),

View file

@ -245,8 +245,20 @@ impl Context {
self.nominal_supertype_of(rhs, lhs) self.nominal_supertype_of(rhs, lhs)
} }
fn _find_compatible_patch(&self, sup: &Type, sub: &Type) -> Option<&Context> { pub(crate) fn find_patches_of<'a>(
for patch in self._all_patches().into_iter() { &'a self,
typ: &'a Type,
) -> impl Iterator<Item = &'a Context> {
self.all_patches().into_iter().filter(|ctx| {
if let ContextKind::Patch(base) = &ctx.kind {
return self.supertype_of(base, typ);
}
false
})
}
fn _find_compatible_glue_patch(&self, sup: &Type, sub: &Type) -> Option<&Context> {
for patch in self.all_patches().into_iter() {
if let ContextKind::GluePatch(tr_inst) = &patch.kind { if let ContextKind::GluePatch(tr_inst) = &patch.kind {
if self.subtype_of(sub, &tr_inst.sub_type) if self.subtype_of(sub, &tr_inst.sub_type)
&& self.subtype_of(&tr_inst.sup_trait, sup) && self.subtype_of(&tr_inst.sup_trait, sup)

View file

@ -246,7 +246,12 @@ impl Context {
_ => unreachable!(), _ => unreachable!(),
} }
} else { } else {
todo!() Err(EvalErrors::from(EvalError::not_const_expr(
self.cfg.input.clone(),
line!() as usize,
call.loc(),
self.caused_by(),
)))
} }
} }
@ -311,12 +316,15 @@ impl Context {
let elem = self.eval_const_expr(&elem.expr)?; let elem = self.eval_const_expr(&elem.expr)?;
elems.push(elem); elems.push(elem);
} }
Ok(ValueObj::Array(RcArray::from(elems)))
} }
_ => { _ => Err(EvalErrors::from(EvalError::not_const_expr(
todo!() self.cfg.input.clone(),
} line!() as usize,
arr.loc(),
self.caused_by(),
))),
} }
Ok(ValueObj::Array(RcArray::from(elems)))
} }
fn eval_const_set(&self, set: &AstSet) -> EvalResult<ValueObj> { fn eval_const_set(&self, set: &AstSet) -> EvalResult<ValueObj> {
@ -327,12 +335,15 @@ impl Context {
let elem = self.eval_const_expr(&elem.expr)?; let elem = self.eval_const_expr(&elem.expr)?;
elems.push(elem); elems.push(elem);
} }
Ok(ValueObj::Set(Set::from(elems)))
} }
_ => { _ => Err(EvalErrors::from(EvalError::not_const_expr(
todo!() self.cfg.input.clone(),
} line!() as usize,
set.loc(),
self.caused_by(),
))),
} }
Ok(ValueObj::Set(Set::from(elems)))
} }
fn eval_const_dict(&self, dict: &AstDict) -> EvalResult<ValueObj> { fn eval_const_dict(&self, dict: &AstDict) -> EvalResult<ValueObj> {
@ -344,12 +355,15 @@ impl Context {
let value = self.eval_const_expr(&elem.value)?; let value = self.eval_const_expr(&elem.value)?;
elems.insert(key, value); elems.insert(key, value);
} }
Ok(ValueObj::Dict(elems))
} }
_ => { _ => Err(EvalErrors::from(EvalError::not_const_expr(
todo!() self.cfg.input.clone(),
} line!() as usize,
dict.loc(),
self.caused_by(),
))),
} }
Ok(ValueObj::Dict(elems))
} }
fn eval_const_tuple(&self, tuple: &Tuple) -> EvalResult<ValueObj> { fn eval_const_tuple(&self, tuple: &Tuple) -> EvalResult<ValueObj> {

View file

@ -143,6 +143,35 @@ pub fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueOb
Ok(ValueObj::gen_t(GenTypeObj::trait_(t, require, impls))) Ok(ValueObj::gen_t(GenTypeObj::trait_(t, require, impls)))
} }
/// Base: Type, Impl := Type -> Patch
pub fn patch_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let base = args.remove_left_or_key("Base").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{REQ_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
let Some(base) = base.as_type() else {
let base = StyledString::new(&format!("{base}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-type object {base} 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::patch(t, base, impls)))
}
/// Super: TraitType, Impl := Type, Additional := Type -> TraitType /// Super: TraitType, Impl := Type, Additional := Type -> TraitType
pub fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> { pub fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let sup = args.remove_left_or_key("Super").ok_or_else(|| { let sup = args.remove_left_or_key("Super").ok_or_else(|| {

View file

@ -491,7 +491,7 @@ impl Context {
iterable.register_superclass(poly("Output", vec![ty_tp(T.clone())]), &output); iterable.register_superclass(poly("Output", vec![ty_tp(T.clone())]), &output);
let Slf = mono_q("Self", subtypeof(poly("Iterable", vec![ty_tp(T.clone())]))); let Slf = mono_q("Self", subtypeof(poly("Iterable", vec![ty_tp(T.clone())])));
let t = fn0_met(Slf.clone(), proj(Slf, "Iter")).quantify(); let t = fn0_met(Slf.clone(), proj(Slf, "Iter")).quantify();
iterable.register_builtin_decl("iter", t, Public); iterable.register_builtin_py_decl("iter", t, Public, Some("__iter__"));
iterable.register_builtin_decl("Iter", Type, Public); iterable.register_builtin_decl("Iter", Type, Public);
let R = mono_q("R", instanceof(Type)); let R = mono_q("R", instanceof(Type));
let params = vec![PS::t("R", WithDefault)]; let params = vec![PS::t("R", WithDefault)];
@ -772,7 +772,7 @@ impl Context {
int.register_marker_trait(mono("Num")); int.register_marker_trait(mono("Num"));
// class("Rational"), // class("Rational"),
// class("Integral"), // class("Integral"),
int.register_builtin_impl("abs", fn0_met(Int, Nat), Immutable, Public); int.register_builtin_py_impl("abs", fn0_met(Int, Nat), Immutable, Public, Some("__abs__"));
let mut int_ord = Self::builtin_methods(Some(mono("Ord")), 2); let mut int_ord = Self::builtin_methods(Some(mono("Ord")), 2);
int_ord.register_builtin_impl( int_ord.register_builtin_impl(
"__partial_cmp__", "__partial_cmp__",
@ -927,6 +927,20 @@ impl Context {
Immutable, Immutable,
Public, Public,
); );
str_.register_builtin_impl(
"lower",
fn_met(Str, vec![], None, vec![], Str),
Immutable,
Public,
);
str_.register_builtin_impl(
"upper",
fn_met(Str, vec![], None, vec![], Str),
Immutable,
Public,
);
let str_getitem_t = fn1_kw_met(Str, kw("idx", Nat), Str);
str_.register_builtin_impl("__getitem__", str_getitem_t, Immutable, Public);
let mut str_eq = Self::builtin_methods(Some(mono("Eq")), 2); let mut str_eq = Self::builtin_methods(Some(mono("Eq")), 2);
str_eq.register_builtin_impl("__eq__", fn1_met(Str, Str, Bool), Const, Public); str_eq.register_builtin_impl("__eq__", fn1_met(Str, Str, Bool), Const, Public);
str_.register_trait(Str, str_eq); str_.register_trait(Str, str_eq);
@ -949,11 +963,12 @@ impl Context {
str_show.register_builtin_impl("to_str", fn0_met(Str, Str), Immutable, Public); str_show.register_builtin_impl("to_str", fn0_met(Str, Str), Immutable, Public);
str_.register_trait(Str, str_show); str_.register_trait(Str, str_show);
let mut str_iterable = Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(Str)])), 2); let mut str_iterable = Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(Str)])), 2);
str_iterable.register_builtin_impl( str_iterable.register_builtin_py_impl(
"iter", "iter",
fn0_met(Str, mono("StrIterator")), fn0_met(Str, mono("StrIterator")),
Immutable, Immutable,
Public, Public,
Some("__iter__"),
); );
str_.register_trait(Str, str_iterable); str_.register_trait(Str, str_iterable);
/* NoneType */ /* NoneType */
@ -1149,12 +1164,12 @@ impl Context {
array_.register_trait(arr_t.clone(), array_show); array_.register_trait(arr_t.clone(), array_show);
let mut array_iterable = let mut array_iterable =
Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(T.clone())])), 2); Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(T.clone())])), 2);
array_iterable.register_builtin_impl( let t = fn0_met(
"iter", array_t(T.clone(), TyParam::erased(Nat)),
fn0_met(Str, mono("ArrayIterator")), poly("ArrayIterator", vec![ty_tp(T.clone())]),
Immutable, )
Public, .quantify();
); array_iterable.register_builtin_py_impl("iter", t, Immutable, Public, Some("__iter__"));
array_.register_trait(arr_t.clone(), array_iterable); array_.register_trait(arr_t.clone(), array_iterable);
/* Set */ /* Set */
let mut set_ = let mut set_ =
@ -1278,8 +1293,10 @@ impl Context {
str_iterator.register_superclass(Obj, &obj); str_iterator.register_superclass(Obj, &obj);
let mut array_iterator = Self::builtin_poly_class("ArrayIterator", vec![PS::t_nd("T")], 1); let mut array_iterator = Self::builtin_poly_class("ArrayIterator", vec![PS::t_nd("T")], 1);
array_iterator.register_superclass(Obj, &obj); array_iterator.register_superclass(Obj, &obj);
array_iterator.register_marker_trait(poly("Output", vec![ty_tp(T.clone())]));
let mut range_iterator = Self::builtin_poly_class("RangeIterator", vec![PS::t_nd("T")], 1); let mut range_iterator = Self::builtin_poly_class("RangeIterator", vec![PS::t_nd("T")], 1);
range_iterator.register_superclass(Obj, &obj); range_iterator.register_superclass(Obj, &obj);
range_iterator.register_marker_trait(poly("Output", vec![ty_tp(T.clone())]));
let mut obj_mut = Self::builtin_mono_class("Obj!", 2); let mut obj_mut = Self::builtin_mono_class("Obj!", 2);
obj_mut.register_superclass(Obj, &obj); obj_mut.register_superclass(Obj, &obj);
let mut obj_mut_mutable = Self::builtin_methods(Some(mono("Mutable")), 2); let mut obj_mut_mutable = Self::builtin_methods(Some(mono("Mutable")), 2);
@ -1526,11 +1543,12 @@ impl Context {
range.register_trait(range_t.clone(), range_eq); range.register_trait(range_t.clone(), range_eq);
let mut range_iterable = let mut range_iterable =
Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(T.clone())])), 2); Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(T.clone())])), 2);
range_iterable.register_builtin_impl( range_iterable.register_builtin_py_impl(
"iter", "iter",
fn0_met(Str, mono("RangeIterator")), fn0_met(Str, mono("RangeIterator")),
Immutable, Immutable,
Public, Public,
Some("__iter__"),
); );
range.register_trait(range_t.clone(), range_iterable); range.register_trait(range_t.clone(), range_iterable);
let range_getitem_t = fn1_kw_met(range_t.clone(), anon(T.clone()), T.clone()).quantify(); let range_getitem_t = fn1_kw_met(range_t.clone(), anon(T.clone()), T.clone()).quantify();
@ -1897,6 +1915,14 @@ impl Context {
// TODO: register Del function object // TODO: register Del function object
let t_del = nd_func(vec![kw("obj", Obj)], None, NoneType); let t_del = nd_func(vec![kw("obj", Obj)], None, NoneType);
self.register_builtin_impl("Del", t_del, Immutable, Private); self.register_builtin_impl("Del", t_del, Immutable, Private);
let patch_t = func(
vec![kw("Requirement", Type)],
None,
vec![kw("Impl", Type)],
TraitType,
);
let patch = ConstSubr::Builtin(BuiltinConstSubr::new("Patch", patch_func, patch_t, None));
self.register_builtin_const("Patch", Private, ValueObj::Subr(patch));
} }
fn init_builtin_procs(&mut self) { fn init_builtin_procs(&mut self) {

View file

@ -35,7 +35,7 @@ use crate::AccessKind;
use RegistrationMode::*; use RegistrationMode::*;
use Visibility::*; use Visibility::*;
use super::MethodInfo; use super::{ContextKind, MethodInfo};
impl Context { impl Context {
pub(crate) fn validate_var_sig_t( pub(crate) fn validate_var_sig_t(
@ -151,6 +151,7 @@ impl Context {
) -> SingleTyCheckResult<&Context> { ) -> SingleTyCheckResult<&Context> {
self.get_mod(ident.inspect()) self.get_mod(ident.inspect())
.or_else(|| self.rec_get_type(ident.inspect()).map(|(_, ctx)| ctx)) .or_else(|| self.rec_get_type(ident.inspect()).map(|(_, ctx)| ctx))
.or_else(|| self.rec_get_patch(ident.inspect()))
.ok_or_else(|| { .ok_or_else(|| {
TyCheckError::no_var_error( TyCheckError::no_var_error(
self.cfg.input.clone(), self.cfg.input.clone(),
@ -468,6 +469,26 @@ impl Context {
return Err(e); return Err(e);
} }
} }
for patch in self.find_patches_of(obj.ref_t()) {
if let Some(vi) = patch
.locals
.get(ident.inspect())
.or_else(|| patch.decls.get(ident.inspect()))
{
self.validate_visibility(ident, vi, input, namespace)?;
return Ok(vi.clone());
}
for (_, methods_ctx) in patch.methods_list.iter() {
if let Some(vi) = methods_ctx
.locals
.get(ident.inspect())
.or_else(|| methods_ctx.decls.get(ident.inspect()))
{
self.validate_visibility(ident, vi, input, namespace)?;
return Ok(vi.clone());
}
}
}
// TODO: dependent type widening // TODO: dependent type widening
if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) { if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
parent.rec_get_attr_info(obj, ident, input, namespace) parent.rec_get_attr_info(obj, ident, input, namespace)
@ -607,7 +628,10 @@ impl Context {
line!() as usize, line!() as usize,
)) ))
} }
other => todo!("{other}"), _other => Err(TyCheckError::dummy(
self.cfg.input.clone(),
line!() as usize,
)),
} }
} else { } else {
Err(TyCheckError::dummy( Err(TyCheckError::dummy(
@ -701,7 +725,26 @@ impl Context {
} }
_ => {} _ => {}
} }
// TODO: patch for patch in self.find_patches_of(obj.ref_t()) {
if let Some(vi) = patch
.locals
.get(attr_name.inspect())
.or_else(|| patch.decls.get(attr_name.inspect()))
{
self.validate_visibility(attr_name, vi, input, namespace)?;
return Ok(vi.clone());
}
for (_, methods_ctx) in patch.methods_list.iter() {
if let Some(vi) = methods_ctx
.locals
.get(attr_name.inspect())
.or_else(|| methods_ctx.decls.get(attr_name.inspect()))
{
self.validate_visibility(attr_name, vi, input, namespace)?;
return Ok(vi.clone());
}
}
}
Err(TyCheckError::no_attr_error( Err(TyCheckError::no_attr_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,
@ -741,6 +784,7 @@ impl Context {
&& &self.name[..] != namespace && &self.name[..] != namespace
&& !namespace.contains(&self.name[..]) && !namespace.contains(&self.name[..])
{ {
log!(err "{namespace}/{}", self.name);
Err(TyCheckError::visibility_error( Err(TyCheckError::visibility_error(
input.clone(), input.clone(),
line!() as usize, line!() as usize,
@ -1879,19 +1923,9 @@ impl Context {
} }
} }
pub(crate) fn _rec_get_patch(&self, name: &VarName) -> Option<&Context> { pub(crate) fn all_patches(&self) -> Vec<&Context> {
if let Some(patch) = self.patches.get(name) {
Some(patch)
} else if let Some(outer) = self.get_outer().or_else(|| self.get_builtins()) {
outer._rec_get_patch(name)
} else {
None
}
}
pub(crate) fn _all_patches(&self) -> Vec<&Context> {
if let Some(outer) = self.get_outer().or_else(|| self.get_builtins()) { if let Some(outer) = self.get_outer().or_else(|| self.get_builtins()) {
[outer._all_patches(), self.patches.values().collect()].concat() [outer.all_patches(), self.patches.values().collect()].concat()
} else { } else {
self.patches.values().collect() self.patches.values().collect()
} }
@ -1984,15 +2018,9 @@ impl Context {
// TODO: poly type // TODO: poly type
pub(crate) fn rec_get_self_t(&self) -> Option<Type> { pub(crate) fn rec_get_self_t(&self) -> Option<Type> {
if self.kind.is_method_def() || self.kind.is_type() { if self.kind.is_method_def() || self.kind.is_type() {
// let name = self.name.split(&[':', '.']).last().unwrap();
/*if let Some((t, _)) = self.rec_get_type(name) {
log!("{t}");
Some(t.clone())
} else {
log!("none");
None
}*/
Some(mono(self.name.clone())) Some(mono(self.name.clone()))
} else if let ContextKind::PatchMethodDefs(t) = &self.kind {
Some(t.clone())
} else if let Some(outer) = self.get_outer() { } else if let Some(outer) = self.get_outer() {
outer.rec_get_self_t() outer.rec_get_self_t()
} else { } else {
@ -2063,6 +2091,16 @@ impl Context {
} }
} }
pub(crate) fn rec_get_patch(&self, name: &str) -> Option<&Context> {
if let Some(ctx) = self.patches.get(name) {
Some(ctx)
} else if let Some(outer) = self.get_outer().or_else(|| self.get_builtins()) {
outer.rec_get_patch(name)
} else {
None
}
}
fn get_method_type_by_name(&self, name: &Identifier) -> SingleTyCheckResult<&MethodInfo> { fn get_method_type_by_name(&self, name: &Identifier) -> SingleTyCheckResult<&MethodInfo> {
// TODO: min_by // TODO: min_by
if let Some(candidates) = self.method_to_traits.get(name.inspect()) { if let Some(candidates) = self.method_to_traits.get(name.inspect()) {

View file

@ -232,6 +232,7 @@ pub enum ContextKind {
Proc, Proc,
Class, Class,
MethodDefs(Option<Type>), // Type: trait implemented MethodDefs(Option<Type>), // Type: trait implemented
PatchMethodDefs(Type),
Trait, Trait,
StructuralTrait, StructuralTrait,
Patch(Type), Patch(Type),
@ -270,6 +271,10 @@ impl ContextKind {
pub fn is_trait(&self) -> bool { pub fn is_trait(&self) -> bool {
matches!(self, Self::Trait | Self::StructuralTrait) matches!(self, Self::Trait | Self::StructuralTrait)
} }
pub fn is_patch(&self) -> bool {
matches!(self, Self::Patch(_) | Self::GluePatch(_))
}
} }
/// 記号表に登録されているモードを表す /// 記号表に登録されているモードを表す
@ -682,6 +687,31 @@ impl Context {
) )
} }
#[allow(clippy::too_many_arguments)]
pub fn poly_patch<S: Into<Str>>(
name: S,
base: Type,
params: Vec<ParamSpec>,
cfg: ErgConfig,
mod_cache: Option<SharedModuleCache>,
py_mod_cache: Option<SharedModuleCache>,
capacity: usize,
level: usize,
) -> Self {
let name = name.into();
Self::poly(
name,
cfg,
ContextKind::Patch(base),
params,
None,
mod_cache,
py_mod_cache,
capacity,
level,
)
}
#[inline] #[inline]
pub fn mono_trait<S: Into<Str>>( pub fn mono_trait<S: Into<Str>>(
name: S, name: S,
@ -730,6 +760,28 @@ impl Context {
) )
} }
#[inline]
pub fn mono_patch<S: Into<Str>>(
name: S,
base: Type,
cfg: ErgConfig,
mod_cache: Option<SharedModuleCache>,
py_mod_cache: Option<SharedModuleCache>,
capacity: usize,
level: usize,
) -> Self {
Self::poly_patch(
name,
base,
vec![],
cfg,
mod_cache,
py_mod_cache,
capacity,
level,
)
}
#[inline] #[inline]
pub fn methods( pub fn methods(
impl_trait: Option<Type>, impl_trait: Option<Type>,

View file

@ -81,6 +81,11 @@ impl Context {
let vis = ident.vis(); let vis = ident.vis();
let kind = id.map_or(VarKind::Declared, VarKind::Defined); 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)?; let sig_t = self.instantiate_var_sig_t(sig.t_spec.as_ref(), opt_t, PreRegister)?;
let py_name = if let ContextKind::PatchMethodDefs(_base) = &self.kind {
Some(Str::from(format!("::{}{}", self.name, ident)))
} else {
None
};
if let Some(_decl) = self.decls.remove(&ident.name) { if let Some(_decl) = self.decls.remove(&ident.name) {
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error( Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
self.cfg.input.clone(), self.cfg.input.clone(),
@ -90,10 +95,8 @@ impl Context {
ident.name.inspect(), ident.name.inspect(),
))) )))
} else { } else {
self.future_defined_locals.insert( let vi = VarInfo::new(sig_t, muty, vis, kind, None, self.impl_of(), py_name);
ident.name.clone(), self.future_defined_locals.insert(ident.name.clone(), vi);
VarInfo::new(sig_t, muty, vis, kind, None, self.impl_of(), None),
);
Ok(()) Ok(())
} }
} }
@ -123,6 +126,11 @@ impl Context {
Ok(t) => (TyCheckErrors::empty(), t), Ok(t) => (TyCheckErrors::empty(), t),
Err((errs, t)) => (errs, t), Err((errs, t)) => (errs, t),
}; };
let py_name = if let ContextKind::PatchMethodDefs(_base) = &self.kind {
Some(Str::from(format!("::{}{}", self.name, sig.ident)))
} else {
None
};
let vi = VarInfo::new( let vi = VarInfo::new(
t, t,
muty, muty,
@ -130,7 +138,7 @@ impl Context {
kind, kind,
Some(comptime_decos), Some(comptime_decos),
self.impl_of(), self.impl_of(),
None, py_name,
); );
if let Some(_decl) = self.decls.remove(name) { if let Some(_decl) = self.decls.remove(name) {
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error( Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
@ -182,8 +190,15 @@ impl Context {
} }
self.validate_var_sig_t(ident, sig.t_spec.as_ref(), body_t, Normal)?; self.validate_var_sig_t(ident, sig.t_spec.as_ref(), body_t, Normal)?;
let muty = Mutability::from(&ident.inspect()[..]); let muty = Mutability::from(&ident.inspect()[..]);
self.decls.remove(ident.inspect()); let py_name = if let Some(vi) = self
self.future_defined_locals.remove(ident.inspect()); .decls
.remove(ident.inspect())
.or_else(|| self.future_defined_locals.remove(ident.inspect()))
{
vi.py_name
} else {
py_name
};
let vis = ident.vis(); let vis = ident.vis();
let vi = VarInfo::new( let vi = VarInfo::new(
body_t.clone(), body_t.clone(),
@ -456,7 +471,7 @@ impl Context {
}; };
sub_t.lift(); sub_t.lift();
let found_t = self.generalize_t(sub_t); let found_t = self.generalize_t(sub_t);
if let Some(vi) = self.decls.remove(name) { let py_name = if let Some(vi) = self.decls.remove(name) {
if !self.supertype_of(&vi.t, &found_t) { if !self.supertype_of(&vi.t, &found_t) {
return Err(TyCheckErrors::from(TyCheckError::violate_decl_error( return Err(TyCheckErrors::from(TyCheckError::violate_decl_error(
self.cfg.input.clone(), self.cfg.input.clone(),
@ -468,7 +483,10 @@ impl Context {
&found_t, &found_t,
))); )));
} }
} vi.py_name
} else {
None
};
let comptime_decos = decorators let comptime_decos = decorators
.iter() .iter()
.filter_map(|deco| match &deco.0 { .filter_map(|deco| match &deco.0 {
@ -485,7 +503,7 @@ impl Context {
VarKind::Defined(id), VarKind::Defined(id),
Some(comptime_decos), Some(comptime_decos),
self.impl_of(), self.impl_of(),
None, py_name,
); );
let t = vi.t.clone(); let t = vi.t.clone();
log!(info "Registered {}::{name}: {t}", self.name); log!(info "Registered {}::{name}: {t}", self.name);
@ -548,6 +566,11 @@ impl Context {
total_errs.extend(errs.into_iter()); total_errs.extend(errs.into_iter());
} }
} }
ast::Expr::PatchDef(patch_def) => {
if let Err(errs) = self.preregister_def(&patch_def.def) {
total_errs.extend(errs.into_iter());
}
}
_ => {} _ => {}
} }
} }
@ -950,6 +973,23 @@ impl Context {
todo!("polymorphic type definition is not supported yet"); todo!("polymorphic type definition is not supported yet");
} }
} }
GenTypeObj::Patch(_) => {
if gen.typ().is_monomorphic() {
let base = enum_unwrap!(gen.require_or_sup().unwrap(), TypeObj::Builtin);
let ctx = Self::mono_patch(
gen.typ().qual_name(),
base.clone(),
self.cfg.clone(),
self.mod_cache.clone(),
self.py_mod_cache.clone(),
2,
self.level,
);
self.register_gen_mono_patch(ident, gen, ctx, Const);
} else {
todo!("polymorphic patch definition is not supported yet");
}
}
other => todo!("{other:?}"), other => todo!("{other:?}"),
} }
} }
@ -1046,6 +1086,72 @@ impl Context {
} }
} }
fn register_gen_mono_patch(
&mut self,
ident: &Identifier,
gen: GenTypeObj,
ctx: Self,
muty: Mutability,
) {
// FIXME: not panic but error
// FIXME: recursive search
if self.patches.contains_key(ident.inspect()) {
panic!("{ident} has already been registered");
} else if self.rec_get_const_obj(ident.inspect()).is_some() && ident.vis().is_private() {
panic!("{ident} has already been registered as const");
} else {
let t = gen.typ().clone();
let meta_t = gen.meta_type();
let name = &ident.name;
let id = DefId(get_hash(&(&self.name, &name)));
self.decls.insert(
name.clone(),
VarInfo::new(
meta_t,
muty,
ident.vis(),
VarKind::Defined(id),
None,
self.impl_of(),
None,
),
);
self.consts
.insert(name.clone(), ValueObj::Type(TypeObj::Generated(gen)));
for impl_trait in ctx.super_traits.iter() {
if let Some(impls) = self.trait_impls.get_mut(&impl_trait.qual_name()) {
impls.insert(TypeRelationInstance::new(t.clone(), impl_trait.clone()));
} else {
self.trait_impls.insert(
impl_trait.qual_name(),
set![TypeRelationInstance::new(t.clone(), impl_trait.clone())],
);
}
}
for (trait_method, vi) in ctx.decls.iter() {
if let Some(types) = self.method_to_traits.get_mut(trait_method.inspect()) {
types.push(MethodInfo::new(t.clone(), vi.clone()));
} else {
self.method_to_traits.insert(
trait_method.inspect().clone(),
vec![MethodInfo::new(t.clone(), vi.clone())],
);
}
}
for (class_method, vi) in ctx.locals.iter() {
if let Some(types) = self.method_to_classes.get_mut(class_method.inspect()) {
types.push(MethodInfo::new(t.clone(), vi.clone()));
} else {
self.method_to_classes.insert(
class_method.inspect().clone(),
vec![MethodInfo::new(t.clone(), vi.clone())],
);
}
}
self.patches.insert(name.clone(), ctx);
}
}
pub(crate) fn import_mod( pub(crate) fn import_mod(
&mut self, &mut self,
kind: OperationKind, kind: OperationKind,

View file

@ -358,6 +358,7 @@ impl Context {
Variance::Covariant => Ok(sub_t), Variance::Covariant => Ok(sub_t),
Variance::Contravariant => Ok(super_t), Variance::Contravariant => Ok(super_t),
Variance::Invariant => { Variance::Invariant => {
// need to check if sub_t == super_t
if self.supertype_of(&sub_t, &super_t) { if self.supertype_of(&sub_t, &super_t) {
Ok(sub_t) Ok(sub_t)
} else { } else {
@ -772,6 +773,12 @@ impl Context {
} }
Ok(()) Ok(())
} }
hir::Expr::PatchDef(patch_def) => {
for def in patch_def.methods.iter_mut() {
self.resolve_expr_t(def)?;
}
Ok(())
}
hir::Expr::AttrDef(attr_def) => { hir::Expr::AttrDef(attr_def) => {
// REVIEW: attr_def.attr is not dereferenced // REVIEW: attr_def.attr is not dereferenced
for chunk in attr_def.block.iter_mut() { for chunk in attr_def.block.iter_mut() {

View file

@ -95,11 +95,19 @@ 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());
// 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);
} }
} }
Expr::PatchDef(patch_def) => {
self.check_expr(patch_def.base.as_ref());
// TODO: grow
for def in patch_def.methods.iter() {
self.check_expr(def);
}
}
Expr::Call(call) => { Expr::Call(call) => {
for parg in call.args.pos_args.iter() { for parg in call.args.pos_args.iter() {
self.check_expr(&parg.expr); self.check_expr(&parg.expr);
@ -271,10 +279,17 @@ 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());
for def in class_def.methods.iter() { for def in class_def.methods.iter() {
self.check_expr(def); self.check_expr(def);
} }
} }
Expr::PatchDef(patch_def) => {
self.check_expr(patch_def.base.as_ref());
for def in patch_def.methods.iter() {
self.check_expr(def);
}
}
Expr::Array(array) => match array { Expr::Array(array) => match array {
Array::Normal(arr) => { Array::Normal(arr) => {
for elem in arr.elems.pos_args.iter() { for elem in arr.elems.pos_args.iter() {

View file

@ -442,8 +442,12 @@ impl Identifier {
} }
/// show dot + name (no qual_name & type) /// show dot + name (no qual_name & type)
pub fn show(&self) -> String { pub fn to_string_without_type(&self) -> String {
format!("{}{}", fmt_option!(self.dot), self.name) if self.dot.is_some() {
format!(".{}", self.name)
} else {
format!("::{}", self.name)
}
} }
pub fn is_procedural(&self) -> bool { pub fn is_procedural(&self) -> bool {
@ -1410,7 +1414,7 @@ impl NestedDisplay for SubrSignature {
write!( write!(
f, f,
"{}{} (: {})", "{}{} (: {})",
self.ident.show(), self.ident.to_string_without_type(),
self.params, self.params,
self.ident.t() self.ident.t()
) )
@ -1781,6 +1785,54 @@ impl ClassDef {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PatchDef {
pub sig: Signature,
pub base: Box<Expr>,
pub methods: Block,
}
impl NestedDisplay for PatchDef {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
write!(f, "{} = Patch ", self.sig)?;
self.base.fmt_nest(f, level)?;
writeln!(f, ":")?;
self.methods.fmt_nest(f, level + 1)
}
}
impl_display_from_nested!(PatchDef);
impl_locational!(PatchDef, sig);
impl HasType for PatchDef {
#[inline]
fn ref_t(&self) -> &Type {
Type::NONE
}
#[inline]
fn ref_mut_t(&mut self) -> &mut Type {
todo!()
}
#[inline]
fn signature_t(&self) -> Option<&Type> {
None
}
#[inline]
fn signature_mut_t(&mut self) -> Option<&mut Type> {
None
}
}
impl PatchDef {
pub fn new(sig: Signature, base: Expr, methods: Block) -> Self {
Self {
sig,
base: Box::new(base),
methods,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AttrDef { pub struct AttrDef {
pub attr: Accessor, pub attr: Accessor,
@ -1881,6 +1933,7 @@ pub enum Expr {
Lambda(Lambda), Lambda(Lambda),
Def(Def), Def(Def),
ClassDef(ClassDef), ClassDef(ClassDef),
PatchDef(PatchDef),
AttrDef(AttrDef), AttrDef(AttrDef),
TypeAsc(TypeAscription), TypeAsc(TypeAscription),
Code(Block), // code object Code(Block), // code object
@ -1888,10 +1941,10 @@ pub enum Expr {
Import(Accessor), Import(Accessor),
} }
impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import); impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, PatchDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
impl_display_from_nested!(Expr); impl_display_from_nested!(Expr);
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import); impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, PatchDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
impl_t_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import); impl_t_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, PatchDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
impl Default for Expr { impl Default for Expr {
fn default() -> Self { fn default() -> Self {

View file

@ -139,6 +139,11 @@ impl<'a> Linker<'a> {
self.resolve_pymod_path(def); self.resolve_pymod_path(def);
} }
} }
Expr::PatchDef(patch_def) => {
for def in patch_def.methods.iter_mut() {
self.resolve_pymod_path(def);
}
}
Expr::AttrDef(attr_def) => { Expr::AttrDef(attr_def) => {
// REVIEW: // REVIEW:
for chunk in attr_def.block.iter_mut() { for chunk in attr_def.block.iter_mut() {
@ -255,6 +260,11 @@ impl<'a> Linker<'a> {
self.replace_import(def); self.replace_import(def);
} }
} }
Expr::PatchDef(patch_def) => {
for def in patch_def.methods.iter_mut() {
self.replace_import(def);
}
}
Expr::AttrDef(attr_def) => { Expr::AttrDef(attr_def) => {
// REVIEW: // REVIEW:
for chunk in attr_def.block.iter_mut() { for chunk in attr_def.block.iter_mut() {

View file

@ -220,8 +220,15 @@ impl ASTLowerer {
} }
} }
} }
hir::Expr::ClassDef(cl_def) => { hir::Expr::ClassDef(class_def) => {
for chunk in cl_def.methods.iter() { for chunk in class_def.methods.iter() {
if let Err(ws) = self.use_check(chunk, mode) {
warns.extend(ws);
}
}
}
hir::Expr::PatchDef(patch_def) => {
for chunk in patch_def.methods.iter() {
if let Err(ws) = self.use_check(chunk, mode) { if let Err(ws) = self.use_check(chunk, mode) {
warns.extend(ws); warns.extend(ws);
} }
@ -1287,7 +1294,7 @@ impl ASTLowerer {
} else { } else {
todo!() todo!()
}; };
let require_or_sup = self.get_require_or_sup(hir_def.body.block.remove(0)); let require_or_sup = self.get_require_or_sup_or_base(hir_def.body.block.remove(0));
Ok(hir::ClassDef::new( Ok(hir::ClassDef::new(
type_obj.clone(), type_obj.clone(),
hir_def.sig, hir_def.sig,
@ -1298,6 +1305,71 @@ impl ASTLowerer {
)) ))
} }
fn lower_patch_def(&mut self, class_def: ast::PatchDef) -> LowerResult<hir::PatchDef> {
log!(info "entered {}({class_def})", fn_name!());
let base_t = {
let base_t_expr =
enum_unwrap!(class_def.def.body.block.get(0).unwrap(), ast::Expr::Call)
.args
.get_left_or_key("Base")
.unwrap();
let spec = Parser::expr_to_type_spec(base_t_expr.clone()).unwrap();
let mut dummy_tv_cache = TyVarCache::new(self.ctx.level, &self.ctx);
self.ctx.instantiate_typespec(
&spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?
};
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 mut hir_methods = hir::Block::empty();
for mut methods in class_def.methods_list.into_iter() {
let kind = ContextKind::PatchMethodDefs(base_t.clone());
self.ctx
.grow(hir_def.sig.ident().inspect(), kind, hir_def.sig.vis(), None);
for attr in methods.attrs.iter_mut() {
match attr {
ast::ClassAttr::Def(def) => {
if methods.vis.is(TokenKind::Dot) {
def.sig.ident_mut().unwrap().dot = Some(Token::new(
TokenKind::Dot,
".",
def.sig.ln_begin().unwrap(),
def.sig.col_begin().unwrap(),
));
}
self.ctx.preregister_def(def)?;
}
ast::ClassAttr::Decl(_decl) => {}
}
}
for attr in methods.attrs.into_iter() {
match attr {
ast::ClassAttr::Def(def) => match self.lower_def(def) {
Ok(def) => {
hir_methods.push(hir::Expr::Def(def));
}
Err(errs) => {
self.errs.extend(errs);
}
},
ast::ClassAttr::Decl(decl) => {
let decl = self.lower_type_asc(decl)?;
hir_methods.push(hir::Expr::TypeAsc(decl));
}
}
}
if let Err(mut errs) = self.ctx.check_decls() {
self.errs.append(&mut errs);
}
self.push_patch();
}
Ok(hir::PatchDef::new(hir_def.sig, base, hir_methods))
}
fn register_trait_impl( fn register_trait_impl(
&mut self, &mut self,
class: &Type, class: &Type,
@ -1561,16 +1633,56 @@ impl ASTLowerer {
.push((ClassDefType::Simple(class), methods)); .push((ClassDefType::Simple(class), methods));
} }
fn push_patch(&mut self) {
let methods = self.ctx.pop();
let ContextKind::PatchMethodDefs(base) = &methods.kind else { unreachable!() };
let patch_name = *methods.name.split_with(&["::", "."]).last().unwrap();
let patch_root = self
.ctx
.patches
.get_mut(patch_name)
.unwrap_or_else(|| todo!("{} not found", methods.name));
for (newly_defined_name, vi) in methods.locals.clone().into_iter() {
for (_, already_defined_methods) in patch_root.methods_list.iter_mut() {
// TODO: 特殊化なら同じ名前でもOK
// TODO: 定義のメソッドもエラー表示
if let Some((_already_defined_name, already_defined_vi)) =
already_defined_methods.get_local_kv(newly_defined_name.inspect())
{
if already_defined_vi.kind != VarKind::Auto
&& already_defined_vi.impl_of == vi.impl_of
{
self.errs.push(LowerError::duplicate_definition_error(
self.cfg.input.clone(),
line!() as usize,
newly_defined_name.loc(),
methods.caused_by(),
newly_defined_name.inspect(),
));
} else {
already_defined_methods
.locals
.remove(&newly_defined_name.inspect()[..]);
}
}
}
}
patch_root
.methods_list
.push((ClassDefType::Simple(base.clone()), methods));
}
#[allow(clippy::only_used_in_recursion)] #[allow(clippy::only_used_in_recursion)]
fn get_require_or_sup(&self, expr: hir::Expr) -> hir::Expr { fn get_require_or_sup_or_base(&self, expr: hir::Expr) -> hir::Expr {
match expr { match expr {
acc @ hir::Expr::Accessor(_) => acc, acc @ hir::Expr::Accessor(_) => 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").unwrap(),
Some("Inherit") => call.args.remove_left_or_key("Super").unwrap(), Some("Inherit") => call.args.remove_left_or_key("Super").unwrap(),
Some("Inheritable") => { Some("Inheritable") => {
self.get_require_or_sup(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(),
_ => todo!(), _ => todo!(),
}, },
other => todo!("{other}"), other => todo!("{other}"),
@ -1637,6 +1749,7 @@ impl ASTLowerer {
ast::Expr::Lambda(lambda) => Ok(hir::Expr::Lambda(self.lower_lambda(lambda)?)), ast::Expr::Lambda(lambda) => Ok(hir::Expr::Lambda(self.lower_lambda(lambda)?)),
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.lower_def(def)?)), ast::Expr::Def(def) => Ok(hir::Expr::Def(self.lower_def(def)?)),
ast::Expr::ClassDef(defs) => Ok(hir::Expr::ClassDef(self.lower_class_def(defs)?)), ast::Expr::ClassDef(defs) => Ok(hir::Expr::ClassDef(self.lower_class_def(defs)?)),
ast::Expr::PatchDef(defs) => Ok(hir::Expr::PatchDef(self.lower_patch_def(defs)?)),
ast::Expr::TypeAsc(tasc) => Ok(hir::Expr::TypeAsc(self.lower_type_asc(tasc)?)), ast::Expr::TypeAsc(tasc) => Ok(hir::Expr::TypeAsc(self.lower_type_asc(tasc)?)),
other => todo!("{other}"), other => todo!("{other}"),
} }

View file

@ -126,6 +126,18 @@ impl OwnershipChecker {
self.check_block(&def.body.block); self.check_block(&def.body.block);
self.path_stack.pop(); self.path_stack.pop();
} }
Expr::ClassDef(class_def) => {
self.check_expr(&class_def.require_or_sup, Ownership::Owned, false);
for def in class_def.methods.iter() {
self.check_expr(def, Ownership::Owned, true);
}
}
Expr::PatchDef(patch_def) => {
self.check_expr(&patch_def.base, Ownership::Owned, false);
for def in patch_def.methods.iter() {
self.check_expr(def, Ownership::Owned, true);
}
}
// Access in chunks does not drop variables (e.g., access in REPL) // Access in chunks does not drop variables (e.g., access in REPL)
Expr::Accessor(acc) => self.check_acc(acc, ownership, chunk), Expr::Accessor(acc) => self.check_acc(acc, ownership, chunk),
// TODO: referenced // TODO: referenced

View file

@ -4,7 +4,7 @@ use erg_common::log;
use erg_common::traits::{Locational, Stream}; use erg_common::traits::{Locational, Stream};
use erg_common::Str; use erg_common::Str;
use erg_parser::ast::{ClassDef, Expr, Methods, Module, PreDeclTypeSpec, TypeSpec, AST}; use erg_parser::ast::{ClassDef, Expr, Methods, Module, PatchDef, PreDeclTypeSpec, TypeSpec, AST};
use crate::error::{TyCheckError, TyCheckErrors}; use crate::error::{TyCheckError, TyCheckErrors};
@ -46,6 +46,14 @@ impl Reorderer {
let type_def = ClassDef::new(def, vec![]); let type_def = ClassDef::new(def, vec![]);
new.push(Expr::ClassDef(type_def)); new.push(Expr::ClassDef(type_def));
} }
Some("Patch") => {
self.def_root_pos_map.insert(
def.sig.ident().unwrap().inspect().clone(),
new.len(),
);
let type_def = PatchDef::new(def, vec![]);
new.push(Expr::PatchDef(type_def));
}
_ => { _ => {
new.push(Expr::Def(def)); new.push(Expr::Def(def));
} }
@ -97,12 +105,17 @@ impl Reorderer {
fn link_methods(&mut self, name: Str, new: &mut Vec<Expr>, methods: Methods) { fn link_methods(&mut self, name: Str, new: &mut Vec<Expr>, methods: Methods) {
if let Some(pos) = self.def_root_pos_map.get(&name) { if let Some(pos) = self.def_root_pos_map.get(&name) {
let mut class_def = match new.remove(*pos) { match new.remove(*pos) {
Expr::ClassDef(class_def) => class_def, Expr::ClassDef(mut class_def) => {
class_def.methods_list.push(methods);
new.insert(*pos, Expr::ClassDef(class_def));
}
Expr::PatchDef(mut patch_def) => {
patch_def.methods_list.push(methods);
new.insert(*pos, Expr::PatchDef(patch_def));
}
_ => unreachable!(), _ => unreachable!(),
}; }
class_def.methods_list.push(methods);
new.insert(*pos, Expr::ClassDef(class_def));
} else { } else {
let similar_name = self let similar_name = self
.def_root_pos_map .def_root_pos_map

View file

@ -130,6 +130,23 @@ impl UnionTypeObj {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PatchObj {
pub t: Type,
pub base: Box<TypeObj>,
pub impls: Option<Box<TypeObj>>,
}
impl PatchObj {
pub fn new(t: Type, base: TypeObj, impls: Option<TypeObj>) -> Self {
Self {
t,
base: Box::new(base),
impls: impls.map(Box::new),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum GenTypeObj { pub enum GenTypeObj {
Class(ClassTypeObj), Class(ClassTypeObj),
@ -138,6 +155,7 @@ pub enum GenTypeObj {
Subtrait(SubsumedTypeObj), Subtrait(SubsumedTypeObj),
StructuralTrait(TraitTypeObj), StructuralTrait(TraitTypeObj),
Union(UnionTypeObj), Union(UnionTypeObj),
Patch(PatchObj),
} }
impl fmt::Display for GenTypeObj { impl fmt::Display for GenTypeObj {
@ -164,6 +182,10 @@ impl GenTypeObj {
GenTypeObj::Trait(TraitTypeObj::new(t, require, impls)) GenTypeObj::Trait(TraitTypeObj::new(t, require, impls))
} }
pub fn patch(t: Type, base: TypeObj, impls: Option<TypeObj>) -> Self {
GenTypeObj::Patch(PatchObj::new(t, base, impls))
}
pub fn subsumed( pub fn subsumed(
t: Type, t: Type,
sup: TypeObj, sup: TypeObj,
@ -184,6 +206,7 @@ impl GenTypeObj {
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()),
Self::StructuralTrait(trait_) => Some(trait_.require.as_ref()), Self::StructuralTrait(trait_) => Some(trait_.require.as_ref()),
Self::Patch(patch) => Some(patch.base.as_ref()),
_ => None, _ => None,
} }
} }
@ -193,6 +216,7 @@ impl GenTypeObj {
Self::Class(class) => class.impls.as_ref().map(|x| x.as_ref()), Self::Class(class) => class.impls.as_ref().map(|x| x.as_ref()),
Self::Subclass(subclass) => subclass.impls.as_ref().map(|x| x.as_ref()), Self::Subclass(subclass) => subclass.impls.as_ref().map(|x| x.as_ref()),
Self::Subtrait(subtrait) => subtrait.impls.as_ref().map(|x| x.as_ref()), Self::Subtrait(subtrait) => subtrait.impls.as_ref().map(|x| x.as_ref()),
Self::Patch(patch) => patch.impls.as_ref().map(|x| x.as_ref()),
_ => None, _ => None,
} }
} }
@ -202,6 +226,7 @@ impl GenTypeObj {
Self::Class(class) => Some(&mut class.impls), Self::Class(class) => Some(&mut class.impls),
Self::Subclass(subclass) => Some(&mut subclass.impls), Self::Subclass(subclass) => Some(&mut subclass.impls),
Self::Subtrait(subtrait) => Some(&mut subtrait.impls), Self::Subtrait(subtrait) => Some(&mut subtrait.impls),
Self::Patch(patch) => Some(&mut patch.impls),
_ => None, _ => None,
} }
} }
@ -218,6 +243,7 @@ impl GenTypeObj {
match self { match self {
Self::Class(_) | Self::Subclass(_) => Type::ClassType, Self::Class(_) | Self::Subclass(_) => Type::ClassType,
Self::Trait(_) | Self::Subtrait(_) | Self::StructuralTrait(_) => Type::TraitType, Self::Trait(_) | Self::Subtrait(_) | Self::StructuralTrait(_) => Type::TraitType,
Self::Patch(_) => Type::Patch,
_ => Type::Type, _ => Type::Type,
} }
} }
@ -230,6 +256,7 @@ impl GenTypeObj {
Self::Subtrait(subtrait) => &subtrait.t, Self::Subtrait(subtrait) => &subtrait.t,
Self::StructuralTrait(trait_) => &trait_.t, Self::StructuralTrait(trait_) => &trait_.t,
Self::Union(union_) => &union_.t, Self::Union(union_) => &union_.t,
Self::Patch(patch) => &patch.t,
} }
} }
@ -241,6 +268,7 @@ impl GenTypeObj {
Self::Subtrait(subtrait) => &mut subtrait.t, Self::Subtrait(subtrait) => &mut subtrait.t,
Self::StructuralTrait(trait_) => &mut trait_.t, Self::StructuralTrait(trait_) => &mut trait_.t,
Self::Union(union_) => &mut union_.t, Self::Union(union_) => &mut union_.t,
Self::Patch(patch) => &mut patch.t,
} }
} }
@ -252,6 +280,7 @@ impl GenTypeObj {
Self::Subtrait(subtrait) => subtrait.t, Self::Subtrait(subtrait) => subtrait.t,
Self::StructuralTrait(trait_) => trait_.t, Self::StructuralTrait(trait_) => trait_.t,
Self::Union(union_) => union_.t, Self::Union(union_) => union_.t,
Self::Patch(patch) => patch.t,
} }
} }
} }

View file

@ -3412,6 +3412,36 @@ impl ClassDef {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PatchDef {
pub def: Def,
pub methods_list: Vec<Methods>,
}
impl NestedDisplay for PatchDef {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
write!(f, "(patch)")?;
self.def.fmt_nest(f, level)?;
for methods in self.methods_list.iter() {
write!(f, "(methods)")?;
methods.fmt_nest(f, level + 1)?;
}
Ok(())
}
}
impl_display_from_nested!(PatchDef);
impl_locational!(PatchDef, def);
impl PatchDef {
pub const fn new(def: Def, methods: Vec<Methods>) -> Self {
Self {
def,
methods_list: methods,
}
}
}
/// Expression(式) /// Expression(式)
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr { pub enum Expr {
@ -3431,11 +3461,12 @@ pub enum Expr {
Def(Def), Def(Def),
Methods(Methods), Methods(Methods),
ClassDef(ClassDef), ClassDef(ClassDef),
PatchDef(PatchDef),
} }
impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAsc, Def, Methods, ClassDef); impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAsc, Def, Methods, ClassDef, PatchDef);
impl_display_from_nested!(Expr); impl_display_from_nested!(Expr);
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAsc, Def, Methods, ClassDef); impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAsc, Def, Methods, ClassDef, PatchDef);
impl Expr { impl Expr {
pub fn is_match_call(&self) -> bool { pub fn is_match_call(&self) -> bool {
@ -3451,7 +3482,10 @@ impl Expr {
} }
pub const fn is_definition(&self) -> bool { pub const fn is_definition(&self) -> bool {
matches!(self, Expr::Def(_) | Expr::ClassDef(_) | Expr::Methods(_)) matches!(
self,
Expr::Def(_) | Expr::ClassDef(_) | Expr::PatchDef(_) | Expr::Methods(_)
)
} }
pub fn need_to_be_closed(&self) -> bool { pub fn need_to_be_closed(&self) -> bool {

View file

@ -16,9 +16,10 @@ use crate::ast::{
ClassAttr, ClassAttrs, ClassDef, ConstExpr, DataPack, Def, DefBody, DefId, Dict, Expr, ClassAttr, ClassAttrs, ClassDef, ConstExpr, DataPack, Def, DefBody, DefId, Dict, Expr,
Identifier, KeyValue, KwArg, Lambda, LambdaSignature, Literal, Methods, MixedRecord, Module, Identifier, KeyValue, KwArg, Lambda, LambdaSignature, Literal, Methods, MixedRecord, Module,
NonDefaultParamSignature, NormalArray, NormalDict, NormalRecord, NormalSet, NormalTuple, NonDefaultParamSignature, NormalArray, NormalDict, NormalRecord, NormalSet, NormalTuple,
ParamPattern, ParamRecordAttr, Params, PosArg, Record, RecordAttrOrIdent, RecordAttrs, ParamPattern, ParamRecordAttr, Params, PatchDef, PosArg, Record, RecordAttrOrIdent,
Set as astSet, SetWithLength, Signature, SubrSignature, Tuple, TypeAppArgs, TypeBoundSpecs, RecordAttrs, Set as astSet, SetWithLength, Signature, SubrSignature, Tuple, TypeAppArgs,
TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr, VarSignature, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr,
VarSignature,
}; };
use crate::token::{Token, TokenKind, COLON, DOT}; use crate::token::{Token, TokenKind, COLON, DOT};
@ -225,6 +226,15 @@ impl Desugarer {
.collect(); .collect();
Expr::ClassDef(ClassDef::new(def, methods)) Expr::ClassDef(ClassDef::new(def, methods))
} }
Expr::PatchDef(class_def) => {
let def = enum_unwrap!(desugar(Expr::Def(class_def.def)), Expr::Def);
let methods = class_def
.methods_list
.into_iter()
.map(|method| enum_unwrap!(desugar(Expr::Methods(method)), Expr::Methods))
.collect();
Expr::PatchDef(PatchDef::new(def, methods))
}
Expr::Lambda(lambda) => { Expr::Lambda(lambda) => {
let mut chunks = vec![]; let mut chunks = vec![];
for chunk in lambda.body.into_iter() { for chunk in lambda.body.into_iter() {

View file

@ -34,7 +34,7 @@ StrangeInt.
# Overriding methods must be given Override decorators. # Overriding methods must be given Override decorators.
# In addition, you need to override all Int methods that depend on Int.`_+_`. # In addition, you need to override all Int methods that depend on Int.`_+_`.
@Override @Override
`_+_` = Super.`_-_` # OverrideError: Int.`_+_` is referenced by ... ````` , so these methods must also be overridden `_+_` = Super.`_-_` # OverrideError: Int.`_+_` is referenced by ... , so these methods must also be overridden
``` ```
## Selecting Patches ## Selecting Patches

View file

@ -14,23 +14,14 @@ namedtupleについては、[こちら](https://docs.python.jp/3/library/collect
似たような機能にdataclassがありますが、dataclassは`__eq__``__hash__`が自動実装されるなどの影響で少しパフォーマンスが落ちます。 似たような機能にdataclassがありますが、dataclassは`__eq__``__hash__`が自動実装されるなどの影響で少しパフォーマンスが落ちます。
```python ```python
Employee = Class {.name = Str; .id = Int} employee = {.name = "John Smith"; .id = 100}
employee = Employee.new({.name = "John Smith"; .id = 100})
assert employee.name == "John Smith" assert employee.name == "John Smith"
``` ```
```python ```python
from typing import NamedTuple from typing import NamedTuple
class Employee(NamedTuple): employee = NamedTuple(['name', 'id'])('John Smith', 100)
__records__ = ['name', 'id']
name: str
id: int
employee = Employee('John Smith', 100)
assert employee.name == 'John Smith' assert employee.name == 'John Smith'
``` ```
@ -90,3 +81,19 @@ y::x = 2
assert module::x == 2 assert module::x == 2
y = None y = None
``` ```
## Patch
```python
func b: Bool =
Invert = Patch Bool
Invert.
invert self = not self
b.invert()
```
```python
def func(b):
def Invert::invert(self): return not self
return Invert::invert(b)
```

View file

@ -1,12 +1,20 @@
Binary = Patch {0, 1} Invert = Patch Bool
Binary. Invert::
invert self = _zero = False
if self == 0, 1 else 0 _invert self = not self
assert 1.invert() == 0 Invert.
assert 0.invert() == 1 zero = Invert::_zero
invert self = self::_invert()
Nat = Patch {I | I >= 0} assert False.invert()
Nat.times! self, block! =
for! 0..<self, _ => block!() .ToLower = Patch Str
10.times! do!: .ToLower::
print! "!" _lowers = "abcdefghijklmnopqrstuvwxyz"
_to_lowercase self = self.lower()
.ToLower.
lowers = .ToLower::_lowers
to_lowercase self = self::_to_lowercase()
print! "".lowers
print! "AAA".to_lowercase()

View file

@ -84,6 +84,11 @@ fn exec_infer_trait() -> Result<(), ()> {
expect_success("tests/should_ok/infer_trait.er") expect_success("tests/should_ok/infer_trait.er")
} }
#[test]
fn exec_patch() -> Result<(), ()> {
expect_success("examples/patch.er")
}
#[test] #[test]
fn exec_pattern() -> Result<(), ()> { fn exec_pattern() -> Result<(), ()> {
expect_success("tests/should_ok/pattern.er") expect_success("tests/should_ok/pattern.er")