ESBMC
Efficient SMT-based Context-Bounded Model Checker
Loading...
Searching...
No Matches
preprocessor.sequence_iterator_mixin.SequenceIteratorMixin Class Reference
Inheritance diagram for preprocessor.sequence_iterator_mixin.SequenceIteratorMixin:
[legend]

Public Attributes

 seq_iterator_index

Protected Member Functions

 _seq_const_int (self, node)
 _range_static_len (self, call)
 _static_len_of (self, node)
 _scan_sequence_iterators (self, node)
 _track_static_seq_binding (self, node)
 _maybe_track_seq_iterator (self, node)
 _make_seq_next_stop (self, template)
 _build_seq_next (self, iter_var, targets, template)
 _maybe_rewrite_seq_next_assign (self, node)
 _maybe_rewrite_seq_next_expr (self, node)

Static Protected Member Functions

 _is_dunder_iter_call (node)
 _seq_next_consumed_name (call)
 _ast_types (*names)

Protected Attributes

 _seq_iter_blacklist = set()

Detailed Description

Lowering for the explicit iterator protocol on statically-sized
sequences.

Handles the pattern ``it = seq.__iter__()`` followed by ``next(it)`` or
``it.__next__()``. The ``__iter__`` call already returns the sequence
object itself (see ``__iter__ on builtin iterables`` in the C/C++ frontend),
so ``it`` aliases the sequence and ``it[k]`` reads its k-th element. Each
``next``/``__next__`` is rewritten to ``it[k]`` with a per-iterator
compile-time index, raising ``StopIteration`` once the captured length is
exhausted (CPython semantics).

Because the consumption index is tracked statically in source order, the
rewrite is only sound for iterators consumed in straight-line code. The
pre-scan (``_scan_sequence_iterators``) blacklists any iterator whose
binding or consumption is inside a loop, comprehension, or conditional, or
that is rebound, consumed in a non-rewritable position, or closed over
across scopes; blacklisted iterators are left untouched (falling back to the
frontend's existing behaviour, so there is no regression).

Member Function Documentation

◆ _ast_types()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._ast_types ( * names)
staticprotected
Tuple of the named ``ast`` node classes that exist in this Python
(some — ``Match``, ``TryStar`` — are version-dependent).

◆ _build_seq_next()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._build_seq_next ( self,
iter_var,
targets,
template )
protected
Lower a single ``next``/``__next__`` consumption of ``iter_var``.

Returns a list of statements (the indexed assignment, or a
``StopIteration`` raise when the sequence is exhausted).

◆ _is_dunder_iter_call()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._is_dunder_iter_call ( node)
staticprotected
True for ``RECV.__iter__()`` with no arguments.

◆ _make_seq_next_stop()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._make_seq_next_stop ( self,
template )
protected

◆ _maybe_rewrite_seq_next_assign()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._maybe_rewrite_seq_next_assign ( self,
node )
protected
Rewrite ``x = next(it)`` / ``x = it.__next__()`` for a tracked
iterator, or return None to leave the node unchanged.

◆ _maybe_rewrite_seq_next_expr()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._maybe_rewrite_seq_next_expr ( self,
node )
protected
Rewrite a standalone ``next(it)`` / ``it.__next__()`` statement.

◆ _maybe_track_seq_iterator()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._maybe_track_seq_iterator ( self,
node )
protected
Record iterator state for ``it = RECV.__iter__()`` when RECV is a
statically-sized sequence and ``it`` is not blacklisted.

◆ _range_static_len()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._range_static_len ( self,
call )
protected
Length of a ``range(...)`` call with constant integer args, or None.

◆ _scan_sequence_iterators()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._scan_sequence_iterators ( self,
node )
protected
Blacklist iterator names that cannot be lowered with a static index.

Static index tracking is only sound when the binding and every
consumption of an iterator run exactly once, in source order, in the
same scope. An iterator is disqualified when any of the following holds:

  * it is bound or consumed inside a *non-linear* construct — a loop,
    comprehension, or conditional (`if`/`try`/`with`/`match`) — where
    source order no longer matches runtime execution order;
  * a consumption appears in a position the rewriter does not handle
    (anything other than a bare ``x = next(it)`` / ``next(it)``
    statement), e.g. a call argument, ``return``, or nested expression,
    because the real ``next()`` left in place would advance the live
    iterator out of step with the static index;
  * it is rebound (assigned more than once); or
  * its binding and a consumption are in different scopes (a closure
    over an outer iterator, which may run any number of times).

Blacklisted iterators are left untouched, falling back to the frontend's
existing behaviour.

◆ _seq_const_int()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._seq_const_int ( self,
node )
protected
Best-effort evaluation of ``node`` to a Python int, or None.

Resolves integer constants, negations thereof, and names bound to an
integer literal (via ``_known_literal_values``).

◆ _seq_next_consumed_name()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._seq_next_consumed_name ( call)
staticprotected
Name of the iterator consumed by ``next(it)`` / ``it.__next__()``.

◆ _static_len_of()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._static_len_of ( self,
node )
protected
Static length of ``node`` as a sequence, or None if not known.

Only *immutable* sequences qualify: ``range(<const>)`` calls, tuple
literals, and names previously bound to either. Lists are excluded on
purpose — an iterator aliases the underlying list object, so a mutation
between two ``next()`` calls (``xs.append(...)``) would make a captured
static length diverge from CPython's live iterator. range and tuple
cannot be mutated in valid Python, so their length is fixed at binding.

◆ _track_static_seq_binding()

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._track_static_seq_binding ( self,
node )
protected
Record/clear ``_static_seq_len`` for ``name = <sized sequence>``.

Member Data Documentation

◆ _seq_iter_blacklist

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin._seq_iter_blacklist = set()
protected

◆ seq_iterator_index

preprocessor.sequence_iterator_mixin.SequenceIteratorMixin.seq_iterator_index

The documentation for this class was generated from the following file: