Skip to content

Fix/ternary precedence codegen#445

Merged
cukas merged 4 commits into
mainfrom
fix/ternary-precedence-codegen
Jun 16, 2026
Merged

Fix/ternary precedence codegen#445
cukas merged 4 commits into
mainfrom
fix/ternary-precedence-codegen

Conversation

@cukas

@cukas cukas commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

What

Why

How

Checklist

  • tsc -b passes
  • pnpm test passes
  • pnpm test:kern passes
  • pnpm lint passes
  • kern review packages/ --recursive checked

…rand must be parenthesized)

Confirmed-systemic bug (agon codex audit + verified on both legs): KERN's expression
emitter drops the parens a `conditional` operand needs under a tighter operator, so
`(true ? 2 : 3) * 5` silently compiles to `true ? 2 : 3 * 5` (=2, not 10) on BOTH the TS
and Python legs — and breaks TS↔Python byte-parity for the Python call-callee + await
cases (TS wraps, Python doesn't).

This is the frozen spec for the fix. RED-at-base (4 failing fixtures): `(ternary)*x`,
`x*(ternary)` on both legs, `(ternary)(x)` + `await (ternary)` on Python. GREEN guards
pin already-correct output (TS call/await, member receiver, plain binary chains) so the
fix can't over-wrap or regress. `*` is used (raw infix on both legs) because Python lowers
`+` to a call-delimited `__kern_add(...)` that is already safe.

Root cause = ad-hoc parenthesization, not one precedence-aware operand policy; TS emitter
is codegen-expression.ts, Python is codegen-body-python.ts.

⚔️ Forged by [Agon](https://github.com/KERNlang/agon)

Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
… call / await, both legs)

Closes the systemic ternary-precedence miscompile for the three direct operand positions:
- TS (codegen-expression.ts): `needsParens` now returns true for a `conditional` child, so a
  ternary binary operand is parenthesized (`(true ? 2 : 3) * 5`, not `true ? 2 : 3 * 5`).
- Python (codegen-body-python.ts): a shared `needsLowPrecedenceOperandParens` predicate wraps a
  `conditional` child in binary operands (left+right), `await` operands, and call callees.

Makes the original RED oracle green (4 fixtures, both legs) with no ripple — full emission/codegen/
golden/ternary/portable-expr suites stay green. Does NOT over-wrap (plain binary chains unchanged).
Transparent-wrapper case (`(ternary as T)`) is handled in the follow-up commit.

⚔️ Forged by [Agon](https://github.com/KERNlang/agon)

Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…l (D1, RED)

agon impl-review (full roster) surfaced a real gap the first oracle missed: Python ERASES
transparent wrappers (`as T` / `!`), so `((true ? 2 : 3) as any) * 5` emits a BARE conditional
into a tight position → `2 if _kern_truthy(True) else 3 * 5` (=2), while TS keeps `(... as any)`
and is correct — a miscompile AND a TS↔Python byte-divergence. RED fixtures for the binary, call-
callee, and await positions; member-object guard (already wrapped) pins no-regress. The operand
predicate must peel transparent wrappers and test the inner kind.

⚔️ Forged by [Agon](https://github.com/KERNlang/agon)

Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…erand predicate

`needsLowPrecedenceOperandParens` now peels `typeAssert`/`nonNull` (`.expression`) before testing
for an inner `conditional`, so a wrapped ternary in a low-precedence operand position is
parenthesized on the Python leg too. Fixes `((true ? 2 : 3) as any) * 5` → `(2 if _kern_truthy(True)
else 3) * 5` (was `2 if ... else 3 * 5`), closing the miscompile AND the TS↔Python byte-divergence
(TS already kept `(... as any)`). One shared helper → all three positions (binary/await/call) covered.

Gate green: full ternary oracle (incl. the D1 block) + codegen/golden/ternary/emission suites; no
over-wrap, no ripple. TS emitter untouched (already correct).

⚔️ Forged by [Agon](https://github.com/KERNlang/agon)

Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
@cukas cukas merged commit 38a66bd into main Jun 16, 2026
4 checks passed
@cukas cukas deleted the fix/ternary-precedence-codegen branch June 16, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants