A value-position match's arms are now lowered with `target_type` set to
the merge's `result_type`, so positive and negated integer literals pick
the same width. Fixes the `PHI node operands are not the same type as the
result` failure for `if n == { case 0: 100; else: -1; }`-style returns.
Regression: examples/0043-basic-match-value-mixed-width.sx.
Gates: zig build, zig build test, run_examples.sh -> 345 passed.
2.8 KiB
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 withtarget_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;coerceToTypestill 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
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.