Skip to content

dis: FOR_ITER says it no longer pops the stack in 3.12 but it still does when the iterator ended normally #121399

@frigus02

Description

@frigus02

Documentation

The FOR_ITER docs in the dis module say "Up until 3.11 the iterator was popped when it was exhausted". This sounds like in 3.12+ the iterator is not popped anymore:

cpython/Doc/library/dis.rst

Lines 1334 to 1342 in cecd601

.. opcode:: FOR_ITER (delta)
``STACK[-1]`` is an :term:`iterator`. Call its :meth:`~iterator.__next__` method.
If this yields a new value, push it on the stack (leaving the iterator below
it). If the iterator indicates it is exhausted then the byte code counter is
incremented by *delta*.
.. versionchanged:: 3.12
Up until 3.11 the iterator was popped when it was exhausted.

Instead there is a new opcode END_FOR, which takes care of popping the iterator off the stack. This surprised me because in 3.12 END_FOR is supposed to remove 2 elements from the top of the stack. But if the iterator ends normally, there will only be the iterator at the top. I tried to document my thought process in this godbolt repro.

Reading the generated code for FOR_ITER, specifically:

/* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */

STACK_SHRINK(1);
/* Jump forward oparg, then skip following END_FOR and POP_TOP instruction */
JUMPBY(oparg + 2);

it looks like there are 2 cases:

  • If the iterator ends normally, FOR_ITER pops the iterator off the stack, then it skips the next END_FOR (and in 3.13 POP_TOP) instructions.
  • Otherwise (I'm not sure when that happens?), the iterator ends with both the iter and iter() on the stack, which are both popped by END_FOR (and in 3.13 POP_TOP).

Is it worth documenting the 2 different cases?

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsDocumentation in the Doc dir

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions