Skip to content

Commit

Permalink
bpo-39934: Account for control blocks in 'except' in compiler. (pytho…
Browse files Browse the repository at this point in the history
…nGH-22395)

* Account for control blocks in 'except' in compiler. Fixes python#39934.
  • Loading branch information
markshannon authored and Seth Sims committed Oct 18, 2020
1 parent 6bcb6bc commit c436e06
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 8 deletions.
9 changes: 9 additions & 0 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,15 @@ def test_empty_line_after_linecont(self):
except SyntaxError:
self.fail("Empty line after a line continuation character is valid.")

@support.cpython_only
def test_nested_named_except_blocks(self):
code = ""
for i in range(12):
code += f"{' '*i}try:\n"
code += f"{' '*(i+1)}raise Exception\n"
code += f"{' '*i}except Exception as e:\n"
code += f"{' '*4*12}pass"
self._check_error(code, "too many statically nested blocks")

def test_main():
support.run_unittest(SyntaxTestCase)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Correctly count control blocks in 'except' in compiler. Ensures that a
syntax error, rather a fatal error, occurs for deeply nested, named
exception handlers.
19 changes: 11 additions & 8 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ It's called a frame block to distinguish it from a basic block in the
compiler IR.
*/

enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_END,
WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE };
enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END,
WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER };

struct fblockinfo {
enum fblocktype fb_type;
Expand Down Expand Up @@ -1623,9 +1623,7 @@ compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b,
{
struct fblockinfo *f;
if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
PyErr_SetString(PyExc_SyntaxError,
"too many statically nested blocks");
return 0;
return compiler_error(c, "too many statically nested blocks");
}
f = &c->u->u_fblock[c->u->u_nfblocks++];
f->fb_type = t;
Expand Down Expand Up @@ -1665,6 +1663,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
{
switch (info->fb_type) {
case WHILE_LOOP:
case EXCEPTION_HANDLER:
return 1;

case FOR_LOOP:
Expand All @@ -1675,7 +1674,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
ADDOP(c, POP_TOP);
return 1;

case EXCEPT:
case TRY_EXCEPT:
ADDOP(c, POP_BLOCK);
return 1;

Expand Down Expand Up @@ -3060,14 +3059,17 @@ compiler_try_except(struct compiler *c, stmt_ty s)
return 0;
ADDOP_JUMP(c, SETUP_FINALLY, except);
compiler_use_next_block(c, body);
if (!compiler_push_fblock(c, EXCEPT, body, NULL, NULL))
if (!compiler_push_fblock(c, TRY_EXCEPT, body, NULL, NULL))
return 0;
VISIT_SEQ(c, stmt, s->v.Try.body);
ADDOP(c, POP_BLOCK);
compiler_pop_fblock(c, EXCEPT, body);
compiler_pop_fblock(c, TRY_EXCEPT, body);
ADDOP_JUMP(c, JUMP_FORWARD, orelse);
n = asdl_seq_LEN(s->v.Try.handlers);
compiler_use_next_block(c, except);
/* Runtime will push a block here, so we need to account for that */
if (!compiler_push_fblock(c, EXCEPTION_HANDLER, NULL, NULL, NULL))
return 0;
for (i = 0; i < n; i++) {
excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET(
s->v.Try.handlers, i);
Expand Down Expand Up @@ -3152,6 +3154,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
}
compiler_use_next_block(c, except);
}
compiler_pop_fblock(c, EXCEPTION_HANDLER, NULL);
ADDOP(c, RERAISE);
compiler_use_next_block(c, orelse);
VISIT_SEQ(c, stmt, s->v.Try.orelse);
Expand Down

0 comments on commit c436e06

Please sign in to comment.