← board

Meta: constant normalisation — one shape, so future fixes are single work

The point

Not speed. Not memory — a constant expression may have its own variable living for the whole program; that cost is accepted and settled.

The point is that a large amount of existing compiler code branches on constant-or-variable, and every one of those branches is two paths that a future fix has to be applied to twice — which is exactly the failure mode this repo keeps hitting, where a bug is fixed on the arm it was observed on and the sibling stays broken for months (devdocs/dev/normalise-dont-special-case.md has the tally: five in one day).

So: give each constant expression its own uniquely-named variable, bound once. Downstream then only ever sees a variable. One shape, one path, one fix.

The rule for new code

When you are about to write "if this operand is a literal, do X, else do Y" — don't. Bind the literal to a variable and write only Y.

That is the whole contract. Everything below is the backlog of places where it was already written the other way.

One caveat, stated once and not turned into a design exercise: the variable is read-only by construction — a constant expression's temp is bound and then only read. If a site would ever hand that temp to something that MUTATES it, it needs its own fresh build instead; that is a per-site judgement, not a whole analysis pass, and today's candidates below are all read-only positions.

Known double-case sites

These are the cohesion — the reason this is one ticket and not four unrelated ones. File a child ticket when you take one, and link it back here.

What "done" looks like

It does not — standing index. The measurable goal: no site branches on "literal or variable" to decide semantics, and the constant predicate lives in one place.

Gate

Per item, per the owning lane's normal gate. Nothing here should change observable behaviour, so a CPython differential (tools/pydiff.py) over the affected shapes is the check that it did not.