commit e6be5cfe3e280656339d81fdd0cc7451941eba09 (tree)
parent b23404af3d3141037f83257379743c9cdb0de473
Author: Justus Klausecker <justus@klausecker.de>
Date: Tue, 16 Jun 2026 18:23:12 +0200
Sema: allow overwriting previous analysis results when using switch inst to store temporary information
Essentially `inst_map.putAssumeCapacityNoClobber` is always wrong here
because we *want* to clobber previous results when analyzing the same
switch expression multiple times.
Diffstat:
2 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -9843,7 +9843,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
const maybe_switch_ref: ?Air.Inst.Ref = ref: {
// make err capture (i.e. switch operand) available to switch prong bodies
- sema.inst_map.putAssumeCapacityNoClobber(inst, raw_switch_operand);
+ sema.inst_map.putAssumeCapacity(inst, raw_switch_operand);
defer assert(sema.inst_map.remove(inst));
break :ref try sema.analyzeSwitchBlock(block, &switch_block, raw_switch_operand, false, merges, inst, &zir_switch, &validated_switch);
};
diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig
@@ -1542,3 +1542,40 @@ test "error captures narrow error sets" {
try comptime S.doTheTest(error.B);
try comptime S.doTheTest(error.C);
}
+
+test "repeated switch analysis overrides previous analysis results" {
+ // This tests an implementation detail where semantic analysis of switch
+ // statements uses the switch inst itself to store capture values and result
+ // type information while analyzing (parts of) that switch inst.
+ // If that inst has already been assigned a result by a previous analysis
+ // that result needs to be overwritten.
+
+ comptime {
+ const x: u32 = 123;
+ for (0..2) |_| _ = switch (x) {
+ 123 => |capture| capture,
+ else => unreachable,
+ };
+ }
+ comptime {
+ const x: union(enum) { a, b, c } = .a;
+ for (0..2) |_| _ = switch (x) {
+ .a => |_, tag| tag,
+ else => unreachable,
+ };
+ }
+ comptime {
+ const x: enum { a, b, c } = .a;
+ for (0..2) |_| _ = label: switch (x) {
+ .a => continue :label .b,
+ else => 123,
+ };
+ }
+ comptime {
+ const x: anyerror!void = error.MyError;
+ for (0..2) |_| _ = x catch |err| switch (err) {
+ error.MyError => {},
+ else => unreachable,
+ };
+ }
+}