Skip to content

Commit

Permalink
Merge pull request #377 from erg-lang/feature-var-params-fn
Browse files Browse the repository at this point in the history
Feature: user-defined variable-length param
  • Loading branch information
mtshiba authored Feb 1, 2023
2 parents adbec23 + 208433d commit c5e2b0d
Show file tree
Hide file tree
Showing 29 changed files with 550 additions and 215 deletions.
5 changes: 4 additions & 1 deletion crates/els/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,13 @@ impl ASTSemanticState {

fn gen_from_args(&mut self, args: Args) -> Vec<SemanticToken> {
let mut tokens = vec![];
let (pos_args, kw_args, ..) = args.deconstruct();
let (pos_args, var_args, kw_args, ..) = args.deconstruct();
for arg in pos_args {
tokens.extend(self.gen_from_expr(arg.expr));
}
if let Some(var_args) = var_args {
tokens.extend(self.gen_from_expr(var_args.expr));
}
for arg in kw_args {
tokens.extend(self.gen_from_expr(arg.expr));
}
Expand Down
36 changes: 29 additions & 7 deletions crates/erg_compiler/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,12 @@ impl PyCodeGenUnit {
filename: S,
name: T,
firstlineno: u32,
flags: u32,
) -> Self {
Self {
id,
py_version,
codeobj: CodeObj::empty(params, filename, name, firstlineno),
codeobj: CodeObj::empty(params, filename, name, firstlineno, flags),
stack_len: 0,
prev_lineno: firstlineno,
lasti: 0,
Expand Down Expand Up @@ -890,7 +891,7 @@ impl PyCodeGenerator {
.non_defaults
.iter()
.map(|p| p.inspect().map(|s| &s[..]).unwrap_or("_"))
.chain(if let Some(var_args) = &params.var_args {
.chain(if let Some(var_args) = &params.var_params {
vec![var_args.inspect().map(|s| &s[..]).unwrap_or("_")]
} else {
vec![]
Expand Down Expand Up @@ -1060,6 +1061,7 @@ impl PyCodeGenerator {
Str::rc(self.cfg.input.enclosed_name()),
&name,
firstlineno,
0,
));
let mod_name = self.toplevel_block_codeobj().name.clone();
self.emit_load_const(mod_name);
Expand Down Expand Up @@ -1128,6 +1130,7 @@ impl PyCodeGenerator {
Str::rc(self.cfg.input.enclosed_name()),
ident.inspect(),
ident.ln_begin().unwrap(),
0,
));
self.emit_load_const(ValueObj::None);
self.write_instr(RETURN_VALUE);
Expand Down Expand Up @@ -1263,7 +1266,12 @@ impl PyCodeGenerator {
self.stack_dec_n(defaults_len - 1);
make_function_flag += MakeFunctionFlags::Defaults as usize;
}
let code = self.emit_block(body.block, Some(name.clone()), params);
let flags = if sig.params.var_params.is_some() {
CodeObjFlags::VarArgs as u32
} else {
0
};
let code = self.emit_block(body.block, Some(name.clone()), params, flags);
// code.flags += CodeObjFlags::Optimized as u32;
self.register_cellvars(&mut make_function_flag);
self.emit_load_const(code);
Expand Down Expand Up @@ -1302,7 +1310,12 @@ impl PyCodeGenerator {
self.stack_dec_n(defaults_len - 1);
make_function_flag += MakeFunctionFlags::Defaults as usize;
}
let code = self.emit_block(lambda.body, Some("<lambda>".into()), params);
let flags = if lambda.params.var_params.is_some() {
CodeObjFlags::VarArgs as u32
} else {
0
};
let code = self.emit_block(lambda.body, Some("<lambda>".into()), params, flags);
self.register_cellvars(&mut make_function_flag);
self.emit_load_const(code);
if self.py_version.minor < Some(11) {
Expand Down Expand Up @@ -2689,7 +2702,7 @@ impl PyCodeGenerator {
Expr::Dict(dict) => self.emit_dict(dict),
Expr::Record(rec) => self.emit_record(rec),
Expr::Code(code) => {
let code = self.emit_block(code, None, vec![]);
let code = self.emit_block(code, None, vec![], 0);
self.emit_load_const(code);
}
Expr::Compound(chunks) => self.emit_compound(chunks),
Expand Down Expand Up @@ -2718,7 +2731,7 @@ impl PyCodeGenerator {
Expr::Dict(dict) => self.emit_dict(dict),
Expr::Record(rec) => self.emit_record(rec),
Expr::Code(code) => {
let code = self.emit_block(code, None, vec![]);
let code = self.emit_block(code, None, vec![], 0);
self.emit_load_const(code);
}
Expr::Compound(chunks) => self.emit_compound(chunks),
Expand Down Expand Up @@ -2777,6 +2790,7 @@ impl PyCodeGenerator {
Str::rc(self.cfg.input.enclosed_name()),
&name,
firstlineno,
0,
));
let init_stack_len = self.stack_len();
let mod_name = self.toplevel_block_codeobj().name.clone();
Expand Down Expand Up @@ -2943,7 +2957,13 @@ impl PyCodeGenerator {
}
}

fn emit_block(&mut self, block: Block, opt_name: Option<Str>, params: Vec<Str>) -> CodeObj {
fn emit_block(
&mut self,
block: Block,
opt_name: Option<Str>,
params: Vec<Str>,
flags: u32,
) -> CodeObj {
log!(info "entered {}", fn_name!());
self.unit_size += 1;
let name = if let Some(name) = opt_name {
Expand All @@ -2962,6 +2982,7 @@ impl PyCodeGenerator {
Str::rc(self.cfg.input.enclosed_name()),
name,
firstlineno,
flags,
));
let idx_copy_free_vars = if self.py_version.minor >= Some(11) {
let idx_copy_free_vars = self.lasti();
Expand Down Expand Up @@ -3154,6 +3175,7 @@ impl PyCodeGenerator {
Str::rc(self.cfg.input.enclosed_name()),
"<module>",
1,
0,
));
if self.py_version.minor >= Some(11) {
self.write_instr(Opcode311::RESUME);
Expand Down
9 changes: 5 additions & 4 deletions crates/erg_compiler/context/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use crate::ty::typaram::{OpKind, TyParam};
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{ConstSubr, HasType, Predicate, SubrKind, Type, UserConstSubr, ValueArgs};

use crate::context::instantiate::ParamKind;
use crate::context::{ClassDefType, Context, ContextKind, RegistrationMode};
use crate::error::{EvalError, EvalErrors, EvalResult, SingleEvalResult};

Expand Down Expand Up @@ -451,19 +452,19 @@ impl Context {
let pt = self.instantiate_param_ty(
sig,
None,
None,
&mut tmp_tv_cache,
RegistrationMode::Normal,
ParamKind::NonDefault,
)?;
non_default_params.push(pt);
}
let var_params = if let Some(p) = lambda.sig.params.var_args.as_ref() {
let var_params = if let Some(p) = lambda.sig.params.var_params.as_ref() {
let pt = self.instantiate_param_ty(
p,
None,
None,
&mut tmp_tv_cache,
RegistrationMode::Normal,
ParamKind::VarParams,
)?;
Some(pt)
} else {
Expand All @@ -474,10 +475,10 @@ impl Context {
let expr = self.eval_const_expr(&sig.default_val)?;
let pt = self.instantiate_param_ty(
&sig.sig,
Some(expr.t()),
None,
&mut tmp_tv_cache,
RegistrationMode::Normal,
ParamKind::Default(expr.t()),
)?;
default_params.push(pt);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/erg_compiler/context/inquire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use crate::{feature_error, hir};
use RegistrationMode::*;
use Visibility::*;

use super::instantiate::ParamKind;
use super::{ContextKind, MethodInfo};

impl Context {
Expand Down Expand Up @@ -274,6 +275,7 @@ impl Context {
None,
&mut dummy_tv_cache,
Normal,
ParamKind::NonDefault,
)?;
union_pat_t = self.union(&union_pat_t, &rhs);
}
Expand Down
95 changes: 71 additions & 24 deletions crates/erg_compiler/context/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,38 @@ use crate::unreachable_error;
use TyParamOrdering::*;
use Type::*;

use crate::context::{Context, RegistrationMode};
use crate::context::{Context, DefaultInfo, RegistrationMode};
use crate::error::{TyCheckError, TyCheckErrors, TyCheckResult};
use crate::hir;
use crate::AccessKind;
use RegistrationMode::*;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParamKind {
NonDefault,
Default(Type),
VarParams,
KwParams,
}

impl ParamKind {
pub const fn is_var_params(&self) -> bool {
matches!(self, ParamKind::VarParams)
}
pub const fn is_kw_params(&self) -> bool {
matches!(self, ParamKind::KwParams)
}
pub const fn is_default(&self) -> bool {
matches!(self, ParamKind::Default(_))
}
pub const fn default_info(&self) -> DefaultInfo {
match self {
ParamKind::Default(_) => DefaultInfo::WithDefault,
_ => DefaultInfo::NonDefault,
}
}
}

/// Context for instantiating a quantified type
/// For example, cloning each type variable of quantified type `?T -> ?T` would result in `?1 -> ?2`.
/// To avoid this, an environment to store type variables is needed, which is `TyVarCache`.
Expand Down Expand Up @@ -252,24 +278,30 @@ impl Context {
let opt_decl_t = opt_decl_sig_t
.as_ref()
.and_then(|subr| subr.non_default_params.get(n));
match self.instantiate_param_ty(param, None, opt_decl_t, &mut tmp_tv_cache, mode) {
match self.instantiate_param_ty(
param,
opt_decl_t,
&mut tmp_tv_cache,
mode,
ParamKind::NonDefault,
) {
Ok(t) => non_defaults.push(t),
Err(es) => {
errs.extend(es);
non_defaults.push(ParamTy::pos(param.inspect().cloned(), Type::Failure));
}
}
}
let var_args = if let Some(var_args) = sig.params.var_args.as_ref() {
let var_args = if let Some(var_args) = sig.params.var_params.as_ref() {
let opt_decl_t = opt_decl_sig_t
.as_ref()
.and_then(|subr| subr.var_params.as_ref().map(|v| v.as_ref()));
let pt = match self.instantiate_param_ty(
var_args,
None,
opt_decl_t,
&mut tmp_tv_cache,
mode,
ParamKind::VarParams,
) {
Ok(pt) => pt,
Err(es) => {
Expand All @@ -288,10 +320,10 @@ impl Context {
.and_then(|subr| subr.default_params.get(n));
match self.instantiate_param_ty(
&p.sig,
Some(default_t),
opt_decl_t,
&mut tmp_tv_cache,
mode,
ParamKind::Default(default_t),
) {
Ok(t) => defaults.push(t),
Err(es) => {
Expand Down Expand Up @@ -345,6 +377,7 @@ impl Context {
opt_decl_t: Option<&ParamTy>,
tmp_tv_cache: &mut TyVarCache,
mode: RegistrationMode,
kind: ParamKind,
) -> TyCheckResult<Type> {
let spec_t = if let Some(spec_with_op) = &sig.t_spec {
self.instantiate_typespec(&spec_with_op.t_spec, opt_decl_t, tmp_tv_cache, mode, false)?
Expand All @@ -365,33 +398,47 @@ impl Context {
}
};
if let Some(decl_pt) = opt_decl_t {
self.sub_unify(
decl_pt.typ(),
&spec_t,
sig.t_spec
.as_ref()
.map(|s| s.loc())
.unwrap_or_else(|| sig.loc()),
None,
)?;
if kind.is_var_params() {
let spec_t = unknown_len_array_t(spec_t.clone());
self.sub_unify(
decl_pt.typ(),
&spec_t,
sig.t_spec
.as_ref()
.map(|s| s.loc())
.unwrap_or_else(|| sig.loc()),
None,
)?;
} else {
self.sub_unify(
decl_pt.typ(),
&spec_t,
sig.t_spec
.as_ref()
.map(|s| s.loc())
.unwrap_or_else(|| sig.loc()),
None,
)?;
}
}
Ok(spec_t)
}

pub(crate) fn instantiate_param_ty(
&self,
sig: &NonDefaultParamSignature,
opt_default_t: Option<Type>,
opt_decl_t: Option<&ParamTy>,
tmp_tv_cache: &mut TyVarCache,
mode: RegistrationMode,
kind: ParamKind,
) -> TyCheckResult<ParamTy> {
let t = self.instantiate_param_sig_t(sig, opt_decl_t, tmp_tv_cache, mode)?;
match (sig.inspect(), opt_default_t) {
(Some(name), Some(default_t)) => Ok(ParamTy::kw_default(name.clone(), t, default_t)),
(Some(name), None) => Ok(ParamTy::kw(name.clone(), t)),
(None, None) => Ok(ParamTy::anonymous(t)),
_ => unreachable!(),
let t = self.instantiate_param_sig_t(sig, opt_decl_t, tmp_tv_cache, mode, kind.clone())?;
match (sig.inspect(), kind) {
(Some(name), ParamKind::Default(default_t)) => {
Ok(ParamTy::kw_default(name.clone(), t, default_t))
}
(Some(name), _) => Ok(ParamTy::kw(name.clone(), t)),
(None, _) => Ok(ParamTy::anonymous(t)),
}
}

Expand Down Expand Up @@ -919,8 +966,8 @@ impl Context {
let non_defaults = try_map_mut(subr.non_defaults.iter(), |p| {
self.instantiate_func_param_spec(p, opt_decl_t, None, tmp_tv_ctx, mode)
})?;
let var_args = subr
.var_args
let var_params = subr
.var_params
.as_ref()
.map(|p| {
self.instantiate_func_param_spec(p, opt_decl_t, None, tmp_tv_ctx, mode)
Expand All @@ -947,7 +994,7 @@ impl Context {
Ok(subr_t(
SubrKind::from(subr.arrow.kind),
non_defaults,
var_args,
var_params,
defaults,
return_t,
))
Expand Down
Loading

0 comments on commit c5e2b0d

Please sign in to comment.