← board

The function-object ABI

Repro

from typing import Callable
NOTES = {"C": ["C", "E", "G"]}
def notes_of(ch):                 # unannotated -> infers a VARIANT result
    return NOTES.get(ch, [])
def run(f: Callable[[str], list[str]]) -> int:
    return len(f("C"))
print(run(notes_of))              # pxx: SIGSEGV      CPython: 3

Cause

Callable[[str], list[str]] registered a $proctype whose result is a class (register return) and whose parameter is a string (by value). The def that arrives is compiled from its OWN inference: a variant result (hidden-destination convention) and an unannotated, i.e. variant, by-reference parameter. The indirect call therefore passed a string where the callee dereferenced a variant address, and left the hidden destination register holding whatever was there — the callee wrote its result variant through it.

The declared types cannot be authoritative: one signature marshals calls to whatever def is handed in, and Python does not check annotations either.

Fix

One function-object ABI on both ends — variant parameters, variant result:

Gate

test/test_nilpy_fnvalue_abi.npy (in make test-nilpy), self-host byte-identical, tools/gate.sh quick.