authorgravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-03-12 00:01:38+01:00
committergravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-03-12 00:01:38+01:00
log0a931393c0f1a8b4882753ad37c303268ccaa7fa
tree7c62fb12cc490d8ab135b0cf87d1d34bbd28acc7
parent4f782d1e853accbe1c4bfab2617c3813d4b1e59f

std.enums: Increase eval branch quotas


1 files changed, 60 insertions(+), 4 deletions(-)

lib/std/enums.zig+60-4
...@@ -5,14 +5,18 @@ const assert = std.debug.assert;...@@ -5,14 +5,18 @@ const assert = std.debug.assert;
5const testing = std.testing;5const testing = std.testing;
6const EnumField = std.builtin.Type.EnumField;6const EnumField = std.builtin.Type.EnumField;
77
8/// Increment this value when adding APIs that add single backwards branches.
9const 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, only12/// 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 provided13/// 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.
12pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type {15pub 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 {
76pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {80pub 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 {
258262
259 /// Initializes the set using a struct of bools263 /// 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 {
438443
439 /// Initializes the map using a sparse struct of optionals444 /// 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 }
451458
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 {
641649
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 {
10441053
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}
12151225
1216pub fn EnumIndexer(comptime E: type) type {1226pub 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 }
12491263
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;
12531267
...@@ -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 limiting1308 // 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
1458test "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}