Skip to content
Open
Changes from all commits
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
74 changes: 43 additions & 31 deletions Python/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -4765,40 +4765,16 @@ codegen_sync_comprehension_generator(compiler *c, location loc,
}

static int
codegen_async_comprehension_generator(compiler *c, location loc,
asdl_comprehension_seq *generators,
int gen_index, int depth,
expr_ty elt, expr_ty val, int type,
IterStackPosition iter_pos, bool avoid_creation)
codegen_async_comprehension_generator_body(
compiler *c, location loc,
asdl_comprehension_seq *generators, int gen_index, int depth,
expr_ty elt, expr_ty val, int type, bool avoid_creation,
comprehension_ty gen, jump_target_label start)
{
NEW_JUMP_TARGET_LABEL(c, start);
NEW_JUMP_TARGET_LABEL(c, send);
NEW_JUMP_TARGET_LABEL(c, except);
NEW_JUMP_TARGET_LABEL(c, if_cleanup);

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

if (iter_pos == ITERABLE_IN_LOCAL) {
if (gen_index == 0) {
assert(METADATA(c)->u_argcount == 1);
ADDOP_I(c, loc, LOAD_FAST, 0);
}
else {
/* Sub-iter - calculate on the fly */
VISIT(c, expr, gen->iter);
}
}
if (iter_pos != ITERATOR_ON_STACK) {
ADDOP(c, LOC(gen->iter), GET_AITER);
}

USE_LABEL(c, start);
/* Runtime will push a block here, so we need to account for that */
RETURN_IF_ERROR(
_PyCompile_PushFBlock(c, loc, COMPILE_FBLOCK_ASYNC_COMPREHENSION_GENERATOR,
start, NO_LABEL, NULL));

ADDOP_JUMP(c, loc, SETUP_FINALLY, except);
ADDOP(c, loc, GET_ANEXT);
ADDOP(c, loc, PUSH_NULL);
Expand Down Expand Up @@ -4907,15 +4883,51 @@ codegen_async_comprehension_generator(compiler *c, location loc,
USE_LABEL(c, if_cleanup);
ADDOP_JUMP(c, elt_loc, JUMP, start);

_PyCompile_PopFBlock(c, COMPILE_FBLOCK_ASYNC_COMPREHENSION_GENERATOR, start);

USE_LABEL(c, except);

ADDOP_JUMP(c, loc, END_ASYNC_FOR, send);

return SUCCESS;
}

static int
codegen_async_comprehension_generator(compiler *c, location loc,
asdl_comprehension_seq *generators,
int gen_index, int depth,
expr_ty elt, expr_ty val, int type,
IterStackPosition iter_pos, bool avoid_creation)
{
NEW_JUMP_TARGET_LABEL(c, start);

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

if (iter_pos == ITERABLE_IN_LOCAL) {
if (gen_index == 0) {
assert(METADATA(c)->u_argcount == 1);
ADDOP_I(c, loc, LOAD_FAST, 0);
}
else {
/* Sub-iter - calculate on the fly */
VISIT(c, expr, gen->iter);
}
}
if (iter_pos != ITERATOR_ON_STACK) {
ADDOP(c, LOC(gen->iter), GET_AITER);
}

/* Runtime will push a block here, so we need to account for that */
RETURN_IF_ERROR(
_PyCompile_PushFBlock(c, loc, COMPILE_FBLOCK_ASYNC_COMPREHENSION_GENERATOR,
start, NO_LABEL, NULL));

int ret = codegen_async_comprehension_generator_body(
c, loc, generators, gen_index, depth, elt, val, type, avoid_creation,
gen, start);
_PyCompile_PopFBlock(c, COMPILE_FBLOCK_ASYNC_COMPREHENSION_GENERATOR, start);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can pop the wrong block after a codegen error in a nested inlined comprehension. For example, compiling this source aborts a debug build:

async def f(it):
    return [[f(a=1, a=2) for y in z] async for x in it]

I verified that the parent commit raises SyntaxError: keyword argument repeated: a, while this PR aborts on the block-type assertion in _PyCompile_PopFBlock.

The inner comprehension leaves its COMPILE_FBLOCK_INLINED_COMPREHENSION pushed when codegen_comprehension takes its error path, so it is still above the async block when we get here. Could we make sure nested inlined-comprehension blocks are cleaned up on error before making this pop unconditional, and add a regression test for this case?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be all or nothing: either we always pop any block on error, or we allow them to remain on the stack until they are bulk removed on unrolling. I prefer the former, but we probably need to switch to that in one go. I'll try to stay in the current scheme on the subscope branch for now.

return ret;
}

static int
codegen_push_inlined_comprehension_locals(compiler *c, location loc,
PySTEntryObject *comp,
Expand Down
Loading