authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-10 16:22:20-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-10 16:22:20-05:00
log96f614d374cf76ca58b1932009d339dec9dc99ed
treea3e6012d38c15d34c847773c27189c8a4f1edadb
parent0b82c02945c69e2e0465b5a4d9de471ea3c76d50
parent9f163310f2bfa4f5289c1db6231089f3be0c9b93
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11098 from mitchellh/error-merge-eql

stage2: error set type equality, error and error union value equality

8 files changed, 175 insertions(+), 65 deletions(-)

src/Module.zig+13-1
...@@ -824,7 +824,7 @@ pub const ErrorSet = struct {...@@ -824,7 +824,7 @@ pub const ErrorSet = struct {
824 /// Offset from Decl node index, points to the error set AST node.824 /// Offset from Decl node index, points to the error set AST node.
825 node_offset: i32,825 node_offset: i32,
826 /// The string bytes are stored in the owner Decl arena.826 /// The string bytes are stored in the owner Decl arena.
827 /// They are in the same order they appear in the AST.827 /// These must be in sorted order. See sortNames.
828 names: NameMap,828 names: NameMap,
829829
830 pub const NameMap = std.StringArrayHashMapUnmanaged(void);830 pub const NameMap = std.StringArrayHashMapUnmanaged(void);
...@@ -836,6 +836,18 @@ pub const ErrorSet = struct {...@@ -836,6 +836,18 @@ pub const ErrorSet = struct {
836 .lazy = .{ .node_offset = self.node_offset },836 .lazy = .{ .node_offset = self.node_offset },
837 };837 };
838 }838 }
839
840 /// sort the NameMap. This should be called whenever the map is modified.
841 /// alloc should be the allocator used for the NameMap data.
842 pub fn sortNames(names: *NameMap) void {
843 const Context = struct {
844 keys: [][]const u8,
845 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
846 return std.mem.lessThan(u8, ctx.keys[a_index], ctx.keys[b_index]);
847 }
848 };
849 names.sort(Context{ .keys = names.keys() });
850 }
839};851};
840852
841pub const RequiresComptime = enum { no, yes, unknown, wip };853pub const RequiresComptime = enum { no, yes, unknown, wip };
src/Sema.zig+4
...@@ -2212,6 +2212,10 @@ fn zirErrorSetDecl(...@@ -2212,6 +2212,10 @@ fn zirErrorSetDecl(
2212 return sema.fail(block, src, "duplicate error set field {s}", .{name});2212 return sema.fail(block, src, "duplicate error set field {s}", .{name});
2213 }2213 }
2214 }2214 }
2215
2216 // names must be sorted.
2217 Module.ErrorSet.sortNames(&names);
2218
2215 error_set.* = .{2219 error_set.* = .{
2216 .owner_decl = new_decl,2220 .owner_decl = new_decl,
2217 .node_offset = inst_data.src_node,2221 .node_offset = inst_data.src_node,
src/type.zig+48-25
...@@ -556,35 +556,37 @@ pub const Type = extern union {...@@ -556,35 +556,37 @@ pub const Type = extern union {
556 return info_a.signedness == info_b.signedness and info_a.bits == info_b.bits;556 return info_a.signedness == info_b.signedness and info_a.bits == info_b.bits;
557 },557 },
558558
559 .error_set_inferred => {
560 // Inferred error sets are only equal if both are inferred
561 // and they originate from the exact same function.
562 const a_set = a.castTag(.error_set_inferred).?.data;
563 const b_set = (b.castTag(.error_set_inferred) orelse return false).data;
564 return a_set.func == b_set.func;
565 },
566
567 .anyerror => {
568 return b.tag() == .anyerror;
569 },
570
559 .error_set,571 .error_set,
560 .error_set_single,572 .error_set_single,
561 .anyerror,
562 .error_set_inferred,
563 .error_set_merged,573 .error_set_merged,
564 => {574 => {
565 if (b.zigTypeTag() != .ErrorSet) return false;575 switch (b.tag()) {
566576 .error_set, .error_set_single, .error_set_merged => {},
567 // TODO: revisit the language specification for how to evaluate equality577 else => return false,
568 // for error set types.
569
570 if (a.tag() == .anyerror and b.tag() == .anyerror) {
571 return true;
572 }
573
574 if (a.tag() == .error_set and b.tag() == .error_set) {
575 return a.castTag(.error_set).?.data.owner_decl == b.castTag(.error_set).?.data.owner_decl;
576 }
577
578 if (a.tag() == .error_set_inferred and b.tag() == .error_set_inferred) {
579 return a.castTag(.error_set_inferred).?.data == b.castTag(.error_set_inferred).?.data;
580 }578 }
581579
582 if (a.tag() == .error_set_single and b.tag() == .error_set_single) {580 // Two resolved sets match if their error set names match.
583 const a_data = a.castTag(.error_set_single).?.data;581 // Since they are pre-sorted we compare them element-wise.
584 const b_data = b.castTag(.error_set_single).?.data;582 const a_set = a.errorSetNames();
585 return std.mem.eql(u8, a_data, b_data);583 const b_set = b.errorSetNames();
584 if (a_set.len != b_set.len) return false;
585 for (a_set) |a_item, i| {
586 const b_item = b_set[i];
587 if (!std.mem.eql(u8, a_item, b_item)) return false;
586 }588 }
587 return false;589 return true;
588 },590 },
589591
590 .@"opaque" => {592 .@"opaque" => {
...@@ -961,12 +963,30 @@ pub const Type = extern union {...@@ -961,12 +963,30 @@ pub const Type = extern union {
961963
962 .error_set,964 .error_set,
963 .error_set_single,965 .error_set_single,
964 .anyerror,
965 .error_set_inferred,
966 .error_set_merged,966 .error_set_merged,
967 => {967 => {
968 // all are treated like an "error set" for hashing
968 std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet);969 std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet);
969 // TODO implement this after revisiting Type.Eql for error sets970 std.hash.autoHash(hasher, Tag.error_set);
971
972 const names = ty.errorSetNames();
973 std.hash.autoHash(hasher, names.len);
974 assert(std.sort.isSorted([]const u8, names, u8, std.mem.lessThan));
975 for (names) |name| hasher.update(name);
976 },
977
978 .anyerror => {
979 // anyerror is distinct from other error sets
980 std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet);
981 std.hash.autoHash(hasher, Tag.anyerror);
982 },
983
984 .error_set_inferred => {
985 // inferred error sets are compared using their data pointer
986 const set = ty.castTag(.error_set_inferred).?.data;
987 std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet);
988 std.hash.autoHash(hasher, Tag.error_set_inferred);
989 std.hash.autoHash(hasher, set.func);
970 },990 },
971991
972 .@"opaque" => {992 .@"opaque" => {
...@@ -4365,6 +4385,9 @@ pub const Type = extern union {...@@ -4365,6 +4385,9 @@ pub const Type = extern union {
4365 try names.put(arena, name, {});4385 try names.put(arena, name, {});
4366 }4386 }
43674387
4388 // names must be sorted
4389 Module.ErrorSet.sortNames(&names);
4390
4368 return try Tag.error_set_merged.create(arena, names);4391 return try Tag.error_set_merged.create(arena, names);
4369 }4392 }
43704393
src/value.zig+10
...@@ -1870,6 +1870,16 @@ pub const Value = extern union {...@@ -1870,6 +1870,16 @@ pub const Value = extern union {
18701870
1871 return eql(a_payload.container_ptr, b_payload.container_ptr, ty);1871 return eql(a_payload.container_ptr, b_payload.container_ptr, ty);
1872 },1872 },
1873 .@"error" => {
1874 const a_name = a.castTag(.@"error").?.data.name;
1875 const b_name = b.castTag(.@"error").?.data.name;
1876 return std.mem.eql(u8, a_name, b_name);
1877 },
1878 .eu_payload => {
1879 const a_payload = a.castTag(.eu_payload).?.data;
1880 const b_payload = b.castTag(.eu_payload).?.data;
1881 return eql(a_payload, b_payload, ty.errorUnionPayload());
1882 },
1873 .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),1883 .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
1874 .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),1884 .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
1875 .array => {1885 .array => {
test/behavior/cast.zig+8-8
...@@ -669,8 +669,8 @@ test "peer type resolution: disjoint error sets" {...@@ -669,8 +669,8 @@ test "peer type resolution: disjoint error sets" {
669 try expect(error_set_info == .ErrorSet);669 try expect(error_set_info == .ErrorSet);
670 try expect(error_set_info.ErrorSet.?.len == 3);670 try expect(error_set_info.ErrorSet.?.len == 3);
671 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));671 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
672 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two"));672 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Three"));
673 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three"));673 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));
674 }674 }
675675
676 {676 {
...@@ -678,8 +678,8 @@ test "peer type resolution: disjoint error sets" {...@@ -678,8 +678,8 @@ test "peer type resolution: disjoint error sets" {
678 const error_set_info = @typeInfo(ty);678 const error_set_info = @typeInfo(ty);
679 try expect(error_set_info == .ErrorSet);679 try expect(error_set_info == .ErrorSet);
680 try expect(error_set_info.ErrorSet.?.len == 3);680 try expect(error_set_info.ErrorSet.?.len == 3);
681 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "Three"));681 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
682 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "One"));682 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Three"));
683 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));683 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));
684 }684 }
685}685}
...@@ -704,8 +704,8 @@ test "peer type resolution: error union and error set" {...@@ -704,8 +704,8 @@ test "peer type resolution: error union and error set" {
704704
705 const error_set_info = @typeInfo(info.ErrorUnion.error_set);705 const error_set_info = @typeInfo(info.ErrorUnion.error_set);
706 try expect(error_set_info.ErrorSet.?.len == 3);706 try expect(error_set_info.ErrorSet.?.len == 3);
707 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "Three"));707 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
708 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "One"));708 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Three"));
709 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));709 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));
710 }710 }
711711
...@@ -717,8 +717,8 @@ test "peer type resolution: error union and error set" {...@@ -717,8 +717,8 @@ test "peer type resolution: error union and error set" {
717 const error_set_info = @typeInfo(info.ErrorUnion.error_set);717 const error_set_info = @typeInfo(info.ErrorUnion.error_set);
718 try expect(error_set_info.ErrorSet.?.len == 3);718 try expect(error_set_info.ErrorSet.?.len == 3);
719 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));719 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
720 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two"));720 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Three"));
721 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three"));721 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));
722 }722 }
723}723}
724724
test/behavior/error.zig+87-8
...@@ -330,7 +330,11 @@ fn intLiteral(str: []const u8) !?i64 {...@@ -330,7 +330,11 @@ fn intLiteral(str: []const u8) !?i64 {
330}330}
331331
332test "nested error union function call in optional unwrap" {332test "nested error union function call in optional unwrap" {
333 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO333 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
334 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
335 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
336 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
337 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
334338
335 const S = struct {339 const S = struct {
336 const Foo = struct {340 const Foo = struct {
...@@ -375,7 +379,11 @@ test "nested error union function call in optional unwrap" {...@@ -375,7 +379,11 @@ test "nested error union function call in optional unwrap" {
375}379}
376380
377test "return function call to error set from error union function" {381test "return function call to error set from error union function" {
378 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO382 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
383 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
384 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
385 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
386 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
379387
380 const S = struct {388 const S = struct {
381 fn errorable() anyerror!i32 {389 fn errorable() anyerror!i32 {
...@@ -404,7 +412,11 @@ test "optional error set is the same size as error set" {...@@ -404,7 +412,11 @@ test "optional error set is the same size as error set" {
404}412}
405413
406test "nested catch" {414test "nested catch" {
407 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO415 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
416 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
417 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
418 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
419 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
408420
409 const S = struct {421 const S = struct {
410 fn entry() !void {422 fn entry() !void {
...@@ -428,11 +440,20 @@ test "nested catch" {...@@ -428,11 +440,20 @@ test "nested catch" {
428}440}
429441
430test "function pointer with return type that is error union with payload which is pointer of parent struct" {442test "function pointer with return type that is error union with payload which is pointer of parent struct" {
431 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO443 if (builtin.zig_backend == .stage1) {
444 // stage1 has wrong function pointer semantics
445 return error.SkipZigTest;
446 }
447
448 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
449 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
450 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
451 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
452 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
432453
433 const S = struct {454 const S = struct {
434 const Foo = struct {455 const Foo = struct {
435 fun: fn (a: i32) (anyerror!*Foo),456 fun: *const fn (a: i32) (anyerror!*Foo),
436 };457 };
437458
438 const Err = error{UnspecifiedErr};459 const Err = error{UnspecifiedErr};
...@@ -480,7 +501,11 @@ test "return result loc as peer result loc in inferred error set function" {...@@ -480,7 +501,11 @@ test "return result loc as peer result loc in inferred error set function" {
480}501}
481502
482test "error payload type is correctly resolved" {503test "error payload type is correctly resolved" {
483 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO504 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
505 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
506 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
507 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
508 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
484509
485 const MyIntWrapper = struct {510 const MyIntWrapper = struct {
486 const Self = @This();511 const Self = @This();
...@@ -496,8 +521,6 @@ test "error payload type is correctly resolved" {...@@ -496,8 +521,6 @@ test "error payload type is correctly resolved" {
496}521}
497522
498test "error union comptime caching" {523test "error union comptime caching" {
499 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
500
501 const S = struct {524 const S = struct {
502 fn quux(comptime arg: anytype) void {525 fn quux(comptime arg: anytype) void {
503 arg catch {};526 arg catch {};
...@@ -539,3 +562,59 @@ test "@errorName sentinel length matches slice length" {...@@ -539,3 +562,59 @@ test "@errorName sentinel length matches slice length" {
539pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 {562pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 {
540 return @errorName(err);563 return @errorName(err);
541}564}
565
566test "error set equality" {
567 // This tests using stage2 logic (#11022)
568 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
569
570 const a = error{One};
571 const b = error{One};
572
573 try expect(a == a);
574 try expect(a == b);
575 try expect(a == error{One});
576
577 // should treat as a set
578 const c = error{ One, Two };
579 const d = error{ Two, One };
580
581 try expect(c == d);
582}
583
584test "inferred error set equality" {
585 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
586
587 const S = struct {
588 fn foo() !void {
589 return @This().bar();
590 }
591
592 fn bar() !void {
593 return error.Bad;
594 }
595
596 fn baz() !void {
597 return quux();
598 }
599
600 fn quux() anyerror!void {}
601 };
602
603 const FooError = @typeInfo(@typeInfo(@TypeOf(S.foo)).Fn.return_type.?).ErrorUnion.error_set;
604 const BarError = @typeInfo(@typeInfo(@TypeOf(S.bar)).Fn.return_type.?).ErrorUnion.error_set;
605 const BazError = @typeInfo(@typeInfo(@TypeOf(S.baz)).Fn.return_type.?).ErrorUnion.error_set;
606
607 try expect(BarError != error{Bad});
608
609 try expect(FooError != anyerror);
610 try expect(BarError != anyerror);
611 try expect(BazError != anyerror);
612
613 try expect(FooError != BarError);
614 try expect(FooError != BazError);
615 try expect(BarError != BazError);
616
617 try expect(FooError == FooError);
618 try expect(BarError == BarError);
619 try expect(BazError == BazError);
620}
test/behavior/type_info.zig+5-4
...@@ -205,11 +205,12 @@ test "type info: error set single value" {...@@ -205,11 +205,12 @@ test "type info: error set single value" {
205}205}
206206
207test "type info: error set merged" {207test "type info: error set merged" {
208 // #11022 forces ordering of error sets in stage2
209 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
210
208 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;211 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
209 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
210 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO212 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
211 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO213 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
212 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
213214
214 const TestSet = error{ One, Two } || error{Three};215 const TestSet = error{ One, Two } || error{Three};
215216
...@@ -217,8 +218,8 @@ test "type info: error set merged" {...@@ -217,8 +218,8 @@ test "type info: error set merged" {
217 try expect(error_set_info == .ErrorSet);218 try expect(error_set_info == .ErrorSet);
218 try expect(error_set_info.ErrorSet.?.len == 3);219 try expect(error_set_info.ErrorSet.?.len == 3);
219 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));220 try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
220 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two"));221 try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Three"));
221 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three"));222 try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));
222}223}
223224
224test "type info: enum info" {225test "type info: enum info" {
test/stage2/x86_64.zig-19
...@@ -1412,25 +1412,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1412,25 +1412,6 @@ pub fn addCases(ctx: *TestContext) !void {
1412 });1412 });
1413 }1413 }
14141414
1415 {
1416 var case = ctx.exe("error set equality", target);
1417
1418 case.addCompareOutput(
1419 \\pub fn main() void {
1420 \\ assert(@TypeOf(error.Foo) == @TypeOf(error.Foo));
1421 \\ assert(@TypeOf(error.Bar) != @TypeOf(error.Foo));
1422 \\ assert(anyerror == anyerror);
1423 \\ assert(error{Foo} != error{Foo});
1424 \\ // TODO put inferred error sets here when @typeInfo works
1425 \\}
1426 \\fn assert(b: bool) void {
1427 \\ if (!b) unreachable;
1428 \\}
1429 ,
1430 "",
1431 );
1432 }
1433
1434 {1415 {
1435 var case = ctx.exe("comptime var", target);1416 var case = ctx.exe("comptime var", target);
14361417