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(
1097010970 .case_idx = index,
1097110971 } }),
1097210972 capture == .by_ref,
10973 is_special,
10974 if (!is_special) case_vals else undefined,
10973 if (is_special) .special else .{ .item_refs = &.{.fromValue(item_opv)} },
1097510974 if (is_inline) .fromValue(item_opv) else .none,
1097610975 validated_switch.else_err_ty,
1097710976 );
......@@ -12569,11 +12568,7 @@ fn resolveSwitchProng(
1256912568 operand_src,
1257012569 capture_src,
1257112570 capture == .by_ref,
12572 kind == .special,
12573 switch (kind) {
12574 .item_refs => |item_refs| item_refs,
12575 .has_ranges, .special => undefined,
12576 },
12571 kind,
1257712572 inline_case_capture,
1257812573 else_err_ty,
1257912574 );
......@@ -12702,11 +12697,7 @@ fn analyzeSwitchProng(
1270212697 operand_src,
1270312698 capture_src,
1270412699 capture == .by_ref,
12705 kind == .special,
12706 switch (kind) {
12707 .item_refs => |item_refs| item_refs,
12708 .has_ranges, .special => undefined,
12709 },
12700 kind,
1271012701 inline_case_capture,
1271112702 else_err_ty,
1271212703 );
......@@ -12783,9 +12774,7 @@ fn analyzeSwitchPayloadCapture(
1278312774 operand_src: LazySrcLoc,
1278412775 capture_src: LazySrcLoc,
1278512776 capture_by_ref: bool,
12786 is_special_prong: bool,
12787 /// May be `undefined` if `is_special_prong` is `true`.
12788 case_vals: []const Air.Inst.Ref,
12777 kind: SwitchProngKind,
1278912778 /// If this is not `.none`, this is an inline capture.
1279012779 inline_case_capture: Air.Inst.Ref,
1279112780 else_err_ty: ?Type,
......@@ -12829,7 +12818,7 @@ fn analyzeSwitchPayloadCapture(
1282912818
1283012819 const operand_ptr_ty = if (capture_by_ref) sema.typeOf(operand_ptr) else undefined;
1283112820
12832 if (is_special_prong) {
12821 if (kind == .special) {
1283312822 if (capture_by_ref) return operand_ptr;
1283412823 return switch (operand_ty.zigTypeTag(zcu)) {
1283512824 .error_set => e: {
......@@ -12846,6 +12835,8 @@ fn analyzeSwitchPayloadCapture(
1284612835
1284712836 switch (operand_ty.zigTypeTag(zcu)) {
1284812837 .@"union" => {
12838 const case_vals = kind.item_refs;
12839
1284912840 const union_obj = zcu.typeToUnion(operand_ty).?;
1285012841 const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
1285112842
......@@ -13141,6 +13132,7 @@ fn analyzeSwitchPayloadCapture(
1314113132 );
1314213133 }
1314313134
13135 const case_vals = kind.item_refs;
1314413136 if (case_vals.len == 1) {
1314513137 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
1314613138 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
......@@ -13161,11 +13153,16 @@ fn analyzeSwitchPayloadCapture(
1316113153 // switch condition. It is comptime-known if there is only one item.
1316213154 if (capture_by_ref) {
1316313155 return operand_ptr;
13164 } else if (case_vals.len == 1) {
13165 return case_vals[0];
13166 } else {
13167 return operand_val;
1316813156 }
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;
1316913166 },
1317013167 }
1317113168}
test/behavior/switch.zig+15
......@@ -1307,3 +1307,18 @@ test "single-item prong in switch on enum has comptime-known capture" {
13071307 try E.doTheTest(.a);
13081308 try comptime E.doTheTest(.a);
13091309}
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}