authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-01-11 20:02:27+01:00
committergravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-01-11 20:22:32+01:00
loge2338edb47a23e002a7e46ea8108dc7d6f621d86
treefa3cb1a2580827d010dec5ab6927f7386ba35b53
parent76dd39d5316d302de6aa9bcab45ccb3f5a123bcb

Sema: fix single-range switch prong capture

This kind of capture cannot always be comptime-known, assuming so caused access of undefined memory during payload capture analysis!

2 files changed, 32 insertions(+), 20 deletions(-)

src/Sema.zig+17-20
...@@ -10970,8 +10970,7 @@ fn analyzeSwitchBlock(...@@ -10970,8 +10970,7 @@ fn analyzeSwitchBlock(
10970 .case_idx = index,10970 .case_idx = index,
10971 } }),10971 } }),
10972 capture == .by_ref,10972 capture == .by_ref,
10973 is_special,10973 if (is_special) .special else .{ .item_refs = &.{.fromValue(item_opv)} },
10974 if (!is_special) case_vals else undefined,
10975 if (is_inline) .fromValue(item_opv) else .none,10974 if (is_inline) .fromValue(item_opv) else .none,
10976 validated_switch.else_err_ty,10975 validated_switch.else_err_ty,
10977 );10976 );
...@@ -12569,11 +12568,7 @@ fn resolveSwitchProng(...@@ -12569,11 +12568,7 @@ fn resolveSwitchProng(
12569 operand_src,12568 operand_src,
12570 capture_src,12569 capture_src,
12571 capture == .by_ref,12570 capture == .by_ref,
12572 kind == .special,12571 kind,
12573 switch (kind) {
12574 .item_refs => |item_refs| item_refs,
12575 .has_ranges, .special => undefined,
12576 },
12577 inline_case_capture,12572 inline_case_capture,
12578 else_err_ty,12573 else_err_ty,
12579 );12574 );
...@@ -12702,11 +12697,7 @@ fn analyzeSwitchProng(...@@ -12702,11 +12697,7 @@ fn analyzeSwitchProng(
12702 operand_src,12697 operand_src,
12703 capture_src,12698 capture_src,
12704 capture == .by_ref,12699 capture == .by_ref,
12705 kind == .special,12700 kind,
12706 switch (kind) {
12707 .item_refs => |item_refs| item_refs,
12708 .has_ranges, .special => undefined,
12709 },
12710 inline_case_capture,12701 inline_case_capture,
12711 else_err_ty,12702 else_err_ty,
12712 );12703 );
...@@ -12783,9 +12774,7 @@ fn analyzeSwitchPayloadCapture(...@@ -12783,9 +12774,7 @@ fn analyzeSwitchPayloadCapture(
12783 operand_src: LazySrcLoc,12774 operand_src: LazySrcLoc,
12784 capture_src: LazySrcLoc,12775 capture_src: LazySrcLoc,
12785 capture_by_ref: bool,12776 capture_by_ref: bool,
12786 is_special_prong: bool,12777 kind: SwitchProngKind,
12787 /// May be `undefined` if `is_special_prong` is `true`.
12788 case_vals: []const Air.Inst.Ref,
12789 /// If this is not `.none`, this is an inline capture.12778 /// If this is not `.none`, this is an inline capture.
12790 inline_case_capture: Air.Inst.Ref,12779 inline_case_capture: Air.Inst.Ref,
12791 else_err_ty: ?Type,12780 else_err_ty: ?Type,
...@@ -12829,7 +12818,7 @@ fn analyzeSwitchPayloadCapture(...@@ -12829,7 +12818,7 @@ fn analyzeSwitchPayloadCapture(
1282912818
12830 const operand_ptr_ty = if (capture_by_ref) sema.typeOf(operand_ptr) else undefined;12819 const operand_ptr_ty = if (capture_by_ref) sema.typeOf(operand_ptr) else undefined;
1283112820
12832 if (is_special_prong) {12821 if (kind == .special) {
12833 if (capture_by_ref) return operand_ptr;12822 if (capture_by_ref) return operand_ptr;
12834 return switch (operand_ty.zigTypeTag(zcu)) {12823 return switch (operand_ty.zigTypeTag(zcu)) {
12835 .error_set => e: {12824 .error_set => e: {
...@@ -12846,6 +12835,8 @@ fn analyzeSwitchPayloadCapture(...@@ -12846,6 +12835,8 @@ fn analyzeSwitchPayloadCapture(
1284612835
12847 switch (operand_ty.zigTypeTag(zcu)) {12836 switch (operand_ty.zigTypeTag(zcu)) {
12848 .@"union" => {12837 .@"union" => {
12838 const case_vals = kind.item_refs;
12839
12849 const union_obj = zcu.typeToUnion(operand_ty).?;12840 const union_obj = zcu.typeToUnion(operand_ty).?;
12850 const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;12841 const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
1285112842
...@@ -13141,6 +13132,7 @@ fn analyzeSwitchPayloadCapture(...@@ -13141,6 +13132,7 @@ fn analyzeSwitchPayloadCapture(
13141 );13132 );
13142 }13133 }
1314313134
13135 const case_vals = kind.item_refs;
13144 if (case_vals.len == 1) {13136 if (case_vals.len == 1) {
13145 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;13137 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
13146 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);13138 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
...@@ -13161,11 +13153,16 @@ fn analyzeSwitchPayloadCapture(...@@ -13161,11 +13153,16 @@ fn analyzeSwitchPayloadCapture(
13161 // switch condition. It is comptime-known if there is only one item.13153 // switch condition. It is comptime-known if there is only one item.
13162 if (capture_by_ref) {13154 if (capture_by_ref) {
13163 return operand_ptr;13155 return operand_ptr;
13164 } else if (case_vals.len == 1) {
13165 return case_vals[0];
13166 } else {
13167 return operand_val;
13168 }13156 }
13157 switch (kind) {
13158 .special => unreachable,
13159 .item_refs => |case_vals| {
13160 // If there's only a single item, the capture is comptime-known!
13161 if (case_vals.len == 1) return case_vals[0];
13162 },
13163 .has_ranges => {},
13164 }
13165 return operand_val;
13169 },13166 },
13170 }13167 }
13171}13168}
test/behavior/switch.zig+15
...@@ -1307,3 +1307,18 @@ test "single-item prong in switch on enum has comptime-known capture" {...@@ -1307,3 +1307,18 @@ test "single-item prong in switch on enum has comptime-known capture" {
1307 try E.doTheTest(.a);1307 try E.doTheTest(.a);
1308 try comptime E.doTheTest(.a);1308 try comptime E.doTheTest(.a);
1309}1309}
1310
1311test "single-range switch prong capture" {
1312 const S = struct {
1313 fn doTheTest(x: u8) !void {
1314 switch (x) {
1315 1...5 => |val| {
1316 try expect(val == 2);
1317 },
1318 else => return error.TestFailed,
1319 }
1320 }
1321 };
1322 try S.doTheTest(2);
1323 try comptime S.doTheTest(2);
1324}