diff --git a/examples/0043-basic-match-value-mixed-width.sx b/examples/0043-basic-match-value-mixed-width.sx new file mode 100644 index 0000000..fb46ce4 --- /dev/null +++ b/examples/0043-basic-match-value-mixed-width.sx @@ -0,0 +1,31 @@ +// A value-position match (`if subject == { case … }`) returning a small +// integer type works when arms mix positive and negated literals: every arm +// value is lowered against the merge's result type, so the phi operands all +// share one width. +// +// Regression (issue 0066): a negated-literal arm (`else: -1`) previously +// lowered at a narrower width than the positive arms, tripping LLVM's +// "PHI node operands are not the same type as the result". + +#import "modules/std.sx"; + +sign :: (n: s32) -> s32 { + if n == { + case 0: 0; + else: if n > 0 then 1 else -1; + } +} + +classify :: (n: s32) -> s32 { + if n == { + case 0: 100; + case 1: 10; + else: -1; + } +} + +main :: () -> s32 { + print("sign: {} {} {}\n", sign(-9), sign(0), sign(9)); // -1 0 1 + print("classify: {} {} {}\n", classify(0), classify(1), classify(5)); // 100 10 -1 + 0 +} diff --git a/examples/expected/0043-basic-match-value-mixed-width.exit b/examples/expected/0043-basic-match-value-mixed-width.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/examples/expected/0043-basic-match-value-mixed-width.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/0043-basic-match-value-mixed-width.stderr b/examples/expected/0043-basic-match-value-mixed-width.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/expected/0043-basic-match-value-mixed-width.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/0043-basic-match-value-mixed-width.stdout b/examples/expected/0043-basic-match-value-mixed-width.stdout new file mode 100644 index 0000000..24232c6 --- /dev/null +++ b/examples/expected/0043-basic-match-value-mixed-width.stdout @@ -0,0 +1,2 @@ +sign: -1 0 1 +classify: 100 10 -1 diff --git a/issues/0066-match-arm-negative-literal-phi-width.md b/issues/0066-match-arm-negative-literal-phi-width.md index c906641..f40b7a5 100644 --- a/issues/0066-match-arm-negative-literal-phi-width.md +++ b/issues/0066-match-arm-negative-literal-phi-width.md @@ -1,5 +1,11 @@ # 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 diff --git a/src/ir/lower.zig b/src/ir/lower.zig index 35ba192..f96a202 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -4497,7 +4497,13 @@ pub const Lowering = struct { } if (has_value_merge) { + // Lower the arm body against the merge's result type so literals + // (and negated literals) in the arm pick the right width — the + // phi operands must all match `result_type` (issue 0066). + const saved_arm_target = self.target_type; + self.target_type = result_type; const maybe_v = self.lowerBlockValue(arm.body); + self.target_type = saved_arm_target; self.current_match_tags = saved_match_tags; self.scope = old_scope; arm_scope.deinit();