diff --git a/py/compile.c b/py/compile.c index 7a359e662e..772c099074 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2425,8 +2425,17 @@ static void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar } star_flags |= MP_EMIT_STAR_FLAG_SINGLE; star_args |= (mp_uint_t)1 << i; + + if (n_keyword == 0) { + // star-args before kwargs encoded as positional arg + n_positional++; + } else { + // star-args after kwargs encoded as kw arg with key=NULL + EMIT(load_null); + n_keyword++; + } + compile_node(comp, pns_arg->nodes[0]); - n_positional++; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) { star_flags |= MP_EMIT_STAR_FLAG_DOUBLE; // double-star args are stored as kw arg with key of None diff --git a/py/runtime.c b/py/runtime.c index 1836f5d92a..05565b802f 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -757,17 +757,30 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ mp_obj_t *args2; size_t args2_alloc; size_t args2_len = 0; + size_t n_args_star_args = n_args; // Try to get a hint for unpacked * args length ssize_t list_len = 0; - if (star_args != 0) { - for (size_t i = 0; i < n_args; i++) { - if ((star_args >> i) & 1) { - mp_obj_t len = mp_obj_len_maybe(args[i]); - if (len != MP_OBJ_NULL) { + if (star_args) { + // kw can also contain star args. + n_args_star_args += n_kw; + + for (size_t i = 0; i < n_args_star_args; i++) { + if (!((star_args >> i) & 1)) { + continue; + } + + mp_obj_t arg = i >= n_args ? args[n_args + 2 * (i - n_args) + 1] : args[i]; + + mp_obj_t len = mp_obj_len_maybe(arg); + + if (len != MP_OBJ_NULL) { + list_len += mp_obj_get_int(len); + + if (i < n_args) { // -1 accounts for 1 of n_args occupied by this arg - list_len += mp_obj_get_int(len) - 1; + list_len--; } } } @@ -779,9 +792,20 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ for (size_t i = 0; i < n_kw; i++) { mp_obj_t key = args[n_args + i * 2]; mp_obj_t value = args[n_args + i * 2 + 1]; - if (key == MP_OBJ_NULL && value != MP_OBJ_NULL && mp_obj_is_type(value, &mp_type_dict)) { + + if (key == MP_OBJ_NULL) { // -1 accounts for 1 of n_kw occupied by this arg - kw_dict_len += mp_obj_dict_len(value) - 1; + kw_dict_len--; + + if (((star_args >> (n_args + i)) & 1)) { + // star args were already handled above + continue; + } + + // double-star args + if (mp_obj_is_type(value, &mp_type_dict)) { + kw_dict_len += mp_obj_dict_len(value); + } } } @@ -814,8 +838,9 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ args2[args2_len++] = self; } - for (size_t i = 0; i < n_args; i++) { - mp_obj_t arg = args[i]; + for (size_t i = 0; i < n_args_star_args; i++) { + mp_obj_t arg = i >= n_args ? args[n_args + 2 * (i - n_args) + 1] : args[i]; + if ((star_args >> i) & 1) { // star arg if (mp_obj_is_type(arg, &mp_type_tuple) || mp_obj_is_type(arg, &mp_type_list)) { @@ -846,7 +871,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ args2[args2_len++] = item; } } - } else { + } else if (i < n_args) { // normal argument assert(args2_len < args2_alloc); args2[args2_len++] = arg; @@ -870,6 +895,11 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ mp_obj_t kw_key = args[n_args + i * 2]; mp_obj_t kw_value = args[n_args + i * 2 + 1]; if (kw_key == MP_OBJ_NULL) { + if ((star_args >> (n_args + i)) & 1) { + // star args have already been handled above + continue; + } + // double-star args if (mp_obj_is_type(kw_value, &mp_type_dict)) { // dictionary diff --git a/tests/basics/fun_callstar.py b/tests/basics/fun_callstar.py index 53d2ece3e1..9c3b199297 100644 --- a/tests/basics/fun_callstar.py +++ b/tests/basics/fun_callstar.py @@ -23,6 +23,10 @@ foo(*range(3)) # pos then iterator foo(1, *range(2, 4)) +# star after kw +foo(1, 2, c=3, *()) +foo(b=2, *(1,), c=3) + # an iterator with many elements def foo(*rest): print(rest)