From e48779fe1f6127d50008277fb8662566769975da Mon Sep 17 00:00:00 2001 From: Justus Klausecker Date: Fri, 22 May 2026 17:32:38 +0200 Subject: [PATCH] Sema: improve invalid switch type compile errors Makes them more similar to other existing compile errors. --- src/Sema.zig | 58 ++++++---- .../compile_errors/switch_on_invalid_type.zig | 103 ++++++++++++++++++ .../switch_on_non_packed_struct.zig | 25 ----- 3 files changed, 139 insertions(+), 47 deletions(-) create mode 100644 test/cases/compile_errors/switch_on_invalid_type.zig delete mode 100644 test/cases/compile_errors/switch_on_non_packed_struct.zig diff --git a/src/Sema.zig b/src/Sema.zig index a52b11b506a71f7d4c4c734dbded00d5f5996a40..e1035ea5d58e77d94e08725975cda95982c10ab0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11186,14 +11186,8 @@ fn validateSwitchBlock( operand_ty.assertHasLayout(zcu); const union_obj = ip.loadUnionType(operand_ty.toIntern()); switch (union_obj.tag_usage) { - .tagged => { - break :item_ty .fromInterned(union_obj.enum_tag_type); - }, - .none => { - if (union_obj.layout == .@"packed") { - break :item_ty operand_ty; - } - }, + .tagged => break :item_ty .fromInterned(union_obj.enum_tag_type), + .none => if (union_obj.layout == .@"packed") break :item_ty operand_ty, .safety => {}, } return sema.failWithOwnedErrorMsg(block, msg: { @@ -11208,27 +11202,47 @@ fn validateSwitchBlock( .@"struct" => { operand_ty.assertHasLayout(zcu); - const layout = operand_ty.containerLayout(zcu); - if (layout == .@"packed") { - break :item_ty operand_ty; - } + if (operand_ty.containerLayout(zcu) == .@"packed") break :item_ty operand_ty; return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(operand_src, "switch on struct with {t} layout", .{layout}); + const msg = try sema.errMsg(operand_src, "switch on non-packed struct", .{}); errdefer msg.destroy(sema.gpa); - if (operand_ty.srcLocOrNull(zcu)) |struct_src| { - try sema.errNote(struct_src, msg, "consider 'packed struct' here", .{}); - } + try sema.addDeclaredHereNote(msg, operand_ty); break :msg msg; }); }, - .pointer => { - if (!operand_ty.isSlice(zcu)) { - break :item_ty operand_ty; - } - }, + .pointer => if (!operand_ty.isSlice(zcu)) break :item_ty operand_ty, - else => {}, + .optional => return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(operand_src, "switch on optional type '{f}'", .{ + operand_ty.fmt(pt), + }); + errdefer msg.destroy(gpa); + try sema.errNote(operand_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); + break :msg msg; + }), + + .error_union => return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(operand_src, "switch on error union type '{f}'", .{ + operand_ty.fmt(pt), + }); + errdefer msg.destroy(gpa); + try sema.errNote(operand_src, msg, "consider using 'try', 'catch', or 'if'", .{}); + break :msg msg; + }), + + .noreturn, + .float, + .comptime_float, + .array, + .vector, + .undefined, + .null, + .@"opaque", + .frame, + .@"anyframe", + .spirv, + => {}, } return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)}); }; diff --git a/test/cases/compile_errors/switch_on_invalid_type.zig b/test/cases/compile_errors/switch_on_invalid_type.zig new file mode 100644 index 0000000000000000000000000000000000000000..8d10d75a66c4b4f082bc83628cbc741090ff8428 --- /dev/null +++ b/test/cases/compile_errors/switch_on_invalid_type.zig @@ -0,0 +1,103 @@ +const AutoUnion = union { a: u8 }; +export fn entry1() void { + switch (@as(AutoUnion, .{ .a = 123 })) { + else => {}, + } +} + +const ExternUnion = union { a: u8 }; +export fn entry2() void { + switch (@as(ExternUnion, .{ .a = 123 })) { + else => {}, + } +} + +const AutoStruct = struct { a: u8 }; +export fn entry3() void { + switch (@as(AutoStruct, .{ .a = 123 })) { + else => {}, + } +} + +const ExternStruct = extern struct { a: u8 }; +export fn entry4() void { + switch (@as(ExternStruct, .{ .a = 123 })) { + else => {}, + } +} + +export fn entry5() void { + switch (@as([]const u16, &.{ 1, 2, 3 })) { + else => {}, + } +} + +export fn entry6() void { + switch (@as([3]u16, .{ 1, 2, 3 })) { + else => {}, + } +} + +export fn entry7() void { + switch (@as(@Vector(3, u16), .{ 1, 2, 3 })) { + else => {}, + } +} + +export fn entry8() void { + switch (@as(?u16, 123)) { + else => {}, + } +} + +export fn entry9() void { + switch (@as(anyerror!u16, 123)) { + else => {}, + } +} + +export fn entry10() void { + switch (@as(f32, 123)) { + else => {}, + } +} + +export fn entry11() void { + switch (@as(comptime_float, 123)) { + else => {}, + } +} + +export fn entry12() void { + switch (undefined) { + else => {}, + } +} + +export fn entry13() void { + switch (null) { + else => {}, + } +} + +// error +// +// :3:13: error: switch on union with no attached enum +// :1:19: note: consider 'union(enum)' here +// :10:13: error: switch on union with no attached enum +// :8:21: note: consider 'union(enum)' here +// :17:13: error: switch on non-packed struct +// :15:20: note: struct declared here +// :24:13: error: switch on non-packed struct +// :22:29: note: struct declared here +// :30:13: error: switch on type '[]const u16' +// :36:13: error: switch on type '[3]u16' +// :42:13: error: switch on type '@Vector(3, u16)' +// :48:13: error: switch on optional type '?u16' +// :48:13: note: consider using '.?', 'orelse', or 'if' +// :54:13: error: switch on error union type 'anyerror!u16' +// :54:13: note: consider using 'try', 'catch', or 'if' +// :60:13: error: switch on type 'f32' +// :66:13: error: switch on type 'comptime_float' +// :72:13: error: switch on type '@TypeOf(undefined)' +// :78:13: error: switch on type '@TypeOf(null)' diff --git a/test/cases/compile_errors/switch_on_non_packed_struct.zig b/test/cases/compile_errors/switch_on_non_packed_struct.zig deleted file mode 100644 index ef17bb3ca533361c38f1cb9caae5b391c1f4ab54..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/switch_on_non_packed_struct.zig +++ /dev/null @@ -1,25 +0,0 @@ -const Auto = struct { - a: u8, -}; -export fn entry1(a: u8) void { - const s: Auto = .{ .a = a }; - switch (s) { - else => {}, - } -} - -const Extern = extern struct { - a: u8, -}; -export fn entry2(s: Extern) void { - switch (s) { - else => {}, - } -} - -// error -// -// :6:13: error: switch on struct with auto layout -// :1:14: note: consider 'packed struct' here -// :15:13: error: switch on struct with extern layout -// :11:23: note: consider 'packed struct' here -- 2.54.0