Skip to content

Commit 7f4fdcc

Browse files
Hejsilandrewrk
authored andcommitted
stage2: Implement validating switch on errors
1 parent 5931546 commit 7f4fdcc

File tree

3 files changed

+140
-22
lines changed

3 files changed

+140
-22
lines changed

src/Sema.zig

Lines changed: 125 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4826,26 +4826,8 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
48264826
return Air.Inst.Ref.anyerror_type;
48274827
}
48284828
// Resolve both error sets now.
4829-
const lhs_names = switch (lhs_ty.tag()) {
4830-
.error_set_single => blk: {
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-
};
4829+
const lhs_names = lhs_ty.errorSetNames();
4830+
const rhs_names = rhs_ty.errorSetNames();
48494831

48504832
// TODO do we really want to create a Decl for this?
48514833
// The reason we do it right now is for memory management.
@@ -6080,6 +6062,8 @@ fn zirSwitchCond(
60806062
}
60816063
}
60826064

6065+
const SwitchErrorSet = std.StringHashMap(Module.SwitchProngSrc);
6066+
60836067
fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60846068
const tracy = trace(@src());
60856069
defer tracy.end();
@@ -6250,8 +6234,110 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
62506234
},
62516235
}
62526236
},
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+
},
62556341
.Union => return sema.fail(block, src, "TODO validate switch .Union", .{}),
62566342
.Int, .ComptimeInt => {
62576343
var range_set = RangeSet.init(gpa);
@@ -6924,6 +7010,24 @@ fn validateSwitchItemEnum(
69247010
return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
69257011
}
69267012

7013+
fn 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+
69277031
fn validateSwitchDupe(
69287032
sema: *Sema,
69297033
block: *Block,

src/type.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3617,6 +3617,20 @@ pub const Type = extern union {
36173617
};
36183618
}
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+
36203634
pub fn enumFields(ty: Type) Module.EnumFull.NameMap {
36213635
return switch (ty.tag()) {
36223636
.enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.fields,

test/behavior.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ test {
6363
_ = @import("behavior/int128.zig");
6464
_ = @import("behavior/optional.zig");
6565
_ = @import("behavior/translate_c_macros.zig");
66+
_ = @import("behavior/try.zig");
6667
_ = @import("behavior/undefined.zig");
6768

6869
if (builtin.zig_backend != .stage2_c) {
@@ -190,7 +191,6 @@ test {
190191
_ = @import("behavior/switch_prong_implicit_cast.zig");
191192
_ = @import("behavior/switch_stage1.zig");
192193
_ = @import("behavior/truncate_stage1.zig");
193-
_ = @import("behavior/try.zig");
194194
_ = @import("behavior/tuple.zig");
195195
_ = @import("behavior/type_stage1.zig");
196196
_ = @import("behavior/type_info_stage1.zig");

0 commit comments

Comments
 (0)