← board

from m import f makes every def in an imported module use the function-object ABI

The name in a from <module> import <name> statement is a BARE identifier, so PyDefUsedAsValue (pyparser.inc) counts it as "used as a value" and normalises that def to the function-object ABI: variant result, and every parameter forced to tyVariant (pyparser.inc, PyParseDefHeader, the if PyDefUsedAsValue(PyHdrName) block).

For most parameter types the boxing is only wasteful. For a Callable[...] parameter it is fatal: the callee's cb slot becomes a variant (passed by reference), while the call site still hands over a function VALUE, and the callee's call_ind then jumps to the variant's TAG word.

Repro — SIGSEGV, CPython prints ['C', 'C']

# kalib2.py
from typing import Callable
def run2(chords: list[str], cb: Callable[[str], list[str]]) -> list[str]:
    return cb(chords[0])
# drv2.py
from kalib2 import run2

def notes_of(ch: str) -> list[str]:
    return [ch, ch]

print(run2(["C"], notes_of))     # SIGSEGV

This is the standing wall in songformatter's key analysis (~/songformatter, kadrv.py), where gdb names it NoteCountingDetector.analyze (..., chord_to_notes=0x2, ...) — 0x2 is a variant tag word, not a value. Not a regression: reproduces against a compiler built from b7c524a89.

Measured, do not re-derive

Fix direction

PyDefUsedAsValue must not count a mention that is part of an import statement (a logical line opening with tkUses (import) or the ident from). Narrowing its scan to PyScanLo..MainProgramTokCount instead would ALSO cut the false positive, but it would lose the genuine case — a def in a module passed as a value by the main program — so the import-line skip is the right cut.

RESOLVED @ 242b96878

PyDefUsedAsValue now skips a logical line that opens with import (tkUses) or the ident from. The scan is deliberately NOT narrowed to the current unit: a def defined in a module and passed as a value by the main program is the genuine case, and it lives outside PyScanLo.

The repro stopped crashing but still returned [] — a SECOND, independent defect in the same wall: module code got no return-ownership retain, so the caller received a reference the callee's scope exit had already dropped. Fixed in 33db0107d ([[bug-nilpy-object-reclamation-disabled-inside-py-modules]]). With both, seven cross-module callable shapes match CPython exactly.

Gate: tools/gate.sh quick GREEN at the time of the fix; the follow-up landed under tools/gate.sh full GREEN.

Log