summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2019-08-24 10:11:52 (GMT)
committerGitHub <noreply@github.com>2019-08-24 10:11:52 (GMT)
commitef61c524ddeeb56da3858b86e349e7288d68178e (patch)
treee8a9defe85faff05464db6335140eb0990499f15 /Python/compile.c
parente9c90aa43144b0be1e4e393e8cb549573437a5da (diff)
downloadcpython-ef61c524ddeeb56da3858b86e349e7288d68178e.zip
cpython-ef61c524ddeeb56da3858b86e349e7288d68178e.tar.gz
cpython-ef61c524ddeeb56da3858b86e349e7288d68178e.tar.bz2
bpo-37830: Fix compilation of break and continue in finally. (GH-15320)
Fix compilation of "break" and "continue" in the "finally" block when the corresponding "try" block contains "return" with a non-constant value.
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c66
1 files changed, 55 insertions, 11 deletions
diff --git a/Python/compile.c b/Python/compile.c
index d2de7a7..1bf05e2 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -81,7 +81,7 @@ 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,
+enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_TRY2, FINALLY_END,
WITH, ASYNC_WITH, HANDLER_CLEANUP };
struct fblockinfo {
@@ -1664,7 +1664,12 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
return 1;
case FINALLY_END:
+ info->fb_exit = NULL;
ADDOP_I(c, POP_FINALLY, preserve_tos);
+ if (preserve_tos) {
+ ADDOP(c, ROT_TWO);
+ }
+ ADDOP(c, POP_TOP);
return 1;
case FOR_LOOP:
@@ -1684,6 +1689,19 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
return 1;
+ case FINALLY_TRY2:
+ ADDOP(c, POP_BLOCK);
+ if (preserve_tos) {
+ ADDOP(c, ROT_TWO);
+ ADDOP(c, POP_TOP);
+ ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
+ }
+ else {
+ ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
+ ADDOP(c, POP_TOP);
+ }
+ return 1;
+
case WITH:
case ASYNC_WITH:
ADDOP(c, POP_BLOCK);
@@ -2869,17 +2887,47 @@ compiler_continue(struct compiler *c)
static int
compiler_try_finally(struct compiler *c, stmt_ty s)
{
- basicblock *body, *end;
+ basicblock *start, *newcurblock, *body, *end;
+ int break_finally = 1;
body = compiler_new_block(c);
end = compiler_new_block(c);
if (body == NULL || end == NULL)
return 0;
+ start = c->u->u_curblock;
+
+ /* `finally` block. Compile it first to determine if any of "break",
+ "continue" or "return" are used in it. */
+ compiler_use_next_block(c, end);
+ if (!compiler_push_fblock(c, FINALLY_END, end, end))
+ return 0;
+ VISIT_SEQ(c, stmt, s->v.Try.finalbody);
+ ADDOP(c, END_FINALLY);
+ break_finally = (c->u->u_fblock[c->u->u_nfblocks - 1].fb_exit == NULL);
+ if (break_finally) {
+ /* Pops a placeholder. See below */
+ ADDOP(c, POP_TOP);
+ }
+ compiler_pop_fblock(c, FINALLY_END, end);
+
+ newcurblock = c->u->u_curblock;
+ c->u->u_curblock = start;
+ start->b_next = NULL;
+
/* `try` block */
+ c->u->u_lineno_set = 0;
+ c->u->u_lineno = s->lineno;
+ c->u->u_col_offset = s->col_offset;
+ if (break_finally) {
+ /* Pushes a placeholder for the value of "return" in the "try" block
+ to balance the stack for "break", "continue" and "return" in
+ the "finally" block. */
+ ADDOP_LOAD_CONST(c, Py_None);
+ }
ADDOP_JREL(c, SETUP_FINALLY, end);
compiler_use_next_block(c, body);
- if (!compiler_push_fblock(c, FINALLY_TRY, body, end))
+ if (!compiler_push_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body, end))
return 0;
if (s->v.Try.handlers && asdl_seq_LEN(s->v.Try.handlers)) {
if (!compiler_try_except(c, s))
@@ -2890,15 +2938,11 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
}
ADDOP(c, POP_BLOCK);
ADDOP(c, BEGIN_FINALLY);
- compiler_pop_fblock(c, FINALLY_TRY, body);
+ compiler_pop_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body);
+
+ c->u->u_curblock->b_next = end;
+ c->u->u_curblock = newcurblock;
- /* `finally` block */
- compiler_use_next_block(c, end);
- if (!compiler_push_fblock(c, FINALLY_END, end, NULL))
- return 0;
- VISIT_SEQ(c, stmt, s->v.Try.finalbody);
- ADDOP(c, END_FINALLY);
- compiler_pop_fblock(c, FINALLY_END, end);
return 1;
}