From 23a5a31d6f7fdc5bd2497dbdb50cdeba7ee2cc57 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 12 May 2021 09:52:07 +0100 Subject: [PATCH 1/3] Guarantee that line number is set for returns. --- Lib/test/test_sys_settrace.py | 1 + Python/compile.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 40dd92ca8e23ac..117567133d5da2 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -1647,6 +1647,7 @@ async def test_no_jump_forwards_into_async_for_block(output): output.append(1) async for i in asynciter([1, 2]): output.append(3) + pass @jump_test(3, 2, [2, 2], (ValueError, 'into')) def test_no_jump_backwards_into_for_block(output): diff --git a/Python/compile.c b/Python/compile.c index bb88c06c08d8f3..570efd5d25d821 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -6960,6 +6960,36 @@ insert_generator_prefix(struct compiler *c, basicblock *entryblock) { return 0; } +/* Make sure that all returns have a line number, even if early passes + * have failed to propagate a correct line number. + * The resulting line number may not be correct according to PEP 626, + * but should be "good enough", and no worse than in older versions. */ +static void +guarantee_lineno_for_exits(struct assembler *a, int firstlineno) { + int lineno = firstlineno; + assert(lineno > 0); + for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) { + if (b->b_iused == 0) { + continue; + } + struct instr *last = &b->b_instr[b->b_iused-1]; + if (last->i_lineno < 0) { + /* A return in a block by itself can't have a linenumber, or + * we can mess up frame.setlineno */ + if (last->i_opcode == RETURN_VALUE) + { + for (int i = 0; i < b->b_iused; i++) { + assert(b->b_instr[i].i_lineno < 0); + b->b_instr[i].i_lineno = lineno; + } + } + } + else { + lineno = last->i_lineno; + } + } +} + static PyCodeObject * assemble(struct compiler *c, int addNone) { @@ -7022,6 +7052,7 @@ assemble(struct compiler *c, int addNone) if (optimize_cfg(c, &a, consts)) { goto error; } + guarantee_lineno_for_exits(&a, c->u->u_firstlineno); /* Can't modify the bytecode after computing jump offsets. */ assemble_jump_offsets(&a, c); From b89dd60f5f8bdafaddfd5ac0f7eeb23f8c0fe058 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 10 May 2021 12:31:28 +0100 Subject: [PATCH 2/3] Add test for linenumber of return in nested try statements. --- Lib/test/test_sys_settrace.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 117567133d5da2..3296ee0139cc9c 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -976,6 +976,26 @@ class A: (3, 'return'), (1, 'return')]) + def test_try_in_try(self): + def func(): + try: + try: + pass + except Exception as ex: + pass + except Exception: + pass + + # This doesn't conform to PEP 626 + self.run_and_compare(func, + [(0, 'call'), + (1, 'line'), + (2, 'line'), + (3, 'line'), + (5, 'line'), + (5, 'return')]) + + class SkipLineEventsTraceTestCase(TraceTestCase): """Repeat the trace tests, but with per-line events skipped""" From 2a34a68d184dcbd79a13213308e82348765abdd5 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 12 May 2021 10:22:14 +0100 Subject: [PATCH 3/3] Remove misleading comment --- Python/compile.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 570efd5d25d821..3c69ce2eb83428 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -6974,8 +6974,6 @@ guarantee_lineno_for_exits(struct assembler *a, int firstlineno) { } struct instr *last = &b->b_instr[b->b_iused-1]; if (last->i_lineno < 0) { - /* A return in a block by itself can't have a linenumber, or - * we can mess up frame.setlineno */ if (last->i_opcode == RETURN_VALUE) { for (int i = 0; i < b->b_iused; i++) {