aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorJimmi Holst Christensen <jhc@dismail.de>2022-01-06 22:18:27 +0100
committerAndrew Kelley <andrew@ziglang.org>2022-01-06 22:57:45 -0500
commit7f4fdcc4fce79cc1277c2a89a6a4136293c1b2e5 (patch)
tree8b185810946a998f3a1bdf78e13265cb07f7a8dc /src/Sema.zig
parent59315463f2d579d788f9af43e4e74577dfd4e21f (diff)
downloadzig-7f4fdcc4fce79cc1277c2a89a6a4136293c1b2e5.tar.gz
zig-7f4fdcc4fce79cc1277c2a89a6a4136293c1b2e5.zip
stage2: Implement validating switch on errors
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig146
1 files changed, 125 insertions, 21 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 80231ac3cb..de38d7f013 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -4826,26 +4826,8 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
return Air.Inst.Ref.anyerror_type;
}
// Resolve both error sets now.
- const lhs_names = switch (lhs_ty.tag()) {
- .error_set_single => blk: {
- // Work around coercion problems
- const tmp: *const [1][]const u8 = &lhs_ty.castTag(.error_set_single).?.data;
- break :blk tmp;
- },
- .error_set_merged => lhs_ty.castTag(.error_set_merged).?.data.keys(),
- .error_set => lhs_ty.castTag(.error_set).?.data.names.keys(),
- else => unreachable,
- };
-
- const rhs_names = switch (rhs_ty.tag()) {
- .error_set_single => blk: {
- const tmp: *const [1][]const u8 = &rhs_ty.castTag(.error_set_single).?.data;
- break :blk tmp;
- },
- .error_set_merged => rhs_ty.castTag(.error_set_merged).?.data.keys(),
- .error_set => rhs_ty.castTag(.error_set).?.data.names.keys(),
- else => unreachable,
- };
+ const lhs_names = lhs_ty.errorSetNames();
+ const rhs_names = rhs_ty.errorSetNames();
// TODO do we really want to create a Decl for this?
// The reason we do it right now is for memory management.
@@ -6080,6 +6062,8 @@ fn zirSwitchCond(
}
}
+const SwitchErrorSet = std.StringHashMap(Module.SwitchProngSrc);
+
fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -6250,8 +6234,110 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
},
}
},
+ .ErrorSet => {
+ var seen_errors = SwitchErrorSet.init(gpa);
+ defer seen_errors.deinit();
+
+ var extra_index: usize = special.end;
+ {
+ var scalar_i: u32 = 0;
+ while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
+ const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ const body_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ extra_index += body_len;
+
+ try sema.validateSwitchItemError(
+ block,
+ &seen_errors,
+ item_ref,
+ src_node_offset,
+ .{ .scalar = scalar_i },
+ );
+ }
+ }
+ {
+ var multi_i: u32 = 0;
+ while (multi_i < multi_cases_len) : (multi_i += 1) {
+ const items_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const ranges_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const body_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const items = sema.code.refSlice(extra_index, items_len);
+ extra_index += items_len + body_len;
+
+ for (items) |item_ref, item_i| {
+ try sema.validateSwitchItemError(
+ block,
+ &seen_errors,
+ item_ref,
+ src_node_offset,
+ .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
+ );
+ }
+
+ try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
+ }
+ }
+
+ if (operand_ty.isAnyError()) {
+ if (special_prong != .@"else") {
+ return sema.fail(
+ block,
+ src,
+ "switch must handle all possibilities",
+ .{},
+ );
+ }
+ } else {
+ var maybe_msg: ?*Module.ErrorMsg = null;
+ errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
+
+ for (operand_ty.errorSetNames()) |error_name| {
+ if (!seen_errors.contains(error_name) and special_prong != .@"else") {
+ const msg = maybe_msg orelse blk: {
+ maybe_msg = try sema.errMsg(
+ block,
+ src,
+ "switch must handle all possibilities",
+ .{},
+ );
+ break :blk maybe_msg.?;
+ };
+
+ try sema.errNote(
+ block,
+ src,
+ msg,
+ "unhandled error value: error.{s}",
+ .{error_name},
+ );
+ }
+ }
- .ErrorSet => return sema.fail(block, src, "TODO validate switch .ErrorSet", .{}),
+ if (maybe_msg) |msg| {
+ try sema.mod.errNoteNonLazy(
+ operand_ty.declSrcLoc(),
+ msg,
+ "error set '{}' declared here",
+ .{operand_ty},
+ );
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+
+ if (special_prong == .@"else") {
+ return sema.fail(
+ block,
+ special_prong_src,
+ "unreachable else prong; all cases already handled",
+ .{},
+ );
+ }
+ }
+ },
.Union => return sema.fail(block, src, "TODO validate switch .Union", .{}),
.Int, .ComptimeInt => {
var range_set = RangeSet.init(gpa);
@@ -6924,6 +7010,24 @@ fn validateSwitchItemEnum(
return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
}
+fn validateSwitchItemError(
+ sema: *Sema,
+ block: *Block,
+ seen_errors: *SwitchErrorSet,
+ item_ref: Zir.Inst.Ref,
+ src_node_offset: i32,
+ switch_prong_src: Module.SwitchProngSrc,
+) CompileError!void {
+ const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+ // TODO: Do i need to typecheck here?
+ const error_name = item_tv.val.castTag(.@"error").?.data.name;
+ const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev|
+ prev.value
+ else
+ null;
+ return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+}
+
fn validateSwitchDupe(
sema: *Sema,
block: *Block,