@@ -5438,6 +5438,8 @@ typedef struct {
54385438 PyObject * pushed_locals ;
54395439 PyObject * temp_symbols ;
54405440 PyObject * fast_hidden ;
5441+ jump_target_label cleanup ;
5442+ jump_target_label end ;
54415443} inlined_comprehension_state ;
54425444
54435445static int
@@ -5543,11 +5545,45 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
55435545 // `pushed_locals` on the stack, but this will be reversed when we swap
55445546 // out the comprehension result in pop_inlined_comprehension_state
55455547 ADDOP_I (c , loc , SWAP , PyList_GET_SIZE (state -> pushed_locals ) + 1 );
5548+
5549+ // Add our own cleanup handler to restore comprehension locals in case
5550+ // of exception, so they have the correct values inside an exception
5551+ // handler or finally block.
5552+ NEW_JUMP_TARGET_LABEL (c , cleanup );
5553+ state -> cleanup = cleanup ;
5554+ NEW_JUMP_TARGET_LABEL (c , end );
5555+ state -> end = end ;
5556+
5557+ // no need to push an fblock for this "virtual" try/finally; there can't
5558+ // be return/continue/break inside a comprehension
5559+ ADDOP_JUMP (c , loc , SETUP_FINALLY , cleanup );
55465560 }
55475561
55485562 return SUCCESS ;
55495563}
55505564
5565+ static int
5566+ restore_inlined_comprehension_locals (struct compiler * c , location loc ,
5567+ inlined_comprehension_state state )
5568+ {
5569+ PyObject * k ;
5570+ // pop names we pushed to stack earlier
5571+ Py_ssize_t npops = PyList_GET_SIZE (state .pushed_locals );
5572+ // Preserve the comprehension result (or exception) as TOS. This
5573+ // reverses the SWAP we did in push_inlined_comprehension_state to get
5574+ // the outermost iterable to TOS, so we can still just iterate
5575+ // pushed_locals in simple reverse order
5576+ ADDOP_I (c , loc , SWAP , npops + 1 );
5577+ for (Py_ssize_t i = npops - 1 ; i >= 0 ; -- i ) {
5578+ k = PyList_GetItem (state .pushed_locals , i );
5579+ if (k == NULL ) {
5580+ return ERROR ;
5581+ }
5582+ ADDOP_NAME (c , loc , STORE_FAST_MAYBE_NULL , k , varnames );
5583+ }
5584+ return SUCCESS ;
5585+ }
5586+
55515587static int
55525588pop_inlined_comprehension_state (struct compiler * c , location loc ,
55535589 inlined_comprehension_state state )
@@ -5564,19 +5600,22 @@ pop_inlined_comprehension_state(struct compiler *c, location loc,
55645600 Py_CLEAR (state .temp_symbols );
55655601 }
55665602 if (state .pushed_locals ) {
5567- // pop names we pushed to stack earlier
5568- Py_ssize_t npops = PyList_GET_SIZE (state .pushed_locals );
5569- // Preserve the list/dict/set result of the comprehension as TOS. This
5570- // reverses the SWAP we did in push_inlined_comprehension_state to get
5571- // the outermost iterable to TOS, so we can still just iterate
5572- // pushed_locals in simple reverse order
5573- ADDOP_I (c , loc , SWAP , npops + 1 );
5574- for (Py_ssize_t i = npops - 1 ; i >= 0 ; -- i ) {
5575- k = PyList_GetItem (state .pushed_locals , i );
5576- if (k == NULL ) {
5577- return ERROR ;
5578- }
5579- ADDOP_NAME (c , loc , STORE_FAST_MAYBE_NULL , k , varnames );
5603+ ADDOP (c , NO_LOCATION , POP_BLOCK );
5604+ ADDOP_JUMP (c , NO_LOCATION , JUMP , state .end );
5605+
5606+ // cleanup from an exception inside the comprehension
5607+ USE_LABEL (c , state .cleanup );
5608+ // discard incomplete comprehension result (beneath exc on stack)
5609+ ADDOP_I (c , NO_LOCATION , SWAP , 2 );
5610+ ADDOP (c , NO_LOCATION , POP_TOP );
5611+ if (restore_inlined_comprehension_locals (c , loc , state ) < 0 ) {
5612+ return ERROR ;
5613+ }
5614+ ADDOP_I (c , NO_LOCATION , RERAISE , 0 );
5615+
5616+ USE_LABEL (c , state .end );
5617+ if (restore_inlined_comprehension_locals (c , loc , state ) < 0 ) {
5618+ return ERROR ;
55805619 }
55815620 Py_CLEAR (state .pushed_locals );
55825621 }
@@ -5619,7 +5658,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
56195658 expr_ty val )
56205659{
56215660 PyCodeObject * co = NULL ;
5622- inlined_comprehension_state inline_state = {NULL , NULL };
5661+ inlined_comprehension_state inline_state = {NULL , NULL , NULL , NO_LABEL , NO_LABEL };
56235662 comprehension_ty outermost ;
56245663 int scope_type = c -> u -> u_scope_type ;
56255664 int is_top_level_await = IS_TOP_LEVEL_AWAIT (c );
0 commit comments