https://github.com/python/cpython/commit/6a67945f32f216274c8f69fc7e9f97a9886ce859 commit: 6a67945f32f216274c8f69fc7e9f97a9886ce859 branch: 3.15 author: Miss Islington (bot) <[email protected]> committer: encukou <[email protected]> date: 2026-10-13T11:20:24+02:00 summary:
[3.15] gh-155823: Fix stale stack on _SEND_VIRTUAL_TIER_TWO exhausted exit (GH-155844) (GH-158559) (cherry picked from commit 1071f7400ec6f3ffb63b48310440b2ae73c632b4) Co-authored-by: An Long <[email protected]> files: A Misc/NEWS.d/next/Core_and_Builtins/2026-08-15-20-31-20.gh-issue-155823.OhNmzi.rst M Lib/test/test_capi/test_opt.py M Python/bytecodes.c M Python/executor_cases.c.h diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 127a5c9e55fa7a..b8ce8897b37300 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -5699,6 +5699,20 @@ def testfunc(n): self.assertIn("_FOR_ITER_GEN_FRAME", uops) self.assertIn("_SEND_VIRTUAL_TIER_TWO", uops) + def test_send_virtual_exhausted(self): + # gh-155823: warm up on a non-empty list, then take the exhausted exit. + def gen(x): + yield from x + def testfunc(n, x): + total = 0 + for _ in range(n): + for v in gen(x): + total += v + return total + + testfunc(TIER2_THRESHOLD * 10, [1]) + self.assertEqual(testfunc(1000, []), 0) + def test_binary_op_subscr_init_frame(self): class B: def __getitem__(self, other): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-15-20-31-20.gh-issue-155823.OhNmzi.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-15-20-31-20.gh-issue-155823.OhNmzi.rst new file mode 100644 index 00000000000000..7dfbaf02575fd6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-15-20-31-20.gh-issue-155823.OhNmzi.rst @@ -0,0 +1,4 @@ +Fix a JIT bug where the side exit taken when a virtual iterator is exhausted +left a stale value on the stack, so the tier one interpreter resumed with +corrupted state and ``yield from`` raised :exc:`AttributeError` instead of +finishing normally. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0f2a6d758db93f..77aaa8a2f44626 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1785,8 +1785,6 @@ dummy_func( if (index < 0) { ERROR_NO_POP(); } - next = none; - DEAD(none); EXIT_IF(true); } DEAD(none); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4bec7d080054dd..38da59a0f9bf3a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -9020,7 +9020,6 @@ SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - next = none; if (true) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); @@ -9064,10 +9063,9 @@ SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - next = none; if (true) { UOP_STAT_INC(uopcode, miss); - _tos_cache0 = stack_pointer[0]; + _tos_cache0 = none; SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } @@ -9111,10 +9109,9 @@ SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - next = none; if (true) { UOP_STAT_INC(uopcode, miss); - _tos_cache1 = stack_pointer[1]; + _tos_cache1 = none; _tos_cache0 = null_or_index; SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); @@ -9161,10 +9158,9 @@ SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - next = none; if (true) { UOP_STAT_INC(uopcode, miss); - _tos_cache2 = stack_pointer[2]; + _tos_cache2 = none; _tos_cache1 = null_or_index; _tos_cache0 = iter; SET_CURRENT_CACHED_VALUES(3); _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
