authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-13 18:50:12-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-03-13 18:50:12-07:00
log778ab767b1e367233a1a3284e2c24d2c27b44602
tree8d12358dd49801a10448b9555197be7c6d8ff284
parent32f602ad167e90825ef1f980046a7611cfcfb5fe
parent5be7e2c217eb1d0ab660d529979c59e781586454
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19258 from castholm/enums-eval-branch-quota

std.enums: Increase eval branch quotas

3 files changed, 62 insertions(+), 10 deletions(-)

lib/std/enums.zig+23-10
...@@ -5,25 +5,28 @@ const assert = std.debug.assert;...@@ -5,25 +5,28 @@ 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 {
13 const StructField = std.builtin.Type.StructField;16 @setEvalBranchQuota(@typeInfo(E).Enum.fields.len + eval_branch_quota_cushion);
14 var fields: []const StructField = &[_]StructField{};17 var struct_fields: [@typeInfo(E).Enum.fields.len]std.builtin.Type.StructField = undefined;
15 for (std.meta.fields(E)) |field| {18 for (&struct_fields, @typeInfo(E).Enum.fields) |*struct_field, enum_field| {
16 fields = fields ++ &[_]StructField{.{19 struct_field.* = .{
17 .name = field.name ++ "",20 .name = enum_field.name ++ "",
18 .type = Data,21 .type = Data,
19 .default_value = if (field_default) |d| @as(?*const anyopaque, @ptrCast(&d)) else null,22 .default_value = if (field_default) |d| @as(?*const anyopaque, @ptrCast(&d)) else null,
20 .is_comptime = false,23 .is_comptime = false,
21 .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,24 .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,
22 }};25 };
23 }26 }
24 return @Type(.{ .Struct = .{27 return @Type(.{ .Struct = .{
25 .layout = .auto,28 .layout = .auto,
26 .fields = fields,29 .fields = &struct_fields,
27 .decls = &.{},30 .decls = &.{},
28 .is_tuple = false,31 .is_tuple = false,
29 } });32 } });
...@@ -76,7 +79,7 @@ test tagName {...@@ -76,7 +79,7 @@ test tagName {
76pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {79pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
77 var max_value: comptime_int = -1;80 var max_value: comptime_int = -1;
78 const max_usize: comptime_int = ~@as(usize, 0);81 const max_usize: comptime_int = ~@as(usize, 0);
79 const fields = std.meta.fields(E);82 const fields = @typeInfo(E).Enum.fields;
80 for (fields) |f| {83 for (fields) |f| {
81 if (f.value < 0) {84 if (f.value < 0) {
82 @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value.");85 @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value.");
...@@ -258,6 +261,7 @@ pub fn EnumSet(comptime E: type) type {...@@ -258,6 +261,7 @@ pub fn EnumSet(comptime E: type) type {
258261
259 /// Initializes the set using a struct of bools262 /// Initializes the set using a struct of bools
260 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);
261 var result: Self = .{};265 var result: Self = .{};
262 inline for (0..Self.len) |i| {266 inline for (0..Self.len) |i| {
263 const key = comptime Indexer.keyForIndex(i);267 const key = comptime Indexer.keyForIndex(i);
...@@ -438,6 +442,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type {...@@ -438,6 +442,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type {
438442
439 /// Initializes the map using a sparse struct of optionals443 /// Initializes the map using a sparse struct of optionals
440 pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self {444 pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self {
445 @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len);
441 var result: Self = .{};446 var result: Self = .{};
442 inline for (0..Self.len) |i| {447 inline for (0..Self.len) |i| {
443 const key = comptime Indexer.keyForIndex(i);448 const key = comptime Indexer.keyForIndex(i);
...@@ -447,6 +452,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type {...@@ -447,6 +452,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type {
447 result.values[i] = v.*;452 result.values[i] = v.*;
448 }453 }
449 }454 }
455 return result;
450 }456 }
451457
452 /// Initializes a full mapping with all keys set to value.458 /// Initializes a full mapping with all keys set to value.
...@@ -469,6 +475,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type {...@@ -469,6 +475,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type {
469 /// Initializes a full mapping with a provided default.475 /// Initializes a full mapping with a provided default.
470 /// Consider using EnumArray instead if the map will remain full.476 /// Consider using EnumArray instead if the map will remain full.
471 pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {477 pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {
478 @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len);
472 var result: Self = .{479 var result: Self = .{
473 .bits = Self.BitSet.initFull(),480 .bits = Self.BitSet.initFull(),
474 .values = undefined,481 .values = undefined,
...@@ -641,6 +648,7 @@ pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type {...@@ -641,6 +648,7 @@ pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type {
641648
642 /// Initializes the multiset using a struct of counts.649 /// Initializes the multiset using a struct of counts.
643 pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {650 pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {
651 @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len);
644 var self = initWithCount(0);652 var self = initWithCount(0);
645 inline for (@typeInfo(E).Enum.fields) |field| {653 inline for (@typeInfo(E).Enum.fields) |field| {
646 const c = @field(init_counts, field.name);654 const c = @field(init_counts, field.name);
...@@ -1044,6 +1052,7 @@ pub fn EnumArray(comptime E: type, comptime V: type) type {...@@ -1044,6 +1052,7 @@ pub fn EnumArray(comptime E: type, comptime V: type) type {
10441052
1045 /// Initializes values in the enum array, with the specified default.1053 /// Initializes values in the enum array, with the specified default.
1046 pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {1054 pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {
1055 @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len);
1047 var result: Self = .{ .values = undefined };1056 var result: Self = .{ .values = undefined };
1048 inline for (0..Self.len) |i| {1057 inline for (0..Self.len) |i| {
1049 const key = comptime Indexer.keyForIndex(i);1058 const key = comptime Indexer.keyForIndex(i);
...@@ -1214,6 +1223,10 @@ test "EnumSet const iterator" {...@@ -1214,6 +1223,10 @@ test "EnumSet const iterator" {
1214}1223}
12151224
1216pub fn EnumIndexer(comptime E: type) type {1225pub fn EnumIndexer(comptime E: type) type {
1226 // Assumes that the enum fields are sorted in ascending order (optimistic).
1227 // Unsorted enums may require the user to manually increase the quota.
1228 @setEvalBranchQuota(3 * @typeInfo(E).Enum.fields.len + eval_branch_quota_cushion);
1229
1217 if (!@typeInfo(E).Enum.is_exhaustive) {1230 if (!@typeInfo(E).Enum.is_exhaustive) {
1218 const BackingInt = @typeInfo(E).Enum.tag_type;1231 const BackingInt = @typeInfo(E).Enum.tag_type;
1219 if (@bitSizeOf(BackingInt) > @bitSizeOf(usize))1232 if (@bitSizeOf(BackingInt) > @bitSizeOf(usize))
...@@ -1247,7 +1260,7 @@ pub fn EnumIndexer(comptime E: type) type {...@@ -1247,7 +1260,7 @@ pub fn EnumIndexer(comptime E: type) type {
1247 };1260 };
1248 }1261 }
12491262
1250 const const_fields = std.meta.fields(E);1263 const const_fields = @typeInfo(E).Enum.fields;
1251 var fields = const_fields[0..const_fields.len].*;1264 var fields = const_fields[0..const_fields.len].*;
1252 const fields_len = fields.len;1265 const fields_len = fields.len;
12531266
...@@ -1294,7 +1307,7 @@ pub fn EnumIndexer(comptime E: type) type {...@@ -1294,7 +1307,7 @@ pub fn EnumIndexer(comptime E: type) type {
1294 // gives up some safety to avoid artificially limiting1307 // gives up some safety to avoid artificially limiting
1295 // the range of signed enum values to max_isize.1308 // the range of signed enum values to max_isize.
1296 const enum_value = if (min < 0) @as(isize, @bitCast(i)) +% min else i + min;1309 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))));1310 return @as(E, @enumFromInt(@as(@typeInfo(E).Enum.tag_type, @intCast(enum_value))));
1298 }1311 }
1299 };1312 };
1300 }1313 }
test/standalone.zig+1
...@@ -49,6 +49,7 @@ pub const simple_cases = [_]SimpleCase{...@@ -49,6 +49,7 @@ pub const simple_cases = [_]SimpleCase{
49 .{ .src_path = "test/standalone/main_return_error/error_u8_non_zero.zig" },49 .{ .src_path = "test/standalone/main_return_error/error_u8_non_zero.zig" },
50 .{ .src_path = "test/standalone/noreturn_call/inline.zig" },50 .{ .src_path = "test/standalone/noreturn_call/inline.zig" },
51 .{ .src_path = "test/standalone/noreturn_call/as_arg.zig" },51 .{ .src_path = "test/standalone/noreturn_call/as_arg.zig" },
52 .{ .src_path = "test/standalone/std_enums_big_enums.zig" },
5253
53 .{54 .{
54 .src_path = "test/standalone/issue_9402/main.zig",55 .src_path = "test/standalone/issue_9402/main.zig",
test/standalone/std_enums_big_enums.zig created+38
...@@ -0,0 +1,38 @@
1const std = @import("std");
2
3// big enums should not hit the eval branch quota
4pub fn main() void {
5 const big = struct {
6 const Big = @Type(@as(std.builtin.Type, .{
7 .Enum = .{
8 .tag_type = u16,
9 .fields = make_fields: {
10 var fields: [1001]std.builtin.Type.EnumField = undefined;
11 for (&fields, 0..) |*field, i| {
12 field.* = .{ .name = std.fmt.comptimePrint("field_{d}", .{i}), .value = i };
13 }
14 fields[1000] = .{ .name = "field_9999", .value = 9999 };
15 break :make_fields &fields;
16 },
17 .decls = &.{},
18 .is_exhaustive = true,
19 },
20 }));
21 };
22
23 var set = std.enums.EnumSet(big.Big).init(.{});
24 _ = &set;
25
26 var map = std.enums.EnumMap(big.Big, u8).init(undefined);
27 map = std.enums.EnumMap(big.Big, u8).initFullWith(undefined);
28 map = std.enums.EnumMap(big.Big, u8).initFullWithDefault(123, .{});
29
30 var multiset = std.enums.EnumMultiset(big.Big).init(.{});
31 _ = &multiset;
32
33 var bounded_multiset = std.enums.BoundedEnumMultiset(big.Big, u8).init(.{});
34 _ = &bounded_multiset;
35
36 var array = std.enums.EnumArray(big.Big, u8).init(undefined);
37 array = std.enums.EnumArray(big.Big, u8).initDefault(123, .{});
38}