Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5977a79

Browse files
authored
bpo-42246: Make sure that line number is correct after a return, as required by PEP 626 (GH-23495)
Make sure that line number is correct after a return, as defined by PEP 626.
1 parent 4e7a69b commit 5977a79

File tree

7 files changed

+4967
-4818
lines changed

7 files changed

+4967
-4818
lines changed

Lib/test/test_compile.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def test_leading_newlines(self):
156156
s256 = "".join(["\n"] * 256 + ["spam"])
157157
co = compile(s256, 'fn', 'exec')
158158
self.assertEqual(co.co_firstlineno, 1)
159-
self.assertEqual(list(co.co_lines()), [(0, 4, 257), (4, 8, None)])
159+
self.assertEqual(list(co.co_lines()), [(0, 8, 257)])
160160

161161
def test_literals_with_leading_zeroes(self):
162162
for arg in ["077787", "0xj", "0x.", "0e", "090000000000000",
@@ -775,6 +775,39 @@ def or_false(x):
775775
self.assertIn('LOAD_', opcodes[0].opname)
776776
self.assertEqual('RETURN_VALUE', opcodes[1].opname)
777777

778+
def test_lineno_after_implicit_return(self):
779+
TRUE = True
780+
# Don't use constant True or False, as compiler will remove test
781+
def if1(x):
782+
x()
783+
if TRUE:
784+
pass
785+
def if2(x):
786+
x()
787+
if TRUE:
788+
pass
789+
else:
790+
pass
791+
def if3(x):
792+
x()
793+
if TRUE:
794+
pass
795+
else:
796+
return None
797+
def if4(x):
798+
x()
799+
if not TRUE:
800+
pass
801+
funcs = [ if1, if2, if3, if4]
802+
lastlines = [ 3, 3, 3, 2]
803+
frame = None
804+
def save_caller_frame():
805+
nonlocal frame
806+
frame = sys._getframe(1)
807+
for func, lastline in zip(funcs, lastlines, strict=True):
808+
with self.subTest(func=func):
809+
func(save_caller_frame)
810+
self.assertEqual(frame.f_lineno-frame.f_code.co_firstlineno, lastline)
778811

779812
def test_big_dict_literal(self):
780813
# The compiler has a flushing point in "compiler_dict" that calls compiles

Lib/test/test_dis.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,14 @@ def bug708901():
131131
12 STORE_FAST 0 (res)
132132
133133
%3d 14 JUMP_ABSOLUTE 10
134-
>> 16 LOAD_CONST 0 (None)
134+
135+
%3d >> 16 LOAD_CONST 0 (None)
135136
18 RETURN_VALUE
136137
""" % (bug708901.__code__.co_firstlineno + 1,
137138
bug708901.__code__.co_firstlineno + 2,
138139
bug708901.__code__.co_firstlineno + 1,
139-
bug708901.__code__.co_firstlineno + 3)
140+
bug708901.__code__.co_firstlineno + 3,
141+
bug708901.__code__.co_firstlineno + 1)
140142

141143

142144
def bug1333982(x=[]):
@@ -295,13 +297,15 @@ def bug1333982(x=[]):
295297
52 STORE_FAST 0 (e)
296298
54 DELETE_FAST 0 (e)
297299
56 RERAISE
298-
>> 58 RERAISE
300+
301+
%3d >> 58 RERAISE
299302
""" % (TRACEBACK_CODE.co_firstlineno + 1,
300303
TRACEBACK_CODE.co_firstlineno + 2,
301304
TRACEBACK_CODE.co_firstlineno + 5,
302305
TRACEBACK_CODE.co_firstlineno + 3,
303306
TRACEBACK_CODE.co_firstlineno + 4,
304-
TRACEBACK_CODE.co_firstlineno + 5)
307+
TRACEBACK_CODE.co_firstlineno + 5,
308+
TRACEBACK_CODE.co_firstlineno + 3)
305309

306310
def _fstring(a, b, c, d):
307311
return f'{a} {b:4} {c!r} {d!r:4}'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
PEP 626: After a return, the f_lineno attribute of a frame is always the
2+
last line executed.

Python/compile.c

Lines changed: 126 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,28 @@ compiler_use_next_block(struct compiler *c, basicblock *block)
812812
return block;
813813
}
814814

815+
static basicblock *
816+
compiler_copy_block(struct compiler *c, basicblock *block)
817+
{
818+
/* Cannot copy a block if it has a fallthrough, since
819+
* a block can only have one fallthrough predecessor.
820+
*/
821+
assert(block->b_nofallthrough);
822+
basicblock *result = compiler_next_block(c);
823+
if (result == NULL) {
824+
return NULL;
825+
}
826+
for (int i = 0; i < block->b_iused; i++) {
827+
int n = compiler_next_instr(result);
828+
if (n < 0) {
829+
return NULL;
830+
}
831+
result->b_instr[n] = block->b_instr[i];
832+
}
833+
result->b_exit = block->b_exit;
834+
return result;
835+
}
836+
815837
/* Returns the offset of the next instruction in the current block's
816838
b_instr array. Resizes the b_instr as necessary.
817839
Returns -1 on failure.
@@ -2732,12 +2754,13 @@ compiler_if(struct compiler *c, stmt_ty s)
27322754
static int
27332755
compiler_for(struct compiler *c, stmt_ty s)
27342756
{
2735-
basicblock *start, *cleanup, *end;
2757+
basicblock *start, *body, *cleanup, *end;
27362758

27372759
start = compiler_new_block(c);
2760+
body = compiler_new_block(c);
27382761
cleanup = compiler_new_block(c);
27392762
end = compiler_new_block(c);
2740-
if (start == NULL || end == NULL || cleanup == NULL) {
2763+
if (start == NULL || body == NULL || end == NULL || cleanup == NULL) {
27412764
return 0;
27422765
}
27432766
if (!compiler_push_fblock(c, FOR_LOOP, start, end, NULL)) {
@@ -2747,6 +2770,7 @@ compiler_for(struct compiler *c, stmt_ty s)
27472770
ADDOP(c, GET_ITER);
27482771
compiler_use_next_block(c, start);
27492772
ADDOP_JUMP(c, FOR_ITER, cleanup);
2773+
compiler_use_next_block(c, body);
27502774
VISIT(c, expr, s->v.For.target);
27512775
VISIT_SEQ(c, stmt, s->v.For.body);
27522776
ADDOP_JUMP(c, JUMP_ABSOLUTE, start);
@@ -5929,9 +5953,16 @@ dump_basicblock(const basicblock *b)
59295953
}
59305954
#endif
59315955

5956+
5957+
static int
5958+
normalize_basic_block(basicblock *bb);
5959+
59325960
static int
59335961
optimize_cfg(struct assembler *a, PyObject *consts);
59345962

5963+
static int
5964+
ensure_exits_have_lineno(struct compiler *c);
5965+
59355966
static PyCodeObject *
59365967
assemble(struct compiler *c, int addNone)
59375968
{
@@ -5952,6 +5983,16 @@ assemble(struct compiler *c, int addNone)
59525983
ADDOP(c, RETURN_VALUE);
59535984
}
59545985

5986+
for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {
5987+
if (normalize_basic_block(b)) {
5988+
goto error;
5989+
}
5990+
}
5991+
5992+
if (ensure_exits_have_lineno(c)) {
5993+
goto error;
5994+
}
5995+
59555996
nblocks = 0;
59565997
entryblock = NULL;
59575998
for (b = c->u->u_blocks; b != NULL; b = b->b_list) {
@@ -5966,6 +6007,7 @@ assemble(struct compiler *c, int addNone)
59666007
else
59676008
c->u->u_firstlineno = 1;
59686009
}
6010+
59696011
if (!assemble_init(&a, nblocks, c->u->u_firstlineno))
59706012
goto error;
59716013
a.a_entry = entryblock;
@@ -6338,7 +6380,6 @@ clean_basic_block(basicblock *bb) {
63386380
bb->b_iused = dest;
63396381
}
63406382

6341-
63426383
static int
63436384
normalize_basic_block(basicblock *bb) {
63446385
/* Mark blocks as exit and/or nofallthrough.
@@ -6349,7 +6390,8 @@ normalize_basic_block(basicblock *bb) {
63496390
case RAISE_VARARGS:
63506391
case RERAISE:
63516392
bb->b_exit = 1;
6352-
/* fall through */
6393+
bb->b_nofallthrough = 1;
6394+
break;
63536395
case JUMP_ABSOLUTE:
63546396
case JUMP_FORWARD:
63556397
bb->b_nofallthrough = 1;
@@ -6358,16 +6400,21 @@ normalize_basic_block(basicblock *bb) {
63586400
case POP_JUMP_IF_TRUE:
63596401
case JUMP_IF_FALSE_OR_POP:
63606402
case JUMP_IF_TRUE_OR_POP:
6403+
case FOR_ITER:
63616404
if (i != bb->b_iused-1) {
63626405
PyErr_SetString(PyExc_SystemError, "malformed control flow graph.");
63636406
return -1;
63646407
}
6408+
/* Skip over empty basic blocks. */
6409+
while (bb->b_instr[i].i_target->b_iused == 0) {
6410+
bb->b_instr[i].i_target = bb->b_instr[i].i_target->b_next;
6411+
}
6412+
63656413
}
63666414
}
63676415
return 0;
63686416
}
63696417

6370-
63716418
static int
63726419
mark_reachable(struct assembler *a) {
63736420
basicblock **stack, **sp;
@@ -6398,8 +6445,27 @@ mark_reachable(struct assembler *a) {
63986445
return 0;
63996446
}
64006447

6448+
/* If an instruction has no line number, but it's predecessor in the BB does,
6449+
* then copy the line number. This reduces the size of the line number table,
6450+
* but has no impact on the generated line number events.
6451+
*/
6452+
static void
6453+
minimize_lineno_table(struct assembler *a) {
6454+
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
6455+
int prev_lineno = -1;
6456+
for (int i = 0; i < b->b_iused; i++) {
6457+
if (b->b_instr[i].i_lineno < 0) {
6458+
b->b_instr[i].i_lineno = prev_lineno;
6459+
}
6460+
else {
6461+
prev_lineno = b->b_instr[i].i_lineno;
6462+
}
6463+
}
6464+
6465+
}
6466+
}
64016467

6402-
/* Perform basic peephole optimizations on a control flow graph.
6468+
/* Perform optimizations on a control flow graph.
64036469
The consts object should still be in list form to allow new constants
64046470
to be appended.
64056471
@@ -6411,11 +6477,6 @@ mark_reachable(struct assembler *a) {
64116477
static int
64126478
optimize_cfg(struct assembler *a, PyObject *consts)
64136479
{
6414-
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
6415-
if (normalize_basic_block(b)) {
6416-
return -1;
6417-
}
6418-
}
64196480
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
64206481
if (optimize_basic_block(b, consts)) {
64216482
return -1;
@@ -6432,9 +6493,63 @@ optimize_cfg(struct assembler *a, PyObject *consts)
64326493
b->b_iused = 0;
64336494
}
64346495
}
6496+
minimize_lineno_table(a);
6497+
return 0;
6498+
}
6499+
6500+
static inline int
6501+
is_exit_without_lineno(basicblock *b) {
6502+
return b->b_exit && b->b_instr[0].i_lineno < 0;
6503+
}
6504+
6505+
/* PEP 626 mandates that the f_lineno of a frame is correct
6506+
* after a frame terminates. It would be prohibitively expensive
6507+
* to continuously update the f_lineno field at runtime,
6508+
* so we make sure that all exiting instruction (raises and returns)
6509+
* have a valid line number, allowing us to compute f_lineno lazily.
6510+
* We can do this by duplicating the exit blocks without line number
6511+
* so that none have more than one predecessor. We can then safely
6512+
* copy the line number from the sole predecessor block.
6513+
*/
6514+
static int
6515+
ensure_exits_have_lineno(struct compiler *c)
6516+
{
6517+
/* Copy all exit blocks without line number that are targets of a jump.
6518+
*/
6519+
for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {
6520+
if (b->b_iused > 0 && is_jump(&b->b_instr[b->b_iused-1])) {
6521+
switch (b->b_instr[b->b_iused-1].i_opcode) {
6522+
/* Note: Only actual jumps, not exception handlers */
6523+
case SETUP_ASYNC_WITH:
6524+
case SETUP_WITH:
6525+
case SETUP_FINALLY:
6526+
continue;
6527+
}
6528+
basicblock *target = b->b_instr[b->b_iused-1].i_target;
6529+
if (is_exit_without_lineno(target)) {
6530+
basicblock *new_target = compiler_copy_block(c, target);
6531+
if (new_target == NULL) {
6532+
return -1;
6533+
}
6534+
new_target->b_instr[0].i_lineno = b->b_instr[b->b_iused-1].i_lineno;
6535+
b->b_instr[b->b_iused-1].i_target = new_target;
6536+
}
6537+
}
6538+
}
6539+
/* Any remaining reachable exit blocks without line number can only be reached by
6540+
* fall through, and thus can only have a single predecessor */
6541+
for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {
6542+
if (!b->b_nofallthrough && b->b_next && b->b_iused > 0) {
6543+
if (is_exit_without_lineno(b->b_next)) {
6544+
assert(b->b_next->b_iused > 0);
6545+
b->b_next->b_instr[0].i_lineno = b->b_instr[b->b_iused-1].i_lineno;
6546+
}
6547+
}
6548+
}
64356549
return 0;
64366550
}
64376551

6552+
64386553
/* Retained for API compatibility.
64396554
* Optimization is now done in optimize_cfg */
64406555

0 commit comments

Comments
 (0)