| ... | ... | @@ -5,14 +5,18 @@ const assert = std.debug.assert; |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | const EnumField = std.builtin.Type.EnumField; |
| 7 | 7 | |
| 8 | /// Increment this value when adding APIs that add single backwards branches. |
| 9 | const eval_branch_quota_cushion = 5; |
| 10 | |
| 8 | 11 | /// Returns a struct with a field matching each unique named enum element. |
| 9 | 12 | /// If the enum is extern and has multiple names for the same value, only |
| 10 | 13 | /// the first name is used. Each field is of type Data and has the provided |
| 11 | 14 | /// default, which may be undefined. |
| 12 | 15 | pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type { |
| 16 | @setEvalBranchQuota(@typeInfo(E).Enum.fields.len + eval_branch_quota_cushion); |
| 13 | 17 | const StructField = std.builtin.Type.StructField; |
| 14 | 18 | var fields: []const StructField = &[_]StructField{}; |
| 15 | | for (std.meta.fields(E)) |field| { |
| 19 | for (@typeInfo(E).Enum.fields) |field| { |
| 16 | 20 | fields = fields ++ &[_]StructField{.{ |
| 17 | 21 | .name = field.name ++ "", |
| 18 | 22 | .type = Data, |
| ... | ... | @@ -76,7 +80,7 @@ test tagName { |
| 76 | 80 | pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int { |
| 77 | 81 | var max_value: comptime_int = -1; |
| 78 | 82 | const max_usize: comptime_int = ~@as(usize, 0); |
| 79 | | const fields = std.meta.fields(E); |
| 83 | const fields = @typeInfo(E).Enum.fields; |
| 80 | 84 | for (fields) |f| { |
| 81 | 85 | if (f.value < 0) { |
| 82 | 86 | @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value."); |
| ... | ... | @@ -258,6 +262,7 @@ pub fn EnumSet(comptime E: type) type { |
| 258 | 262 | |
| 259 | 263 | /// Initializes the set using a struct of bools |
| 260 | 264 | pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self { |
| 265 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 261 | 266 | var result: Self = .{}; |
| 262 | 267 | inline for (0..Self.len) |i| { |
| 263 | 268 | const key = comptime Indexer.keyForIndex(i); |
| ... | ... | @@ -438,6 +443,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { |
| 438 | 443 | |
| 439 | 444 | /// Initializes the map using a sparse struct of optionals |
| 440 | 445 | pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self { |
| 446 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 441 | 447 | var result: Self = .{}; |
| 442 | 448 | inline for (0..Self.len) |i| { |
| 443 | 449 | const key = comptime Indexer.keyForIndex(i); |
| ... | ... | @@ -447,6 +453,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { |
| 447 | 453 | result.values[i] = v.*; |
| 448 | 454 | } |
| 449 | 455 | } |
| 456 | return result; |
| 450 | 457 | } |
| 451 | 458 | |
| 452 | 459 | /// Initializes a full mapping with all keys set to value. |
| ... | ... | @@ -469,6 +476,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { |
| 469 | 476 | /// Initializes a full mapping with a provided default. |
| 470 | 477 | /// Consider using EnumArray instead if the map will remain full. |
| 471 | 478 | pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self { |
| 479 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 472 | 480 | var result: Self = .{ |
| 473 | 481 | .bits = Self.BitSet.initFull(), |
| 474 | 482 | .values = undefined, |
| ... | ... | @@ -641,6 +649,7 @@ pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type { |
| 641 | 649 | |
| 642 | 650 | /// Initializes the multiset using a struct of counts. |
| 643 | 651 | pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self { |
| 652 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 644 | 653 | var self = initWithCount(0); |
| 645 | 654 | inline for (@typeInfo(E).Enum.fields) |field| { |
| 646 | 655 | const c = @field(init_counts, field.name); |
| ... | ... | @@ -1044,6 +1053,7 @@ pub fn EnumArray(comptime E: type, comptime V: type) type { |
| 1044 | 1053 | |
| 1045 | 1054 | /// Initializes values in the enum array, with the specified default. |
| 1046 | 1055 | pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self { |
| 1056 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 1047 | 1057 | var result: Self = .{ .values = undefined }; |
| 1048 | 1058 | inline for (0..Self.len) |i| { |
| 1049 | 1059 | const key = comptime Indexer.keyForIndex(i); |
| ... | ... | @@ -1214,6 +1224,10 @@ test "EnumSet const iterator" { |
| 1214 | 1224 | } |
| 1215 | 1225 | |
| 1216 | 1226 | pub fn EnumIndexer(comptime E: type) type { |
| 1227 | // Assumes that the enum fields are sorted in ascending order (optimistic). |
| 1228 | // Unsorted enums may require the user to manually increase the quota. |
| 1229 | @setEvalBranchQuota(3 * @typeInfo(E).Enum.fields.len + eval_branch_quota_cushion); |
| 1230 | |
| 1217 | 1231 | if (!@typeInfo(E).Enum.is_exhaustive) { |
| 1218 | 1232 | const BackingInt = @typeInfo(E).Enum.tag_type; |
| 1219 | 1233 | if (@bitSizeOf(BackingInt) > @bitSizeOf(usize)) |
| ... | ... | @@ -1247,7 +1261,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1247 | 1261 | }; |
| 1248 | 1262 | } |
| 1249 | 1263 | |
| 1250 | | const const_fields = std.meta.fields(E); |
| 1264 | const const_fields = @typeInfo(E).Enum.fields; |
| 1251 | 1265 | var fields = const_fields[0..const_fields.len].*; |
| 1252 | 1266 | const fields_len = fields.len; |
| 1253 | 1267 | |
| ... | ... | @@ -1294,7 +1308,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1294 | 1308 | // gives up some safety to avoid artificially limiting |
| 1295 | 1309 | // the range of signed enum values to max_isize. |
| 1296 | 1310 | const enum_value = if (min < 0) @as(isize, @bitCast(i)) +% min else i + min; |
| 1297 | | return @as(E, @enumFromInt(@as(std.meta.Tag(E), @intCast(enum_value)))); |
| 1311 | return @as(E, @enumFromInt(@as(@typeInfo(E).Enum.tag_type, @intCast(enum_value)))); |
| 1298 | 1312 | } |
| 1299 | 1313 | }; |
| 1300 | 1314 | } |
| ... | ... | @@ -1440,3 +1454,45 @@ test values { |
| 1440 | 1454 | }; |
| 1441 | 1455 | try testing.expectEqualSlices(E, &.{ .X, .Y, .Z }, values(E)); |
| 1442 | 1456 | } |
| 1457 | |
| 1458 | test "big enums should not hit the eval branch quota" { |
| 1459 | const big = struct { |
| 1460 | const Big = @Type(@as(std.builtin.Type, .{ |
| 1461 | .Enum = .{ |
| 1462 | .tag_type = u16, |
| 1463 | .fields = make_fields: { |
| 1464 | var fields: []const std.builtin.Type.EnumField = &.{}; |
| 1465 | for (0..1999) |i| { |
| 1466 | fields = fields ++ &[_]std.builtin.Type.EnumField{.{ |
| 1467 | .name = std.fmt.comptimePrint("field_{d}", .{i}), |
| 1468 | .value = i, |
| 1469 | }}; |
| 1470 | } |
| 1471 | fields = fields ++ &[_]std.builtin.Type.EnumField{.{ |
| 1472 | .name = "field_9999", |
| 1473 | .value = 9999, |
| 1474 | }}; |
| 1475 | break :make_fields fields; |
| 1476 | }, |
| 1477 | .decls = &.{}, |
| 1478 | .is_exhaustive = true, |
| 1479 | }, |
| 1480 | })); |
| 1481 | }; |
| 1482 | |
| 1483 | var set = EnumSet(big.Big).init(.{}); |
| 1484 | _ = &set; |
| 1485 | |
| 1486 | var map = EnumMap(big.Big, u8).init(undefined); |
| 1487 | map = EnumMap(big.Big, u8).initFullWith(undefined); |
| 1488 | map = EnumMap(big.Big, u8).initFullWithDefault(123, .{}); |
| 1489 | |
| 1490 | var multiset = EnumMultiset(big.Big).init(.{}); |
| 1491 | _ = &multiset; |
| 1492 | |
| 1493 | var bounded_multiset = BoundedEnumMultiset(big.Big, u8).init(.{}); |
| 1494 | _ = &bounded_multiset; |
| 1495 | |
| 1496 | var array = EnumArray(big.Big, u8).init(undefined); |
| 1497 | array = EnumArray(big.Big, u8).initDefault(123, .{}); |
| 1498 | } |