| author | |
| committer | |
| log | 96f614d374cf76ca58b1932009d339dec9dc99ed |
| tree | a3e6012d38c15d34c847773c27189c8a4f1edadb |
| parent | 0b82c02945c69e2e0465b5a4d9de471ea3c76d50 |
| parent | 9f163310f2bfa4f5289c1db6231089f3be0c9b93 |
| signature |
stage2: error set type equality, error and error union value equality8 files changed, 175 insertions(+), 65 deletions(-)
src/Module.zig+13-1| ... | ... | @@ -824,7 +824,7 @@ pub const ErrorSet = struct { |
| 824 | 824 | /// Offset from Decl node index, points to the error set AST node. |
| 825 | 825 | node_offset: i32, |
| 826 | 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 | 828 | names: NameMap, |
| 829 | 829 | |
| 830 | 830 | pub const NameMap = std.StringArrayHashMapUnmanaged(void); |
| ... | ... | @@ -836,6 +836,18 @@ pub const ErrorSet = struct { |
| 836 | 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 | }; |
| 840 | 852 | |
| 841 | 853 | pub const RequiresComptime = enum { no, yes, unknown, wip }; |
src/Sema.zig+4| ... | ... | @@ -2212,6 +2212,10 @@ fn zirErrorSetDecl( |
| 2212 | 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 | 2219 | error_set.* = .{ |
| 2216 | 2220 | .owner_decl = new_decl, |
| 2217 | 2221 | .node_offset = inst_data.src_node, |
src/type.zig+48-25| ... | ... | @@ -556,35 +556,37 @@ pub const Type = extern union { |
| 556 | 556 | return info_a.signedness == info_b.signedness and info_a.bits == info_b.bits; |
| 557 | 557 | }, |
| 558 | 558 | |
| 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 | 571 | .error_set, |
| 560 | 572 | .error_set_single, |
| 561 | .anyerror, | |
| 562 | .error_set_inferred, | |
| 563 | 573 | .error_set_merged, |
| 564 | 574 | => { |
| 565 | if (b.zigTypeTag() != .ErrorSet) return false; | |
| 566 | ||
| 567 | // TODO: revisit the language specification for how to evaluate equality | |
| 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; | |
| 575 | switch (b.tag()) { | |
| 576 | .error_set, .error_set_single, .error_set_merged => {}, | |
| 577 | else => return false, | |
| 580 | 578 | } |
| 581 | 579 | |
| 582 | if (a.tag() == .error_set_single and b.tag() == .error_set_single) { | |
| 583 | const a_data = a.castTag(.error_set_single).?.data; | |
| 584 | const b_data = b.castTag(.error_set_single).?.data; | |
| 585 | return std.mem.eql(u8, a_data, b_data); | |
| 580 | // Two resolved sets match if their error set names match. | |
| 581 | // Since they are pre-sorted we compare them element-wise. | |
| 582 | const a_set = a.errorSetNames(); | |
| 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 | }, |
| 589 | 591 | |
| 590 | 592 | .@"opaque" => { |
| ... | ... | @@ -961,12 +963,30 @@ pub const Type = extern union { |
| 961 | 963 | |
| 962 | 964 | .error_set, |
| 963 | 965 | .error_set_single, |
| 964 | .anyerror, | |
| 965 | .error_set_inferred, | |
| 966 | 966 | .error_set_merged, |
| 967 | 967 | => { |
| 968 | // all are treated like an "error set" for hashing | |
| 968 | 969 | std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet); |
| 969 | // TODO implement this after revisiting Type.Eql for error sets | |
| 970 | 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 | }, |
| 971 | 991 | |
| 972 | 992 | .@"opaque" => { |
| ... | ... | @@ -4365,6 +4385,9 @@ pub const Type = extern union { |
| 4365 | 4385 | try names.put(arena, name, {}); |
| 4366 | 4386 | } |
| 4367 | 4387 | |
| 4388 | // names must be sorted | |
| 4389 | Module.ErrorSet.sortNames(&names); | |
| 4390 | ||
| 4368 | 4391 | return try Tag.error_set_merged.create(arena, names); |
| 4369 | 4392 | } |
| 4370 | 4393 |
src/value.zig+10| ... | ... | @@ -1870,6 +1870,16 @@ pub const Value = extern union { |
| 1870 | 1870 | |
| 1871 | 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 | 1883 | .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 1874 | 1884 | .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 1875 | 1885 | .array => { |
test/behavior/cast.zig+8-8| ... | ... | @@ -669,8 +669,8 @@ test "peer type resolution: disjoint error sets" { |
| 669 | 669 | try expect(error_set_info == .ErrorSet); |
| 670 | 670 | try expect(error_set_info.ErrorSet.?.len == 3); |
| 671 | 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")); | |
| 673 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three")); | |
| 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, "Two")); | |
| 674 | 674 | } |
| 675 | 675 | |
| 676 | 676 | { |
| ... | ... | @@ -678,8 +678,8 @@ test "peer type resolution: disjoint error sets" { |
| 678 | 678 | const error_set_info = @typeInfo(ty); |
| 679 | 679 | try expect(error_set_info == .ErrorSet); |
| 680 | 680 | try expect(error_set_info.ErrorSet.?.len == 3); |
| 681 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "Three")); | |
| 682 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "One")); | |
| 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, "Three")); | |
| 683 | 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 | 704 | |
| 705 | 705 | const error_set_info = @typeInfo(info.ErrorUnion.error_set); |
| 706 | 706 | try expect(error_set_info.ErrorSet.?.len == 3); |
| 707 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "Three")); | |
| 708 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "One")); | |
| 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, "Three")); | |
| 709 | 709 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two")); |
| 710 | 710 | } |
| 711 | 711 | |
| ... | ... | @@ -717,8 +717,8 @@ test "peer type resolution: error union and error set" { |
| 717 | 717 | const error_set_info = @typeInfo(info.ErrorUnion.error_set); |
| 718 | 718 | try expect(error_set_info.ErrorSet.?.len == 3); |
| 719 | 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")); | |
| 721 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three")); | |
| 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, "Two")); | |
| 722 | 722 | } |
| 723 | 723 | } |
| 724 | 724 |
test/behavior/error.zig+87-8| ... | ... | @@ -330,7 +330,11 @@ fn intLiteral(str: []const u8) !?i64 { |
| 330 | 330 | } |
| 331 | 331 | |
| 332 | 332 | test "nested error union function call in optional unwrap" { |
| 333 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 333 | 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 | |
| 334 | 338 | |
| 335 | 339 | const S = struct { |
| 336 | 340 | const Foo = struct { |
| ... | ... | @@ -375,7 +379,11 @@ test "nested error union function call in optional unwrap" { |
| 375 | 379 | } |
| 376 | 380 | |
| 377 | 381 | test "return function call to error set from error union function" { |
| 378 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 382 | 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 | |
| 379 | 387 | |
| 380 | 388 | const S = struct { |
| 381 | 389 | fn errorable() anyerror!i32 { |
| ... | ... | @@ -404,7 +412,11 @@ test "optional error set is the same size as error set" { |
| 404 | 412 | } |
| 405 | 413 | |
| 406 | 414 | test "nested catch" { |
| 407 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 415 | 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 | |
| 408 | 420 | |
| 409 | 421 | const S = struct { |
| 410 | 422 | fn entry() !void { |
| ... | ... | @@ -428,11 +440,20 @@ test "nested catch" { |
| 428 | 440 | } |
| 429 | 441 | |
| 430 | 442 | test "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; // TODO | |
| 443 | 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 | |
| 432 | 453 | |
| 433 | 454 | const S = struct { |
| 434 | 455 | const Foo = struct { |
| 435 | fun: fn (a: i32) (anyerror!*Foo), | |
| 456 | fun: *const fn (a: i32) (anyerror!*Foo), | |
| 436 | 457 | }; |
| 437 | 458 | |
| 438 | 459 | const Err = error{UnspecifiedErr}; |
| ... | ... | @@ -480,7 +501,11 @@ test "return result loc as peer result loc in inferred error set function" { |
| 480 | 501 | } |
| 481 | 502 | |
| 482 | 503 | test "error payload type is correctly resolved" { |
| 483 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 504 | 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 | |
| 484 | 509 | |
| 485 | 510 | const MyIntWrapper = struct { |
| 486 | 511 | const Self = @This(); |
| ... | ... | @@ -496,8 +521,6 @@ test "error payload type is correctly resolved" { |
| 496 | 521 | } |
| 497 | 522 | |
| 498 | 523 | test "error union comptime caching" { |
| 499 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 500 | ||
| 501 | 524 | const S = struct { |
| 502 | 525 | fn quux(comptime arg: anytype) void { |
| 503 | 526 | arg catch {}; |
| ... | ... | @@ -539,3 +562,59 @@ test "@errorName sentinel length matches slice length" { |
| 539 | 562 | pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 { |
| 540 | 563 | return @errorName(err); |
| 541 | 564 | } |
| 565 | ||
| 566 | test "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 | ||
| 584 | test "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 | 205 | } |
| 206 | 206 | |
| 207 | 207 | test "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 | 211 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 209 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 210 | 212 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 211 | 213 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 212 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 213 | 214 | |
| 214 | 215 | const TestSet = error{ One, Two } || error{Three}; |
| 215 | 216 | |
| ... | ... | @@ -217,8 +218,8 @@ test "type info: error set merged" { |
| 217 | 218 | try expect(error_set_info == .ErrorSet); |
| 218 | 219 | try expect(error_set_info.ErrorSet.?.len == 3); |
| 219 | 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.?[2].name, "Three")); | |
| 221 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Three")); | |
| 222 | try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two")); | |
| 222 | 223 | } |
| 223 | 224 | |
| 224 | 225 | test "type info: enum info" { |
test/stage2/x86_64.zig-19| ... | ... | @@ -1412,25 +1412,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1412 | 1412 | }); |
| 1413 | 1413 | } |
| 1414 | 1414 | |
| 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 | 1416 | var case = ctx.exe("comptime var", target); |
| 1436 | 1417 |