Skip to content

Commit 22863df

Browse files
authored
GH-96793: Change FOR_ITER to not pop the iterator on exhaustion. (GH-96801)
Change FOR_ITER to have the same stack effect regardless of whether it branches or not. Performance is unchanged as FOR_ITER (and specialized forms jump over the cleanup code).
1 parent e60892f commit 22863df

File tree

14 files changed

+277
-247
lines changed

14 files changed

+277
-247
lines changed

Doc/library/dis.rst

+12-1
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,15 @@ The Python compiler currently generates the following bytecode instructions.
413413
Removes the top-of-stack (TOS) item.
414414

415415

416+
.. opcode:: END_FOR
417+
418+
Removes the top two values from the stack.
419+
Equivalent to POP_TOP; POP_TOP.
420+
Used to clean up at the end of loops, hence the name.
421+
422+
.. versionadded:: 3.12
423+
424+
416425
.. opcode:: COPY (i)
417426

418427
Push the *i*-th item to the top of the stack. The item is not removed from its
@@ -1088,9 +1097,11 @@ iterations of the loop.
10881097

10891098
TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. If
10901099
this yields a new value, push it on the stack (leaving the iterator below
1091-
it). If the iterator indicates it is exhausted, TOS is popped, and the byte
1100+
it). If the iterator indicates it is exhausted then the byte
10921101
code counter is incremented by *delta*.
10931102

1103+
.. versionchanged:: 3.12
1104+
Up until 3.11 the iterator was popped when it was exhausted.
10941105

10951106
.. opcode:: LOAD_GLOBAL (namei)
10961107

Include/internal/pycore_opcode.h

+20-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/opcode.h

+73-72
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ def _write_atomic(path, data, mode=0o666):
423423
# Python 3.12a1 3507 (Set lineno of module's RESUME to 0)
424424
# Python 3.12a1 3508 (Add CLEANUP_THROW)
425425
# Python 3.12a1 3509 (Conditional jumps only jump forward)
426+
# Python 3.12a1 3510 (FOR_ITER leaves iterator on the stack)
426427

427428
# Python 3.13 will start with 3550
428429

@@ -435,7 +436,7 @@ def _write_atomic(path, data, mode=0o666):
435436
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
436437
# in PC/launcher.c must also be updated.
437438

438-
MAGIC_NUMBER = (3509).to_bytes(2, 'little') + b'\r\n'
439+
MAGIC_NUMBER = (3510).to_bytes(2, 'little') + b'\r\n'
439440

440441
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
441442

Lib/opcode.py

+2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ def pseudo_op(name, op, real_ops):
7878
def_op('POP_TOP', 1)
7979
def_op('PUSH_NULL', 2)
8080

81+
def_op('END_FOR', 4)
82+
8183
def_op('NOP', 9)
8284
def_op('UNARY_POSITIVE', 10)
8385
def_op('UNARY_NEGATIVE', 11)

0 commit comments

Comments
 (0)