← board

next() over an iterator argument returns the default

Reported by Track T as an open regression: test-core#src:test/test_nilpy_sorted_pairs.npy bad=bb845b13ceb3.

cs = ["a", "bb", "ccc"]
print(next((c for c in cs if len(c) == 2), "none"))   # CPython bb    pxx none

Silent — a plausible wrong value, never a crash.

Cause

Follow-on of [[bug-nilpy-a-generator-expression-is-not-consumed-once]] (ba6022847), which made a genexpr answer a real TPyIter cursor instead of a materialised list. The next arm in compiler/parser.inc still selected pynext_first_or(l: TPyList; const dflt: Variant) statically, so the cursor handle arrived at a TPyList parameter and was misread; the "empty" reading returned the default. Bound to a name first (g = (...); next(g, "none")) the value went through the variant path and was right — which is why the suite's genexpr tests stayed green and only the inline spelling broke.

The shape is the recurring one: a static callee choice where the operand's type is a run-time fact.

Fix

compiler/builtin/pylib.paspynext_v(const v) / pynext_or_v(const v, dflt): a TPyIter advances via pyiter_has / pyiter_next / pyiter_next_or, everything else falls through to pynext_first(pylist_v(v)) as before. compiler/parser.inc's next arm boxes both operands with PyForceVariant and calls those.

Gate: gate.sh quick GREEN, self-host fixedpoint; pinned v339 (pylib changed). Verified sorted_pairs, genexpr, genexpr_is_consumed_once, bare_genexpr_arguments, iterator_protocol byte-identical to CPython.