diff --git a/compiler/erg_compiler/codegen.rs b/compiler/erg_compiler/codegen.rs index 18110790..99ccc400 100644 --- a/compiler/erg_compiler/codegen.rs +++ b/compiler/erg_compiler/codegen.rs @@ -1926,9 +1926,10 @@ impl PyCodeGenerator { self.emit_call_local(ident, call.args) } other => { + let is_py_api = other.is_py_api(); self.emit_push_null(); self.emit_expr(other); - self.emit_args_311(call.args, Name); + self.emit_args_311(call.args, Name, is_py_api); } } } @@ -1952,11 +1953,12 @@ impl PyCodeGenerator { Some(8) => self.emit_with_instr_308(args), _ => todo!(), }, - // "pyimport" | "py" | + // "pyimport" | "py" are here _ => { + let is_py_api = local.is_py_api(); self.emit_push_null(); self.emit_load_name_instr(local); - self.emit_args_311(args, Name); + self.emit_args_311(args, Name, is_py_api); } } } @@ -1973,9 +1975,10 @@ impl PyCodeGenerator { } else if let Some(func_name) = fake_method_to_func(&class, method_name.inspect()) { return self.emit_call_fake_method(obj, func_name, method_name, args); } + let is_py_api = obj.is_py_api(); self.emit_expr(obj); self.emit_load_method_instr(method_name); - self.emit_args_311(args, Method); + self.emit_args_311(args, Method, is_py_api); } fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) { @@ -2004,7 +2007,7 @@ impl PyCodeGenerator { } } - fn emit_args_311(&mut self, mut args: Args, kind: AccessKind) { + fn emit_args_311(&mut self, mut args: Args, kind: AccessKind, is_py_api: bool) { let argc = args.len(); let pos_len = args.pos_args.len(); let mut kws = Vec::with_capacity(args.kw_len()); @@ -2019,7 +2022,12 @@ impl PyCodeGenerator { } } while let Some(arg) = args.try_remove_kw(0) { - kws.push(ValueObj::Str(arg.keyword.content.clone())); + let kw = if is_py_api { + arg.keyword.content + } else { + Str::from(format!("::{}", arg.keyword.content)) + }; + kws.push(ValueObj::Str(kw)); self.emit_expr(arg.expr); } let kwsc = if !kws.is_empty() { @@ -2095,7 +2103,7 @@ impl PyCodeGenerator { self.emit_push_null(); self.emit_load_name_instr(method_name); args.insert_pos(0, PosArg::new(obj)); - self.emit_args_311(args, Name); + self.emit_args_311(args, Name, true); } // assert takes 1 or 2 arguments (0: cond, 1: message) diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index 9078eba4..8cf36caa 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -931,38 +931,25 @@ impl Context { if (params_len < pos_args.len() || params_len < pos_args.len() + kw_args.len()) && subr.var_params.is_none() { - return Err(TyCheckErrors::from(TyCheckError::too_many_args_error( - self.cfg.input.clone(), - line!() as usize, - callee.loc(), - &callee.to_string(), - self.caused_by(), - params_len, - pos_args.len(), - kw_args.len(), - ))); + return Err(self.gen_too_many_args_error(&callee, subr, pos_args, kw_args)); } let mut passed_params = set! {}; - let non_default_params_len = if attr_name.is_some() && is_method { - subr.non_default_params.len() - 1 + let non_default_params = if is_method { + let mut non_default_params = subr.non_default_params.iter(); + let self_pt = non_default_params.next().unwrap(); + if let Err(mut es) = + self.sub_unify(obj.ref_t(), self_pt.typ(), obj.loc(), self_pt.name()) + { + errs.append(&mut es); + } + non_default_params } else { - subr.non_default_params.len() + subr.non_default_params.iter() }; + let non_default_params_len = non_default_params.len(); let mut nth = 1; if pos_args.len() >= non_default_params_len { let (non_default_args, var_args) = pos_args.split_at(non_default_params_len); - let non_default_params = if is_method { - let mut non_default_params = subr.non_default_params.iter(); - let self_pt = non_default_params.next().unwrap(); - if let Err(mut es) = - self.sub_unify(obj.ref_t(), self_pt.typ(), obj.loc(), self_pt.name()) - { - errs.append(&mut es); - } - non_default_params - } else { - subr.non_default_params.iter() - }; for (nd_arg, nd_param) in non_default_args.iter().zip(non_default_params) { if let Err(mut es) = self.substitute_pos_arg( &callee, @@ -1010,7 +997,7 @@ impl Context { attr_name, kw_arg, nth, - &subr.default_params, + subr, &mut passed_params, ) { errs.append(&mut es); @@ -1031,24 +1018,41 @@ impl Context { } } } else { - let missing_len = subr.non_default_params.len() - pos_args.len(); - let missing_params = subr - .non_default_params - .iter() - .rev() - .take(missing_len) - .rev() - .map(|pt| pt.name().cloned().unwrap_or(Str::ever(""))) - .collect(); - return Err(TyCheckErrors::from(TyCheckError::args_missing_error( - self.cfg.input.clone(), - line!() as usize, - callee.loc(), - &callee.to_string(), - self.caused_by(), - missing_len, - missing_params, - ))); + // pos_args.len() < non_default_params_len + for kw_arg in kw_args.iter() { + if let Err(mut es) = self.substitute_kw_arg( + &callee, + attr_name, + kw_arg, + nth, + subr, + &mut passed_params, + ) { + errs.append(&mut es); + } + nth += 1; + } + let missing_len = non_default_params_len - pos_args.len() - passed_params.len(); + if missing_len > 0 { + let missing_params = subr + .non_default_params + .iter() + .rev() + .take(missing_len) + .rev() + .map(|pt| pt.name().cloned().unwrap_or(Str::ever(""))) + .filter(|pt| !passed_params.contains(pt)) + .collect(); + return Err(TyCheckErrors::from(TyCheckError::args_missing_error( + self.cfg.input.clone(), + line!() as usize, + callee.loc(), + &callee.to_string(), + self.caused_by(), + missing_len, + missing_params, + ))); + } } if errs.is_empty() { Ok(()) @@ -1088,6 +1092,76 @@ impl Context { } } + fn gen_too_many_args_error( + &self, + callee: &hir::Expr, + subr_ty: &SubrType, + pos_args: &[hir::PosArg], + kw_args: &[hir::KwArg], + ) -> TyCheckErrors { + let mut unknown_args = vec![]; + let mut passed_args: Vec<&hir::KwArg> = vec![]; + let mut duplicated_args = vec![]; + for kw_arg in kw_args.iter() { + if subr_ty + .non_default_params + .iter() + .all(|pt| pt.name() != Some(kw_arg.keyword.inspect())) + && subr_ty + .var_params + .as_ref() + .map(|pt| pt.name() != Some(kw_arg.keyword.inspect())) + .unwrap_or(true) + && subr_ty + .default_params + .iter() + .all(|pt| pt.name() != Some(kw_arg.keyword.inspect())) + { + unknown_args.push(kw_arg); + } + if passed_args.iter().any(|a| a.keyword == kw_arg.keyword) { + duplicated_args.push(kw_arg); + } else { + passed_args.push(kw_arg); + } + } + if unknown_args.is_empty() && duplicated_args.is_empty() { + let params_len = subr_ty.non_default_params.len() + subr_ty.default_params.len(); + TyCheckErrors::from(TyCheckError::too_many_args_error( + self.cfg.input.clone(), + line!() as usize, + callee.loc(), + &callee.to_string(), + self.caused_by(), + params_len, + pos_args.len(), + kw_args.len(), + )) + } else { + let unknown_arg_errors = unknown_args.into_iter().map(|arg| { + TyCheckError::unexpected_kw_arg_error( + self.cfg.input.clone(), + line!() as usize, + arg.loc(), + &callee.to_string(), + self.caused_by(), + arg.keyword.inspect(), + ) + }); + let duplicated_arg_errors = duplicated_args.into_iter().map(|arg| { + TyCheckError::multiple_args_error( + self.cfg.input.clone(), + line!() as usize, + arg.loc(), + &callee.to_string(), + self.caused_by(), + arg.keyword.inspect(), + ) + }); + unknown_arg_errors.chain(duplicated_arg_errors).collect() + } + } + fn substitute_pos_arg( &self, callee: &hir::Expr, @@ -1190,7 +1264,7 @@ impl Context { attr_name: &Option, arg: &hir::KwArg, nth: usize, - default_params: &[ParamTy], + subr_ty: &SubrType, passed_params: &mut Set, ) -> TyCheckResult<()> { let arg_t = arg.expr.ref_t(); @@ -1207,8 +1281,10 @@ impl Context { } else { passed_params.insert(kw_name.clone()); } - if let Some(pt) = default_params + if let Some(pt) = subr_ty + .non_default_params .iter() + .chain(subr_ty.default_params.iter()) .find(|pt| pt.name().unwrap() == kw_name) { self.sub_unify(arg_t, pt.typ(), arg.loc(), Some(kw_name)) @@ -1263,7 +1339,6 @@ impl Context { ) -> TyCheckResult { if let hir::Expr::Accessor(hir::Accessor::Ident(local)) = obj { if local.vis().is_private() { - #[allow(clippy::single_match)] match &local.inspect()[..] { "match" => { return self.get_match_call_t(SubrKind::Func, pos_args, kw_args); @@ -1271,20 +1346,6 @@ impl Context { "match!" => { return self.get_match_call_t(SubrKind::Proc, pos_args, kw_args); } - /*"import" | "pyimport" | "py" => { - return self.get_import_call_t(pos_args, kw_args); - }*/ - // handle assert casting - /*"assert" => { - if let Some(arg) = pos_args.first() { - match &arg.expr { - hir::Expr::BinOp(bin) if bin.op.is(TokenKind::InOp) && bin.rhs.ref_t() == &Type => { - let t = self.eval_const_expr(bin.lhs.as_ref(), None)?.as_type().unwrap(); - } - _ => {} - } - } - },*/ _ => {} } } diff --git a/compiler/erg_compiler/hir.rs b/compiler/erg_compiler/hir.rs index 83874345..1f5a37ef 100644 --- a/compiler/erg_compiler/hir.rs +++ b/compiler/erg_compiler/hir.rs @@ -422,6 +422,10 @@ impl Identifier { Self::new(dot, name, None, VarInfo::const_default()) } + pub fn is_py_api(&self) -> bool { + self.vi.py_name.is_some() + } + pub fn is_const(&self) -> bool { self.name.is_const() } @@ -539,6 +543,13 @@ impl Accessor { } } + pub fn is_py_api(&self) -> bool { + match self { + Self::Ident(ident) => ident.is_py_api(), + Self::Attr(attr) => attr.ident.is_py_api(), + } + } + // 参照するオブジェクト自体が持っている固有の名前(クラス、モジュールなど) pub fn qual_name(&self) -> Option<&str> { match self { @@ -1919,6 +1930,13 @@ impl Expr { } } + pub fn is_py_api(&self) -> bool { + match self { + Expr::Accessor(acc) => acc.is_py_api(), + _ => false, + } + } + pub fn is_type_asc(&self) -> bool { matches!(self, Expr::TypeAsc(_)) } diff --git a/compiler/erg_compiler/ownercheck.rs b/compiler/erg_compiler/ownercheck.rs index 872bfb60..f53266de 100644 --- a/compiler/erg_compiler/ownercheck.rs +++ b/compiler/erg_compiler/ownercheck.rs @@ -136,20 +136,23 @@ impl OwnershipChecker { } else { args_owns.non_defaults.len() }; - let (non_default_args, var_args) = call.args.pos_args.split_at(non_defaults_len); - for (nd_arg, ownership) in - non_default_args.iter().zip(args_owns.non_defaults.iter()) - { - self.check_expr(&nd_arg.expr, *ownership, false); - } - if let Some(ownership) = args_owns.var_params.as_ref() { - for var_arg in var_args.iter() { - self.check_expr(&var_arg.expr, *ownership, false); + if call.args.pos_args.len() > non_defaults_len { + let (non_default_args, var_args) = + call.args.pos_args.split_at(non_defaults_len); + for (nd_arg, (_, ownership)) in + non_default_args.iter().zip(args_owns.non_defaults.iter()) + { + self.check_expr(&nd_arg.expr, *ownership, false); } - } else { - let kw_args = var_args; - for (arg, (_, ownership)) in kw_args.iter().zip(args_owns.defaults.iter()) { - self.check_expr(&arg.expr, *ownership, false); + if let Some((_, ownership)) = args_owns.var_params.as_ref() { + for var_arg in var_args.iter() { + self.check_expr(&var_arg.expr, *ownership, false); + } + } else { + let kw_args = var_args; + for (arg, (_, ownership)) in kw_args.iter().zip(args_owns.defaults.iter()) { + self.check_expr(&arg.expr, *ownership, false); + } } } for kw_arg in call.args.kw_args.iter() { @@ -159,6 +162,12 @@ impl OwnershipChecker { .find(|(k, _)| k == kw_arg.keyword.inspect()) { self.check_expr(&kw_arg.expr, *ownership, false); + } else if let Some((_, ownership)) = args_owns + .non_defaults + .iter() + .find(|(k, _)| k.as_ref() == Some(kw_arg.keyword.inspect())) + { + self.check_expr(&kw_arg.expr, *ownership, false); } else { todo!() } diff --git a/compiler/erg_compiler/transpile.rs b/compiler/erg_compiler/transpile.rs index 0c634938..d6120ec3 100644 --- a/compiler/erg_compiler/transpile.rs +++ b/compiler/erg_compiler/transpile.rs @@ -431,6 +431,7 @@ impl ScriptGenerator { } fn transpile_simple_call(&mut self, mut call: Call) -> String { + let is_py_api = call.obj.is_py_api(); let mut code = format!("({})", self.transpile_expr(*call.obj)); if let Some(attr) = call.attr_name { code += &format!(".{}", Self::transpile_ident(attr)); @@ -441,7 +442,12 @@ impl ScriptGenerator { code.push(','); } while let Some(arg) = call.args.try_remove_kw(0) { - code += &format!("{}={},", arg.keyword, self.transpile_expr(arg.expr)); + let escape = if is_py_api { "" } else { "__" }; + code += &format!( + "{}{escape}={},", + arg.keyword.content, + self.transpile_expr(arg.expr) + ); } code.push(')'); code diff --git a/compiler/erg_compiler/ty/mod.rs b/compiler/erg_compiler/ty/mod.rs index 09817a6e..2e729db0 100644 --- a/compiler/erg_compiler/ty/mod.rs +++ b/compiler/erg_compiler/ty/mod.rs @@ -882,22 +882,26 @@ impl Ownership { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ArgsOwnership { - pub non_defaults: Vec, - pub var_params: Option, + pub non_defaults: Vec<(Option, Ownership)>, + pub var_params: Option<(Str, Ownership)>, pub defaults: Vec<(Str, Ownership)>, } impl fmt::Display for ArgsOwnership { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "(")?; - for (i, o) in self.non_defaults.iter().enumerate() { + for (i, (name, o)) in self.non_defaults.iter().enumerate() { if i != 0 { write!(f, ", ")?; } - write!(f, "{o:?}")?; + if let Some(name) = name { + write!(f, "{name}: {o:?}")?; + } else { + write!(f, "{o:?}")?; + } } - if let Some(o) = self.var_params.as_ref() { - write!(f, ", ...{o:?}")?; + if let Some((name, o)) = self.var_params.as_ref() { + write!(f, ", ...{name}: {o:?}")?; } for (name, o) in self.defaults.iter() { write!(f, ", {name} := {o:?}")?; @@ -909,8 +913,8 @@ impl fmt::Display for ArgsOwnership { impl ArgsOwnership { pub const fn new( - non_defaults: Vec, - var_params: Option, + non_defaults: Vec<(Option, Ownership)>, + var_params: Option<(Str, Ownership)>, defaults: Vec<(Str, Ownership)>, ) -> Self { Self { @@ -1678,9 +1682,12 @@ impl Type { Self::RefMut { .. } => Ownership::RefMut, _ => Ownership::Owned, }; - nd_args.push(ownership); + nd_args.push((nd_param.name().cloned(), ownership)); } - let var_args = subr.var_params.as_ref().map(|t| t.typ().ownership()); + let var_args = subr + .var_params + .as_ref() + .map(|t| (t.name().unwrap().clone(), t.typ().ownership())); let mut d_args = vec![]; for d_param in subr.default_params.iter() { let ownership = match d_param.typ() {