← board

A class field and a recursive return narrow an arbitrary-precision int

class C:
    def __init__(self):
        self.v = 1
    def grow(self):
        for k in range(70):
            self.v = self.v * 2
c = C()
c.grow()
print(c.v)            # CPython: 1180591620717411303424     pxx: 0

def fa(n):
    if n <= 1:
        return 1
    return n * fa(n - 1)
print(fa(25))         # CPython: 15511210043330985984000000  pxx: 7034535277573963776

The last two open sites from [[task-n-enumerate-the-promo-surface-by-output-diff]], split out so the promotable-int default can land without them: everything the sweep found in locals, module scope, operators, builtins, containers, formatting and call/return boundaries is fixed, and both of these are the SAME residual shape in two different binding kinds.

The shape

A binding's type is inferred from the first int-shaped thing assigned to it and then never widened when a promotable value reaches it later:

Why they were not fixed with the rest

Both need a widening pass rather than a typing arm: the binding is created before the promotable assignment is seen, so the fix is to re-visit it, not to type it better at first sight. For the field that means the class pre-pass gaining a fixpoint like the module pre-pass already has; for the recursion it means the return chase treating a self-call as "unknown, retry" instead of falling to the int default.

Do NOT fix the return case by trusting PyLocals in the body pass — that is the ABI mismatch the existing comment at the chase warns about, and it was already paid for once.

Method

Diff stdout against CPython, never exit status — the whole reason the earlier survey missed four sites. tools/pydiff.py or the sweep harness in [[task-n-enumerate-the-promo-surface-by-output-diff]].

Gate

Per-fix loop. A field/recursion .npy test diffed against CPython; check ls test/ | grep -E 'promo|bigint' for an existing file to extend.

Log