authorgravatar for si@sjbrown.co.ukSimon Brown <si@sjbrown.co.uk> 2024-03-24 22:29:27+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-03-24 22:29:27+00:00
log5c628312b16ce972cc3108ed44eed47960e17af7
tree80bfded3ff2d6f94b6d07f2ac7e13ad7bd4825b5
parentaf0668d6c2c7696e695cf0553c1ae2d593e9effe
signaturebadge-check Signed by PGP key B5690EEEBB952194

std.enums: fix EnumSet.init and EnumMap.init for non-exhaustive enums


1 files changed, 51 insertions(+), 13 deletions(-)

lib/std/enums.zig+51-13
...@@ -240,7 +240,7 @@ test nameCast {...@@ -240,7 +240,7 @@ test nameCast {
240}240}
241241
242/// A set of enum elements, backed by a bitfield. If the enum242/// A set of enum elements, backed by a bitfield. If the enum
243/// is not dense, a mapping will be constructed from enum values243/// is exhaustive but not dense, a mapping will be constructed from enum values
244/// to dense indices. This type does no dynamic allocation and244/// to dense indices. This type does no dynamic allocation and
245/// can be copied by value.245/// can be copied by value.
246pub fn EnumSet(comptime E: type) type {246pub fn EnumSet(comptime E: type) type {
...@@ -263,11 +263,21 @@ pub fn EnumSet(comptime E: type) type {...@@ -263,11 +263,21 @@ pub fn EnumSet(comptime E: type) type {
263 pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {263 pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
264 @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len);264 @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len);
265 var result: Self = .{};265 var result: Self = .{};
266 inline for (0..Self.len) |i| {266 if (@typeInfo(E).Enum.is_exhaustive) {
267 const key = comptime Indexer.keyForIndex(i);267 inline for (0..Self.len) |i| {
268 const tag = @tagName(key);268 const key = comptime Indexer.keyForIndex(i);
269 if (@field(init_values, tag)) {269 const tag = @tagName(key);
270 result.bits.set(i);270 if (@field(init_values, tag)) {
271 result.bits.set(i);
272 }
273 }
274 } else {
275 inline for (std.meta.fields(E)) |field| {
276 const key = @field(E, field.name);
277 if (@field(init_values, field.name)) {
278 const i = comptime Indexer.indexOf(key);
279 result.bits.set(i);
280 }
271 }281 }
272 }282 }
273 return result;283 return result;
...@@ -416,7 +426,7 @@ pub fn EnumSet(comptime E: type) type {...@@ -416,7 +426,7 @@ pub fn EnumSet(comptime E: type) type {
416}426}
417427
418/// A map keyed by an enum, backed by a bitfield and a dense array.428/// A map keyed by an enum, backed by a bitfield and a dense array.
419/// If the enum is not dense, a mapping will be constructed from429/// If the enum is exhaustive but not dense, a mapping will be constructed from
420/// enum values to dense indices. This type does no dynamic430/// enum values to dense indices. This type does no dynamic
421/// allocation and can be copied by value.431/// allocation and can be copied by value.
422pub fn EnumMap(comptime E: type, comptime V: type) type {432pub fn EnumMap(comptime E: type, comptime V: type) type {
...@@ -444,12 +454,23 @@ pub fn EnumMap(comptime E: type, comptime V: type) type {...@@ -444,12 +454,23 @@ pub fn EnumMap(comptime E: type, comptime V: type) type {
444 pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self {454 pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self {
445 @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len);455 @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len);
446 var result: Self = .{};456 var result: Self = .{};
447 inline for (0..Self.len) |i| {457 if (@typeInfo(E).Enum.is_exhaustive) {
448 const key = comptime Indexer.keyForIndex(i);458 inline for (0..Self.len) |i| {
449 const tag = @tagName(key);459 const key = comptime Indexer.keyForIndex(i);
450 if (@field(init_values, tag)) |*v| {460 const tag = @tagName(key);
451 result.bits.set(i);461 if (@field(init_values, tag)) |*v| {
452 result.values[i] = v.*;462 result.bits.set(i);
463 result.values[i] = v.*;
464 }
465 }
466 } else {
467 inline for (std.meta.fields(E)) |field| {
468 const key = @field(E, field.name);
469 if (@field(init_values, field.name)) |*v| {
470 const i = comptime Indexer.indexOf(key);
471 result.bits.set(i);
472 result.values[i] = v.*;
473 }
453 }474 }
454 }475 }
455 return result;476 return result;
...@@ -1222,6 +1243,23 @@ test "EnumSet const iterator" {...@@ -1222,6 +1243,23 @@ test "EnumSet const iterator" {
1222 try testing.expect(result.eql(diag_move));1243 try testing.expect(result.eql(diag_move));
1223}1244}
12241245
1246test "EnumSet non-exhaustive" {
1247 const BitIndices = enum(u4) {
1248 a = 0,
1249 b = 1,
1250 c = 4,
1251 _,
1252 };
1253 const BitField = EnumSet(BitIndices);
1254
1255 var flags = BitField.init(.{ .a = true, .b = true });
1256 flags.insert(.c);
1257 flags.remove(.a);
1258 try testing.expect(!flags.contains(.a));
1259 try testing.expect(flags.contains(.b));
1260 try testing.expect(flags.contains(.c));
1261}
1262
1225pub fn EnumIndexer(comptime E: type) type {1263pub fn EnumIndexer(comptime E: type) type {
1226 // Assumes that the enum fields are sorted in ascending order (optimistic).1264 // Assumes that the enum fields are sorted in ascending order (optimistic).
1227 // Unsorted enums may require the user to manually increase the quota.1265 // Unsorted enums may require the user to manually increase the quota.