authorgravatar for jhc@dismail.deJimmi Holst Christensen <jhc@dismail.de> 2022-01-06 22:18:27+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-06 22:57:45-05:00
log7f4fdcc4fce79cc1277c2a89a6a4136293c1b2e5
tree8b185810946a998f3a1bdf78e13265cb07f7a8dc
parent59315463f2d579d788f9af43e4e74577dfd4e21f

stage2: Implement validating switch on errors


3 files changed, 140 insertions(+), 22 deletions(-)

src/Sema.zig+125-21
...@@ -4826,26 +4826,8 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -4826,26 +4826,8 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
4826 return Air.Inst.Ref.anyerror_type;4826 return Air.Inst.Ref.anyerror_type;
4827 }4827 }
4828 // Resolve both error sets now.4828 // Resolve both error sets now.
4829 const lhs_names = switch (lhs_ty.tag()) {4829 const lhs_names = lhs_ty.errorSetNames();
4830 .error_set_single => blk: {4830 const rhs_names = rhs_ty.errorSetNames();
4831 // Work around coercion problems
4832 const tmp: *const [1][]const u8 = &lhs_ty.castTag(.error_set_single).?.data;
4833 break :blk tmp;
4834 },
4835 .error_set_merged => lhs_ty.castTag(.error_set_merged).?.data.keys(),
4836 .error_set => lhs_ty.castTag(.error_set).?.data.names.keys(),
4837 else => unreachable,
4838 };
4839
4840 const rhs_names = switch (rhs_ty.tag()) {
4841 .error_set_single => blk: {
4842 const tmp: *const [1][]const u8 = &rhs_ty.castTag(.error_set_single).?.data;
4843 break :blk tmp;
4844 },
4845 .error_set_merged => rhs_ty.castTag(.error_set_merged).?.data.keys(),
4846 .error_set => rhs_ty.castTag(.error_set).?.data.names.keys(),
4847 else => unreachable,
4848 };
48494831
4850 // TODO do we really want to create a Decl for this?4832 // TODO do we really want to create a Decl for this?
4851 // The reason we do it right now is for memory management.4833 // The reason we do it right now is for memory management.
...@@ -6080,6 +6062,8 @@ fn zirSwitchCond(...@@ -6080,6 +6062,8 @@ fn zirSwitchCond(
6080 }6062 }
6081}6063}
60826064
6065const SwitchErrorSet = std.StringHashMap(Module.SwitchProngSrc);
6066
6083fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6067fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6084 const tracy = trace(@src());6068 const tracy = trace(@src());
6085 defer tracy.end();6069 defer tracy.end();
...@@ -6250,8 +6234,110 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -6250,8 +6234,110 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
6250 },6234 },
6251 }6235 }
6252 },6236 },
6237 .ErrorSet => {
6238 var seen_errors = SwitchErrorSet.init(gpa);
6239 defer seen_errors.deinit();
6240
6241 var extra_index: usize = special.end;
6242 {
6243 var scalar_i: u32 = 0;
6244 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
6245 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
6246 extra_index += 1;
6247 const body_len = sema.code.extra[extra_index];
6248 extra_index += 1;
6249 extra_index += body_len;
6250
6251 try sema.validateSwitchItemError(
6252 block,
6253 &seen_errors,
6254 item_ref,
6255 src_node_offset,
6256 .{ .scalar = scalar_i },
6257 );
6258 }
6259 }
6260 {
6261 var multi_i: u32 = 0;
6262 while (multi_i < multi_cases_len) : (multi_i += 1) {
6263 const items_len = sema.code.extra[extra_index];
6264 extra_index += 1;
6265 const ranges_len = sema.code.extra[extra_index];
6266 extra_index += 1;
6267 const body_len = sema.code.extra[extra_index];
6268 extra_index += 1;
6269 const items = sema.code.refSlice(extra_index, items_len);
6270 extra_index += items_len + body_len;
6271
6272 for (items) |item_ref, item_i| {
6273 try sema.validateSwitchItemError(
6274 block,
6275 &seen_errors,
6276 item_ref,
6277 src_node_offset,
6278 .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
6279 );
6280 }
6281
6282 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
6283 }
6284 }
6285
6286 if (operand_ty.isAnyError()) {
6287 if (special_prong != .@"else") {
6288 return sema.fail(
6289 block,
6290 src,
6291 "switch must handle all possibilities",
6292 .{},
6293 );
6294 }
6295 } else {
6296 var maybe_msg: ?*Module.ErrorMsg = null;
6297 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
6298
6299 for (operand_ty.errorSetNames()) |error_name| {
6300 if (!seen_errors.contains(error_name) and special_prong != .@"else") {
6301 const msg = maybe_msg orelse blk: {
6302 maybe_msg = try sema.errMsg(
6303 block,
6304 src,
6305 "switch must handle all possibilities",
6306 .{},
6307 );
6308 break :blk maybe_msg.?;
6309 };
6310
6311 try sema.errNote(
6312 block,
6313 src,
6314 msg,
6315 "unhandled error value: error.{s}",
6316 .{error_name},
6317 );
6318 }
6319 }
62536320
6254 .ErrorSet => return sema.fail(block, src, "TODO validate switch .ErrorSet", .{}),6321 if (maybe_msg) |msg| {
6322 try sema.mod.errNoteNonLazy(
6323 operand_ty.declSrcLoc(),
6324 msg,
6325 "error set '{}' declared here",
6326 .{operand_ty},
6327 );
6328 return sema.failWithOwnedErrorMsg(msg);
6329 }
6330
6331 if (special_prong == .@"else") {
6332 return sema.fail(
6333 block,
6334 special_prong_src,
6335 "unreachable else prong; all cases already handled",
6336 .{},
6337 );
6338 }
6339 }
6340 },
6255 .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}),6341 .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}),
6256 .Int, .ComptimeInt => {6342 .Int, .ComptimeInt => {
6257 var range_set = RangeSet.init(gpa);6343 var range_set = RangeSet.init(gpa);
...@@ -6924,6 +7010,24 @@ fn validateSwitchItemEnum(...@@ -6924,6 +7010,24 @@ fn validateSwitchItemEnum(
6924 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);7010 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
6925}7011}
69267012
7013fn validateSwitchItemError(
7014 sema: *Sema,
7015 block: *Block,
7016 seen_errors: *SwitchErrorSet,
7017 item_ref: Zir.Inst.Ref,
7018 src_node_offset: i32,
7019 switch_prong_src: Module.SwitchProngSrc,
7020) CompileError!void {
7021 const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
7022 // TODO: Do i need to typecheck here?
7023 const error_name = item_tv.val.castTag(.@"error").?.data.name;
7024 const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev|
7025 prev.value
7026 else
7027 null;
7028 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
7029}
7030
6927fn validateSwitchDupe(7031fn validateSwitchDupe(
6928 sema: *Sema,7032 sema: *Sema,
6929 block: *Block,7033 block: *Block,
src/type.zig+14
...@@ -3617,6 +3617,20 @@ pub const Type = extern union {...@@ -3617,6 +3617,20 @@ pub const Type = extern union {
3617 };3617 };
3618 }3618 }
36193619
3620 // Asserts that `ty` is an error set and not `anyerror`.
3621 pub fn errorSetNames(ty: Type) []const []const u8 {
3622 return switch (ty.tag()) {
3623 .error_set_single => blk: {
3624 // Work around coercion problems
3625 const tmp: *const [1][]const u8 = &ty.castTag(.error_set_single).?.data;
3626 break :blk tmp;
3627 },
3628 .error_set_merged => ty.castTag(.error_set_merged).?.data.keys(),
3629 .error_set => ty.castTag(.error_set).?.data.names.keys(),
3630 else => unreachable,
3631 };
3632 }
3633
3620 pub fn enumFields(ty: Type) Module.EnumFull.NameMap {3634 pub fn enumFields(ty: Type) Module.EnumFull.NameMap {
3621 return switch (ty.tag()) {3635 return switch (ty.tag()) {
3622 .enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.fields,3636 .enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.fields,
test/behavior.zig+1-1
...@@ -63,6 +63,7 @@ test {...@@ -63,6 +63,7 @@ test {
63 _ = @import("behavior/int128.zig");63 _ = @import("behavior/int128.zig");
64 _ = @import("behavior/optional.zig");64 _ = @import("behavior/optional.zig");
65 _ = @import("behavior/translate_c_macros.zig");65 _ = @import("behavior/translate_c_macros.zig");
66 _ = @import("behavior/try.zig");
66 _ = @import("behavior/undefined.zig");67 _ = @import("behavior/undefined.zig");
6768
68 if (builtin.zig_backend != .stage2_c) {69 if (builtin.zig_backend != .stage2_c) {
...@@ -190,7 +191,6 @@ test {...@@ -190,7 +191,6 @@ test {
190 _ = @import("behavior/switch_prong_implicit_cast.zig");191 _ = @import("behavior/switch_prong_implicit_cast.zig");
191 _ = @import("behavior/switch_stage1.zig");192 _ = @import("behavior/switch_stage1.zig");
192 _ = @import("behavior/truncate_stage1.zig");193 _ = @import("behavior/truncate_stage1.zig");
193 _ = @import("behavior/try.zig");
194 _ = @import("behavior/tuple.zig");194 _ = @import("behavior/tuple.zig");
195 _ = @import("behavior/type_stage1.zig");195 _ = @import("behavior/type_stage1.zig");
196 _ = @import("behavior/type_info_stage1.zig");196 _ = @import("behavior/type_info_stage1.zig");