From 6ce1a15b65c2d7a6a11aba8d88c7b2b7b369c680 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sun, 6 Sep 2026 22:45:40 +0100 Subject: [PATCH] [3.13] gh-156091: Fix crash compiling deeply nested inlined comprehensions (GH-156957) Count inlined-comprehension SETUP_FINALLY handlers toward CO_MAXBLOCKS. Manual backport of 14a93f4d91ed / GH-156993; 3.13 still has this code in compile.c. Co-authored-by: Cursor --- Lib/test/test_syntax.py | 15 +++++++++++++++ ...09-04-14-06-00.gh-issue-156091.nested-comp.rst | 3 +++ Python/compile.c | 10 +++++++--- 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-09-04-14-06-00.gh-issue-156091.nested-comp.rst diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index b035883cf883b93..a7c043a055e59d1 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 000000000000000..fdceb3209b7f7cb --- /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 9151c29a9c1862d..a0b7df0870e8267 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