diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index e9bec3317811dd..491cd8bb7daa42 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2402,6 +2402,31 @@ def bug(): with self.subTest(f"out of range: {n=}"): self._check_error(get_code(n), "too many statically nested blocks") + @support.cpython_only + def test_async_with_statement_many_context_managers(self): + # See gh-116767 + + def get_code(n): + code = [ textwrap.dedent(""" + async def bug(): + async with ( + a + """) ] + for i in range(n): + code.append(f" as a{i}, a\n") + code.append("): yield a") + return "".join(code) + + CO_MAXBLOCKS = 20 # static nesting limit of the compiler + + for n in range(CO_MAXBLOCKS): + with self.subTest(f"within range: {n=}"): + compile(get_code(n), "", "exec") + + for n in range(CO_MAXBLOCKS, CO_MAXBLOCKS + 5): + with self.subTest(f"out of range: {n=}"): + self._check_error(get_code(n), "too many statically nested blocks") + def test_barry_as_flufl_with_syntax_errors(self): # The "barry_as_flufl" rule can produce some "bugs-at-a-distance" if # is reading the wrong token in the presence of syntax errors later diff --git a/Python/compile.c b/Python/compile.c index ca5551a8e64ab0..370c773cad70a6 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -113,7 +113,8 @@ compiler IR. enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END, WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER, - EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR }; + EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR, + STOP_ITERATION }; struct fblockinfo { enum fblocktype fb_type; @@ -1503,6 +1504,7 @@ compiler_unwind_fblock(struct compiler *c, location *ploc, case EXCEPTION_HANDLER: case EXCEPTION_GROUP_HANDLER: case ASYNC_COMPREHENSION_GENERATOR: + case STOP_ITERATION: return SUCCESS; case FOR_LOOP: @@ -2232,6 +2234,16 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f c->u->u_metadata.u_argcount = asdl_seq_LEN(args->args); c->u->u_metadata.u_posonlyargcount = asdl_seq_LEN(args->posonlyargs); c->u->u_metadata.u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs); + + NEW_JUMP_TARGET_LABEL(c, start); + USE_LABEL(c, start); + if (c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator) { + /* wrap_in_stopiteration_handler will push a block, so we need to account for that */ + RETURN_IF_ERROR( + compiler_push_fblock(c, NO_LOCATION, STOP_ITERATION, + start, NO_LABEL, NULL)); + } + for (Py_ssize_t i = first_instr; i < asdl_seq_LEN(body); i++) { VISIT_IN_SCOPE(c, stmt, (stmt_ty)asdl_seq_GET(body, i)); } @@ -2240,6 +2252,7 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f compiler_exit_scope(c); return ERROR; } + compiler_pop_fblock(c, STOP_ITERATION, start); } PyCodeObject *co = optimize_and_assemble(c, 1); compiler_exit_scope(c); diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 83768023a4d870..9a9689f6492a3b 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -263,7 +263,7 @@ basicblock_insert_instruction(basicblock *block, int pos, cfg_instr *instr) { } /* For debugging purposes only */ -#if 0 +#if 1 static void dump_instr(cfg_instr *i) {