| ... | @@ -5,14 +5,18 @@ const assert = std.debug.assert; | ... | @@ -5,14 +5,18 @@ const assert = std.debug.assert; |
| 5 | const testing = std.testing; | 5 | const testing = std.testing; |
| 6 | const EnumField = std.builtin.Type.EnumField; | 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 | /// Returns a struct with a field matching each unique named enum element. | 11 | /// Returns a struct with a field matching each unique named enum element. |
| 9 | /// If the enum is extern and has multiple names for the same value, only | 12 | /// If the enum is extern and has multiple names for the same value, only |
| 10 | /// the first name is used. Each field is of type Data and has the provided | 13 | /// the first name is used. Each field is of type Data and has the provided |
| 11 | /// default, which may be undefined. | 14 | /// default, which may be undefined. |
| 12 | pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type { | 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 | const StructField = std.builtin.Type.StructField; | 17 | const StructField = std.builtin.Type.StructField; |
| 14 | var fields: []const StructField = &[_]StructField{}; | 18 | var fields: []const StructField = &[_]StructField{}; |
| 15 | for (std.meta.fields(E)) |field| { | 19 | for (@typeInfo(E).Enum.fields) |field| { |
| 16 | fields = fields ++ &[_]StructField{.{ | 20 | fields = fields ++ &[_]StructField{.{ |
| 17 | .name = field.name ++ "", | 21 | .name = field.name ++ "", |
| 18 | .type = Data, | 22 | .type = Data, |
| ... | @@ -76,7 +80,7 @@ test tagName { | ... | @@ -76,7 +80,7 @@ test tagName { |
| 76 | pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int { | 80 | pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int { |
| 77 | var max_value: comptime_int = -1; | 81 | var max_value: comptime_int = -1; |
| 78 | const max_usize: comptime_int = ~@as(usize, 0); | 82 | const max_usize: comptime_int = ~@as(usize, 0); |
| 79 | const fields = std.meta.fields(E); | 83 | const fields = @typeInfo(E).Enum.fields; |
| 80 | for (fields) |f| { | 84 | for (fields) |f| { |
| 81 | if (f.value < 0) { | 85 | if (f.value < 0) { |
| 82 | @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value."); | 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,6 +262,7 @@ pub fn EnumSet(comptime E: type) type { |
| 258 | | 262 | |
| 259 | /// Initializes the set using a struct of bools | 263 | /// Initializes the set using a struct of bools |
| 260 | pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self { | 264 | pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self { |
| | 265 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 261 | var result: Self = .{}; | 266 | var result: Self = .{}; |
| 262 | inline for (0..Self.len) |i| { | 267 | inline for (0..Self.len) |i| { |
| 263 | const key = comptime Indexer.keyForIndex(i); | 268 | const key = comptime Indexer.keyForIndex(i); |
| ... | @@ -438,6 +443,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { | ... | @@ -438,6 +443,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { |
| 438 | | 443 | |
| 439 | /// Initializes the map using a sparse struct of optionals | 444 | /// Initializes the map using a sparse struct of optionals |
| 440 | pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self { | 445 | pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self { |
| | 446 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 441 | var result: Self = .{}; | 447 | var result: Self = .{}; |
| 442 | inline for (0..Self.len) |i| { | 448 | inline for (0..Self.len) |i| { |
| 443 | const key = comptime Indexer.keyForIndex(i); | 449 | const key = comptime Indexer.keyForIndex(i); |
| ... | @@ -447,6 +453,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { | ... | @@ -447,6 +453,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { |
| 447 | result.values[i] = v.*; | 453 | result.values[i] = v.*; |
| 448 | } | 454 | } |
| 449 | } | 455 | } |
| | 456 | return result; |
| 450 | } | 457 | } |
| 451 | | 458 | |
| 452 | /// Initializes a full mapping with all keys set to value. | 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,6 +476,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { |
| 469 | /// Initializes a full mapping with a provided default. | 476 | /// Initializes a full mapping with a provided default. |
| 470 | /// Consider using EnumArray instead if the map will remain full. | 477 | /// Consider using EnumArray instead if the map will remain full. |
| 471 | pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self { | 478 | pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self { |
| | 479 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 472 | var result: Self = .{ | 480 | var result: Self = .{ |
| 473 | .bits = Self.BitSet.initFull(), | 481 | .bits = Self.BitSet.initFull(), |
| 474 | .values = undefined, | 482 | .values = undefined, |
| ... | @@ -641,6 +649,7 @@ pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type { | ... | @@ -641,6 +649,7 @@ pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type { |
| 641 | | 649 | |
| 642 | /// Initializes the multiset using a struct of counts. | 650 | /// Initializes the multiset using a struct of counts. |
| 643 | pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self { | 651 | pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self { |
| | 652 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 644 | var self = initWithCount(0); | 653 | var self = initWithCount(0); |
| 645 | inline for (@typeInfo(E).Enum.fields) |field| { | 654 | inline for (@typeInfo(E).Enum.fields) |field| { |
| 646 | const c = @field(init_counts, field.name); | 655 | const c = @field(init_counts, field.name); |
| ... | @@ -1044,6 +1053,7 @@ pub fn EnumArray(comptime E: type, comptime V: type) type { | ... | @@ -1044,6 +1053,7 @@ pub fn EnumArray(comptime E: type, comptime V: type) type { |
| 1044 | | 1053 | |
| 1045 | /// Initializes values in the enum array, with the specified default. | 1054 | /// Initializes values in the enum array, with the specified default. |
| 1046 | pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self { | 1055 | pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self { |
| | 1056 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 1047 | var result: Self = .{ .values = undefined }; | 1057 | var result: Self = .{ .values = undefined }; |
| 1048 | inline for (0..Self.len) |i| { | 1058 | inline for (0..Self.len) |i| { |
| 1049 | const key = comptime Indexer.keyForIndex(i); | 1059 | const key = comptime Indexer.keyForIndex(i); |
| ... | @@ -1214,6 +1224,10 @@ test "EnumSet const iterator" { | ... | @@ -1214,6 +1224,10 @@ test "EnumSet const iterator" { |
| 1214 | } | 1224 | } |
| 1215 | | 1225 | |
| 1216 | pub fn EnumIndexer(comptime E: type) type { | 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 | if (!@typeInfo(E).Enum.is_exhaustive) { | 1231 | if (!@typeInfo(E).Enum.is_exhaustive) { |
| 1218 | const BackingInt = @typeInfo(E).Enum.tag_type; | 1232 | const BackingInt = @typeInfo(E).Enum.tag_type; |
| 1219 | if (@bitSizeOf(BackingInt) > @bitSizeOf(usize)) | 1233 | if (@bitSizeOf(BackingInt) > @bitSizeOf(usize)) |
| ... | @@ -1247,7 +1261,7 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -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 | var fields = const_fields[0..const_fields.len].*; | 1265 | var fields = const_fields[0..const_fields.len].*; |
| 1252 | const fields_len = fields.len; | 1266 | const fields_len = fields.len; |
| 1253 | | 1267 | |
| ... | @@ -1294,7 +1308,7 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -1294,7 +1308,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1294 | // gives up some safety to avoid artificially limiting | 1308 | // gives up some safety to avoid artificially limiting |
| 1295 | // the range of signed enum values to max_isize. | 1309 | // the range of signed enum values to max_isize. |
| 1296 | const enum_value = if (min < 0) @as(isize, @bitCast(i)) +% min else i + min; | 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,3 +1454,45 @@ test values { |
| 1440 | }; | 1454 | }; |
| 1441 | try testing.expectEqualSlices(E, &.{ .X, .Y, .Z }, values(E)); | 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 | } |