@@ -5529,6 +5529,8 @@ typedef struct {
55295529 PyObject * pushed_locals ;
55305530 PyObject * temp_symbols ;
55315531 PyObject * fast_hidden ;
5532+ jump_target_label cleanup ;
5533+ jump_target_label end ;
55325534} inlined_comprehension_state ;
55335535
55345536static int
@@ -5639,11 +5641,45 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
56395641 // `pushed_locals` on the stack, but this will be reversed when we swap
56405642 // out the comprehension result in pop_inlined_comprehension_state
56415643 ADDOP_I (c , loc , SWAP , PyList_GET_SIZE (state -> pushed_locals ) + 1 );
5644+
5645+ // Add our own cleanup handler to restore comprehension locals in case
5646+ // of exception, so they have the correct values inside an exception
5647+ // handler or finally block.
5648+ NEW_JUMP_TARGET_LABEL (c , cleanup );
5649+ state -> cleanup = cleanup ;
5650+ NEW_JUMP_TARGET_LABEL (c , end );
5651+ state -> end = end ;
5652+
5653+ // no need to push an fblock for this "virtual" try/finally; there can't
5654+ // be return/continue/break inside a comprehension
5655+ ADDOP_JUMP (c , loc , SETUP_FINALLY , cleanup );
56425656 }
56435657
56445658 return SUCCESS ;
56455659}
56465660
5661+ static int
5662+ restore_inlined_comprehension_locals (struct compiler * c , location loc ,
5663+ inlined_comprehension_state state )
5664+ {
5665+ PyObject * k ;
5666+ // pop names we pushed to stack earlier
5667+ Py_ssize_t npops = PyList_GET_SIZE (state .pushed_locals );
5668+ // Preserve the comprehension result (or exception) as TOS. This
5669+ // reverses the SWAP we did in push_inlined_comprehension_state to get
5670+ // the outermost iterable to TOS, so we can still just iterate
5671+ // pushed_locals in simple reverse order
5672+ ADDOP_I (c , loc , SWAP , npops + 1 );
5673+ for (Py_ssize_t i = npops - 1 ; i >= 0 ; -- i ) {
5674+ k = PyList_GetItem (state .pushed_locals , i );
5675+ if (k == NULL ) {
5676+ return ERROR ;
5677+ }
5678+ ADDOP_NAME (c , loc , STORE_FAST_MAYBE_NULL , k , varnames );
5679+ }
5680+ return SUCCESS ;
5681+ }
5682+
56475683static int
56485684pop_inlined_comprehension_state (struct compiler * c , location loc ,
56495685 inlined_comprehension_state state )
@@ -5660,19 +5696,22 @@ pop_inlined_comprehension_state(struct compiler *c, location loc,
56605696 Py_CLEAR (state .temp_symbols );
56615697 }
56625698 if (state .pushed_locals ) {
5663- // pop names we pushed to stack earlier
5664- Py_ssize_t npops = PyList_GET_SIZE (state .pushed_locals );
5665- // Preserve the list/dict/set result of the comprehension as TOS. This
5666- // reverses the SWAP we did in push_inlined_comprehension_state to get
5667- // the outermost iterable to TOS, so we can still just iterate
5668- // pushed_locals in simple reverse order
5669- ADDOP_I (c , loc , SWAP , npops + 1 );
5670- for (Py_ssize_t i = npops - 1 ; i >= 0 ; -- i ) {
5671- k = PyList_GetItem (state .pushed_locals , i );
5672- if (k == NULL ) {
5673- return ERROR ;
5674- }
5675- ADDOP_NAME (c , loc , STORE_FAST_MAYBE_NULL , k , varnames );
5699+ ADDOP (c , NO_LOCATION , POP_BLOCK );
5700+ ADDOP_JUMP (c , NO_LOCATION , JUMP , state .end );
5701+
5702+ // cleanup from an exception inside the comprehension
5703+ USE_LABEL (c , state .cleanup );
5704+ // discard incomplete comprehension result (beneath exc on stack)
5705+ ADDOP_I (c , NO_LOCATION , SWAP , 2 );
5706+ ADDOP (c , NO_LOCATION , POP_TOP );
5707+ if (restore_inlined_comprehension_locals (c , loc , state ) < 0 ) {
5708+ return ERROR ;
5709+ }
5710+ ADDOP_I (c , NO_LOCATION , RERAISE , 0 );
5711+
5712+ USE_LABEL (c , state .end );
5713+ if (restore_inlined_comprehension_locals (c , loc , state ) < 0 ) {
5714+ return ERROR ;
56765715 }
56775716 Py_CLEAR (state .pushed_locals );
56785717 }
@@ -5715,7 +5754,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
57155754 expr_ty val )
57165755{
57175756 PyCodeObject * co = NULL ;
5718- inlined_comprehension_state inline_state = {NULL , NULL };
5757+ inlined_comprehension_state inline_state = {NULL , NULL , NULL , NO_LABEL , NO_LABEL };
57195758 comprehension_ty outermost ;
57205759 int scope_type = c -> u -> u_scope_type ;
57215760 int is_top_level_await = IS_TOP_LEVEL_AWAIT (c );
0 commit comments