From a53954f75a5a948f442bc342d929bb9198f4f9a7 Mon Sep 17 00:00:00 2001 From: jbbjarnason Date: Thu, 3 Mar 2022 17:23:34 +0000 Subject: [PATCH 1/4] py: PEP 570 add positional-only parameters. py/bc: Add pos-only to encode/decode bytecode. py/compile: Raise exception when slash in invalid position. py/grammar: Allow slash in argslist_item. py/objfun: Change according to py/bc. py/persistentcode: Add pos-only to bytecode_prelude. py/profile: Add pos-only to prelude extract. py/scope: Add pos-only to scope_t. tools/mpy-tool: Reflect on changes to prelude. --- py/bc.c | 9 +++++---- py/bc.h | 44 ++++++++++++++++++++++++++++++-------------- py/compile.c | 19 +++++++++++++++++++ py/emitbc.c | 2 +- py/emitnative.c | 2 +- py/grammar.h | 4 ++-- py/objfun.c | 10 +++++----- py/persistentcode.c | 1 + py/profile.c | 1 + py/scope.h | 1 + tools/mpy-tool.py | 26 ++++++++++++++++++++------ 11 files changed, 86 insertions(+), 33 deletions(-) diff --git a/py/bc.c b/py/bc.c index 2a21ffd4b7749..e4b680905ac45 100644 --- a/py/bc.c +++ b/py/bc.c @@ -143,8 +143,8 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw size_t n_state = code_state->n_state; // Decode prelude - size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; - MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args); + size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_posonly_args, n_kwonly_args, n_def_pos_args; + MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_posonly_args, n_kwonly_args, n_def_pos_args); MP_BC_PRELUDE_SIZE_DECODE(code_state->ip); (void)n_state_unused; (void)n_exc_stack_unused; @@ -214,7 +214,8 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw const uint8_t *arg_names = code_state->ip; arg_names = mp_decode_uint_skip(arg_names); - for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) { + // Iterating the function definition keywords + for (size_t j = n_posonly_args; j < n_pos_args + n_kwonly_args; j++) { qstr arg_qstr = mp_decode_uint(&arg_names); #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE arg_qstr = self->context->constants.qstr_table[arg_qstr]; @@ -267,7 +268,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw // Check that all mandatory keyword args are specified // Fill in default kw args if we have them const uint8_t *arg_names = mp_decode_uint_skip(code_state->ip); - for (size_t i = 0; i < n_pos_args; i++) { + for (size_t i = n_posonly_args; i < n_pos_args; i++) { arg_names = mp_decode_uint_skip(arg_names); } for (size_t i = 0; i < n_kwonly_args; i++) { diff --git a/py/bc.h b/py/bc.h index 3b7b625fecfdb..6798567571a81 100644 --- a/py/bc.h +++ b/py/bc.h @@ -32,12 +32,13 @@ // bytecode layout: // // func signature : var uint -// contains six values interleaved bit-wise as: xSSSSEAA [xFSSKAED repeated] +// contains six values interleaved bit-wise as: xSSSSEAA [xFSS(PorK)AED repeated] // x = extension another byte follows // S = n_state - 1 number of entries in Python value stack // E = n_exc_stack number of entries in exception stack // F = scope_flags four bits of flags, MP_SCOPE_FLAG_xxx // A = n_pos_args number of arguments this function takes +// P = n_posonly_args number of positional-only arguments this function takes // K = n_kwonly_args number of keyword-only arguments this function takes // D = n_def_pos_args number of default positional arguments // @@ -74,6 +75,7 @@ /*// Get values to store in prelude */ \ size_t F = scope->scope_flags & MP_SCOPE_FLAG_ALL_SIG; \ size_t A = scope->num_pos_args; \ + size_t P = scope->num_posonly_args; \ size_t K = scope->num_kwonly_args; \ size_t D = scope->num_def_pos_args; \ \ @@ -86,22 +88,30 @@ S >>= 4; \ E >>= 1; \ A >>= 2; \ - while (S | E | F | A | K | D) { \ + uint8_t n = 0; \ + while (S | E | F | A | P | K | D) { \ out_byte(out_env, 0x80 | z); \ - /* xFSSKAED */ \ - z = (F & 1) << 6 | (S & 3) << 4 | (K & 1) << 3 \ - | (A & 1) << 2 | (E & 1) << 1 | (D & 1); \ + uint16_t PorK = n % 2 == 0 ? P : K; \ + /* xFSS(PorK)AED */ \ + z = (F & 1) << 6 | (S & 3) << 4 | (PorK & 1) << 3 \ + | (A & 1) << 2 | (E & 1) << 1 \ + | (D & 1); \ S >>= 2; \ E >>= 1; \ F >>= 1; \ A >>= 1; \ - K >>= 1; \ + if (n % 2 == 0) { \ + P >>= 1; \ + } else { \ + K >>= 1; \ + } \ D >>= 1; \ + n++; \ } \ out_byte(out_env, z); \ } while (0) -#define MP_BC_PRELUDE_SIG_DECODE_INTO(ip, S, E, F, A, K, D) \ +#define MP_BC_PRELUDE_SIG_DECODE_INTO(ip, S, E, F, A, P, K, D) \ do { \ uint8_t z = *(ip)++; \ /* xSSSSEAA */ \ @@ -109,26 +119,31 @@ E = (z >> 2) & 0x1; \ F = 0; \ A = z & 0x3; \ + P = 0; \ K = 0; \ D = 0; \ for (unsigned n = 0; z & 0x80; ++n) { \ z = *(ip)++; \ - /* xFSSKAED */ \ - S |= (z & 0x30) << (2 * n); \ - E |= (z & 0x02) << n; \ + /* xFSS(PorK)AED */ \ F |= ((z & 0x40) >> 6) << n; \ + S |= (z & 0x30) << (2 * n); \ + if (n % 2 == 0) { \ + P |= ((z & 0x08) >> 3) << (n >> 1); \ + } else { \ + K |= ((z & 0x08) >> 3) << (n >> 1); \ + } \ A |= (z & 0x4) << n; \ - K |= ((z & 0x08) >> 3) << n; \ + E |= (z & 0x02) << n; \ D |= (z & 0x1) << n; \ } \ S += 1; \ } while (0) #define MP_BC_PRELUDE_SIG_DECODE(ip) \ - size_t n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; \ - MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args); \ + size_t n_state, n_exc_stack, scope_flags, n_pos_args, n_posonly_args, n_kwonly_args, n_def_pos_args; \ + MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack, scope_flags, n_pos_args, n_posonly_args, n_kwonly_args, n_def_pos_args); \ (void)n_state; (void)n_exc_stack; (void)scope_flags; \ - (void)n_pos_args; (void)n_kwonly_args; (void)n_def_pos_args + (void)n_pos_args; (void)n_kwonly_args; (void)n_def_pos_args; (void)n_posonly_args #define MP_BC_PRELUDE_SIZE_ENCODE(I, C, out_byte, out_env) \ do { \ @@ -178,6 +193,7 @@ typedef struct _mp_bytecode_prelude_t { uint n_exc_stack; uint scope_flags; uint n_pos_args; + uint n_posonly_args; uint n_kwonly_args; uint n_def_pos_args; qstr qstr_block_name_idx; diff --git a/py/compile.c b/py/compile.c index e4ead7f1ac2ac..ca3175277ae46 100644 --- a/py/compile.c +++ b/py/compile.c @@ -683,6 +683,12 @@ STATIC void compile_funcdef_lambdef_param(compiler_t *comp, mp_parse_node_t pn) int pn_kind; if (MP_PARSE_NODE_IS_ID(pn)) { pn_kind = -1; + } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_OP_SLASH)) { + if (comp->have_star) { + // Star cannot be in front of a positional only arguments. + compile_syntax_error(comp, pn, MP_ERROR_TEXT("invalid syntax")); + } + return; } else { assert(MP_PARSE_NODE_IS_STRUCT(pn)); pn_kind = MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t *)pn); @@ -2864,6 +2870,19 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn comp->scope_cur->num_pos_args += 1; } mp_emit_common_use_qstr(&comp->emit_common, param_name); + } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_OP_SLASH)) { + // if slash was used, but no args were detected, that is invalid by spec. + if (comp->scope_cur->num_pos_args == 0) { + compile_syntax_error(comp, pn, MP_ERROR_TEXT("invalid syntax")); + return; + } + // slash may have appeared twice if posonly_args is already > 0, invalid by spec. + if (comp->scope_cur->num_posonly_args > 0) { + compile_syntax_error(comp, pn, MP_ERROR_TEXT("invalid syntax")); + return; + } + // comes before a slash, so counts as positional-only parameter + comp->scope_cur->num_posonly_args = comp->scope_cur->num_pos_args; } else { assert(MP_PARSE_NODE_IS_STRUCT(pn)); pns = (mp_parse_node_struct_t *)pn; diff --git a/py/emitbc.c b/py/emitbc.c index a9d5b3799e63e..b32db70a57b94 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -323,7 +323,7 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { // Note: there is some wasted RAM here for the case of storing a qstr // for each closed-over variable, and maybe there is a better way to do // it, but that would require changes to mp_setup_code_state. - for (int i = 0; i < scope->num_pos_args + scope->num_kwonly_args; i++) { + for (int i = scope->num_posonly_args; i < scope->num_pos_args + scope->num_kwonly_args; i++) { qstr qst = MP_QSTR__star_; for (int j = 0; j < scope->id_info_len; ++j) { id_info_t *id = &scope->id_info[j]; diff --git a/py/emitnative.c b/py/emitnative.c index 2863984047a15..7f2572074dbc7 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -669,7 +669,7 @@ STATIC bool emit_native_end_pass(emit_t *emit) { // bytecode prelude: source info (function and argument qstrs) size_t info_start = mp_asm_base_get_code_pos(&emit->as->base); emit_native_write_code_info_qstr(emit, emit->scope->simple_name); - for (int i = 0; i < emit->scope->num_pos_args + emit->scope->num_kwonly_args; i++) { + for (int i = emit->scope->num_posonly_args; i < emit->scope->num_pos_args + emit->scope->num_kwonly_args; i++) { qstr qst = MP_QSTR__star_; for (int j = 0; j < emit->scope->id_info_len; ++j) { id_info_t *id = &emit->scope->id_info[j]; diff --git a/py/grammar.h b/py/grammar.h index 285fbded2fdae..808c6b97e4d72 100644 --- a/py/grammar.h +++ b/py/grammar.h @@ -75,14 +75,14 @@ DEF_RULE(funcdef, c(funcdef), and_blank(8), tok(KW_DEF), tok(NAME), tok(DEL_PARE DEF_RULE_NC(funcdefrettype, and_ident(2), tok(DEL_MINUS_MORE), rule(test)) // note: typedargslist lets through more than is allowed, compiler does further checks DEF_RULE_NC(typedargslist, list_with_end, rule(typedargslist_item), tok(DEL_COMMA)) -DEF_RULE_NC(typedargslist_item, or(3), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star)) +DEF_RULE_NC(typedargslist_item, or(4), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star), tok(OP_SLASH)) DEF_RULE_NC(typedargslist_name, and_ident(3), tok(NAME), opt_rule(generic_colon_test), opt_rule(generic_equal_test)) DEF_RULE_NC(typedargslist_star, and(2), tok(OP_STAR), opt_rule(tfpdef)) DEF_RULE_NC(typedargslist_dbl_star, and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(generic_colon_test)) DEF_RULE_NC(tfpdef, and(2), tok(NAME), opt_rule(generic_colon_test)) // note: varargslist lets through more than is allowed, compiler does further checks DEF_RULE_NC(varargslist, list_with_end, rule(varargslist_item), tok(DEL_COMMA)) -DEF_RULE_NC(varargslist_item, or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star)) +DEF_RULE_NC(varargslist_item, or(4), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star), tok(OP_SLASH)) DEF_RULE_NC(varargslist_name, and_ident(2), tok(NAME), opt_rule(generic_equal_test)) DEF_RULE_NC(varargslist_star, and(2), tok(OP_STAR), opt_rule(vfpdef)) DEF_RULE_NC(varargslist_dbl_star, and(2), tok(OP_DBL_STAR), tok(NAME)) diff --git a/py/objfun.c b/py/objfun.c index 3542cc0e3f0b8..cf45c17bef982 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -198,9 +198,9 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { #define DECODE_CODESTATE_SIZE(bytecode, n_state_out_var, state_size_out_var) \ { \ const uint8_t *ip = bytecode; \ - size_t n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args; \ - MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state_out_var, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args); \ - (void)scope_flags; (void)n_pos_args; (void)n_kwonly_args; (void)n_def_args; \ + size_t n_exc_stack, scope_flags, n_pos_args, n_posonly_args, n_kwonly_args, n_def_args; \ + MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state_out_var, n_exc_stack, scope_flags, n_pos_args, n_posonly_args, n_kwonly_args, n_def_args); \ + (void)scope_flags; (void)n_pos_args; (void)n_kwonly_args; (void)n_def_args; (void)n_posonly_args; \ \ /* state size in bytes */ \ state_size_out_var = n_state_out_var * sizeof(mp_obj_t) \ @@ -298,9 +298,9 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const } const byte *bytecode_ptr = self->bytecode; size_t n_state_unused, n_exc_stack_unused, scope_flags_unused; - size_t n_pos_args, n_kwonly_args, n_def_args_unused; + size_t n_pos_args, n_posonly_args_unused, n_kwonly_args, n_def_args_unused; MP_BC_PRELUDE_SIG_DECODE_INTO(bytecode_ptr, n_state_unused, n_exc_stack_unused, - scope_flags_unused, n_pos_args, n_kwonly_args, n_def_args_unused); + scope_flags_unused, n_pos_args, n_posonly_args_unused, n_kwonly_args, n_def_args_unused); // We can't check the case when an exception is returned in state[0] // and there are no arguments, because in this case our detection slot may have // been overwritten by the returned exception (which is allowed). diff --git a/py/persistentcode.c b/py/persistentcode.c index f64e383a61690..9e6464b860d3f 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -53,6 +53,7 @@ typedef struct _bytecode_prelude_t { uint n_exc_stack; uint scope_flags; uint n_pos_args; + uint n_posonly_args; uint n_kwonly_args; uint n_def_pos_args; uint code_info_size; diff --git a/py/profile.c b/py/profile.c index 4e23e9eac43b5..1e541a0d400ad 100644 --- a/py/profile.c +++ b/py/profile.c @@ -47,6 +47,7 @@ void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelud prelude->n_exc_stack = n_exc_stack; prelude->scope_flags = scope_flags; prelude->n_pos_args = n_pos_args; + prelude->n_posonly_args = n_posonly_args; prelude->n_kwonly_args = n_kwonly_args; prelude->n_def_pos_args = n_def_pos_args; diff --git a/py/scope.h b/py/scope.h index 5006deadea830..5aab8862fc695 100644 --- a/py/scope.h +++ b/py/scope.h @@ -79,6 +79,7 @@ typedef struct _scope_t { uint16_t scope_flags; // see runtime0.h uint16_t emit_options; // see emitglue.h uint16_t num_pos_args; + uint16_t num_posonly_args; uint16_t num_kwonly_args; uint16_t num_def_pos_args; uint16_t num_locals; diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 84c09a0c688d2..2a94b26bae607 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -374,21 +374,25 @@ def read_prelude_sig(read_byte): E = (z >> 2) & 0x1 F = 0 A = z & 0x3 + P = 0 K = 0 D = 0 n = 0 while z & 0x80: z = read_byte() - # xFSSKAED + # xFSS(PorK)AED S |= (z & 0x30) << (2 * n) E |= (z & 0x02) << n F |= ((z & 0x40) >> 6) << n A |= (z & 0x4) << n - K |= ((z & 0x08) >> 3) << n + if n % 2 == 0: + P |= ((z & 0x08) >> 3) << (n >> 1) + else: + P |= ((z & 0x08) >> 3) << (n >> 1) D |= (z & 0x1) << n n += 1 S += 1 - return S, E, F, A, K, D + return S, E, F, A, P, K, D def read_prelude_size(read_byte): @@ -418,6 +422,7 @@ def local_read_byte(): n_exc_stack, scope_flags, n_pos_args, + n_posonly_args, n_kwonly_args, n_def_pos_args, ) = read_prelude_sig(local_read_byte) @@ -445,7 +450,15 @@ def local_read_byte(): ip2, ip, ip_ref[0], - (n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args), + ( + n_state, + n_exc_stack, + scope_flags, + n_pos_args, + n_posonly_args, + n_kwonly_args, + n_def_pos_args, + ), args, ) @@ -791,8 +804,9 @@ def freeze_raw_code(self, qstr_links=(), type_sig=0): print(" .n_exc_stack = %u," % self.prelude[1]) print(" .scope_flags = %u," % self.prelude[2]) print(" .n_pos_args = %u," % self.prelude[3]) - print(" .n_kwonly_args = %u," % self.prelude[4]) - print(" .n_def_pos_args = %u," % self.prelude[5]) + print(" .n_posonly_args = %u," % self.prelude[4]) + print(" .n_kwonly_args = %u," % self.prelude[5]) + print(" .n_def_pos_args = %u," % self.prelude[6]) print(" .qstr_block_name_idx = %u," % self.names[0]) print( " .line_info = fun_data_%s + %u," From df23a784a1ad66e19f88b5344f70bab579964bcb Mon Sep 17 00:00:00 2001 From: Niraj Menon Date: Sat, 2 Apr 2022 11:59:51 +0000 Subject: [PATCH 2/4] tests/basics/fun_positional_args_only: New test. Ported over CPython tests to test positional-only args. Expected output of the run given. --- tests/basics/fun_positional_args_only.py | 382 +++++++++++++++++++ tests/basics/fun_positional_args_only.py.exp | 160 ++++++++ 2 files changed, 542 insertions(+) create mode 100644 tests/basics/fun_positional_args_only.py create mode 100644 tests/basics/fun_positional_args_only.py.exp diff --git a/tests/basics/fun_positional_args_only.py b/tests/basics/fun_positional_args_only.py new file mode 100644 index 0000000000000..e58a5dd578749 --- /dev/null +++ b/tests/basics/fun_positional_args_only.py @@ -0,0 +1,382 @@ +# positional_args_only tests + +# Tests sourced from +# https://github.com/python/cpython/blob/main/Lib/test/test_positional_only_arg.py + +# Not all tests were ported due to incompatibilities with +# MicroPython, such as required modules like pickle and dis +# not being implemented. + +def tryFunctionFail(s): + try: + eval(s) + print("FAILED: " + repr(s) + " did not throw syntax error.") + except Exception as e: + print("PASSED: " + repr(s) + " threw an error.") + +def tryFunctionPass(s): + try: + eval(s) + print("PASSED: " + repr(s) + " did not throw syntax error.") + except Exception as e: + print("FAILED: " + repr(s) + " incorrectly threw syntax error.") + +################################################## +# test_invalid_syntax_errors +# test_invalid_syntax_errors_async +invalid_tests = [ + "def f(a, b = 5, /, c): pass", + "def f(a = 5, b, /, c): pass", + "def f(a = 5, b=1, /, c, *, d=2): pass", + "def f(a = 5, b, /): pass", + "def f(*args, /): pass", + "def f(*args, a, /): pass", + "def f(**kwargs, /): pass", + "def f(/, a = 1): pass", + "def f(/, a): pass", + "def f(/): pass", + "def f(*, a, /): pass", + "def f(*, /, a): pass", + "def f(a, /, a): pass", + "def f(a, /, *, a): pass", + "def f(a, b/2, c): pass", + "def f(a, /, c, /): pass", + "def f(a, /, c, /, d): pass", + "def f(a, /, c, /, d, *, e): pass", + "def f(a, *, c, /, d, e): pass", + "async def f(a, b = 5, /, c): pass", + "async def f(a = 5, b, /, c): pass", + "async def f(a = 5, b=1, /, c, d=2): pass", + "async def f(a = 5, b, /): pass", + "async def f(*args, /): pass", + "async def f(*args, a, /): pass", + "async def f(**kwargs, /): pass", + "async def f(/, a = 1): pass", + "async def f(/, a): pass", + "async def f(/): pass", + "async def f(*, a, /): pass", + "async def f(*, /, a): pass", + "async def f(a, /, a): pass", + "async def f(a, /, *, a): pass", + "async def f(a, b/2, c): pass", + "async def f(a, /, c, /): pass", + "async def f(a, /, c, /, d): pass", + "async def f(a, /, c, /, d, *, e): pass", + "async def f(a, *, c, /, d, e): pass" +] +for s in invalid_tests: + tryFunctionFail(s) + +################################################## +# test_optional_positional_only_args +print("\ntest_optional_positional_only_args") +def assertEqual(f, res): + assert(f == res) + print("PASSED: Assertion check") + +def f(a, b=10, /, c=100): + return a + b + c + +assertEqual(f(1, 2, 3), 6) +assertEqual(f(1, 2, c=3), 6) +tryFunctionFail("f(1, b=2, c=3)") + +assertEqual(f(1, 2), 103) +tryFunctionFail("f(1, b=2)") +assertEqual(f(1, c=2), 13) + +def f(a=1, b=10, /, c=100): + return a + b + c + +assertEqual(f(1, 2, 3), 6) +assertEqual(f(1, 2, c=3), 6) +tryFunctionFail("f(1, b=2, c=3)") + +assertEqual(f(1, 2), 103) +tryFunctionFail("f(1, b=2)") +assertEqual(f(1, c=2), 13) + +################################################## +# test_pos_only_call_via_unpacking +print("\ntest_pos_only_call_via_unpacking") +def f(a, b, /): + return a + b +assertEqual(f(*[1, 2]), 3) + +################################################## +# test_use_positional_as_keyword +print("\ntest_use_positional_as_keyword") +def f(a, /): + pass +expected = r"f() got some positional-only arguments passed as keyword arguments: 'a'" +tryFunctionFail("f(a=1)") + +def f(a, /, b): + pass +expected = r"f() got some positional-only arguments passed as keyword arguments: 'a'" +tryFunctionFail("f(a=1, b=2)") + +def f(a, b, /): + pass +expected = r"f() got some positional-only arguments passed as keyword arguments: 'a, b'" +tryFunctionFail("f(a=1, b=2)") + +################################################## +# test_positional_only_and_arg_invalid_calls +print("\ntest_positional_only_and_arg_invalid_calls") + +def f(a, b, /, c): + pass +# r"f() missing 1 required positional argument: 'c'" +tryFunctionFail("f(1, 2)") +# r"f() missing 2 required positional arguments: 'b' and 'c'" +tryFunctionFail("f(1)") +# r"f() missing 3 required positional arguments: 'a', 'b', and 'c'" +tryFunctionFail("f()") +# r"f() takes 3 positional arguments but 4 were given" +tryFunctionFail("f(1, 2, 3, 4)") + +################################################## +# test_positional_only_and_optional_arg_invalid_calls +print("\ntest_positional_only_and_optional_arg_invalid_calls") +def f(a, b, /, c=3): + pass +tryFunctionPass("f(1, 2)") +# "f() missing 1 required positional argument: 'b'" +tryFunctionFail("f(1)") +# "f() missing 2 required positional arguments: 'a' and 'b'" +tryFunctionFail("f()") +# "f() takes from 2 to 3 positional arguments but 4 were given" +tryFunctionFail("f(1, 2, 3, 4)") + +################################################## +# test_positional_only_and_kwonlyargs_invalid_calls +def f(a, b, /, c, *, d, e): + pass +tryFunctionPass("f(1, 2, 3, d=1, e=2)") +# r"missing 1 required keyword-only argument: 'd'" +tryFunctionFail("f(1, 2, 3, e=2)") +# r"missing 2 required keyword-only arguments: 'd' and 'e'" +tryFunctionFail("f(1, 2, 3)") +# r"f() missing 1 required positional argument: 'c'" +tryFunctionFail("f(1, 2)") +# r"f() missing 2 required positional arguments: 'b' and 'c'" +tryFunctionFail("f(1)") +# r" missing 3 required positional arguments: 'a', 'b', and 'c'" +tryFunctionFail("f()") +# r"f() takes 3 positional arguments but 6 positional arguments \(and 2 keyword-only arguments\) were given" +tryFunctionFail("f(1, 2, 3, 4, 5, 6, d=7, e=8)") +# r"f() got an unexpected keyword argument 'f'" +tryFunctionFail("f(1, 2, 3, d=1, e=4, f=56)") + +################################################## +# test_positional_only_invalid_calls +def f(a, b, /): + pass +tryFunctionPass("f(1, 2)") +# f() missing 1 required positional argument: 'b'" +tryFunctionFail("f(1)") +# f() missing 2 required positional arguments: 'a' and 'b'" +tryFunctionFail("f()") +# f() takes 2 positional arguments but 3 were given" +tryFunctionFail("f(1, 2, 3)") + +################################################## +# test_positional_only_with_optional_invalid_calls +def f(a, b=2, /): + pass +tryFunctionPass("f(1)") +# "f() missing 1 required positional argument: 'a'" +tryFunctionFail("f()") +# "f() takes from 1 to 2 positional arguments but 3 were given" +tryFunctionFail("f(1, 2, 3)") + +################################################## +# test_no_standard_args_usage +print("\ntest_no_standard_args_usage") +def f(a, b, /, *, c): + pass + +tryFunctionPass("f(1, 2, c=3)") +# TypeError +tryFunctionFail("f(1, b=2, c=3)") + +################################################## +# test_lambdas +print("\ntest_lambdas") +try: + x = lambda a, /, b: a + b + assertEqual(x(1,2), 3) + assertEqual(x(1,b=2), 3) + + x = lambda a, /, b=2: a + b + assertEqual(x(1), 3) + + x = lambda a, b, /: a + b + assertEqual(x(1, 2), 3) + + x = lambda a, b, /, : a + b + assertEqual(x(1, 2), 3) +except: + print("FAILED: test_lambdas not working") + +################################################## +# test_invalid_syntax_lambda +try: + tryFunctionFail("lambda a, b = 5, /, c: None") + tryFunctionFail("lambda a = 5, b, /, c: None") + tryFunctionFail("lambda a = 5, b, /: None") + tryFunctionFail("lambda *args, /: None") + tryFunctionFail("lambda *args, a, /: None") + tryFunctionFail("lambda **kwargs, /: None") + tryFunctionFail("lambda /, a = 1: None") + tryFunctionFail("lambda /, a: None") + tryFunctionFail("lambda /: None") + tryFunctionFail("lambda *, a, /: None") + tryFunctionFail("lambda *, /, a: None") + tryFunctionFail("lambda a, /, a: None") + tryFunctionFail("lambda a, /, *, a: None") + tryFunctionFail("lambda a, /, b, /: None") + tryFunctionFail("lambda a, /, b, /, c: None") + tryFunctionFail("lambda a, /, b, /, c, *, d: None") + tryFunctionFail("lambda a, *, b, /, c: None") +except: + print("FAILED: test_invalid_syntax_lambda not working") + +################################################## +# test_posonly_methods +print("\ntest_posonly_methods") +class Example: + def f(self, a, b, /): + return a, b + +assertEqual(Example().f(1, 2), (1, 2)) +assertEqual(Example.f(Example(), 1, 2), (1, 2)) +tryFunctionFail("Example.f(1,2)") +# f() got some positional-only arguments passed as keyword arguments: 'b' +tryFunctionFail("Example().f(1, b=2)") + +################################################## +# test_module_function +print("\ntest_module_function") +def f(a, b, /): + pass +tryFunctionFail("f()") + +################################################## +# test_closures +print("\ntest_closures") +def f(x,y): + def g(x2,/,y2): + return x + y + x2 + y2 + return g + +assertEqual(f(1,2)(3,4), 10) +tryFunctionFail("f(1,2)(3)") +tryFunctionFail("f(1,2)(3,4,5)") + +def f(x,/,y): + def g(x2,y2): + return x + y + x2 + y2 + return g +assertEqual(f(1,2)(3,4), 10) + +def f(x,/,y): + def g(x2,/,y2): + return x + y + x2 + y2 + return g +assertEqual(f(1,2)(3,4), 10) +tryFunctionFail("f(1,2)(3)") +tryFunctionFail("f(1,2)(3,4,5)") + + +################################################## +# test_same_keyword_as_positional_with_kwargs +print("\ntest_same_keyword_as_positional_with_kwargs") +def f(something,/,**kwargs): + return (something, kwargs) + +assertEqual(f(42, something=42), (42, {'something': 42})) +tryFunctionFail("f(something=42)") +assertEqual(f(42), (42, {})) + + +################################################## +# test_mangling +print("\ntest_mangling") +class X: + def f(self, __a=42, /): + return __a + + def f2(self, __a=42, /, __b=43): + return (__a, __b) + + def f3(self, __a=42, /, __b=43, *, __c=44): + return (__a, __b, __c) + +assertEqual(X().f(), 42) +assertEqual(X().f2(), (42, 43)) +assertEqual(X().f3(), (42, 43, 44)) + +################################################## +# test_too_many_arguments +# Ignored due to bug with compile function in micropython-minimal +# print("\ntest_too_many_arguments") +# fundef = "def f(" + ', '.join('i' + str(i) for i in range(300)) + ", /):\n pass\n" +# compile(fundef, "", "single") +# print("PASSED: > 255 arg check") + +################################################## +# test_async +print("\ntest_async") +async def f(a=1, /, b=2): + return a, b + +tryFunctionFail("f(a=1, b=2)") + +def _check_call(*args, **kwargs): + try: + coro = f(*args, **kwargs) + coro.send(None) + except StopIteration as e: + result = e.value + assertEqual(result, (1, 2)) + +_check_call(1, 2) +_check_call(1, b=2) +_check_call(1) +_check_call() + + +################################################## +# test_generator +print("\ntest_generator") +def f(a=1, /, b=2): + yield a, b + +tryFunctionFail("f(a=1, b=2)") + +gen = f(1, 2) +assertEqual(next(gen), (1, 2)) +gen = f(1, b=2) +assertEqual(next(gen), (1, 2)) +gen = f(1) +assertEqual(next(gen), (1, 2)) +gen = f() +assertEqual(next(gen), (1, 2)) + + +################################################## +# test_super +print("\ntest_super") +sentinel = object() + +class A: + def method(self): + return sentinel + +class C(A): + def method(self, /): + return super().method() + +assertEqual(C().method(), sentinel) \ No newline at end of file diff --git a/tests/basics/fun_positional_args_only.py.exp b/tests/basics/fun_positional_args_only.py.exp new file mode 100644 index 0000000000000..fff7557d5e71c --- /dev/null +++ b/tests/basics/fun_positional_args_only.py.exp @@ -0,0 +1,160 @@ +PASSED: 'def f(a, b = 5, /, c): pass' threw an error. +PASSED: 'def f(a = 5, b, /, c): pass' threw an error. +PASSED: 'def f(a = 5, b=1, /, c, *, d=2): pass' threw an error. +PASSED: 'def f(a = 5, b, /): pass' threw an error. +PASSED: 'def f(*args, /): pass' threw an error. +PASSED: 'def f(*args, a, /): pass' threw an error. +PASSED: 'def f(**kwargs, /): pass' threw an error. +PASSED: 'def f(/, a = 1): pass' threw an error. +PASSED: 'def f(/, a): pass' threw an error. +PASSED: 'def f(/): pass' threw an error. +PASSED: 'def f(*, a, /): pass' threw an error. +PASSED: 'def f(*, /, a): pass' threw an error. +PASSED: 'def f(a, /, a): pass' threw an error. +PASSED: 'def f(a, /, *, a): pass' threw an error. +PASSED: 'def f(a, b/2, c): pass' threw an error. +PASSED: 'def f(a, /, c, /): pass' threw an error. +PASSED: 'def f(a, /, c, /, d): pass' threw an error. +PASSED: 'def f(a, /, c, /, d, *, e): pass' threw an error. +PASSED: 'def f(a, *, c, /, d, e): pass' threw an error. +PASSED: 'async def f(a, b = 5, /, c): pass' threw an error. +PASSED: 'async def f(a = 5, b, /, c): pass' threw an error. +PASSED: 'async def f(a = 5, b=1, /, c, d=2): pass' threw an error. +PASSED: 'async def f(a = 5, b, /): pass' threw an error. +PASSED: 'async def f(*args, /): pass' threw an error. +PASSED: 'async def f(*args, a, /): pass' threw an error. +PASSED: 'async def f(**kwargs, /): pass' threw an error. +PASSED: 'async def f(/, a = 1): pass' threw an error. +PASSED: 'async def f(/, a): pass' threw an error. +PASSED: 'async def f(/): pass' threw an error. +PASSED: 'async def f(*, a, /): pass' threw an error. +PASSED: 'async def f(*, /, a): pass' threw an error. +PASSED: 'async def f(a, /, a): pass' threw an error. +PASSED: 'async def f(a, /, *, a): pass' threw an error. +PASSED: 'async def f(a, b/2, c): pass' threw an error. +PASSED: 'async def f(a, /, c, /): pass' threw an error. +PASSED: 'async def f(a, /, c, /, d): pass' threw an error. +PASSED: 'async def f(a, /, c, /, d, *, e): pass' threw an error. +PASSED: 'async def f(a, *, c, /, d, e): pass' threw an error. + +test_optional_positional_only_args +PASSED: Assertion check +PASSED: Assertion check +PASSED: 'f(1, b=2, c=3)' threw an error. +PASSED: Assertion check +PASSED: 'f(1, b=2)' threw an error. +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check +PASSED: 'f(1, b=2, c=3)' threw an error. +PASSED: Assertion check +PASSED: 'f(1, b=2)' threw an error. +PASSED: Assertion check + +test_pos_only_call_via_unpacking +PASSED: Assertion check + +test_use_positional_as_keyword +PASSED: 'f(a=1)' threw an error. +PASSED: 'f(a=1, b=2)' threw an error. +PASSED: 'f(a=1, b=2)' threw an error. + +test_positional_only_and_arg_invalid_calls +PASSED: 'f(1, 2)' threw an error. +PASSED: 'f(1)' threw an error. +PASSED: 'f()' threw an error. +PASSED: 'f(1, 2, 3, 4)' threw an error. + +test_positional_only_and_optional_arg_invalid_calls +PASSED: 'f(1, 2)' did not throw syntax error. +PASSED: 'f(1)' threw an error. +PASSED: 'f()' threw an error. +PASSED: 'f(1, 2, 3, 4)' threw an error. +PASSED: 'f(1, 2, 3, d=1, e=2)' did not throw syntax error. +PASSED: 'f(1, 2, 3, e=2)' threw an error. +PASSED: 'f(1, 2, 3)' threw an error. +PASSED: 'f(1, 2)' threw an error. +PASSED: 'f(1)' threw an error. +PASSED: 'f()' threw an error. +PASSED: 'f(1, 2, 3, 4, 5, 6, d=7, e=8)' threw an error. +PASSED: 'f(1, 2, 3, d=1, e=4, f=56)' threw an error. +PASSED: 'f(1, 2)' did not throw syntax error. +PASSED: 'f(1)' threw an error. +PASSED: 'f()' threw an error. +PASSED: 'f(1, 2, 3)' threw an error. +PASSED: 'f(1)' did not throw syntax error. +PASSED: 'f()' threw an error. +PASSED: 'f(1, 2, 3)' threw an error. + +test_no_standard_args_usage +PASSED: 'f(1, 2, c=3)' did not throw syntax error. +PASSED: 'f(1, b=2, c=3)' threw an error. + +test_lambdas +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check +PASSED: 'lambda a, b = 5, /, c: None' threw an error. +PASSED: 'lambda a = 5, b, /, c: None' threw an error. +PASSED: 'lambda a = 5, b, /: None' threw an error. +PASSED: 'lambda *args, /: None' threw an error. +PASSED: 'lambda *args, a, /: None' threw an error. +PASSED: 'lambda **kwargs, /: None' threw an error. +PASSED: 'lambda /, a = 1: None' threw an error. +PASSED: 'lambda /, a: None' threw an error. +PASSED: 'lambda /: None' threw an error. +PASSED: 'lambda *, a, /: None' threw an error. +PASSED: 'lambda *, /, a: None' threw an error. +PASSED: 'lambda a, /, a: None' threw an error. +PASSED: 'lambda a, /, *, a: None' threw an error. +PASSED: 'lambda a, /, b, /: None' threw an error. +PASSED: 'lambda a, /, b, /, c: None' threw an error. +PASSED: 'lambda a, /, b, /, c, *, d: None' threw an error. +PASSED: 'lambda a, *, b, /, c: None' threw an error. + +test_posonly_methods +PASSED: Assertion check +PASSED: Assertion check +PASSED: 'Example.f(1,2)' threw an error. +PASSED: 'Example().f(1, b=2)' threw an error. + +test_module_function +PASSED: 'f()' threw an error. + +test_closures +PASSED: Assertion check +PASSED: 'f(1,2)(3)' threw an error. +PASSED: 'f(1,2)(3,4,5)' threw an error. +PASSED: Assertion check +PASSED: Assertion check +PASSED: 'f(1,2)(3)' threw an error. +PASSED: 'f(1,2)(3,4,5)' threw an error. + +test_same_keyword_as_positional_with_kwargs +PASSED: Assertion check +PASSED: 'f(something=42)' threw an error. +PASSED: Assertion check + +test_mangling +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check + +test_async +PASSED: 'f(a=1, b=2)' threw an error. +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check + +test_generator +PASSED: 'f(a=1, b=2)' threw an error. +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check +PASSED: Assertion check + +test_super +PASSED: Assertion check From 3efbea4fbe12f6094c92cd57fd1e008b92305a3c Mon Sep 17 00:00:00 2001 From: jbbjarnason Date: Thu, 31 Mar 2022 09:03:46 +0000 Subject: [PATCH 3/4] tests/cmdline/cmd_parsetree: Fix test after change to grammar. --- tests/cmdline/cmd_parsetree.py.exp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 6ee96b7caf293..96dfe8ab01c6d 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -32,11 +32,11 @@ id(h) [ 13] \(rule\|atom_expr_normal\)(44) (n=2) [ 13] literal const(\.\+) -[ 13] \(rule\|atom_expr_trailers\)(142) (n=2) +[ 13] atom_expr_trailers(142) (n=2) [ 13] \(rule\|trailer_period\)(50) (n=1) id(format) [ 13] \(rule\|trailer_paren\)(48) (n=1) -[ 13] \(rule\|arglist\)(164) (n=1) +[ 13] arglist(164) (n=1) id(b) ---------------- File cmdline/cmd_parsetree.py, code block '' (descriptor: \.\+, bytecode @\.\+ 62 bytes) From f7f5f4a75729d130592885c6e9b7be57b671768e Mon Sep 17 00:00:00 2001 From: jbbjarnason Date: Mon, 11 Apr 2022 22:08:03 +0000 Subject: [PATCH 4/4] py: Optimizing prelude sizing. Removing the pos only parameter count from the pos_args counter. --- py/bc.c | 46 ++++++++++++++++++++++++---------------------- py/bc.h | 2 +- py/compile.c | 1 + py/emitbc.c | 4 +++- py/emitnative.c | 6 ++++-- py/objfun.c | 8 ++++---- py/profile.c | 2 +- py/vm.c | 2 +- tools/mpy-tool.py | 2 +- 9 files changed, 40 insertions(+), 33 deletions(-) diff --git a/py/bc.c b/py/bc.c index e4b680905ac45..977cf3658eefa 100644 --- a/py/bc.c +++ b/py/bc.c @@ -123,7 +123,7 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { // - code_state->fun_bc should contain a pointer to the function object // - code_state->ip should contain a pointer to the beginning of the prelude // - code_state->n_state should be the number of objects in the local state -void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) { +void mp_setup_code_state(mp_code_state_t *code_state, size_t n_given_args, size_t 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. @@ -155,21 +155,23 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw // zero out the local stack to begin with memset(code_state->state, 0, n_state * sizeof(*code_state->state)); - const mp_obj_t *kwargs = args + n_args; + const mp_obj_t *kwargs = args + n_given_args; // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed) - mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - n_pos_args - n_kwonly_args]; + uint32_t n_all_pos_args = n_pos_args + n_posonly_args; + uint32_t n_all_args = n_all_pos_args + n_kwonly_args; + mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - n_all_args]; // check positional arguments - if (n_args > n_pos_args) { + if (n_given_args > n_all_pos_args) { // given more than enough arguments if ((scope_flags & MP_SCOPE_FLAG_VARARGS) == 0) { - fun_pos_args_mismatch(self, n_pos_args, n_args); + fun_pos_args_mismatch(self, n_all_pos_args, n_given_args); } // put extra arguments in varargs tuple - *var_pos_kw_args-- = mp_obj_new_tuple(n_args - n_pos_args, args + n_pos_args); - n_args = n_pos_args; + *var_pos_kw_args-- = mp_obj_new_tuple(n_given_args - n_all_pos_args, args + n_all_pos_args); + n_given_args = n_pos_args; } else { if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { DEBUG_printf("passing empty tuple as *args\n"); @@ -178,19 +180,19 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw // 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 && (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) == 0) { - if (n_args >= (size_t)(n_pos_args - n_def_pos_args)) { + if (n_given_args >= (size_t)(n_all_pos_args - n_def_pos_args)) { // given enough arguments, but may need to use some default arguments - for (size_t i = n_args; i < n_pos_args; i++) { - code_state->state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)]; + for (size_t i = n_given_args; i < n_all_pos_args; i++) { + code_state->state[n_state - 1 - i] = self->extra_args[i - (n_all_pos_args - n_def_pos_args)]; } } else { - fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args); + fun_pos_args_mismatch(self, n_all_pos_args - n_def_pos_args, n_given_args); } } } // copy positional args into state - for (size_t i = 0; i < n_args; i++) { + for (size_t i = 0; i < n_given_args; i++) { code_state->state[n_state - 1 - i] = args[i]; } @@ -198,7 +200,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { DEBUG_printf("Initial args: "); - dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); + dump_args(code_state->state + n_state - n_all_args, n_all_args); mp_obj_t dict = MP_OBJ_NULL; if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { @@ -215,7 +217,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw arg_names = mp_decode_uint_skip(arg_names); // Iterating the function definition keywords - for (size_t j = n_posonly_args; j < n_pos_args + n_kwonly_args; j++) { + for (size_t j = n_posonly_args; j < n_all_args; j++) { qstr arg_qstr = mp_decode_uint(&arg_names); #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE arg_qstr = self->context->constants.qstr_table[arg_qstr]; @@ -243,10 +245,10 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw } DEBUG_printf("Args with kws flattened: "); - dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); + dump_args(code_state->state + n_state - n_all_args, n_all_args); // fill in defaults for positional args - mp_obj_t *d = &code_state->state[n_state - n_pos_args]; + mp_obj_t *d = &code_state->state[n_state - n_all_pos_args]; mp_obj_t *s = &self->extra_args[n_def_pos_args - 1]; for (size_t i = n_def_pos_args; i > 0; i--, d++, s--) { if (*d == MP_OBJ_NULL) { @@ -255,7 +257,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw } DEBUG_printf("Args after filling default positional: "); - dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); + dump_args(code_state->state + n_state - n_all_args, n_all_args); // Check that all mandatory positional args are specified while (d < &code_state->state[n_state]) { @@ -268,7 +270,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw // Check that all mandatory keyword args are specified // Fill in default kw args if we have them const uint8_t *arg_names = mp_decode_uint_skip(code_state->ip); - for (size_t i = n_posonly_args; i < n_pos_args; i++) { + for (size_t i = 0; i < n_pos_args; i++) { arg_names = mp_decode_uint_skip(arg_names); } for (size_t i = 0; i < n_kwonly_args; i++) { @@ -276,13 +278,13 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE arg_qstr = self->context->constants.qstr_table[arg_qstr]; #endif - if (code_state->state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) { + if (code_state->state[n_state - 1 - n_all_pos_args - i] == MP_OBJ_NULL) { mp_map_elem_t *elem = NULL; if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { elem = mp_map_lookup(&((mp_obj_dict_t *)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, MP_OBJ_NEW_QSTR(arg_qstr), MP_MAP_LOOKUP); } if (elem != NULL) { - code_state->state[n_state - 1 - n_pos_args - i] = elem->value; + code_state->state[n_state - 1 - n_all_pos_args - i] = elem->value; } else { mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("function missing required keyword argument '%q'"), arg_qstr); @@ -313,8 +315,8 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw // now that we skipped over the prelude, set the ip for the VM code_state->ip = ip; - DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", n_pos_args, n_kwonly_args); - dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); + DEBUG_printf("Calling: n_pos_args=%d, n_posonly_args=%d, n_kwonly_args=%d\n", n_pos_args, n_posonly_args, n_kwonly_args); + dump_args(code_state->state + n_state - n_all_args, n_all_args); dump_args(code_state->state, n_state); } diff --git a/py/bc.h b/py/bc.h index 6798567571a81..33be92933ba98 100644 --- a/py/bc.h +++ b/py/bc.h @@ -284,7 +284,7 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, #endif mp_obj_t inject_exc); mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); -void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); +void mp_setup_code_state(mp_code_state_t *code_state, size_t n_given_args, size_t n_kw, const mp_obj_t *args); void mp_bytecode_print(const mp_print_t *print, const struct _mp_raw_code_t *rc, const mp_module_constants_t *cm); void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm); const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start, const byte *ip, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm); diff --git a/py/compile.c b/py/compile.c index ca3175277ae46..4362511f14dc7 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2883,6 +2883,7 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn } // comes before a slash, so counts as positional-only parameter comp->scope_cur->num_posonly_args = comp->scope_cur->num_pos_args; + comp->scope_cur->num_pos_args = 0; } else { assert(MP_PARSE_NODE_IS_STRUCT(pn)); pns = (mp_parse_node_struct_t *)pn; diff --git a/py/emitbc.c b/py/emitbc.c index b32db70a57b94..f2302c60469c5 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -323,7 +323,9 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { // Note: there is some wasted RAM here for the case of storing a qstr // for each closed-over variable, and maybe there is a better way to do // it, but that would require changes to mp_setup_code_state. - for (int i = scope->num_posonly_args; i < scope->num_pos_args + scope->num_kwonly_args; i++) { + uint32_t num_args = scope->num_pos_args + scope->num_posonly_args + scope->num_kwonly_args; + assert(num_args < UINT16_MAX); + for (uint32_t i = scope->num_posonly_args; i < num_args; i++) { qstr qst = MP_QSTR__star_; for (int j = 0; j < scope->id_info_len; ++j) { id_info_t *id = &scope->id_info[j]; diff --git a/py/emitnative.c b/py/emitnative.c index 7f2572074dbc7..39b99695edea9 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -375,7 +375,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop } // set default type for arguments - mp_uint_t num_args = emit->scope->num_pos_args + emit->scope->num_kwonly_args; + mp_uint_t num_args = emit->scope->num_pos_args + emit->scope->num_posonly_args + emit->scope->num_kwonly_args; if (scope->scope_flags & MP_SCOPE_FLAG_VARARGS) { num_args += 1; } @@ -669,7 +669,9 @@ STATIC bool emit_native_end_pass(emit_t *emit) { // bytecode prelude: source info (function and argument qstrs) size_t info_start = mp_asm_base_get_code_pos(&emit->as->base); emit_native_write_code_info_qstr(emit, emit->scope->simple_name); - for (int i = emit->scope->num_posonly_args; i < emit->scope->num_pos_args + emit->scope->num_kwonly_args; i++) { + uint32_t num_args = emit->scope->num_pos_args + emit->scope->num_posonly_args + emit->scope->num_kwonly_args; + assert(num_args < UINT16_MAX); + for (uint32_t i = emit->scope->num_posonly_args; i < num_args; i++) { qstr qst = MP_QSTR__star_; for (int j = 0; j < emit->scope->id_info_len; ++j) { id_info_t *id = &emit->scope->id_info[j]; diff --git a/py/objfun.c b/py/objfun.c index cf45c17bef982..190f4b460a9bc 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -298,16 +298,16 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const } const byte *bytecode_ptr = self->bytecode; size_t n_state_unused, n_exc_stack_unused, scope_flags_unused; - size_t n_pos_args, n_posonly_args_unused, n_kwonly_args, n_def_args_unused; + size_t n_pos_args, n_posonly_args, n_kwonly_args, n_def_args_unused; MP_BC_PRELUDE_SIG_DECODE_INTO(bytecode_ptr, n_state_unused, n_exc_stack_unused, - scope_flags_unused, n_pos_args, n_posonly_args_unused, n_kwonly_args, n_def_args_unused); + scope_flags_unused, n_pos_args, n_posonly_args, n_kwonly_args, n_def_args_unused); // We can't check the case when an exception is returned in state[0] // and there are no arguments, because in this case our detection slot may have // been overwritten by the returned exception (which is allowed). - if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_pos_args + n_kwonly_args == 0)) { + if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_pos_args + n_posonly_args + n_kwonly_args == 0)) { // Just check to see that we have at least 1 null object left in the state. bool overflow = true; - for (size_t i = 0; i < n_state - n_pos_args - n_kwonly_args; ++i) { + for (size_t i = 0; i < n_state - n_pos_args - n_posonly_args - n_kwonly_args; ++i) { if (code_state->state[i] == MP_OBJ_NULL) { overflow = false; break; diff --git a/py/profile.c b/py/profile.c index 1e541a0d400ad..724228125f044 100644 --- a/py/profile.c +++ b/py/profile.c @@ -57,7 +57,7 @@ void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelud prelude->opcodes = ip + n_info + n_cell; prelude->qstr_block_name_idx = mp_decode_uint_value(ip); - for (size_t i = 0; i < 1 + n_pos_args + n_kwonly_args; ++i) { + for (size_t i = 0; i < 1 + n_pos_args + n_posonly_args + n_kwonly_args; ++i) { ip = mp_decode_uint_skip(ip); } prelude->line_info = ip; diff --git a/py/vm.c b/py/vm.c index 02f8bc88c953e..e100939cfb832 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1406,7 +1406,7 @@ unwind_jump:; const byte *bytecode_start = ip + n_info + n_cell; size_t bc = code_state->ip - bytecode_start; qstr block_name = mp_decode_uint_value(ip); - for (size_t i = 0; i < 1 + n_pos_args + n_kwonly_args; ++i) { + for (size_t i = 0; i < 1 + n_pos_args + n_posonly_args + n_kwonly_args; ++i) { ip = mp_decode_uint_skip(ip); } #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 2a94b26bae607..2736c0074d455 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -437,7 +437,7 @@ def local_read_byte(): # Extract simple_name and argument qstrs (var uints). args = [] - for arg_num in range(1 + n_pos_args + n_kwonly_args): + for arg_num in range(1 + n_pos_args + n_posonly_args + n_kwonly_args): value = 0 while True: b = local_read_byte() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy