diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 4283c30e0a1c19..b00f329e0068b6 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -366,14 +366,12 @@ def bug42562(): LOAD_CONST 2 (0) --> BINARY_OP 11 (/) POP_TOP - -%3d LOAD_FAST 1 (tb) - RETURN_VALUE + JUMP_FORWARD 30 (to 76) >> PUSH_EXC_INFO %3d LOAD_GLOBAL 0 (Exception) CHECK_EXC_MATCH - POP_JUMP_FORWARD_IF_FALSE 18 (to 72) + POP_JUMP_FORWARD_IF_FALSE 17 (to 68) STORE_FAST 0 (e) %3d LOAD_FAST 0 (e) @@ -383,9 +381,7 @@ def bug42562(): LOAD_CONST 0 (None) STORE_FAST 0 (e) DELETE_FAST 0 (e) - -%3d LOAD_FAST 1 (tb) - RETURN_VALUE + JUMP_FORWARD 8 (to 76) >> LOAD_CONST 0 (None) STORE_FAST 0 (e) DELETE_FAST 0 (e) @@ -395,15 +391,17 @@ def bug42562(): >> COPY 3 POP_EXCEPT RERAISE 1 + +%3d >> LOAD_FAST 1 (tb) + RETURN_VALUE ExceptionTable: """ % (TRACEBACK_CODE.co_firstlineno, TRACEBACK_CODE.co_firstlineno + 1, TRACEBACK_CODE.co_firstlineno + 2, - TRACEBACK_CODE.co_firstlineno + 5, TRACEBACK_CODE.co_firstlineno + 3, TRACEBACK_CODE.co_firstlineno + 4, - TRACEBACK_CODE.co_firstlineno + 5, - TRACEBACK_CODE.co_firstlineno + 3) + TRACEBACK_CODE.co_firstlineno + 3, + TRACEBACK_CODE.co_firstlineno + 5) def _fstring(a, b, c, d): return f'{a} {b:4} {c!r} {d!r:4}' diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 6a2f550d67052a..241644ad7d2ff0 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -344,6 +344,8 @@ def f(x): self.assertEqual(len(returns), 1) self.check_lnotab(f) + @unittest.skip("Following gh-92228 the return has two predecessors " + "and that prevents jump elimination.") def test_elim_jump_to_return(self): # JUMP_FORWARD to RETURN --> RETURN def f(cond, true_value, false_value): diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 7ec290dbf04ad5..f03b03e19a2528 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -2042,6 +2042,15 @@ def test_no_jump_within_except_block(output): output.append(6) output.append(7) + @jump_test(6, 1, [1, 5, 1, 5]) + def test_jump_over_try_except(output): + output.append(1) + try: + 1 / 0 + except ZeroDivisionError as e: + output.append(5) + x = 42 # has to be a two-instruction block + @jump_test(2, 4, [1, 4, 5, -4]) def test_jump_across_with(output): output.append(1) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-07-06-14-02-26.gh-issue-92228.44Cbly.rst b/Misc/NEWS.d/next/Core and Builtins/2022-07-06-14-02-26.gh-issue-92228.44Cbly.rst new file mode 100644 index 00000000000000..458ad897cefcb6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-07-06-14-02-26.gh-issue-92228.44Cbly.rst @@ -0,0 +1 @@ +Disable the compiler's inline-small-exit-blocks optimization for exit blocks that are associated with source code lines. This fixes a bug where the debugger cannot tell where an exception handler ends and the following code block begins. diff --git a/Python/compile.c b/Python/compile.c index cfe4b6ec04f9ed..a71e7d31eecd28 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -8976,6 +8976,16 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts) return -1; } +static bool +basicblock_has_lineno(const basicblock *bb) { + for (int i = 0; i < bb->b_iused; i++) { + if (bb->b_instr[i].i_lineno > 0) { + return true; + } + } + return false; +} + /* If this block ends with an unconditional jump to an exit block, * then remove the jump and extend this block with the target. */ @@ -8992,6 +9002,10 @@ extend_block(basicblock *bb) { } if (last->i_target->b_exit && last->i_target->b_iused <= MAX_COPY_SIZE) { basicblock *to_copy = last->i_target; + if (basicblock_has_lineno(to_copy)) { + /* copy only blocks without line number (like implicit 'return None's) */ + return 0; + } last->i_opcode = NOP; for (int i = 0; i < to_copy->b_iused; i++) { int index = compiler_next_instr(bb);