@@ -1512,11 +1512,14 @@ impl Compiler {
15121512 }
15131513
15141514 FBlockType :: ForLoop => {
1515- // Pop the iterator
1515+ // When returning from a for-loop, CPython swaps the preserved
1516+ // value with the iterator and uses POP_TOP for the iterator slot.
15161517 if preserve_tos {
15171518 emit ! ( self , Instruction :: Swap { i: 2 } ) ;
1519+ emit ! ( self , Instruction :: PopTop ) ;
1520+ } else {
1521+ emit ! ( self , Instruction :: PopIter ) ;
15181522 }
1519- emit ! ( self , Instruction :: PopIter ) ;
15201523 }
15211524
15221525 FBlockType :: TryExcept => {
@@ -11148,6 +11151,99 @@ def f(base, cls, state):
1114811151 assert_eq ! ( return_count, 2 ) ;
1114911152 }
1115011153
11154+ #[ test]
11155+ fn test_loop_store_subscr_threads_direct_backedge ( ) {
11156+ let code = compile_exec (
11157+ "\
11158+ def f(kwonlyargs, kwonlydefaults, arg2value):
11159+ missing = 0
11160+ for kwarg in kwonlyargs:
11161+ if kwarg not in arg2value:
11162+ if kwonlydefaults and kwarg in kwonlydefaults:
11163+ arg2value[kwarg] = kwonlydefaults[kwarg]
11164+ else:
11165+ missing += 1
11166+ return missing
11167+ " ,
11168+ ) ;
11169+ let f = find_code ( & code, "f" ) . expect ( "missing function code" ) ;
11170+ let ops: Vec < _ > = f
11171+ . instructions
11172+ . iter ( )
11173+ . map ( |unit| unit. op )
11174+ . filter ( |op| !matches ! ( op, Instruction :: Cache ) )
11175+ . collect ( ) ;
11176+
11177+ let store_subscr = ops
11178+ . iter ( )
11179+ . position ( |op| matches ! ( op, Instruction :: StoreSubscr ) )
11180+ . expect ( "missing STORE_SUBSCR" ) ;
11181+ let next_op = ops
11182+ . get ( store_subscr + 1 )
11183+ . expect ( "missing jump after STORE_SUBSCR" ) ;
11184+ let window_start = store_subscr. saturating_sub ( 3 ) ;
11185+ let window_end = ( store_subscr + 5 ) . min ( ops. len ( ) ) ;
11186+ let window = & ops[ window_start..window_end] ;
11187+
11188+ assert ! (
11189+ matches!( next_op, Instruction :: JumpBackward { .. } ) ,
11190+ "expected direct loop backedge after STORE_SUBSCR, got {next_op:?}; ops={window:?}"
11191+ ) ;
11192+ }
11193+
11194+ #[ test]
11195+ fn test_loop_return_reorders_backedge_before_exit_cleanup ( ) {
11196+ let code = compile_exec (
11197+ "\
11198+ def f(obj):
11199+ for base in obj.__mro__:
11200+ if base is not object:
11201+ doc = base.__doc__
11202+ if doc is not None:
11203+ return doc
11204+ " ,
11205+ ) ;
11206+ let f = find_code ( & code, "f" ) . expect ( "missing function code" ) ;
11207+ let ops: Vec < _ > = f
11208+ . instructions
11209+ . iter ( )
11210+ . map ( |unit| unit. op )
11211+ . filter ( |op| !matches ! ( op, Instruction :: Cache ) )
11212+ . collect ( ) ;
11213+
11214+ let cond_idx = ops
11215+ . iter ( )
11216+ . position ( |op| matches ! ( op, Instruction :: PopJumpIfNotNone { .. } ) )
11217+ . expect ( "missing POP_JUMP_IF_NOT_NONE" ) ;
11218+ assert ! (
11219+ matches!( ops. get( cond_idx + 1 ) , Some ( Instruction :: NotTaken ) ) ,
11220+ "expected NOT_TAKEN after conditional jump, got {:?}; ops={ops:?}" ,
11221+ ops. get( cond_idx + 1 )
11222+ ) ;
11223+ assert ! (
11224+ matches!(
11225+ ops. get( cond_idx + 2 ) ,
11226+ Some ( Instruction :: JumpBackward { .. } )
11227+ ) ,
11228+ "expected loop backedge immediately after NOT_TAKEN, got {:?}; ops={ops:?}" ,
11229+ ops. get( cond_idx + 2 )
11230+ ) ;
11231+
11232+ let end_for_idx = ops
11233+ . iter ( )
11234+ . position ( |op| matches ! ( op, Instruction :: EndFor ) )
11235+ . expect ( "missing END_FOR" ) ;
11236+ let return_before_end = ops[ ..end_for_idx]
11237+ . iter ( )
11238+ . rposition ( |op| matches ! ( op, Instruction :: ReturnValue ) )
11239+ . expect ( "missing loop-body RETURN_VALUE" ) ;
11240+ assert ! (
11241+ matches!( ops. get( return_before_end - 1 ) , Some ( Instruction :: PopTop ) ) ,
11242+ "expected POP_TOP before loop-body RETURN_VALUE, got {:?}; ops={ops:?}" ,
11243+ ops. get( return_before_end. saturating_sub( 1 ) )
11244+ ) ;
11245+ }
11246+
1115111247 #[ test]
1115211248 fn test_assert_without_message_raises_class_directly ( ) {
1115311249 let code = compile_exec (
0 commit comments