# 0066 — match-as-value with a negated-literal arm builds a mismatched phi

> **RESOLVED.** `lowerMatch`'s value path (`has_value_merge`) now lowers each
> arm body with `target_type = result_type`, so literals and negated literals in
> the arms pick the merge's width instead of leaking a narrower one. The phi
> operands are uniform; `coerceToType` still runs afterward as a backstop.
> Regression: `examples/0043-basic-match-value-mixed-width.sx`.

## Symptom

A value-position `match` (the `if subject == { case ... }` sugar) returning a
small integer type, where one arm is a **negated integer literal** (`-1`) and
others are plain positive literals, fails LLVM verification:

```
LLVM verification failed: PHI node operands are not the same type as the result!
  %bp = phi i64 [ 100, %match.arm.1 ], [ 10, %match.arm.2 ], [ -1, %match.arm.3 ]
```

The negated arm lowers its value at a different integer width (it emits an i32
that is then sign-extended) than the positive-literal arms (i64), so the merge
phi's operands disagree with its result type. Positive literals in every arm
work; `if/else` with `-1` works. So it is specific to a **negated literal in a
match arm value**.

This is orthogonal to the trailing-`;` block-value rule — the repro uses the
exempt `case …: expr;` arm form (unchanged by that migration) and reproduces
with ordinary semicolon-terminated arms. Filed while writing the block-value
regression example (0040); the example sidesteps it with positive arm values.

## Reproduction

```sx
classify :: (n: s32) -> s32 {
    if n == {
        case 0: 100;
        case 1: 10;
        else:  -1;        // ← negated literal arm → phi width mismatch
    }
}
main :: () -> s32 { classify(1) }
```

Expected: compiles, `classify(1)` returns 10. Actual: LLVM verification failure.

Workaround: give the arm an explicitly-typed value (`else: { x : s32 = -1; x }`)
or avoid the negation.

## Investigation prompt

Look at the match-as-value lowering in `src/ir/lower.zig` (`lowerMatch`, the
`has_value_merge` path ~line 4500, and the arm `result_type` inference
~line 11698). The arm value is lowered via `lowerBlockValue(arm.body)` then
`coerceToType(v, v_ty, result_type)`. For a negated-literal arm the value's
type comes out narrower than `result_type` (s64 here), and the coercion path
that should widen it before the `br merge_bb, {v}` either runs against the wrong
target or is skipped — so the phi gets an i32 operand under an i64 result. Make
the arm value coerce to the merge's `result_type` consistently (mirror how the
positive-literal arms are handled), or set `target_type = result_type` while
lowering each arm body so the negate picks the right width. Verify with the
repro above (expect exit 10) and add a regression example.

## Status

OPEN. Pre-existing match-value codegen quirk, surfaced (not caused) by the
block-value work.
