← board

dict.fromkeys of a str segfaults

Found 2026-08-16 by a tools/pydiff.py sweep over the builtin surface.

Measured

dict.fromkeys("ab", 0)     # CPython {'a': 0, 'b': 0}     pxx SIGSEGV
dict.fromkeys("ab")        # CPython {'a': None, 'b': None} pxx SIGSEGV
dict.fromkeys(["a", "b"])  # correct on both

Not exotic: list(dict.fromkeys(xs)) is the standard order-preserving dedupe, so deduping the characters of a word lands exactly here.

Cause — the known population, not a new one

PyParseStdlibCall builds pydict_fromkeys by name and re-targets only by ARITY. pydict_fromkeys(l: TPyList) therefore received a str and dereferenced it as an object. This is the population [[project_nilpy_byname_findproc_lowerings_are_the_unchecked_population]] names: every by-name lowering that lands on a pylib routine with a TPyList parameter is one wrong argument type away from a crash.

Fix

Both overloads take a Variant and go through pylist_v, the one bridge that turns any Python iterable (str, list, tuple, set, dict) into a TPyList, and which already raises a TypeError naming the offending tag for anything else. The cure is in the callee rather than a check at the call site, because a call-site check would have to be repeated per site — and the point of the population note is that there are many sites.

Gate

test/test_nilpy_dict_fromkeys_any_iterable.npy — str, list, tuple, dict and the deduping idiom, both arities, every value CPython's. The existing test_nilpy_dict_fromkeys.npy is unchanged. gate.sh quick green.