From 8a7b04f5dd034e81c564372a76373f2ca3d062b2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 13 Mar 2024 22:26:11 +0000 Subject: [PATCH 1/3] Fix EnumSet.init with non-exhaustive enum --- lib/std/enums.zig | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/lib/std/enums.zig b/lib/std/enums.zig index 40a2566cbecd..a2d9b7a300f0 100644 --- a/lib/std/enums.zig +++ b/lib/std/enums.zig @@ -237,7 +237,7 @@ test nameCast { } /// A set of enum elements, backed by a bitfield. If the enum -/// is not dense, a mapping will be constructed from enum values +/// is exhaustive but not dense, a mapping will be constructed from enum values /// to dense indices. This type does no dynamic allocation and /// can be copied by value. pub fn EnumSet(comptime E: type) type { @@ -259,11 +259,10 @@ pub fn EnumSet(comptime E: type) type { /// Initializes the set using a struct of bools pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self { var result: Self = .{}; - inline for (0..Self.len) |i| { - const key = comptime Indexer.keyForIndex(i); - const tag = @tagName(key); - if (@field(init_values, tag)) { - result.bits.set(i); + inline for (std.meta.fields(E)) |field| { + const key = @field(E, field.name); + if (@field(init_values, field.name)) { + result.bits.set(Indexer.indexOf(key)); } } return result; @@ -412,7 +411,7 @@ pub fn EnumSet(comptime E: type) type { } /// A map keyed by an enum, backed by a bitfield and a dense array. -/// If the enum is not dense, a mapping will be constructed from +/// If the enum is exhaustive but not dense, a mapping will be constructed from /// enum values to dense indices. This type does no dynamic /// allocation and can be copied by value. pub fn EnumMap(comptime E: type, comptime V: type) type { @@ -439,14 +438,15 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { /// Initializes the map using a sparse struct of optionals pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self { var result: Self = .{}; - inline for (0..Self.len) |i| { - const key = comptime Indexer.keyForIndex(i); - const tag = @tagName(key); - if (@field(init_values, tag)) |*v| { + inline for (std.meta.fields(E)) |field| { + const key = @field(E, field.name); + if (@field(init_values, field.name)) |*v| { + const i = Indexer.indexOf(key); result.bits.set(i); result.values[i] = v.*; } } + return result; } /// Initializes a full mapping with all keys set to value. @@ -1213,6 +1213,23 @@ test "EnumSet const iterator" { try testing.expect(result.eql(diag_move)); } +test "EnumSet non-exhaustive" { + const BitIndices = enum(u4) { + a = 0, + b = 1, + c = 4, + _, + }; + const BitField = EnumSet(BitIndices); + + var flags = BitField.init(.{ .a = true, .b = true }); + flags.insert(.c); + flags.remove(.a); + try testing.expect(!flags.contains(.a)); + try testing.expect(flags.contains(.b)); + try testing.expect(flags.contains(.c)); +} + pub fn EnumIndexer(comptime E: type) type { if (!@typeInfo(E).Enum.is_exhaustive) { const BackingInt = @typeInfo(E).Enum.tag_type; From a0d9b3a78e1c9116cc651327228546a83ef21a64 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 14 Mar 2024 19:04:41 +0000 Subject: [PATCH 2/3] Ensure the index is comptime-known --- lib/std/enums.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/enums.zig b/lib/std/enums.zig index a2d9b7a300f0..9ef8ec964eda 100644 --- a/lib/std/enums.zig +++ b/lib/std/enums.zig @@ -262,7 +262,8 @@ pub fn EnumSet(comptime E: type) type { inline for (std.meta.fields(E)) |field| { const key = @field(E, field.name); if (@field(init_values, field.name)) { - result.bits.set(Indexer.indexOf(key)); + const i = comptime Indexer.indexOf(key); + result.bits.set(i); } } return result; @@ -441,7 +442,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { inline for (std.meta.fields(E)) |field| { const key = @field(E, field.name); if (@field(init_values, field.name)) |*v| { - const i = Indexer.indexOf(key); + const i = comptime Indexer.indexOf(key); result.bits.set(i); result.values[i] = v.*; } From 3b77fa4fd9ee3907c91db575fa906e5458c2406e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 24 Mar 2024 15:40:11 +0000 Subject: [PATCH 3/3] Only use indexOf for (cheap) non-exhaustive case --- lib/std/enums.zig | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/lib/std/enums.zig b/lib/std/enums.zig index f8fa18acea4f..79c69c851048 100644 --- a/lib/std/enums.zig +++ b/lib/std/enums.zig @@ -263,11 +263,21 @@ pub fn EnumSet(comptime E: type) type { pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self { @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); var result: Self = .{}; - inline for (std.meta.fields(E)) |field| { - const key = @field(E, field.name); - if (@field(init_values, field.name)) { - const i = comptime Indexer.indexOf(key); - result.bits.set(i); + if (@typeInfo(E).Enum.is_exhaustive) { + inline for (0..Self.len) |i| { + const key = comptime Indexer.keyForIndex(i); + const tag = @tagName(key); + if (@field(init_values, tag)) { + result.bits.set(i); + } + } + } else { + inline for (std.meta.fields(E)) |field| { + const key = @field(E, field.name); + if (@field(init_values, field.name)) { + const i = comptime Indexer.indexOf(key); + result.bits.set(i); + } } } return result; @@ -444,12 +454,23 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self { @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); var result: Self = .{}; - inline for (std.meta.fields(E)) |field| { - const key = @field(E, field.name); - if (@field(init_values, field.name)) |*v| { - const i = comptime Indexer.indexOf(key); - result.bits.set(i); - result.values[i] = v.*; + if (@typeInfo(E).Enum.is_exhaustive) { + inline for (0..Self.len) |i| { + const key = comptime Indexer.keyForIndex(i); + const tag = @tagName(key); + if (@field(init_values, tag)) |*v| { + result.bits.set(i); + result.values[i] = v.*; + } + } + } else { + inline for (std.meta.fields(E)) |field| { + const key = @field(E, field.name); + if (@field(init_values, field.name)) |*v| { + const i = comptime Indexer.indexOf(key); + result.bits.set(i); + result.values[i] = v.*; + } } } return result;