fix: union types bug & multi-pattern def bug

This commit is contained in:
Shunsuke Shibayama 2023-04-10 22:26:46 +09:00
parent 7c8b8a66a1
commit fc85265d9f
14 changed files with 299 additions and 88 deletions

View file

@ -8,7 +8,7 @@ use erg_common::traits::StructuralEq;
use erg_common::Str; use erg_common::Str;
use erg_common::{assume_unreachable, log}; use erg_common::{assume_unreachable, log};
use crate::ty::constructors::{and, not, or, poly}; use crate::ty::constructors::{and, bounded, not, or, poly};
use crate::ty::free::{Constraint, FreeKind}; use crate::ty::free::{Constraint, FreeKind};
use crate::ty::typaram::{OpKind, TyParam, TyParamOrdering}; use crate::ty::typaram::{OpKind, TyParam, TyParamOrdering};
use crate::ty::value::ValueObj; use crate::ty::value::ValueObj;
@ -982,6 +982,18 @@ impl Context {
} }
(Refinement(l), Refinement(r)) => Type::Refinement(self.union_refinement(l, r)), (Refinement(l), Refinement(r)) => Type::Refinement(self.union_refinement(l, r)),
(Structural(l), Structural(r)) => self.union(l, r).structuralize(), (Structural(l), Structural(r)) => self.union(l, r).structuralize(),
// Int..Obj or Nat..Obj ==> Int..Obj
// Str..Obj or Int..Obj ==> Str..Obj or Int..Obj
(
Bounded { sub, sup },
Bounded {
sub: sub2,
sup: sup2,
},
) => match (self.max(sub, sub2), self.min(sup, sup2)) {
(Some(sub), Some(sup)) => bounded(sub.clone(), sup.clone()),
_ => self.simple_union(lhs, rhs),
},
(t, Type::Never) | (Type::Never, t) => t.clone(), (t, Type::Never) | (Type::Never, t) => t.clone(),
// Array({1, 2}, 2), Array({3, 4}, 2) ==> Array({1, 2, 3, 4}, 2) // Array({1, 2}, 2), Array({3, 4}, 2) ==> Array({1, 2, 3, 4}, 2)
( (
@ -997,26 +1009,10 @@ impl Context {
debug_assert_eq!(lps.len(), rps.len()); debug_assert_eq!(lps.len(), rps.len());
let mut unified_params = vec![]; let mut unified_params = vec![];
for (lp, rp) in lps.iter().zip(rps.iter()) { for (lp, rp) in lps.iter().zip(rps.iter()) {
match (lp, rp) { if let Some(union) = self.union_tp(lp, rp) {
(TyParam::Value(ValueObj::Type(l)), TyParam::Value(ValueObj::Type(r))) => { unified_params.push(union);
unified_params.push(TyParam::t(self.union(l.typ(), r.typ()))); } else {
} return self.simple_union(lhs, rhs);
(TyParam::Value(ValueObj::Type(l)), TyParam::Type(r)) => {
unified_params.push(TyParam::t(self.union(l.typ(), r)));
}
(TyParam::Type(l), TyParam::Value(ValueObj::Type(r))) => {
unified_params.push(TyParam::t(self.union(l, r.typ())));
}
(TyParam::Type(l), TyParam::Type(r)) => {
unified_params.push(TyParam::t(self.union(l, r)));
}
(_, _) => {
if self.eq_tp(lp, rp) {
unified_params.push(lp.clone());
} else {
return self.simple_union(lhs, rhs);
}
}
} }
} }
poly(ln, unified_params) poly(ln, unified_params)
@ -1025,6 +1021,39 @@ impl Context {
} }
} }
fn union_tp(&self, lhs: &TyParam, rhs: &TyParam) -> Option<TyParam> {
match (lhs, rhs) {
(TyParam::Value(ValueObj::Type(l)), TyParam::Value(ValueObj::Type(r))) => {
Some(TyParam::t(self.union(l.typ(), r.typ())))
}
(TyParam::Value(ValueObj::Type(l)), TyParam::Type(r)) => {
Some(TyParam::t(self.union(l.typ(), r)))
}
(TyParam::Type(l), TyParam::Value(ValueObj::Type(r))) => {
Some(TyParam::t(self.union(l, r.typ())))
}
(TyParam::Type(l), TyParam::Type(r)) => Some(TyParam::t(self.union(l, r))),
(TyParam::Array(l), TyParam::Array(r)) => {
let mut tps = vec![];
for (l, r) in l.iter().zip(r.iter()) {
if let Some(tp) = self.union_tp(l, r) {
tps.push(tp);
} else {
return None;
}
}
Some(TyParam::Array(tps))
}
(_, _) => {
if self.eq_tp(lhs, rhs) {
Some(lhs.clone())
} else {
None
}
}
}
}
fn simple_union(&self, lhs: &Type, rhs: &Type) -> Type { fn simple_union(&self, lhs: &Type, rhs: &Type) -> Type {
// `?T or ?U` will not be unified // `?T or ?U` will not be unified
// `Set!(?T(<: Int), 3) or Set(?U(<: Nat), 3)` wii be unified to Set(?T, 3) // `Set!(?T(<: Int), 3) or Set(?U(<: Nat), 3)` wii be unified to Set(?T, 3)

View file

@ -21,7 +21,7 @@ use crate::ty::constructors::{
array_t, dict_t, mono, poly, proj, proj_call, ref_, ref_mut, refinement, subr_t, tuple_t, array_t, dict_t, mono, poly, proj, proj_call, ref_, ref_mut, refinement, subr_t, tuple_t,
v_enum, v_enum,
}; };
use crate::ty::free::{Constraint, HasLevel}; use crate::ty::free::{Constraint, FreeTyVar, HasLevel};
use crate::ty::typaram::{OpKind, TyParam}; use crate::ty::typaram::{OpKind, TyParam};
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj}; use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{ConstSubr, HasType, Predicate, SubrKind, Type, UserConstSubr, ValueArgs}; use crate::ty::{ConstSubr, HasType, Predicate, SubrKind, Type, UserConstSubr, ValueArgs};
@ -1275,7 +1275,7 @@ impl Context {
let t = self let t = self
.convert_tp_into_type(params[0].clone()) .convert_tp_into_type(params[0].clone())
.map_err(|_| ())?; .map_err(|_| ())?;
let len = enum_unwrap!(params[1], TyParam::Value:(ValueObj::Nat:(_))); let TyParam::Value(ValueObj::Nat(len)) = params[1] else { unreachable!() };
Ok(vec![ValueObj::builtin_type(t); len as usize]) Ok(vec![ValueObj::builtin_type(t); len as usize])
} }
_ => Err(()), _ => Err(()),
@ -1432,7 +1432,7 @@ impl Context {
Ok(()) Ok(())
} }
TyParam::Type(gt) if gt.is_generalized() => { TyParam::Type(gt) if gt.is_generalized() => {
let qt = enum_unwrap!(gt.as_ref(), Type::FreeVar); let Ok(qt) = <&FreeTyVar>::try_from(gt.as_ref()) else { unreachable!() };
let Ok(st) = Type::try_from(stp) else { todo!(); }; let Ok(st) = Type::try_from(stp) else { todo!(); };
if !st.is_generalized() { if !st.is_generalized() {
qt.undoable_link(&st); qt.undoable_link(&st);
@ -1442,7 +1442,7 @@ impl Context {
TyParam::Type(qt) => { TyParam::Type(qt) => {
let Ok(st) = Type::try_from(stp) else { todo!(); }; let Ok(st) = Type::try_from(stp) else { todo!(); };
let st = if st.typarams_len() != qt.typarams_len() { let st = if st.typarams_len() != qt.typarams_len() {
let st = enum_unwrap!(st, Type::FreeVar); let Ok(st) = <&FreeTyVar>::try_from(&st) else { unreachable!() };
st.get_sub().unwrap() st.get_sub().unwrap()
} else { } else {
st st
@ -1461,7 +1461,7 @@ impl Context {
match tp { match tp {
TyParam::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(), TyParam::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(),
TyParam::Type(t) if t.is_free_var() => { TyParam::Type(t) if t.is_free_var() => {
let subst = enum_unwrap!(t.as_ref(), Type::FreeVar); let Ok(subst) = <&FreeTyVar>::try_from(t.as_ref()) else { unreachable!() };
if subst.is_undoable_linked() { if subst.is_undoable_linked() {
subst.undo(); subst.undo();
} }

View file

@ -1213,4 +1213,32 @@ impl Context {
hir::Expr::Import(_) => unreachable!(), hir::Expr::Import(_) => unreachable!(),
} }
} }
/// ```erg
/// squash_tyvar(?1 or ?2) == ?1(== ?2)
/// squash_tyvar(?T or ?U) == ?T or ?U
/// ```
pub(crate) fn squash_tyvar(&self, typ: Type) -> Type {
match typ {
Type::Or(l, r) => {
let l = self.squash_tyvar(*l);
let r = self.squash_tyvar(*r);
if l.is_named_unbound_var() && r.is_named_unbound_var() {
self.union(&l, &r)
} else {
match (self.subtype_of(&l, &r), self.subtype_of(&r, &l)) {
(true, true) | (true, false) => {
let _ = self.sub_unify(&l, &r, &(), None);
}
(false, true) => {
let _ = self.sub_unify(&r, &l, &(), None);
}
_ => {}
}
self.union(&l, &r)
}
}
other => other,
}
}
} }

View file

@ -656,8 +656,16 @@ impl Context {
TyCheckErrors::new( TyCheckErrors::new(
errs.into_iter() errs.into_iter()
.map(|e| { .map(|e| {
let expect = self.readable_type(spec_ret_t.clone()); let expect = if cfg!(feature = "debug") {
let found = self.readable_type(body_t.clone()); spec_ret_t.clone()
} else {
self.readable_type(spec_ret_t.clone())
};
let found = if cfg!(feature = "debug") {
body_t.clone()
} else {
self.readable_type(body_t.clone())
};
TyCheckError::return_type_error( TyCheckError::return_type_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,

View file

@ -30,6 +30,8 @@ impl Context {
/// occur(X -> ?T, X -> ?T) ==> OK /// occur(X -> ?T, X -> ?T) ==> OK
/// occur(?T, ?T -> X) ==> Error /// occur(?T, ?T -> X) ==> Error
/// occur(?T, Option(?T)) ==> Error /// occur(?T, Option(?T)) ==> Error
/// occur(?T or ?U, ?T) ==> OK
/// occur(?T(<: Str) or ?U(<: Int), ?T(<: Str)) ==> Error
/// occur(?T, ?T.Output) ==> OK /// occur(?T, ?T.Output) ==> OK
pub(crate) fn occur( pub(crate) fn occur(
&self, &self,
@ -118,6 +120,10 @@ impl Context {
} }
Ok(()) Ok(())
} }
(Or(l, r), Or(l2, r2)) | (And(l, r), And(l2, r2)) => {
self.occur(l, l2, loc)?;
self.occur(r, r2, loc)
}
(lhs, Or(l, r)) | (lhs, And(l, r)) => { (lhs, Or(l, r)) | (lhs, And(l, r)) => {
self.occur_inner(lhs, l, loc)?; self.occur_inner(lhs, l, loc)?;
self.occur_inner(lhs, r, loc) self.occur_inner(lhs, r, loc)
@ -787,14 +793,29 @@ impl Context {
self.caused_by(), self.caused_by(),
))); )));
}; };
if sub_fv.level().unwrap_or(GENERIC_LEVEL) match sub_fv
<= sup_fv.level().unwrap_or(GENERIC_LEVEL) .level()
.unwrap_or(GENERIC_LEVEL)
.cmp(&sup_fv.level().unwrap_or(GENERIC_LEVEL))
{ {
sub_fv.update_constraint(new_constraint, false); std::cmp::Ordering::Less => {
sup_fv.link(maybe_sub); sub_fv.update_constraint(new_constraint, false);
} else { sup_fv.link(maybe_sub);
sup_fv.update_constraint(new_constraint, false); }
sub_fv.link(maybe_sup); std::cmp::Ordering::Greater => {
sup_fv.update_constraint(new_constraint, false);
sub_fv.link(maybe_sup);
}
std::cmp::Ordering::Equal => {
// choose named one
if sup_fv.is_named_unbound() {
sup_fv.update_constraint(new_constraint, false);
sub_fv.link(maybe_sup);
} else {
sub_fv.update_constraint(new_constraint, false);
sup_fv.link(maybe_sub);
}
}
} }
Ok(()) Ok(())
} }

View file

@ -1024,6 +1024,7 @@ impl LowerWarning {
fn_name: &str, fn_name: &str,
typ: &Type, typ: &Type,
) -> Self { ) -> Self {
let fn_name = fn_name.with_color(Color::Yellow);
let hint = switch_lang!( let hint = switch_lang!(
"japanese" => format!("`{fn_name}(...): {typ} = ...`など明示的に戻り値型を指定してください"), "japanese" => format!("`{fn_name}(...): {typ} = ...`など明示的に戻り値型を指定してください"),
"simplified_chinese" => format!("请明确指定函数{fn_name}的返回类型,例如`{fn_name}(...): {typ} = ...`"), "simplified_chinese" => format!("请明确指定函数{fn_name}的返回类型,例如`{fn_name}(...): {typ} = ...`"),

View file

@ -10,14 +10,18 @@ def in_operator(elem, y):
return True return True
# TODO: trait check # TODO: trait check
return False return False
elif issubclass(type(y), list) and ( elif isinstance(y, list) and (
type(y[0]) == type or issubclass(type(y[0]), Range) type(y[0]) == type or isinstance(y[0], Range)
): ):
# FIXME: # FIXME:
type_check = in_operator(elem[0], y[0]) type_check = in_operator(elem[0], y[0])
len_check = len(elem) == len(y) len_check = len(elem) == len(y)
return type_check and len_check return type_check and len_check
elif issubclass(type(y), dict) and issubclass(type(next(iter(y.keys()))), type): elif isinstance(y, tuple):
type_check = all(map(lambda x: in_operator(x[0], x[1]), zip(elem, y)))
len_check = len(elem) == len(y)
return type_check and len_check
elif isinstance(y, dict) and isinstance(next(iter(y.keys())), type):
# TODO: # TODO:
type_check = True # in_operator(x[next(iter(x.keys()))], next(iter(y.keys()))) type_check = True # in_operator(x[next(iter(x.keys()))], next(iter(y.keys())))
len_check = len(elem) >= len(y) len_check = len(elem) >= len(y)

View file

@ -16,7 +16,7 @@ use crate::ty::{HasType, Type, ValueObj, VisibilityModifier};
use crate::error::{ use crate::error::{
CompileErrors, LowerError, LowerResult, LowerWarning, LowerWarnings, SingleLowerResult, CompileErrors, LowerError, LowerResult, LowerWarning, LowerWarnings, SingleLowerResult,
}; };
use crate::hir::{self, Expr, HIR}; use crate::hir::{self, Expr, Signature, HIR};
use crate::lower::ASTLowerer; use crate::lower::ASTLowerer;
use crate::varinfo::VarInfo; use crate::varinfo::VarInfo;
@ -279,4 +279,47 @@ impl ASTLowerer {
self.check_doc_comments(&hir); self.check_doc_comments(&hir);
self.module.context.pop(); self.module.context.pop();
} }
pub(crate) fn warn_implicit_union(&mut self, hir: &HIR) {
for chunk in hir.module.iter() {
self.warn_implicit_union_chunk(chunk);
}
}
fn warn_implicit_union_chunk(&mut self, chunk: &Expr) {
match chunk {
Expr::ClassDef(class_def) => {
for chunk in class_def.methods.iter() {
self.warn_implicit_union_chunk(chunk);
}
}
Expr::PatchDef(patch_def) => {
for chunk in patch_def.methods.iter() {
self.warn_implicit_union_chunk(chunk);
}
}
Expr::Def(def) => {
if let Signature::Subr(subr) = &def.sig {
let return_t = subr.ref_t().return_t().unwrap();
if return_t.union_pair().is_some() && subr.return_t_spec.is_none() {
let typ = if cfg!(feature = "debug") {
return_t.clone()
} else {
self.module.context.readable_type(return_t.clone())
};
let warn = LowerWarning::union_return_type_warning(
self.input().clone(),
line!() as usize,
subr.loc(),
self.module.context.caused_by(),
subr.ident.inspect(),
&typ,
);
self.warns.push(warn);
}
}
}
_ => {}
}
}
} }

View file

@ -1582,25 +1582,13 @@ impl ASTLowerer {
} }
match self.lower_block(body.block) { match self.lower_block(body.block) {
Ok(block) => { Ok(block) => {
let found_body_t = block.ref_t(); let found_body_t = self.module.context.squash_tyvar(block.t());
let vi = self.module.context.outer.as_mut().unwrap().assign_subr( let vi = self.module.context.outer.as_mut().unwrap().assign_subr(
&sig, &sig,
body.id, body.id,
found_body_t, &found_body_t,
block.last().unwrap(), block.last().unwrap(),
)?; )?;
let return_t = vi.t.return_t().unwrap();
if return_t.union_pair().is_some() && sig.return_t_spec.is_none() {
let warn = LowerWarning::union_return_type_warning(
self.input().clone(),
line!() as usize,
sig.loc(),
self.module.context.caused_by(),
sig.ident.inspect(),
&self.module.context.readable_type(return_t.clone()),
);
self.warns.push(warn);
}
let ident = hir::Identifier::new(sig.ident, None, vi); let ident = hir::Identifier::new(sig.ident, None, vi);
let sig = let sig =
hir::SubrSignature::new(ident, sig.bounds, params, sig.return_t_spec); hir::SubrSignature::new(ident, sig.bounds, params, sig.return_t_spec);
@ -2519,6 +2507,7 @@ impl ASTLowerer {
return Err(self.return_incomplete_artifact(hir)); return Err(self.return_incomplete_artifact(hir));
} }
}; };
self.warn_implicit_union(&hir);
self.warn_unused_expr(&hir.module, mode); self.warn_unused_expr(&hir.module, mode);
self.warn_unused_vars(mode); self.warn_unused_vars(mode);
self.check_doc_comments(&hir); self.check_doc_comments(&hir);

View file

@ -462,6 +462,14 @@ impl<T> FreeKind<T> {
} }
} }
} }
pub const fn is_named_unbound(&self) -> bool {
matches!(self, Self::NamedUnbound { .. })
}
pub const fn is_undoable_linked(&self) -> bool {
matches!(self, Self::UndoableLinked { .. })
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -753,14 +761,15 @@ impl<T> Free<T> {
} }
pub fn is_linked(&self) -> bool { pub fn is_linked(&self) -> bool {
matches!( self.borrow().linked().is_some()
&*self.borrow(),
FreeKind::Linked(_) | FreeKind::UndoableLinked { .. }
)
} }
pub fn is_undoable_linked(&self) -> bool { pub fn is_undoable_linked(&self) -> bool {
matches!(&*self.borrow(), FreeKind::UndoableLinked { .. }) self.borrow().is_undoable_linked()
}
pub fn is_named_unbound(&self) -> bool {
self.borrow().is_named_unbound()
} }
pub fn unsafe_crack(&self) -> &T { pub fn unsafe_crack(&self) -> &T {

View file

@ -2081,6 +2081,22 @@ impl Type {
matches!(self, Self::FreeVar(fv) if fv.is_unbound() || fv.crack().is_unbound_var()) matches!(self, Self::FreeVar(fv) if fv.is_unbound() || fv.crack().is_unbound_var())
} }
pub fn is_named_unbound_var(&self) -> bool {
matches!(self, Self::FreeVar(fv) if fv.is_named_unbound() || (fv.is_linked() && fv.crack().is_named_unbound_var()))
}
pub fn is_totally_unbound(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_unbound() => true,
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_totally_unbound(),
Self::Or(t1, t2) | Self::And(t1, t2) => {
t1.is_totally_unbound() && t2.is_totally_unbound()
}
Self::Not(t) => t.is_totally_unbound(),
_ => false,
}
}
/// See also: `is_monomorphized` /// See also: `is_monomorphized`
pub fn is_monomorphic(&self) -> bool { pub fn is_monomorphic(&self) -> bool {
matches!(self.typarams_len(), Some(0) | None) matches!(self.typarams_len(), Some(0) | None)

View file

@ -13,10 +13,10 @@ use crate::ast::{
ClassAttr, ClassAttrs, ClassDef, ConstExpr, DataPack, Def, DefBody, DefId, Dict, Dummy, Expr, ClassAttr, ClassAttrs, ClassDef, ConstExpr, DataPack, Def, DefBody, DefId, Dict, Dummy, 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, PatchDef, PosArg, ReDef, Record, RecordAttrOrIdent, ParamPattern, ParamRecordAttr, ParamTuplePattern, Params, PatchDef, PosArg, ReDef, Record,
RecordAttrs, Set as astSet, SetWithLength, Signature, SubrSignature, Tuple, TupleTypeSpec, RecordAttrOrIdent, RecordAttrs, Set as astSet, SetWithLength, Signature, SubrSignature, Tuple,
TypeAppArgs, TypeAppArgsKind, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, TupleTypeSpec, TypeAppArgs, TypeAppArgsKind, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp,
VarPattern, VarRecordAttr, VarSignature, VisModifierSpec, VarName, VarPattern, VarRecordAttr, VarSignature, VisModifierSpec,
}; };
use crate::token::{Token, TokenKind, COLON, DOT}; use crate::token::{Token, TokenKind, COLON, DOT};
@ -145,7 +145,7 @@ impl Desugarer {
}, },
Expr::DataPack(pack) => { Expr::DataPack(pack) => {
let class = desugar(*pack.class); let class = desugar(*pack.class);
let args = enum_unwrap!(desugar(Expr::Record(pack.args)), Expr::Record); let Expr::Record(args) = desugar(Expr::Record(pack.args)) else { unreachable!() };
Expr::DataPack(DataPack::new(class, pack.connector, args)) Expr::DataPack(DataPack::new(class, pack.connector, args))
} }
Expr::Array(array) => match array { Expr::Array(array) => match array {
@ -249,7 +249,7 @@ impl Desugarer {
Expr::Def(Def::new(def.sig, body)) Expr::Def(Def::new(def.sig, body))
} }
Expr::ClassDef(class_def) => { Expr::ClassDef(class_def) => {
let def = enum_unwrap!(desugar(Expr::Def(class_def.def)), Expr::Def); let Expr::Def(def) = desugar(Expr::Def(class_def.def)) else { unreachable!() };
let methods = class_def let methods = class_def
.methods_list .methods_list
.into_iter() .into_iter()
@ -258,7 +258,7 @@ impl Desugarer {
Expr::ClassDef(ClassDef::new(def, methods)) Expr::ClassDef(ClassDef::new(def, methods))
} }
Expr::PatchDef(class_def) => { Expr::PatchDef(class_def) => {
let def = enum_unwrap!(desugar(Expr::Def(class_def.def)), Expr::Def); let Expr::Def(def) = desugar(Expr::Def(class_def.def)) else { unreachable!() };
let methods = class_def let methods = class_def
.methods_list .methods_list
.into_iter() .into_iter()
@ -343,7 +343,7 @@ impl Desugarer {
if let Some(Expr::Def(previous)) = new.last() { if let Some(Expr::Def(previous)) = new.last() {
if previous.is_subr() && previous.sig.name_as_str() == def.sig.name_as_str() if previous.is_subr() && previous.sig.name_as_str() == def.sig.name_as_str()
{ {
let previous = enum_unwrap!(new.pop().unwrap(), Expr::Def); let Some(Expr::Def(previous)) = new.pop() else { unreachable!() };
let name = def.sig.ident().unwrap().clone(); let name = def.sig.ident().unwrap().clone();
let id = def.body.id; let id = def.body.id;
let op = def.body.op.clone(); let op = def.body.op.clone();
@ -354,17 +354,46 @@ impl Desugarer {
} else { } else {
self.gen_match_call(previous, def) self.gen_match_call(previous, def)
}; };
let param_name = enum_unwrap!(&call.args.pos_args().iter().next().unwrap().expr, Expr::Accessor:(Accessor::Ident:(_))).inspect(); let params = match &call.args.pos_args().iter().next().unwrap().expr {
// FIXME: multiple params Expr::Tuple(Tuple::Normal(tup)) => {
let param = VarName::new(Token::new( let mut params = vec![];
TokenKind::Symbol, for arg in tup.elems.pos_args().iter() {
param_name, match &arg.expr {
name.ln_begin().unwrap_or(1), Expr::Accessor(Accessor::Ident(ident)) => {
name.col_end().unwrap_or(0) + 1, // HACK: `(name) %x = ...`という形を想定 let param_name = ident.inspect();
)); let param = VarName::new(Token::new(
let param = TokenKind::Symbol,
NonDefaultParamSignature::new(ParamPattern::VarName(param), None); param_name,
let params = Params::single(param); name.ln_begin().unwrap_or(1),
name.col_end().unwrap_or(0) + 1,
));
let param = NonDefaultParamSignature::new(
ParamPattern::VarName(param),
None,
);
params.push(param);
}
_ => unreachable!(),
}
}
Params::new(params, None, vec![], None)
}
Expr::Accessor(Accessor::Ident(ident)) => {
let param_name = ident.inspect();
let param = VarName::new(Token::new(
TokenKind::Symbol,
param_name,
name.ln_begin().unwrap_or(1),
name.col_end().unwrap_or(0) + 1, // HACK: `(name) %x = ...`という形を想定
));
let param = NonDefaultParamSignature::new(
ParamPattern::VarName(param),
None,
);
Params::single(param)
}
_ => unreachable!(),
};
let sig = Signature::Subr(SubrSignature::new( let sig = Signature::Subr(SubrSignature::new(
set! {}, set! {},
name, name,
@ -392,8 +421,8 @@ impl Desugarer {
fn add_arg_to_match_call(&self, mut previous: Def, def: Def) -> (Call, Option<TypeSpec>) { fn add_arg_to_match_call(&self, mut previous: Def, def: Def) -> (Call, Option<TypeSpec>) {
let op = Token::from_str(TokenKind::FuncArrow, "->"); let op = Token::from_str(TokenKind::FuncArrow, "->");
let mut call = enum_unwrap!(previous.body.block.remove(0), Expr::Call); let Expr::Call(mut call) = previous.body.block.remove(0) else { unreachable!() };
let sig = enum_unwrap!(def.sig, Signature::Subr); let Signature::Subr(sig) = def.sig else { unreachable!() };
let return_t_spec = sig.return_t_spec; let return_t_spec = sig.return_t_spec;
let first_arg = sig.params.non_defaults.first().unwrap(); let first_arg = sig.params.non_defaults.first().unwrap();
// 最後の定義の引数名を関数全体の引数名にする // 最後の定義の引数名を関数全体の引数名にする
@ -406,7 +435,14 @@ impl Desugarer {
)); ));
call.args.insert_pos(0, arg); call.args.insert_pos(0, arg);
} }
let sig = LambdaSignature::new(sig.params, return_t_spec.clone(), sig.bounds); // f(x, y, z) = ... => match x, ((x, y, z),) -> ...
let params = if sig.params.len() == 1 {
sig.params
} else {
let pat = ParamPattern::Tuple(ParamTuplePattern::new(sig.params));
Params::single(NonDefaultParamSignature::new(pat, None))
};
let sig = LambdaSignature::new(params, return_t_spec.clone(), sig.bounds);
let new_branch = Lambda::new(sig, op, def.body.block, def.body.id); let new_branch = Lambda::new(sig, op, def.body.block, def.body.id);
call.args.push_pos(PosArg::new(Expr::Lambda(new_branch))); call.args.push_pos(PosArg::new(Expr::Lambda(new_branch)));
(call, return_t_spec) (call, return_t_spec)
@ -415,17 +451,39 @@ impl Desugarer {
// TODO: procedural match // TODO: procedural match
fn gen_match_call(&self, previous: Def, def: Def) -> (Call, Option<TypeSpec>) { fn gen_match_call(&self, previous: Def, def: Def) -> (Call, Option<TypeSpec>) {
let op = Token::from_str(TokenKind::FuncArrow, "->"); let op = Token::from_str(TokenKind::FuncArrow, "->");
let sig = enum_unwrap!(previous.sig, Signature::Subr); let Signature::Subr(prev_sig) = previous.sig else { unreachable!() };
let params_len = prev_sig.params.len();
let params = if params_len == 1 {
prev_sig.params
} else {
let pat = ParamPattern::Tuple(ParamTuplePattern::new(prev_sig.params));
Params::single(NonDefaultParamSignature::new(pat, None))
};
let match_symbol = Expr::static_local("match"); let match_symbol = Expr::static_local("match");
let sig = LambdaSignature::new(sig.params, sig.return_t_spec, sig.bounds); let sig = LambdaSignature::new(params, prev_sig.return_t_spec, prev_sig.bounds);
let first_branch = Lambda::new(sig, op.clone(), previous.body.block, previous.body.id); let first_branch = Lambda::new(sig, op.clone(), previous.body.block, previous.body.id);
let sig = enum_unwrap!(def.sig, Signature::Subr); let Signature::Subr(sig) = def.sig else { unreachable!() };
let params = if sig.params.len() == 1 {
sig.params
} else {
let pat = ParamPattern::Tuple(ParamTuplePattern::new(sig.params));
Params::single(NonDefaultParamSignature::new(pat, None))
};
let return_t_spec = sig.return_t_spec; let return_t_spec = sig.return_t_spec;
let sig = LambdaSignature::new(sig.params, return_t_spec.clone(), sig.bounds); let sig = LambdaSignature::new(params, return_t_spec.clone(), sig.bounds);
let second_branch = Lambda::new(sig, op, def.body.block, def.body.id); let second_branch = Lambda::new(sig, op, def.body.block, def.body.id);
let first_arg = if params_len == 1 {
Expr::dummy_local(&fresh_varname())
} else {
let args = (0..params_len).map(|_| PosArg::new(Expr::dummy_local(&fresh_varname())));
Expr::Tuple(Tuple::Normal(NormalTuple::new(Args::pos_only(
args.collect(),
None,
))))
};
let args = Args::pos_only( let args = Args::pos_only(
vec![ vec![
PosArg::new(Expr::dummy_local("_")), // dummy argument, will be removed in line 56 PosArg::new(first_arg), // dummy argument, will be removed in line 56
PosArg::new(Expr::Lambda(first_branch)), PosArg::new(Expr::Lambda(first_branch)),
PosArg::new(Expr::Lambda(second_branch)), PosArg::new(Expr::Lambda(second_branch)),
], ],

View file

@ -9,5 +9,10 @@ stop_or_call n, f: (Nat -> Nat), g: (Nat -> Nat) =
fact(n: Nat): Nat = fact(n: Nat): Nat =
stop_or_call n, fact, (r, ) -> r * n stop_or_call n, fact, (r, ) -> r * n
print! fact assert fact(5) == 120
print! fact 5
iterate(_, 0, x) = x
iterate(f, n: Int, x) = iterate f, n-1, f x
assert iterate((x -> x + 1), 5, 0) == 5
assert iterate((x -> x + "a"), 5, "b") == "baaaaa"

View file

@ -154,7 +154,7 @@ fn exec_raw_ident() -> Result<(), ()> {
#[test] #[test]
fn exec_rec() -> Result<(), ()> { fn exec_rec() -> Result<(), ()> {
expect_success("tests/should_ok/rec.er", 1) expect_success("tests/should_ok/rec.er", 0)
} }
#[test] #[test]