From 74793540e61f88e08b9e9b662b1e385163c9ccc7 Mon Sep 17 00:00:00 2001 From: iwa Date: Sat, 7 Nov 2026 18:55:24 +0900 Subject: [PATCH 1/7] =?UTF-8?q?WIP:=20I=20believe=20this=20is=20the=20rele?= =?UTF-8?q?vant=20code,=20so=20I=E2=80=99m=20adding=20it.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/analyser/InternPool.zig | 12 ++++++++++-- src/analysis.zig | 14 +++++++++----- tests/analysis/error_union.zig | 5 +++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 1cef0eeaf4..be46353278 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -110,6 +110,7 @@ pub const Key = union(enum) { pub const ErrorSet = struct { owner_decl: Decl.OptionalIndex, names: StringSlice, + has_unknown: Index = .bool_type, }; pub const Function = struct { @@ -3544,8 +3545,8 @@ pub fn scalarType(ip: *InternPool, ty: Index) Index { } pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error!Index { - assert(ip.zigTypeTag(a_ty) == .error_set); - assert(ip.zigTypeTag(b_ty) == .error_set); + assert(ip.zigTypeTag(a_ty) == .error_set or ip.isUnknown(a_ty)); + assert(ip.zigTypeTag(b_ty) == .error_set or ip.isUnknown(b_ty)); // Anything merged with anyerror is anyerror. if (a_ty == .anyerror_type or b_ty == .anyerror_type) { @@ -3574,6 +3575,7 @@ pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error! .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(set.keys()), + .has_unknown = if (ip.isUnknown(a_ty) or ip.isUnknown(b_ty)) Index.bool_true else Index.bool_false, }, }); } @@ -4041,6 +4043,12 @@ fn printInternal(ip: *InternPool, ty: Index, writer: *std.Io.Writer, options: Fo const name = error_set_info.names.at(@intCast(i), ip); try writer.print("{f}", .{ip.fmtId(name)}); } + if (error_set_info.has_unknown == .bool_true) { + if (error_set_info.names.len > 0) { + try writer.writeByte(','); + } + try writer.writeAll("..."); + } try writer.writeByte('}'); }, .enum_type => return panicOrElse(?Index, "TODO", null), diff --git a/src/analysis.zig b/src/analysis.zig index a0bec82593..36df16c061 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2253,7 +2253,8 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error try strings.put(analyser.gpa, index, {}); } const names = try analyser.ip.getStringSlice(strings.keys()); - const ip_index = try analyser.ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = names } }); + const ip_index = try analyser.ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = names, .has_unknown = .bool_true } }); // TODO has_unknown + log.debug("DEBUG: {}", .{ip_index}); return Type.fromIP(analyser, .type_type, ip_index); }, @@ -2812,10 +2813,13 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error const name = offsets.identifierTokenToNameSlice(tree, name_token); const name_index = try analyser.ip.string_pool.getOrPutString(analyser.store.io, analyser.gpa, name); - const error_set_type = try analyser.ip.get(.{ .error_set_type = .{ - .owner_decl = .none, - .names = try analyser.ip.getStringSlice(&.{name_index}), - } }); + const error_set_type = try analyser.ip.get(.{ + .error_set_type = .{ + .owner_decl = .none, + .names = try analyser.ip.getStringSlice(&.{name_index}), + .has_unknown = .bool_true, // TODO not true + }, + }); const error_value = try analyser.ip.get(.{ .error_value = .{ .ty = error_set_type, .error_tag_name = name_index, diff --git a/tests/analysis/error_union.zig b/tests/analysis/error_union.zig index 708999ec28..94acc3cabf 100644 --- a/tests/analysis/error_union.zig +++ b/tests/analysis/error_union.zig @@ -19,3 +19,8 @@ const ErrorUnionUnknownError = Unknown!u32; const ErrorUnionUnknownPayload = Error!Unknown; // ^^^^^^^^^^^^^^^^^^^^^^^^ (type)(error{Foo,Bar}!(unknown type)) + +const ErrorSet = error{A} || error{B}; +const ErrorSetWithUnknown = error{A} || undefined.Unknown; +const ErrorSetWithUnknown2 = undefined.Unknown || error{A}; +const AllUnknown = undefined.Unknown || undefined.Unknown; // error{ ... } From 35bbf0273329cfb744cacb92d79703abbfcbd3a6 Mon Sep 17 00:00:00 2001 From: iwa Date: Mon, 9 Nov 2026 01:32:58 +0900 Subject: [PATCH 2/7] feat: ability to resolve partially known error sets - Modified the logic for determining `has_unknown`. - Updated the branching logic in `merge_error_sets` to return `unknown_type` instead of `null`. - Removed debugging code. - Configured `has_unknown` to be set to `bool_false` when encountering `error_set_type` or `error_union` (as these should not contain an `unknown` state). - Added a `has_unknown` check within `eql` (previously, it defaulted to `bool_false` during `get` operations). - Updated the tests. --- src/analyser/InternPool.zig | 79 +++++++++++++++++++++++----------- src/analysis.zig | 9 ++-- tests/analysis/error_union.zig | 6 ++- 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index be46353278..e6c6e50d1e 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -435,6 +435,7 @@ pub const Key = union(enum) { const b_info = b.error_set_type; if (a_info.owner_decl != b_info.owner_decl) return false; + if (a_info.has_unknown != b_info.has_unknown) return false; if (a_info.names.len != b_info.names.len) return false; @@ -3545,39 +3546,67 @@ pub fn scalarType(ip: *InternPool, ty: Index) Index { } pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error!Index { - assert(ip.zigTypeTag(a_ty) == .error_set or ip.isUnknown(a_ty)); - assert(ip.zigTypeTag(b_ty) == .error_set or ip.isUnknown(b_ty)); + const has_unknown = ip.isUnknown(a_ty) or ip.isUnknown(b_ty); + + if (has_unknown) { + var set: std.array_hash_map.Auto(String, void) = .empty; + defer set.deinit(ip.gpa); + + if (!ip.isUnknown(a_ty)) { + assert(ip.zigTypeTag(a_ty) == .error_set); + const a_names = try ip.indexToKey(a_ty).error_set_type.names.dupe(ip.gpa, ip); + defer ip.gpa.free(a_names); + try set.ensureTotalCapacity(ip.gpa, a_names.len); + for (a_names) |name| set.putAssumeCapacity(name, {}); + } + if (!ip.isUnknown(b_ty)) { + assert(ip.zigTypeTag(b_ty) == .error_set); + const b_names = try ip.indexToKey(b_ty).error_set_type.names.dupe(ip.gpa, ip); + defer ip.gpa.free(b_names); + try set.ensureTotalCapacity(ip.gpa, b_names.len); + for (b_names) |name| set.putAssumeCapacity(name, {}); + } - // Anything merged with anyerror is anyerror. - if (a_ty == .anyerror_type or b_ty == .anyerror_type) { - return .anyerror_type; - } + ip.string_pool.sortStrings(ip.io, set.keys()); - if (a_ty == b_ty) return a_ty; + return try ip.get(.{ + .error_set_type = .{ + .owner_decl = .none, + .names = try ip.getStringSlice(set.keys()), + .has_unknown = .bool_true, + }, + }); + } else { + assert(ip.zigTypeTag(a_ty) == .error_set); + assert(ip.zigTypeTag(b_ty) == .error_set); - const a_names = try ip.indexToKey(a_ty).error_set_type.names.dupe(ip.gpa, ip); - defer ip.gpa.free(a_names); + // Anything merged with anyerror is anyerror. + if (a_ty == .anyerror_type or b_ty == .anyerror_type) { + return .anyerror_type; + } - const b_names = try ip.indexToKey(b_ty).error_set_type.names.dupe(ip.gpa, ip); - defer ip.gpa.free(b_names); + if (a_ty == b_ty) return a_ty; - var set: std.array_hash_map.Auto(String, void) = .empty; - defer set.deinit(ip.gpa); + const a_names = try ip.indexToKey(a_ty).error_set_type.names.dupe(ip.gpa, ip); + defer ip.gpa.free(a_names); - try set.ensureTotalCapacity(ip.gpa, a_names.len + b_names.len); + const b_names = try ip.indexToKey(b_ty).error_set_type.names.dupe(ip.gpa, ip); + defer ip.gpa.free(b_names); - for (a_names) |name| set.putAssumeCapacityNoClobber(name, {}); - for (b_names) |name| set.putAssumeCapacity(name, {}); + var set: std.array_hash_map.Auto(String, void) = .empty; + defer set.deinit(ip.gpa); - ip.string_pool.sortStrings(ip.io, set.keys()); + try set.ensureTotalCapacity(ip.gpa, a_names.len + b_names.len); - return try ip.get(.{ - .error_set_type = .{ - .owner_decl = .none, - .names = try ip.getStringSlice(set.keys()), - .has_unknown = if (ip.isUnknown(a_ty) or ip.isUnknown(b_ty)) Index.bool_true else Index.bool_false, - }, - }); + for (a_names) |name| set.putAssumeCapacityNoClobber(name, {}); + for (b_names) |name| set.putAssumeCapacity(name, {}); + + ip.string_pool.sortStrings(ip.io, set.keys()); + + return try ip.get(.{ + .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(set.keys()), .has_unknown = .bool_false }, + }); + } } /// Asserts the type is a vector or tuple. @@ -4047,7 +4076,7 @@ fn printInternal(ip: *InternPool, ty: Index, writer: *std.Io.Writer, options: Fo if (error_set_info.names.len > 0) { try writer.writeByte(','); } - try writer.writeAll("..."); + try writer.writeAll(" ... "); } try writer.writeByte('}'); }, diff --git a/src/analysis.zig b/src/analysis.zig index 36df16c061..0c93d4b1a8 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2233,8 +2233,8 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error .merge_error_sets => { const lhs, const rhs = tree.nodeData(node).node_and_node; - const lhs_index = try analyser.resolveErrorSetIPIndex(.of(lhs, handle)) orelse return null; - const rhs_index = try analyser.resolveErrorSetIPIndex(.of(rhs, handle)) orelse return null; + const lhs_index = try analyser.resolveErrorSetIPIndex(.of(lhs, handle)) orelse .unknown_type; + const rhs_index = try analyser.resolveErrorSetIPIndex(.of(rhs, handle)) orelse .unknown_type; const ip_index = try analyser.ip.errorSetMerge(lhs_index, rhs_index); return Type.fromIP(analyser, .type_type, ip_index); }, @@ -2253,8 +2253,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error try strings.put(analyser.gpa, index, {}); } const names = try analyser.ip.getStringSlice(strings.keys()); - const ip_index = try analyser.ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = names, .has_unknown = .bool_true } }); // TODO has_unknown - log.debug("DEBUG: {}", .{ip_index}); + const ip_index = try analyser.ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = names, .has_unknown = .bool_false } }); return Type.fromIP(analyser, .type_type, ip_index); }, @@ -2817,7 +2816,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error .error_set_type = .{ .owner_decl = .none, .names = try analyser.ip.getStringSlice(&.{name_index}), - .has_unknown = .bool_true, // TODO not true + .has_unknown = .bool_false, }, }); const error_value = try analyser.ip.get(.{ .error_value = .{ diff --git a/tests/analysis/error_union.zig b/tests/analysis/error_union.zig index 94acc3cabf..e3f47907f1 100644 --- a/tests/analysis/error_union.zig +++ b/tests/analysis/error_union.zig @@ -21,6 +21,10 @@ const ErrorUnionUnknownPayload = Error!Unknown; // ^^^^^^^^^^^^^^^^^^^^^^^^ (type)(error{Foo,Bar}!(unknown type)) const ErrorSet = error{A} || error{B}; +// ^^^^^^^^ (type)(error{A,B}) const ErrorSetWithUnknown = error{A} || undefined.Unknown; +// ^^^^^^^^^^^^^^^^^^^ (type)(error{A, ... }) const ErrorSetWithUnknown2 = undefined.Unknown || error{A}; -const AllUnknown = undefined.Unknown || undefined.Unknown; // error{ ... } +// ^^^^^^^^^^^^^^^^^^^^ (type)(error{B, ... }) +const AllUnknown = undefined.Unknown || undefined.Unknown; +// ^^^^^^^^^^ (type)(error{ ... }) From 029db88f3f3d8a3dbab78a2f83dd14bf4f83f8cb Mon Sep 17 00:00:00 2001 From: iwa Date: Mon, 9 Nov 2026 06:51:32 +0900 Subject: [PATCH 3/7] fix typo --- tests/analysis/error_union.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/analysis/error_union.zig b/tests/analysis/error_union.zig index e3f47907f1..4abc52ad1d 100644 --- a/tests/analysis/error_union.zig +++ b/tests/analysis/error_union.zig @@ -24,7 +24,7 @@ const ErrorSet = error{A} || error{B}; // ^^^^^^^^ (type)(error{A,B}) const ErrorSetWithUnknown = error{A} || undefined.Unknown; // ^^^^^^^^^^^^^^^^^^^ (type)(error{A, ... }) -const ErrorSetWithUnknown2 = undefined.Unknown || error{A}; +const ErrorSetWithUnknown2 = undefined.Unknown || error{B}; // ^^^^^^^^^^^^^^^^^^^^ (type)(error{B, ... }) const AllUnknown = undefined.Unknown || undefined.Unknown; // ^^^^^^^^^^ (type)(error{ ... }) From f2bc63f48b27ee016553565499b4dd0b431fa9ec Mon Sep 17 00:00:00 2001 From: iwa Date: Mon, 9 Nov 2026 07:48:27 +0900 Subject: [PATCH 4/7] fix: an oversight regarding the handling of `error_set`. - Updated evaluation to use `isUnknownDeep` instead of `isUnknown` (this ensures that `error_set` is handled correctly when encountered). - Added logic to `isUnknownDeep` to check whether `has_unknown` is set to `bool_true` when evaluating an `error_set`. - Added tests; applied some formatting adjustments. --- src/analyser/InternPool.zig | 9 +++++++-- tests/analysis/error_union.zig | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index e6c6e50d1e..9ebd7b22a1 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -3223,7 +3223,12 @@ fn isUnknownDeepInternal( }, .optional_type => |optional_info| try ip.isUnknownDeepInternal(optional_info.payload_type, set), .error_union_type => |error_union_info| try ip.isUnknownDeepInternal(error_union_info.payload_type, set), - .error_set_type => false, + .error_set_type => |error_set_info| { + if (error_set_info.has_unknown == .bool_true) { + return true; + } + return false; + }, .enum_type => |enum_index| { const enum_info = ip.getEnum(enum_index); for (enum_info.values.keys()) |val| { @@ -3546,7 +3551,7 @@ pub fn scalarType(ip: *InternPool, ty: Index) Index { } pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error!Index { - const has_unknown = ip.isUnknown(a_ty) or ip.isUnknown(b_ty); + const has_unknown = try ip.isUnknownDeep(a_ty) or try ip.isUnknownDeep(b_ty); if (has_unknown) { var set: std.array_hash_map.Auto(String, void) = .empty; diff --git a/tests/analysis/error_union.zig b/tests/analysis/error_union.zig index 4abc52ad1d..8e904133a5 100644 --- a/tests/analysis/error_union.zig +++ b/tests/analysis/error_union.zig @@ -22,9 +22,15 @@ const ErrorUnionUnknownPayload = Error!Unknown; const ErrorSet = error{A} || error{B}; // ^^^^^^^^ (type)(error{A,B}) + const ErrorSetWithUnknown = error{A} || undefined.Unknown; // ^^^^^^^^^^^^^^^^^^^ (type)(error{A, ... }) + const ErrorSetWithUnknown2 = undefined.Unknown || error{B}; // ^^^^^^^^^^^^^^^^^^^^ (type)(error{B, ... }) + +const ErrorSetWithUnknown3 = undefined.Unknown || error{A} || error{B}; +// ^^^^^^^^^^ (type)(error{A,B, ... }) + const AllUnknown = undefined.Unknown || undefined.Unknown; // ^^^^^^^^^^ (type)(error{ ... }) From 55025ea971487d576bdace9b6ff6136a204194fc Mon Sep 17 00:00:00 2001 From: iwami Date: Sun, 8 Nov 2026 18:11:50 -0500 Subject: [PATCH 5/7] fix: added a caret, as one was missing. --- tests/analysis/error_union.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/analysis/error_union.zig b/tests/analysis/error_union.zig index 8e904133a5..20f351c861 100644 --- a/tests/analysis/error_union.zig +++ b/tests/analysis/error_union.zig @@ -30,7 +30,7 @@ const ErrorSetWithUnknown2 = undefined.Unknown || error{B}; // ^^^^^^^^^^^^^^^^^^^^ (type)(error{B, ... }) const ErrorSetWithUnknown3 = undefined.Unknown || error{A} || error{B}; -// ^^^^^^^^^^ (type)(error{A,B, ... }) +// ^^^^^^^^^^^^^^^^^^^^ (type)(error{A,B, ... }) const AllUnknown = undefined.Unknown || undefined.Unknown; // ^^^^^^^^^^ (type)(error{ ... }) From 8bfe62bf48e2fc4e63c26ab85af242c49e8639b3 Mon Sep 17 00:00:00 2001 From: iwa Date: Tue, 10 Nov 2026 04:31:47 +0900 Subject: [PATCH 6/7] fix: the code based on feedback. - And added tests. - The initial value was incorrectly set to `bool_type`; it has been corrected to `bool_false`. - This ensures the default value is `bool_false`, which aligns with the return value of `errorSetMerge` and allows the tests to pass. --- src/analyser/InternPool.zig | 12 ++++++------ tests/analysis/error_union.zig | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 9ebd7b22a1..dba3e4c5ce 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -110,7 +110,7 @@ pub const Key = union(enum) { pub const ErrorSet = struct { owner_decl: Decl.OptionalIndex, names: StringSlice, - has_unknown: Index = .bool_type, + has_unknown: Index = .bool_false, }; pub const Function = struct { @@ -3553,6 +3553,11 @@ pub fn scalarType(ip: *InternPool, ty: Index) Index { pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error!Index { const has_unknown = try ip.isUnknownDeep(a_ty) or try ip.isUnknownDeep(b_ty); + // Anything merged with anyerror is anyerror. + if (a_ty == .anyerror_type or b_ty == .anyerror_type) { + return .anyerror_type; + } + if (has_unknown) { var set: std.array_hash_map.Auto(String, void) = .empty; defer set.deinit(ip.gpa); @@ -3585,11 +3590,6 @@ pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error! assert(ip.zigTypeTag(a_ty) == .error_set); assert(ip.zigTypeTag(b_ty) == .error_set); - // Anything merged with anyerror is anyerror. - if (a_ty == .anyerror_type or b_ty == .anyerror_type) { - return .anyerror_type; - } - if (a_ty == b_ty) return a_ty; const a_names = try ip.indexToKey(a_ty).error_set_type.names.dupe(ip.gpa, ip); diff --git a/tests/analysis/error_union.zig b/tests/analysis/error_union.zig index 20f351c861..af2580eba5 100644 --- a/tests/analysis/error_union.zig +++ b/tests/analysis/error_union.zig @@ -23,14 +23,23 @@ const ErrorUnionUnknownPayload = Error!Unknown; const ErrorSet = error{A} || error{B}; // ^^^^^^^^ (type)(error{A,B}) -const ErrorSetWithUnknown = error{A} || undefined.Unknown; +const ErrorSetWithUnknown = error{A} || Unknown; // ^^^^^^^^^^^^^^^^^^^ (type)(error{A, ... }) -const ErrorSetWithUnknown2 = undefined.Unknown || error{B}; +const ErrorSetWithUnknown2 = Unknown || error{B}; // ^^^^^^^^^^^^^^^^^^^^ (type)(error{B, ... }) -const ErrorSetWithUnknown3 = undefined.Unknown || error{A} || error{B}; +const ErrorSetWithUnknown3 = Unknown || error{A} || error{B}; // ^^^^^^^^^^^^^^^^^^^^ (type)(error{A,B, ... }) -const AllUnknown = undefined.Unknown || undefined.Unknown; +const AllUnknown = Unknown || Unknown; // ^^^^^^^^^^ (type)(error{ ... }) + +const AnyerrorUnknown = anyerror || Unknown; +// ^^^^^^^^^^^^^^^ (type)(anyerror) + +const UnknownAnyerror = Unknown || anyerror; +// ^^^^^^^^^^^^^^^ (type)(anyerror) + +const AnyerrorPartiallyUnknown = anyerror || error{A} || Unknown; +// ^^^^^^^^^^^^^^^^^^^^^^^^ (type)(anyerror) From 2cdb34e2803b3e8c7d2aece8f7a25bcadd973231 Mon Sep 17 00:00:00 2001 From: iwami Date: Sat, 14 Nov 2026 20:33:31 +0900 Subject: [PATCH 7/7] fix: Updated the implementation based on the proposal: - Modified `ErrorSet` to define `flags` and reference `has_unknown` through it. - Removed the implementation previously added to `isUnknownDeepInternal`. - Updated the logic to determine `has_unknown` via `ip.isUnknown`. - Adjusted the code to reflect the renaming of `UnknownType` in the tests. --- src/analyser/InternPool.zig | 91 ++++++++++++++++++++++------------ src/analyser/degibberish.zig | 2 + src/analysis.zig | 4 +- tests/analysis/error_union.zig | 14 +++--- 4 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 564a2bedc5..e5a1ec1978 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -110,7 +110,9 @@ pub const Key = union(enum) { pub const ErrorSet = struct { owner_decl: Decl.OptionalIndex, names: StringSlice, - has_unknown: Index = .bool_false, + flags: Flags, + + pub const Flags = bitpack struct(u32) { has_unknown: bool, _: u31 = 0 }; }; pub const Function = struct { @@ -435,7 +437,7 @@ pub const Key = union(enum) { const b_info = b.error_set_type; if (a_info.owner_decl != b_info.owner_decl) return false; - if (a_info.has_unknown != b_info.has_unknown) return false; + if (a_info.flags.has_unknown != b_info.flags.has_unknown) return false; if (a_info.names.len != b_info.names.len) return false; @@ -1566,6 +1568,7 @@ fn addExtra(ip: *InternPool, comptime T: type, extra: T) Allocator.Error!u32 { Key.Pointer.Flags, Key.Pointer.PackedOffset, Key.Function.Flags, + Key.ErrorSet.Flags, => ip.extra.appendAssumeCapacity(@bitCast(item)), u64, @@ -1607,6 +1610,7 @@ fn extraData(ip: *const InternPool, comptime T: type, index: u32) T { Key.Pointer.Flags, Key.Pointer.PackedOffset, Key.Function.Flags, + Key.ErrorSet.Flags, => @bitCast(item), u64, @@ -3223,12 +3227,7 @@ fn isUnknownDeepInternal( }, .optional_type => |optional_info| try ip.isUnknownDeepInternal(optional_info.payload_type, set), .error_union_type => |error_union_info| try ip.isUnknownDeepInternal(error_union_info.payload_type, set), - .error_set_type => |error_set_info| { - if (error_set_info.has_unknown == .bool_true) { - return true; - } - return false; - }, + .error_set_type => return false, .enum_type => |enum_index| { const enum_info = ip.getEnum(enum_index); for (enum_info.values.keys()) |val| { @@ -3551,7 +3550,7 @@ pub fn scalarType(ip: *InternPool, ty: Index) Index { } pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error!Index { - const has_unknown = try ip.isUnknownDeep(a_ty) or try ip.isUnknownDeep(b_ty); + const has_unknown = ip.isUnknown(a_ty) or ip.isUnknown(b_ty); // Anything merged with anyerror is anyerror. if (a_ty == .anyerror_type or b_ty == .anyerror_type) { @@ -3559,33 +3558,47 @@ pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error! } if (has_unknown) { - var set: std.array_hash_map.Auto(String, void) = .empty; - defer set.deinit(ip.gpa); - - if (!ip.isUnknown(a_ty)) { - assert(ip.zigTypeTag(a_ty) == .error_set); - const a_names = try ip.indexToKey(a_ty).error_set_type.names.dupe(ip.gpa, ip); - defer ip.gpa.free(a_names); - try set.ensureTotalCapacity(ip.gpa, a_names.len); - for (a_names) |name| set.putAssumeCapacity(name, {}); + if (ip.isUnknown(a_ty) and ip.isUnknown(b_ty)) { + return try ip.get(.{ + .error_set_type = .{ + .owner_decl = .none, + .names = .empty, + .flags = .{ + .has_unknown = true, + }, + }, + }); } - if (!ip.isUnknown(b_ty)) { + + if (ip.isUnknown(a_ty)) { assert(ip.zigTypeTag(b_ty) == .error_set); - const b_names = try ip.indexToKey(b_ty).error_set_type.names.dupe(ip.gpa, ip); - defer ip.gpa.free(b_names); - try set.ensureTotalCapacity(ip.gpa, b_names.len); - for (b_names) |name| set.putAssumeCapacity(name, {}); + const names = ip.indexToKey(b_ty).error_set_type.names; + return try ip.get(.{ + .error_set_type = .{ + .owner_decl = .none, + .names = names, + .flags = .{ + .has_unknown = true, + }, + }, + }); } - ip.string_pool.sortStrings(ip.io, set.keys()); + if (ip.isUnknown(b_ty)) { + assert(ip.zigTypeTag(a_ty) == .error_set); + const names = ip.indexToKey(a_ty).error_set_type.names; + return try ip.get(.{ + .error_set_type = .{ + .owner_decl = .none, + .names = names, + .flags = .{ + .has_unknown = true, + }, + }, + }); + } - return try ip.get(.{ - .error_set_type = .{ - .owner_decl = .none, - .names = try ip.getStringSlice(set.keys()), - .has_unknown = .bool_true, - }, - }); + unreachable; } else { assert(ip.zigTypeTag(a_ty) == .error_set); assert(ip.zigTypeTag(b_ty) == .error_set); @@ -3608,8 +3621,9 @@ pub fn errorSetMerge(ip: *InternPool, a_ty: Index, b_ty: Index) Allocator.Error! ip.string_pool.sortStrings(ip.io, set.keys()); + const a_or_b_has_unknown = ip.indexToKey(a_ty).error_set_type.flags.has_unknown or ip.indexToKey(b_ty).error_set_type.flags.has_unknown; return try ip.get(.{ - .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(set.keys()), .has_unknown = .bool_false }, + .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(set.keys()), .flags = .{ .has_unknown = a_or_b_has_unknown } }, }); } } @@ -4077,7 +4091,7 @@ fn printInternal(ip: *InternPool, ty: Index, writer: *std.Io.Writer, options: Fo const name = error_set_info.names.at(@intCast(i), ip); try writer.print("{f}", .{ip.fmtId(name)}); } - if (error_set_info.has_unknown == .bool_true) { + if (error_set_info.flags.has_unknown) { if (error_set_info.names.len > 0) { try writer.writeByte(','); } @@ -4625,16 +4639,19 @@ test "error set type" { const empty_error_set = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = StringSlice.empty, + .flags = .{ .has_unknown = false }, } }); const foo_bar_baz_set = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(&.{ foo_name, bar_name, baz_name }), + .flags = .{ .has_unknown = false }, } }); const foo_bar_set = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(&.{ foo_name, bar_name }), + .flags = .{ .has_unknown = false }, } }); try expect(empty_error_set != foo_bar_baz_set); @@ -4655,6 +4672,7 @@ test "error union type" { const empty_error_set = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = StringSlice.empty, + .flags = .{ .has_unknown = false }, } }); const bool_type = try ip.get(.{ .simple_type = .bool }); @@ -5021,18 +5039,22 @@ test "coerceInMemoryAllowed error set" { const foo_bar_baz_set = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(&.{ baz_name, bar_name, foo_name }), + .flags = .{ .has_unknown = false }, } }); const foo_bar_set = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(&.{ foo_name, bar_name }), + .flags = .{ .has_unknown = false }, } }); const foo_set = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(&.{foo_name}), + .flags = .{ .has_unknown = false }, } }); const empty_set = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = StringSlice.empty, + .flags = .{ .has_unknown = false }, } }); try expect(try ip.coerceInMemoryAllowed(arena, .anyerror_type, foo_bar_baz_set, true, builtin.target) == .ok); @@ -5319,16 +5341,19 @@ test "resolvePeerTypes error sets" { const @"error{foo}" = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(&.{foo_name}), + .flags = .{ .has_unknown = false }, } }); const @"error{bar}" = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(&.{bar_name}), + .flags = .{ .has_unknown = false }, } }); const @"error{bar,foo}" = try ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(&.{ bar_name, foo_name }), + .flags = .{ .has_unknown = false }, } }); const @"error{bar}!i32" = try ip.get(.{ .error_union_type = .{ diff --git a/src/analyser/degibberish.zig b/src/analyser/degibberish.zig index 3daf9a122c..e653eedd4d 100644 --- a/src/analyser/degibberish.zig +++ b/src/analyser/degibberish.zig @@ -209,6 +209,7 @@ test "degibberish - error union types" { const @"error{foo,bar,baz}" = try ip.get(.{ .error_set_type = .{ .names = try ip.getStringSlice(&.{ foo_string, bar_string, baz_string }), .owner_decl = .none, + .flags = .{ .has_unknown = false }, } }); const @"error{foo,bar,baz}!u32" = try ip.get(.{ .error_union_type = .{ @@ -232,6 +233,7 @@ test "degibberish - error set types" { const @"error{foo,bar,baz}" = try ip.get(.{ .error_set_type = .{ .names = try ip.getStringSlice(&.{ foo_string, bar_string, baz_string }), .owner_decl = .none, + .flags = .{ .has_unknown = false }, } }); try std.testing.expectFmt("error set of (foo,bar,baz)", "{f}", .{fmtDegibberish(&ip, @"error{foo,bar,baz}")}); diff --git a/src/analysis.zig b/src/analysis.zig index 7fedd9ba5e..9f3787655e 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2242,7 +2242,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error try strings.put(analyser.gpa, index, {}); } const names = try analyser.ip.getStringSlice(strings.keys()); - const ip_index = try analyser.ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = names, .has_unknown = .bool_false } }); + const ip_index = try analyser.ip.get(.{ .error_set_type = .{ .owner_decl = .none, .names = names, .flags = .{ .has_unknown = false } } }); return Type.fromIP(analyser, .type_type, ip_index); }, @@ -2805,7 +2805,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error .error_set_type = .{ .owner_decl = .none, .names = try analyser.ip.getStringSlice(&.{name_index}), - .has_unknown = .bool_false, + .flags = .{ .has_unknown = false }, }, }); const error_value = try analyser.ip.get(.{ .error_value = .{ diff --git a/tests/analysis/error_union.zig b/tests/analysis/error_union.zig index 16a0703e8a..6197ad7deb 100644 --- a/tests/analysis/error_union.zig +++ b/tests/analysis/error_union.zig @@ -23,23 +23,23 @@ const ErrorUnionUnknownPayload = Error!UnknownType; const ErrorSet = error{A} || error{B}; // ^^^^^^^^ (type)(error{A,B}) -const ErrorSetWithUnknown = error{A} || Unknown; +const ErrorSetWithUnknown = error{A} || UnknownType; // ^^^^^^^^^^^^^^^^^^^ (type)(error{A, ... }) -const ErrorSetWithUnknown2 = Unknown || error{B}; +const ErrorSetWithUnknown2 = UnknownType || error{B}; // ^^^^^^^^^^^^^^^^^^^^ (type)(error{B, ... }) -const ErrorSetWithUnknown3 = Unknown || error{A} || error{B}; +const ErrorSetWithUnknown3 = UnknownType || error{A} || error{B}; // ^^^^^^^^^^^^^^^^^^^^ (type)(error{A,B, ... }) -const AllUnknown = Unknown || Unknown; +const AllUnknown = UnknownType || UnknownType; // ^^^^^^^^^^ (type)(error{ ... }) -const AnyerrorUnknown = anyerror || Unknown; +const AnyerrorUnknown = anyerror || UnknownType; // ^^^^^^^^^^^^^^^ (type)(anyerror) -const UnknownAnyerror = Unknown || anyerror; +const UnknownAnyerror = UnknownType || anyerror; // ^^^^^^^^^^^^^^^ (type)(anyerror) -const AnyerrorPartiallyUnknown = anyerror || error{A} || Unknown; +const AnyerrorPartiallyUnknown = anyerror || error{A} || UnknownType; // ^^^^^^^^^^^^^^^^^^^^^^^^ (type)(anyerror)