Skip to content

Commit 845fb34

Browse files
[3.15] gh-156091: Fix crash compiling deeply nested inlined comprehensions (GH-156957) (GH-156992)
(cherry picked from commit 14a93f4) Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
1 parent 5646ec8 commit 845fb34

4 files changed

Lines changed: 27 additions & 2 deletions

File tree

‎Include/internal/pycore_compile.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ enum _PyCompile_FBlockType {
110110
COMPILE_FBLOCK_EXCEPTION_HANDLER,
111111
COMPILE_FBLOCK_EXCEPTION_GROUP_HANDLER,
112112
COMPILE_FBLOCK_ASYNC_COMPREHENSION_GENERATOR,
113+
COMPILE_FBLOCK_INLINED_COMPREHENSION,
113114
COMPILE_FBLOCK_STOP_ITERATION,
114115
};
115116

‎Lib/test/test_syntax.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3503,6 +3503,21 @@ def test_syntax_error_on_deeply_nested_blocks(self):
35033503
"""
35043504
self._check_error(source, "too many statically nested blocks")
35053505

3506+
@support.cpython_only
3507+
def test_nested_inlined_comprehensions_block_limit(self):
3508+
# Each inlined comprehension with locals emits SETUP_FINALLY, which
3509+
# must count toward CO_MAXBLOCKS (gh-156091).
3510+
def src(depth):
3511+
e = "i for i in r"
3512+
for _ in range(depth - 1):
3513+
e = "[" + e + "] for i in r"
3514+
return "x = [" + e + "]"
3515+
3516+
CO_MAXBLOCKS = 21
3517+
compile(src(CO_MAXBLOCKS), "<testcase>", "exec")
3518+
self._check_error(src(CO_MAXBLOCKS + 1),
3519+
"too many statically nested blocks")
3520+
35063521
@support.cpython_only
35073522
def test_error_on_parser_stack_overflow(self):
35083523
source = "-" * 100000 + "4"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash when compiling deeply nested inlined list, set, or dict
2+
comprehensions. A :exc:`SyntaxError` is now raised when the nesting exceeds
3+
the compiler's static block limit.

‎Python/codegen.c‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ codegen_unwind_fblock(compiler *c, location *ploc,
544544
case COMPILE_FBLOCK_EXCEPTION_HANDLER:
545545
case COMPILE_FBLOCK_EXCEPTION_GROUP_HANDLER:
546546
case COMPILE_FBLOCK_ASYNC_COMPREHENSION_GENERATOR:
547+
case COMPILE_FBLOCK_INLINED_COMPREHENSION:
547548
case COMPILE_FBLOCK_STOP_ITERATION:
548549
return SUCCESS;
549550

@@ -4871,8 +4872,11 @@ codegen_push_inlined_comprehension_locals(compiler *c, location loc,
48714872
NEW_JUMP_TARGET_LABEL(c, cleanup);
48724873
state->cleanup = cleanup;
48734874

4874-
// no need to push an fblock for this "virtual" try/finally; there can't
4875-
// be return/continue/break inside a comprehension
4875+
// Count against CO_MAXBLOCKS: SETUP_FINALLY consumes an except-stack
4876+
// slot even though return/continue/break cannot appear here.
4877+
RETURN_IF_ERROR(_PyCompile_PushFBlock(
4878+
c, loc, COMPILE_FBLOCK_INLINED_COMPREHENSION,
4879+
cleanup, NO_LABEL, NULL));
48764880
ADDOP_JUMP(c, loc, SETUP_FINALLY, cleanup);
48774881
}
48784882
return SUCCESS;
@@ -4918,6 +4922,8 @@ codegen_pop_inlined_comprehension_locals(compiler *c, location loc,
49184922
{
49194923
if (state->pushed_locals) {
49204924
ADDOP(c, NO_LOCATION, POP_BLOCK);
4925+
_PyCompile_PopFBlock(c, COMPILE_FBLOCK_INLINED_COMPREHENSION,
4926+
state->cleanup);
49214927

49224928
NEW_JUMP_TARGET_LABEL(c, end);
49234929
ADDOP_JUMP(c, NO_LOCATION, JUMP_NO_INTERRUPT, end);

0 commit comments

Comments
 (0)