8000 gh-98461: Fix source location in comprehensions bytecode by iritkatriel · Pull Request #98464 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-98461: Fix source location in comprehensions bytecode #98464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-98461: Fix source location in comprehensions bytecode
  • Loading branch information
iritkatriel committed Oct 19, 2022
commit 800219cf9b0253e26edbd9ee7413cf3bae751b5c
80 changes: 80 additions & 0 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,86 @@ def test_multiline_assert(self):
self.assertOpcodeSourcePositionIs(compiled_code, 'RAISE_VARARGS',
line=1, end_line=3, column=0, end_column=30, occurrence=1)

def test_multiline_generator_expression(self):
snippet = """\
((x,
2*x)
for x
in [1,2,3] if (x > 0
and x < 100
and x != 50))
"""

compiled_code, _ = self.check_positions_against_ast(snippet)
compiled_code = compiled_code.co_consts[0]
self.assertIsInstance(compiled_code, types.CodeType)
self.assertOpcodeSourcePositionIs(compiled_code, 'YIELD_VALUE',
line=1, end_line=2, column=1, end_column=8, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
line=1, end_line=2, column=1, end_column=8, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=1, end_line=6, column=0, end_column=32, occurrence=1)

def test_multiline_list_comprehension(self):
snippet = """\
[(x,
2*x)
for x
in [1,2,3] if (x > 0
and x < 100
and x != 50)]
"""

compiled_code, _ = self.check_positions_against_ast(snippet)
compiled_code = compiled_code.co_consts[0]
self.assertIsInstance(compiled_code, types.CodeType)
self.assertOpcodeSourcePositionIs(compiled_code, 'LIST_APPEND',
line=1, end_line=2, column=1, end_column=8, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
line=1, end_line=2, column=1, end_column=8, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=1, end_line=6, column=0, end_column=32, occurrence=1)

def test_multiline_set_comprehension(self):
snippet = """\
{(x,
2*x)
for x
in [1,2,3] if (x > 0
and x < 100
and x != 50)}
"""

compiled_code, _ = self.check_positions_against_ast(snippet)
compiled_code = compiled_code.co_consts[0]
self.assertIsInstance(compiled_code, types.CodeType)
self.assertOpcodeSourcePositionIs(compiled_code, 'SET_ADD',
line=1, end_line=2, column=1, end_column=8, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
line=1, end_line=2, column=1, end_column=8, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=1, end_line=6, column=0, end_column=32, occurrence=1)

def test_multiline_dict_comprehension(self):
snippet = """\
{x:
2*x
for x
in [1,2,3] if (x > 0
and x < 100
and x != 50)}
"""

compiled_code, _ = self.check_positions_against_ast(snippet)
compiled_code = compiled_code.co_consts[0]
self.assertIsInstance(compiled_code, types.CodeType)
self.assertOpcodeSourcePositionIs(compiled_code, 'MAP_ADD',
line=1, end_line=2, column=1, end_column=7, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
line=1, end_line=2, column=1, end_column=7, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=1, end_line=6, column=0, end_column=32, occurrence=1)

def test_very_long_line_end_offset(self):
# Make sure we get the correct column offset for offsets
# too large to store in a byte.
Expand Down
48 changes: 28 additions & 20 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -5208,21 +5208,19 @@ compiler_comprehension_generator(struct compiler *c, location *ploc,

static int
compiler_sync_comprehension_generator(struct compiler *c, location *ploc,
asdl_comprehension_seq *generators, int gen_index,
int depth,
asdl_comprehension_seq *generators,
int gen_index, int depth,
expr_ty elt, expr_ty val, int type)
{
/* generate code for the iterator, then each of the ifs,
and then write to the element */

comprehension_ty gen;
Py_ssize_t i, n;

NEW_JUMP_TARGET_LABEL(c, start);
NEW_JUMP_TARGET_LABEL(c, if_cleanup);
NEW_JUMP_TARGET_LABEL(c, anchor);

gen = (comprehension_ty)asdl_seq_GET(generators, gen_index);
comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators,
gen_index);

if (gen_index == 0) {
/* Receive outermost iter as an implicit argument */
Expand Down Expand Up @@ -5265,42 +5263,51 @@ compiler_sync_comprehension_generator(struct compiler *c, location *ploc,
VISIT(c, expr, gen->target);

/* XXX this needs to be cleaned up...a lot! */
n = asdl_seq_LEN(gen->ifs);
for (i = 0; i < n; i++) {
Py_ssize_t n = asdl_seq_LEN(gen->ifs);
for (Py_ssize_t i = 0; i < n; i++) {
expr_ty e = (expr_ty)asdl_seq_GET(gen->ifs, i);
if (!compiler_jump_if(c, ploc, e, if_cleanup, 0))
if (!compiler_jump_if(c, ploc, e, if_cleanup, 0)) {
return 0;
}
}

if (++gen_index < asdl_seq_LEN(generators))
if (++gen_index < asdl_seq_LEN(generators)) {
if (!compiler_comprehension_generator(c, ploc,
generators, gen_index, depth,
elt, val, type))
return 0;
elt, val, type)) {
return 0;
}
}

location elt_loc = LOC(elt);

/* only append after the last for generator */
if (gen_index >= asdl_seq_LEN(generators)) {
/* comprehension specific code */
switch (type) {
case COMP_GENEXP:
VISIT(c, expr, elt);
ADDOP_YIELD(c, *ploc);
ADDOP(c, *ploc, POP_TOP);
ADDOP_YIELD(c, elt_loc);
ADDOP(c, elt_loc, POP_TOP);
break;
case COMP_LISTCOMP:
VISIT(c, expr, elt);
ADDOP_I(c, *ploc, LIST_APPEND, depth + 1);
ADDOP_I(c, elt_loc, LIST_APPEND, depth + 1);
break;
case COMP_SETCOMP:
VISIT(c, expr, elt);
ADDOP_I(c, *ploc, SET_ADD, depth + 1);
ADDOP_I(c, elt_loc, SET_ADD, depth + 1);
break;
case COMP_DICTCOMP:
/* With '{k: v}', k is evaluated before v, so we do
the same. */
VISIT(c, expr, elt);
VISIT(c, expr, val);
ADDOP_I(c, *ploc, MAP_ADD, depth + 1);
elt_loc = LOCATION(elt->lineno,
val->end_lineno,
elt->col_offset,
val->end_col_offset);
ADDOP_I(c, elt_loc, MAP_ADD, depth + 1);
break;
default:
return 0;
Expand All @@ -5309,7 +5316,7 @@ compiler_sync_comprehension_generator(struct compiler *c, location *ploc,

USE_LABEL(c, if_cleanup);
if (IS_LABEL(start)) {
ADDOP_JUMP(c, *ploc, JUMP, start);
ADDOP_JUMP(c, elt_loc, JUMP, start);

USE_LABEL(c, anchor);
}
Expand Down Expand Up @@ -5467,11 +5474,12 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
}

if (!compiler_comprehension_generator(c, &loc, generators, 0, 0,
elt, val, type))
elt, val, type)) {
goto error_in_scope;
}

if (type != COMP_GENEXP) {
ADDOP(c, loc, RETURN_VALUE);
ADDOP(c, LOC(e), RETURN_VALUE);
}

co = assemble(c, 1);
Expand Down
0