diff --git a/py/compile.c b/py/compile.c index 532e29a233..2580cbeb51 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2789,14 +2789,17 @@ void compile_node(compiler_t *comp, mp_parse_node_t pn) { void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star, bool allow_annotations) { // TODO verify that *k and **k are last etc - qstr param_name = 0; + qstr param_name = MP_QSTR_NULL; + uint param_flag = ID_FLAG_IS_PARAM; mp_parse_node_t pn_annotation = MP_PARSE_NODE_NULL; if (MP_PARSE_NODE_IS_ID(pn)) { param_name = MP_PARSE_NODE_LEAF_ARG(pn); if (comp->have_star) { - // comes after a bare star, so doesn't count as a parameter + // comes after a star, so counts as a keyword-only parameter + comp->scope_cur->num_kwonly_args += 1; } else { - comp->scope_cur->num_params += 1; + // comes before a star, so counts as a positional parameter + comp->scope_cur->num_pos_args += 1; } } else { assert(MP_PARSE_NODE_IS_STRUCT(pn)); @@ -2822,12 +2825,15 @@ void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_ki } */ if (comp->have_star) { - // comes after a bare star, so doesn't count as a parameter + // comes after a star, so counts as a keyword-only parameter + comp->scope_cur->num_kwonly_args += 1; } else { - comp->scope_cur->num_params += 1; + // comes before a star, so counts as a positional parameter + comp->scope_cur->num_pos_args += 1; } } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_star) { comp->have_star = true; + param_flag = ID_FLAG_IS_PARAM | ID_FLAG_IS_STAR_PARAM; if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { // bare star // TODO see http://www.python.org/dev/peps/pep-3102/ @@ -2848,6 +2854,7 @@ void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_ki } } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_dbl_star) { param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + param_flag = ID_FLAG_IS_PARAM | ID_FLAG_IS_DBL_STAR_PARAM; if (allow_annotations && !MP_PARSE_NODE_IS_NULL(pns->nodes[1])) { // this parameter has an annotation pn_annotation = pns->nodes[1]; @@ -2859,7 +2866,7 @@ void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_ki } } - if (param_name != 0) { + if (param_name != MP_QSTR_NULL) { if (!MP_PARSE_NODE_IS_NULL(pn_annotation)) { // TODO this parameter has an annotation } @@ -2870,15 +2877,15 @@ void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_ki return; } id_info->kind = ID_INFO_KIND_LOCAL; - id_info->flags |= ID_FLAG_IS_PARAM; + id_info->flags = param_flag; } } -void compile_scope_func_param(compiler_t *comp, mp_parse_node_t pn) { +STATIC void compile_scope_func_param(compiler_t *comp, mp_parse_node_t pn) { compile_scope_func_lambda_param(comp, pn, PN_typedargslist_name, PN_typedargslist_star, PN_typedargslist_dbl_star, true); } -void compile_scope_lambda_param(compiler_t *comp, mp_parse_node_t pn) { +STATIC void compile_scope_lambda_param(compiler_t *comp, mp_parse_node_t pn) { compile_scope_func_lambda_param(comp, pn, PN_varargslist_name, PN_varargslist_star, PN_varargslist_dbl_star, false); } @@ -3051,7 +3058,7 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qstr_arg, &added); assert(added); id_info->kind = ID_INFO_KIND_LOCAL; - scope->num_params = 1; + scope->num_pos_args = 1; } if (scope->kind == SCOPE_LIST_COMP) { @@ -3144,7 +3151,7 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind if (comp->pass == PASS_2) { mp_parse_node_t *pn_params; int n_params = list_get(&pns->nodes[1], PN_typedargslist, &pn_params); - scope->num_params = EMIT_INLINE_ASM_ARG(count_params, n_params, pn_params); + scope->num_pos_args = EMIT_INLINE_ASM_ARG(count_params, n_params, pn_params); } assert(MP_PARSE_NODE_IS_NULL(pns->nodes[2])); // type @@ -3235,6 +3242,25 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind #endif STATIC void compile_scope_compute_things(compiler_t *comp, scope_t *scope) { +#if !MICROPY_EMIT_CPYTHON + // in Micro Python we put the *x parameter after all other parameters (except **y) + if (scope->scope_flags & MP_SCOPE_FLAG_VARARGS) { + id_info_t *id_param = NULL; + for (int i = scope->id_info_len - 1; i >= 0; i--) { + id_info_t *id = &scope->id_info[i]; + if (id->flags & ID_FLAG_IS_STAR_PARAM) { + if (id_param != NULL) { + // swap star param with last param + id_info_t temp = *id_param; *id_param = *id; *id = temp; + } + break; + } else if (id_param == NULL && id->flags == ID_FLAG_IS_PARAM) { + id_param = id; + } + } + } +#endif + // in functions, turn implicit globals into explicit globals // compute the index of each local scope->num_locals = 0; @@ -3247,10 +3273,9 @@ STATIC void compile_scope_compute_things(compiler_t *comp, scope_t *scope) { if (scope->kind >= SCOPE_FUNCTION && scope->kind <= SCOPE_GEN_EXPR && id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { id->kind = ID_INFO_KIND_GLOBAL_EXPLICIT; } - // note: params always count for 1 local, even if they are a cell + // params always count for 1 local, even if they are a cell if (id->kind == ID_INFO_KIND_LOCAL || (id->flags & ID_FLAG_IS_PARAM)) { - id->local_num = scope->num_locals; - scope->num_locals += 1; + id->local_num = scope->num_locals++; } } @@ -3309,7 +3334,7 @@ STATIC void compile_scope_compute_things(compiler_t *comp, scope_t *scope) { id->local_num += num_free; } } - scope->num_params += num_free; // free vars are counted as params for passing them into the function + scope->num_pos_args += num_free; // free vars are counted as params for passing them into the function scope->num_locals += num_free; } #endif diff --git a/py/emitbc.c b/py/emitbc.c index f58cec1f1f..3e3f0751ca 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -300,14 +300,14 @@ STATIC void emit_bc_end_pass(emit_t *emit) { emit->code_base = m_new0(byte, emit->code_info_size + emit->byte_code_size); } else if (emit->pass == PASS_3) { - qstr *arg_names = m_new(qstr, emit->scope->num_params); - for (int i = 0; i < emit->scope->num_params; i++) { + qstr *arg_names = m_new(qstr, emit->scope->num_pos_args + emit->scope->num_kwonly_args); + for (int i = 0; i < emit->scope->num_pos_args + emit->scope->num_kwonly_args; i++) { arg_names[i] = emit->scope->id_info[i].qstr; } mp_emit_glue_assign_byte_code(emit->scope->raw_code, emit->code_base, emit->code_info_size + emit->byte_code_size, - emit->scope->num_params, emit->scope->num_locals, - emit->scope->scope_flags, arg_names); + emit->scope->num_pos_args, emit->scope->num_kwonly_args, arg_names, + emit->scope->scope_flags); } } diff --git a/py/emitglue.c b/py/emitglue.c index dddeea1b57..d39f554a3b 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -47,16 +47,22 @@ mp_raw_code_t *mp_emit_glue_new_raw_code(void) { return rc; } -void mp_emit_glue_assign_byte_code(mp_raw_code_t *rc, byte *code, uint len, int n_args, int n_locals, uint scope_flags, qstr *arg_names) { +void mp_emit_glue_assign_byte_code(mp_raw_code_t *rc, byte *code, uint len, uint n_pos_args, uint n_kwonly_args, qstr *arg_names, uint scope_flags) { rc->kind = MP_CODE_BYTE; rc->scope_flags = scope_flags; - rc->n_args = n_args; + rc->n_pos_args = n_pos_args; + rc->n_kwonly_args = n_kwonly_args; + rc->arg_names = arg_names; rc->u_byte.code = code; rc->u_byte.len = len; - rc->arg_names = arg_names; #ifdef DEBUG_PRINT - DEBUG_printf("assign byte code: code=%p len=%u n_args=%d n_locals=%d\n", code, len, n_args, n_locals); + DEBUG_printf("assign byte code: code=%p len=%u n_pos_args=%d n_kwonly_args=%d flags=%x\n", code, len, n_pos_args, n_kwonly_args, scope_flags); + DEBUG_printf(" arg names:"); + for (int i = 0; i < n_pos_args + n_kwonly_args; i++) { + DEBUG_printf(" %s", qstr_str(arg_names[i])); + } + DEBUG_printf("\n"); for (int i = 0; i < 128 && i < len; i++) { if (i > 0 && i % 16 == 0) { DEBUG_printf("\n"); @@ -73,7 +79,7 @@ void mp_emit_glue_assign_byte_code(mp_raw_code_t *rc, byte *code, uint len, int void mp_emit_glue_assign_native_code(mp_raw_code_t *rc, void *fun, uint len, int n_args) { rc->kind = MP_CODE_NATIVE; rc->scope_flags = 0; - rc->n_args = n_args; + rc->n_pos_args = n_args; rc->u_native.fun = fun; #ifdef DEBUG_PRINT @@ -99,7 +105,7 @@ void mp_emit_glue_assign_native_code(mp_raw_code_t *rc, void *fun, uint len, int void mp_emit_glue_assign_inline_asm_code(mp_raw_code_t *rc, void *fun, uint len, int n_args) { rc->kind = MP_CODE_INLINE_ASM; rc->scope_flags = 0; - rc->n_args = n_args; + rc->n_pos_args = n_args; rc->u_inline_asm.fun = fun; #ifdef DEBUG_PRINT @@ -136,13 +142,13 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp mp_obj_t fun; switch (rc->kind) { case MP_CODE_BYTE: - fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_args, def_args, rc->u_byte.code); + fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_pos_args, rc->n_kwonly_args, def_args, rc->u_byte.code); break; case MP_CODE_NATIVE: - fun = mp_make_function_n(rc->n_args, rc->u_native.fun); + fun = mp_make_function_n(rc->n_pos_args, rc->u_native.fun); break; case MP_CODE_INLINE_ASM: - fun = mp_obj_new_fun_asm(rc->n_args, rc->u_inline_asm.fun); + fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->u_inline_asm.fun); break; default: // raw code was never set (this should not happen) diff --git a/py/emitglue.h b/py/emitglue.h index 9aea2e4d45..f15e5b7161 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -9,9 +9,11 @@ typedef enum { } mp_raw_code_kind_t; typedef struct _mp_code_t { - mp_raw_code_kind_t kind : 8; - uint scope_flags : 8; - uint n_args : 16; + mp_raw_code_kind_t kind : 3; + uint scope_flags : 7; + uint n_pos_args : 11; + uint n_kwonly_args : 11; + qstr *arg_names; union { struct { byte *code; @@ -24,7 +26,6 @@ typedef struct _mp_code_t { void *fun; } u_inline_asm; }; - qstr *arg_names; } mp_raw_code_t; void mp_emit_glue_init(void); @@ -32,7 +33,7 @@ void mp_emit_glue_deinit(void); mp_raw_code_t *mp_emit_glue_new_raw_code(void); -void mp_emit_glue_assign_byte_code(mp_raw_code_t *rc, byte *code, uint len, int n_args, int n_locals, uint scope_flags, qstr *arg_names); +void mp_emit_glue_assign_byte_code(mp_raw_code_t *rc, byte *code, uint len, uint n_pos_args, uint n_kwonly_args, qstr *arg_names, uint scope_flags); void mp_emit_glue_assign_native_code(mp_raw_code_t *rc, void *f, uint len, int n_args); void mp_emit_glue_assign_inline_asm_code(mp_raw_code_t *rc, void *f, uint len, int n_args); diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 8b0e6af5e5..d83aa8b594 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -73,7 +73,7 @@ STATIC bool emit_inline_thumb_end_pass(emit_inline_asm_t *emit) { if (emit->pass == PASS_3) { void *f = asm_thumb_get_code(emit->as); - mp_emit_glue_assign_inline_asm_code(emit->scope->raw_code, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params); + mp_emit_glue_assign_inline_asm_code(emit->scope->raw_code, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args); } return emit->success; diff --git a/py/emitnative.c b/py/emitnative.c index 79d6da67c1..1949009ebc 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -230,7 +230,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop // initialise locals from parameters #if N_X64 - for (int i = 0; i < scope->num_params; i++) { + for (int i = 0; i < scope->num_pos_args; i++) { if (i == 0) { asm_x64_mov_r64_to_r64(emit->as, REG_ARG_1, REG_LOCAL_1); } else if (i == 1) { @@ -243,7 +243,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop } } #elif N_THUMB - for (int i = 0; i < scope->num_params; i++) { + for (int i = 0; i < scope->num_pos_args; i++) { if (i == 0) { asm_thumb_mov_reg_reg(emit->as, REG_LOCAL_1, REG_ARG_1); } else if (i == 1) { @@ -283,10 +283,10 @@ STATIC void emit_native_end_pass(emit_t *emit) { if (emit->pass == PASS_3) { #if N_X64 void *f = asm_x64_get_code(emit->as); - mp_emit_glue_assign_native_code(emit->scope->raw_code, f, asm_x64_get_code_size(emit->as), emit->scope->num_params); + mp_emit_glue_assign_native_code(emit->scope->raw_code, f, asm_x64_get_code_size(emit->as), emit->scope->num_pos_args); #elif N_THUMB void *f = asm_thumb_get_code(emit->as); - mp_emit_glue_assign_native_code(emit->scope->raw_code, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params); + mp_emit_glue_assign_native_code(emit->scope->raw_code, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args); #endif } } diff --git a/py/obj.h b/py/obj.h index 8b835b28ee..5bf5352a1e 100644 --- a/py/obj.h +++ b/py/obj.h @@ -356,7 +356,7 @@ mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg); mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, uint n_args, const mp_obj_t *args); mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg); mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) -mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t def_args, const byte *code); +mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n_kwonly_args, mp_obj_t def_args, const byte *code); mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun); mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun); mp_obj_t mp_obj_new_closure(mp_obj_t fun, uint n_closed, const mp_obj_t *closed); diff --git a/py/objfun.c b/py/objfun.c index 940b64a66e..a5266eb184 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -152,17 +152,18 @@ bool mp_obj_fun_prepare_simple_args(mp_obj_t self_in, uint n_args, uint n_kw, co mp_obj_fun_bc_t *self = self_in; assert(n_kw == 0); + assert(self->n_kwonly_args == 0); assert(self->takes_var_args == 0); assert(self->takes_kw_args == 0); mp_obj_t *extra_args = self->extra_args + self->n_def_args; uint n_extra_args = 0; - if (n_args > self->n_args) { - goto arg_error; + if (n_args > self->n_pos_args) { + goto arg_error; } else { - extra_args -= self->n_args - n_args; - n_extra_args += self->n_args - n_args; + extra_args -= self->n_pos_args - n_args; + n_extra_args += self->n_pos_args - n_args; } *out_args1 = args; *out_args1_len = n_args; @@ -171,10 +172,15 @@ bool mp_obj_fun_prepare_simple_args(mp_obj_t self_in, uint n_args, uint n_kw, co return true; arg_error: - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_args, n_args)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_pos_args, n_args)); } STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) { + // This function is pretty complicated. It's main aim is to be efficient in speed and RAM + // usage for the common case of positional only args. + // + // extra_args layout: def_args, var_arg tuple, kwonly args, var_kw dict + DEBUG_printf("Input n_args: %d, n_kw: %d\n", n_args, n_kw); DEBUG_printf("Input pos args: "); dump_args(args, n_args); @@ -187,19 +193,18 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o mp_obj_t *extra_args = self->extra_args + self->n_def_args; uint n_extra_args = 0; - // check positional arguments - if (n_args > self->n_args) { + if (n_args > self->n_pos_args) { // given more than enough arguments if (!self->takes_var_args) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, - "function takes %d positional arguments but %d were given", self->n_args, n_args)); + "function takes %d positional arguments but %d were given", self->n_pos_args, n_args)); } // put extra arguments in varargs tuple - *extra_args = mp_obj_new_tuple(n_args - self->n_args, args + self->n_args); + *extra_args = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args); n_extra_args = 1; - n_args = self->n_args; + n_args = self->n_pos_args; } else { if (self->takes_var_args) { DEBUG_printf("passing empty tuple as *args\n"); @@ -209,14 +214,14 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o // Apply processing and check below only if we don't have kwargs, // otherwise, kw handling code below has own extensive checks. if (n_kw == 0) { - if (n_args >= self->n_args - self->n_def_args) { + if (n_args >= self->n_pos_args - self->n_def_args) { // given enough arguments, but may need to use some default arguments - extra_args -= self->n_args - n_args; - n_extra_args += self->n_args - n_args; + extra_args -= self->n_pos_args - n_args; + n_extra_args += self->n_pos_args - n_args; } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes at least %d positional arguments but %d were given", - self->n_args - self->n_def_args, n_args)); + self->n_pos_args - self->n_def_args, n_args)); } } } @@ -229,13 +234,13 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o // So, we have 2 choices: allocate it unconditionally at the top of function // (wastes stack), or use alloca which is guaranteed to dealloc on func exit. //mp_obj_t flat_args[self->n_args]; - mp_obj_t *flat_args = alloca(self->n_args * sizeof(mp_obj_t)); - for (int i = self->n_args - 1; i >= 0; i--) { + mp_obj_t *flat_args = alloca((self->n_pos_args + self->n_kwonly_args) * sizeof(mp_obj_t)); + for (int i = self->n_pos_args + self->n_kwonly_args - 1; i >= 0; i--) { flat_args[i] = MP_OBJ_NULL; } memcpy(flat_args, args, sizeof(*args) * n_args); DEBUG_printf("Initial args: "); - dump_args(flat_args, self->n_args); + dump_args(flat_args, self->n_pos_args + self->n_kwonly_args); mp_obj_t dict = MP_OBJ_NULL; if (self->takes_kw_args) { @@ -243,7 +248,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o } for (uint i = 0; i < n_kw; i++) { qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]); - for (uint j = 0; j < self->n_args; j++) { + for (uint j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) { if (arg_name == self->args[j]) { if (flat_args[j] != MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, @@ -261,10 +266,10 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o continue2:; } DEBUG_printf("Args with kws flattened: "); - dump_args(flat_args, self->n_args); + dump_args(flat_args, self->n_pos_args + self->n_kwonly_args); - // Now fill in defaults - mp_obj_t *d = &flat_args[self->n_args - 1]; + // Now fill in defaults for positional args + mp_obj_t *d = &flat_args[self->n_pos_args - 1]; mp_obj_t *s = &self->extra_args[self->n_def_args - 1]; for (int i = self->n_def_args; i > 0; i--, d--, s--) { if (*d == MP_OBJ_NULL) { @@ -272,9 +277,9 @@ continue2:; } } DEBUG_printf("Args after filling defaults: "); - dump_args(flat_args, self->n_args); + dump_args(flat_args, self->n_pos_args + self->n_kwonly_args); - // Now check that all mandatory args specified + // Check that all mandatory positional args are specified while (d >= flat_args) { if (*d-- == MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, @@ -282,8 +287,16 @@ continue2:; } } + // Check that all mandatory keyword args are specified + for (int i = 0; i < self->n_kwonly_args; i++) { + if (flat_args[self->n_pos_args + i] == MP_OBJ_NULL) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, + "function missing required keyword argument '%s'", qstr_str(self->args[self->n_pos_args + i]))); + } + } + args = flat_args; - n_args = self->n_args; + n_args = self->n_pos_args + self->n_kwonly_args; if (self->takes_kw_args) { extra_args[n_extra_args] = dict; @@ -291,6 +304,10 @@ continue2:; } } else { // no keyword arguments given + if (self->n_kwonly_args != 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, + "function missing keyword-only argument")); + } if (self->takes_kw_args) { extra_args[n_extra_args] = mp_obj_new_dict(0); n_extra_args += 1; @@ -320,7 +337,7 @@ const mp_obj_type_t mp_type_fun_bc = { .binary_op = fun_binary_op, }; -mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t def_args_in, const byte *code) { +mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n_kwonly_args, mp_obj_t def_args_in, const byte *code) { uint n_def_args = 0; uint n_extra_args = 0; mp_obj_tuple_t *def_args = def_args_in; @@ -339,14 +356,22 @@ mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t d o->base.type = &mp_type_fun_bc; o->globals = mp_globals_get(); o->args = args; - o->n_args = n_args; + o->n_pos_args = n_pos_args; + o->n_kwonly_args = n_kwonly_args; o->n_def_args = n_def_args; o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0; o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0; o->bytecode = code; + memset(o->extra_args, 0, n_extra_args * sizeof(mp_obj_t)); if (def_args != MP_OBJ_NULL) { memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t)); } + if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { + o->extra_args[n_def_args] = MP_OBJ_NULL; + } + if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { + o->extra_args[n_extra_args - 1] = MP_OBJ_NULL; + } return o; } diff --git a/py/objfun.h b/py/objfun.h index 07ca623ece..8207b161a2 100644 --- a/py/objfun.h +++ b/py/objfun.h @@ -1,8 +1,9 @@ typedef struct _mp_obj_fun_bc_t { mp_obj_base_t base; mp_obj_dict_t *globals; // the context within which this function was defined - machine_uint_t n_args : 15; // number of arguments this function takes - machine_uint_t n_def_args : 15; // number of default arguments + machine_uint_t n_pos_args : 16; // number of arguments this function takes + machine_uint_t n_kwonly_args : 16; // number of arguments this function takes + machine_uint_t n_def_args : 16; // number of default arguments machine_uint_t takes_var_args : 1; // set if this function takes variable args machine_uint_t takes_kw_args : 1; // set if this function takes keyword args const byte *bytecode; // bytecode for the function diff --git a/py/scope.c b/py/scope.c index 391ad5da87..1f92729d59 100644 --- a/py/scope.c +++ b/py/scope.c @@ -236,7 +236,7 @@ void scope_print_info(scope_t *s) { printf("\n"); */ printf(" flags %04x\n", s->scope_flags); - printf(" argcount %d\n", s->num_params); + printf(" argcount %d\n", s->num_pos_args); printf(" nlocals %d\n", s->num_locals); printf(" stacksize %d\n", s->stack_size); } diff --git a/py/scope.h b/py/scope.h index 6e0990ea9c..7ef81b0c83 100644 --- a/py/scope.h +++ b/py/scope.h @@ -8,7 +8,8 @@ enum { enum { ID_FLAG_IS_PARAM = 0x01, - ID_FLAG_IS_DELETED = 0x02, + ID_FLAG_IS_STAR_PARAM = 0x02, + ID_FLAG_IS_DBL_STAR_PARAM = 0x04, }; typedef struct _id_info_t { @@ -32,7 +33,8 @@ typedef struct _scope_t { mp_raw_code_t *raw_code; uint8_t scope_flags; // see runtime0.h uint8_t emit_options; // see compile.h - uint16_t num_params; + uint16_t num_pos_args; + uint16_t num_kwonly_args; uint16_t num_locals; uint16_t stack_size; // maximum size of the locals stack uint16_t exc_stack_size; // maximum size of the exception stack diff --git a/tests/basics/fun-kwonly.py b/tests/basics/fun-kwonly.py new file mode 100644 index 0000000000..8bda68d95a --- /dev/null +++ b/tests/basics/fun-kwonly.py @@ -0,0 +1,68 @@ +# to test keyword-only arguments + +# simplest case +def f(*, a): + print(a) + +f(a=1) + +# with 2 keyword-only args +def f(*, a, b): + print(a, b) + +f(a=1, b=2) +f(b=1, a=2) + +# positional followed by bare star +def f(a, *, b, c): + print(a, b, c) + +f(1, b=3, c=4) +f(1, c=3, b=4) +f(1, **{'b':'3', 'c':4}) + +try: + f(1) +except TypeError: + print("TypeError") + +try: + f(1, b=2) +except TypeError: + print("TypeError") + +try: + f(1, c=2) +except TypeError: + print("TypeError") + +# with **kw +def f(a, *, b, **kw): + print(a, b, kw) + +f(1, b=2) +f(1, b=2, c=3) + +## with a default value; not currently working +#def g(a, *, b=2, c): +# print(a, b, c) +# +#g(1, c=3) +#g(1, b=3, c=4) +#g(1, **{'c':3}) +#g(1, **{'b':'3', 'c':4}) + +# with named star +def f(*a, b, c): + print(a, b, c) + +f(b=1, c=2) +f(c=1, b=2) + +# with positional and named star +def f(a, *b, c): + print(a, b, c) + +f(1, c=2) +f(1, 2, c=3) +f(a=1, c=3)