diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index b035883cf883b9..a7c043a055e59d 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2697,6 +2697,21 @@ def test_syntax_error_on_deeply_nested_blocks(self): """ self._check_error(source, "too many statically nested blocks") + @support.cpython_only + def test_nested_inlined_comprehensions_block_limit(self): + # Each inlined comprehension with locals emits SETUP_FINALLY, which + # must count toward CO_MAXBLOCKS (gh-156091). + def src(depth): + e = "i for i in r" + for _ in range(depth - 1): + e = "[" + e + "] for i in r" + return "x = [" + e + "]" + + CO_MAXBLOCKS = 21 + compile(src(CO_MAXBLOCKS), "", "exec") + self._check_error(src(CO_MAXBLOCKS + 1), + "too many statically nested blocks") + @support.cpython_only def test_error_on_parser_stack_overflow(self): source = "-" * 100000 + "4" diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-04-14-06-00.gh-issue-156091.nested-comp.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-04-14-06-00.gh-issue-156091.nested-comp.rst new file mode 100644 index 00000000000000..fdceb3209b7f7c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-04-14-06-00.gh-issue-156091.nested-comp.rst @@ -0,0 +1,3 @@ +Fix a crash when compiling deeply nested inlined list, set, or dict +comprehensions. A :exc:`SyntaxError` is now raised when the nesting exceeds +the compiler's static block limit. diff --git a/Python/compile.c b/Python/compile.c index 9151c29a9c1862..a0b7df0870e826 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -114,7 +114,7 @@ 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, - STOP_ITERATION }; + INLINED_COMPREHENSION, STOP_ITERATION }; struct fblockinfo { enum fblocktype fb_type; @@ -1523,6 +1523,7 @@ compiler_unwind_fblock(struct compiler *c, location *ploc, case EXCEPTION_HANDLER: case EXCEPTION_GROUP_HANDLER: case ASYNC_COMPREHENSION_GENERATOR: + case INLINED_COMPREHENSION: case STOP_ITERATION: return SUCCESS; @@ -5714,8 +5715,10 @@ push_inlined_comprehension_state(struct compiler *c, location loc, NEW_JUMP_TARGET_LABEL(c, end); state->end = end; - // no need to push an fblock for this "virtual" try/finally; there can't - // be return/continue/break inside a comprehension + // Count against CO_MAXBLOCKS: SETUP_FINALLY consumes an except-stack + // slot even though return/continue/break cannot appear here. + RETURN_IF_ERROR(compiler_push_fblock(c, loc, INLINED_COMPREHENSION, + cleanup, NO_LABEL, NULL)); ADDOP_JUMP(c, loc, SETUP_FINALLY, cleanup); } @@ -5761,6 +5764,7 @@ pop_inlined_comprehension_state(struct compiler *c, location loc, } if (state.pushed_locals) { ADDOP(c, NO_LOCATION, POP_BLOCK); + compiler_pop_fblock(c, INLINED_COMPREHENSION, state.cleanup); ADDOP_JUMP(c, NO_LOCATION, JUMP_NO_INTERRUPT, state.end); // cleanup from an exception inside the comprehension