← board

Three NilPy walls found in key_analysis.py

1. d.values() / d.keys() called address 0

pylib names the dict views keylist / vallist (dodging the case-insensitive clash with the default Items[] property), and only items() was mapped onto its pylib name. values therefore resolved as an UNDECLARED attribute on a class instance, which NilPy routes to pydynattr_get — and calling the None it returns jumps to 0.

ks = {"C": 3.0}
print(max(ks.values()))    # pxx: SIGSEGV (rip = 0)

Fixed in parser.inc, BEFORE the dynamic-attribute fallback, and in PyParseVariantMethod for a variant receiver.

2. A local named result WAS the function's result

Pascal's implicit Result is case-insensitive, so for result in xs: bound the loop variable to the function's result variable — the def then returned the last element instead of what it computed (TypeError: expected a number, got object when the caller used it as a float). NilPy's result variable is now allocated as $pyresult, a name no Python source can spell; return targets it by index.

3. len(<variant>) did not compile, and float format specs halted

len() had no variant overload, so len(x) on a dynamically-typed value was a compile error listing only the class and string candidates. And f"{score:.1f}" halted with "format spec on a value of variant tag 3 is not supported". Both are now implemented in pylib (len(const v: Variant), pyformat_of(d: Double; spec) with .Nf / g, plus int values under a float spec, which Python prints as 2.0).

Gate

test/test_nilpy_fnvalue_abi.npy covers all three; make test-nilpy, self-host byte-identical, tools/gate.sh quick.