Skip to content

Commit

Permalink
stage2: implement the core logic of ziglang#11022 (error set equality)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Mar 9, 2022
1 parent 13cf3e2 commit 9582b51
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 26 deletions.
59 changes: 33 additions & 26 deletions src/type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -564,34 +564,41 @@ pub const Type = extern union {
=> {
if (b.zigTypeTag() != .ErrorSet) return false;

// TODO: revisit the language specification for how to evaluate equality
// for error set types. (See #11022)

// TODO this isn't correct, but deferring a fix until #11022
if (a.tag() != b.tag()) return false;

return switch (a.tag()) {
.anyerror => true,
.error_set => a.castTag(.error_set).?.data.owner_decl == b.castTag(.error_set).?.data.owner_decl,
.error_set_inferred => a.castTag(.error_set_inferred).?.data == b.castTag(.error_set_inferred).?.data,
.error_set_single => blk: {
const a_data = a.castTag(.error_set_single).?.data;
const b_data = b.castTag(.error_set_single).?.data;
break :blk std.mem.eql(u8, a_data, b_data);
},
.error_set_merged => blk: {
const a_data = a.castTag(.error_set_merged).?.data;
const b_data = b.castTag(.error_set_merged).?.data;
if (a_data.count() != b_data.count()) break :blk false;
for (a_data.keys()) |name| {
if (!b_data.contains(name)) break :blk false;
}
// anyerror matches exactly.
const a_is_any = a.isAnyError();
const b_is_any = b.isAnyError();
if (a_is_any or b_is_any) return a_is_any and b_is_any;

// attempt to confirm equality without requiring inferred resolution
// there are cases the caller has to compare type equality
// without being able to resolve inferred error sets
// (i.e. comparing function type signatures for generic
// instantiation) so we DO NOT require inferred set resolution
if (a.castTag(.error_set_inferred)) |a_pl| {
if (b.castTag(.error_set_inferred)) |b_pl| {
// We can verify equality without resolution if data matches exactly
if (a_pl.data == b_pl.data) return true;

if (!b_pl.data.is_resolved) return false;
}

break :blk true;
},
// we did everything we can to confirm equality without
// resolution. caller must resolve to do more checks.
if (!a_pl.data.is_resolved) return false;
} else if (b.castTag(.error_set_inferred)) |b_pl| {
// b must be resolved if a is not an inferred set
if (!b_pl.data.is_resolved) return false;
}

else => false,
};
// two resolved sets match if their error set names match.
const a_set = a.errorSetNames();
const b_set = b.errorSetNames();
if (a_set.len != b_set.len) return false;
for (b_set) |b_val| {
if (!a.errorSetHasField(b_val)) return false;
}

return true;
},

.@"opaque" => {
Expand Down
24 changes: 24 additions & 0 deletions test/behavior/error.zig
Original file line number Diff line number Diff line change
Expand Up @@ -569,3 +569,27 @@ test "@errorName sentinel length matches slice length" {
pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 {
return @errorName(err);
}

test "error set equality" {
// This tests using stage2 logic (#11022)
if (builtin.zig_backend == .stage1) return error.SkipZigTest;

if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;

const a = error{One};
const b = error{One};

try expect(a == a);
try expect(a == b);
try expect(a == error{One});

// should treat as a set
const c = error{ One, Two };
const d = error{ Two, One };

try expect(c == d);
}

0 comments on commit 9582b51

Please sign in to comment.