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!
This commit is contained in:
Justus Klausecker 2026-01-11 20:02:27 +01:00
parent 76dd39d531
commit e2338edb47
2 changed files with 32 additions and 20 deletions

View file

@ -10970,8 +10970,7 @@ fn analyzeSwitchBlock(
.case_idx = index, .case_idx = index,
} }), } }),
capture == .by_ref, capture == .by_ref,
is_special, if (is_special) .special else .{ .item_refs = &.{.fromValue(item_opv)} },
if (!is_special) case_vals else undefined,
if (is_inline) .fromValue(item_opv) else .none, if (is_inline) .fromValue(item_opv) else .none,
validated_switch.else_err_ty, validated_switch.else_err_ty,
); );
@ -12569,11 +12568,7 @@ fn resolveSwitchProng(
operand_src, operand_src,
capture_src, capture_src,
capture == .by_ref, capture == .by_ref,
kind == .special, kind,
switch (kind) {
.item_refs => |item_refs| item_refs,
.has_ranges, .special => undefined,
},
inline_case_capture, inline_case_capture,
else_err_ty, else_err_ty,
); );
@ -12702,11 +12697,7 @@ fn analyzeSwitchProng(
operand_src, operand_src,
capture_src, capture_src,
capture == .by_ref, capture == .by_ref,
kind == .special, kind,
switch (kind) {
.item_refs => |item_refs| item_refs,
.has_ranges, .special => undefined,
},
inline_case_capture, inline_case_capture,
else_err_ty, else_err_ty,
); );
@ -12783,9 +12774,7 @@ fn analyzeSwitchPayloadCapture(
operand_src: LazySrcLoc, operand_src: LazySrcLoc,
capture_src: LazySrcLoc, capture_src: LazySrcLoc,
capture_by_ref: bool, capture_by_ref: bool,
is_special_prong: bool, kind: SwitchProngKind,
/// May be `undefined` if `is_special_prong` is `true`.
case_vals: []const Air.Inst.Ref,
/// If this is not `.none`, this is an inline capture. /// If this is not `.none`, this is an inline capture.
inline_case_capture: Air.Inst.Ref, inline_case_capture: Air.Inst.Ref,
else_err_ty: ?Type, else_err_ty: ?Type,
@ -12829,7 +12818,7 @@ fn analyzeSwitchPayloadCapture(
const operand_ptr_ty = if (capture_by_ref) sema.typeOf(operand_ptr) else undefined; const operand_ptr_ty = if (capture_by_ref) sema.typeOf(operand_ptr) else undefined;
if (is_special_prong) { if (kind == .special) {
if (capture_by_ref) return operand_ptr; if (capture_by_ref) return operand_ptr;
return switch (operand_ty.zigTypeTag(zcu)) { return switch (operand_ty.zigTypeTag(zcu)) {
.error_set => e: { .error_set => e: {
@ -12846,6 +12835,8 @@ fn analyzeSwitchPayloadCapture(
switch (operand_ty.zigTypeTag(zcu)) { switch (operand_ty.zigTypeTag(zcu)) {
.@"union" => { .@"union" => {
const case_vals = kind.item_refs;
const union_obj = zcu.typeToUnion(operand_ty).?; const union_obj = zcu.typeToUnion(operand_ty).?;
const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable; const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
@ -13141,6 +13132,7 @@ fn analyzeSwitchPayloadCapture(
); );
} }
const case_vals = kind.item_refs;
if (case_vals.len == 1) { if (case_vals.len == 1) {
const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable; const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
@ -13161,11 +13153,16 @@ fn analyzeSwitchPayloadCapture(
// switch condition. It is comptime-known if there is only one item. // switch condition. It is comptime-known if there is only one item.
if (capture_by_ref) { if (capture_by_ref) {
return operand_ptr; return operand_ptr;
} else if (case_vals.len == 1) {
return case_vals[0];
} else {
return operand_val;
} }
switch (kind) {
.special => unreachable,
.item_refs => |case_vals| {
// If there's only a single item, the capture is comptime-known!
if (case_vals.len == 1) return case_vals[0];
},
.has_ranges => {},
}
return operand_val;
}, },
} }
} }

View file

@ -1307,3 +1307,18 @@ test "single-item prong in switch on enum has comptime-known capture" {
try E.doTheTest(.a); try E.doTheTest(.a);
try comptime E.doTheTest(.a); try comptime E.doTheTest(.a);
} }
test "single-range switch prong capture" {
const S = struct {
fn doTheTest(x: u8) !void {
switch (x) {
1...5 => |val| {
try expect(val == 2);
},
else => return error.TestFailed,
}
}
};
try S.doTheTest(2);
try comptime S.doTheTest(2);
}