authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-01 13:20:28-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-01 13:20:28-05:00
log3e99495ed8d2a384501338edb4885e709d51bf74
tree1cd6338398a9a011e5144fe8d29c4991acf84e13
parent0298442100b2d5707099f09d29af554e8c6ac87e
parent39983d7ff524a3e1e25dbd6904e28a6dd11120e6
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10742 from ziglang/ArrayHashMapEql

std: make ArrayHashMap eql function accept an additional param

8 files changed, 364 insertions(+), 28 deletions(-)

lib/std/array_hash_map.zig+21-17
...@@ -37,8 +37,9 @@ pub const StringContext = struct {...@@ -37,8 +37,9 @@ pub const StringContext = struct {
37 _ = self;37 _ = self;
38 return hashString(s);38 return hashString(s);
39 }39 }
40 pub fn eql(self: @This(), a: []const u8, b: []const u8) bool {40 pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool {
41 _ = self;41 _ = self;
42 _ = b_index;
42 return eqlString(a, b);43 return eqlString(a, b);
43 }44 }
44};45};
...@@ -76,7 +77,7 @@ pub fn ArrayHashMap(...@@ -76,7 +77,7 @@ pub fn ArrayHashMap(
76 comptime Context: type,77 comptime Context: type,
77 comptime store_hash: bool,78 comptime store_hash: bool,
78) type {79) type {
79 comptime std.hash_map.verifyContext(Context, K, K, u32);80 comptime std.hash_map.verifyContext(Context, K, K, u32, true);
80 return struct {81 return struct {
81 unmanaged: Unmanaged,82 unmanaged: Unmanaged,
82 allocator: Allocator,83 allocator: Allocator,
...@@ -462,7 +463,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -462,7 +463,7 @@ pub fn ArrayHashMapUnmanaged(
462 comptime Context: type,463 comptime Context: type,
463 comptime store_hash: bool,464 comptime store_hash: bool,
464) type {465) type {
465 comptime std.hash_map.verifyContext(Context, K, K, u32);466 comptime std.hash_map.verifyContext(Context, K, K, u32, true);
466 return struct {467 return struct {
467 /// It is permitted to access this field directly.468 /// It is permitted to access this field directly.
468 entries: DataList = .{},469 entries: DataList = .{},
...@@ -700,7 +701,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -700,7 +701,7 @@ pub fn ArrayHashMapUnmanaged(
700 const hashes_array = slice.items(.hash);701 const hashes_array = slice.items(.hash);
701 const keys_array = slice.items(.key);702 const keys_array = slice.items(.key);
702 for (keys_array) |*item_key, i| {703 for (keys_array) |*item_key, i| {
703 if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*)) {704 if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*, i)) {
704 return GetOrPutResult{705 return GetOrPutResult{
705 .key_ptr = item_key,706 .key_ptr = item_key,
706 // workaround for #6974707 // workaround for #6974
...@@ -933,7 +934,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -933,7 +934,7 @@ pub fn ArrayHashMapUnmanaged(
933 const hashes_array = slice.items(.hash);934 const hashes_array = slice.items(.hash);
934 const keys_array = slice.items(.key);935 const keys_array = slice.items(.key);
935 for (keys_array) |*item_key, i| {936 for (keys_array) |*item_key, i| {
936 if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*)) {937 if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*, i)) {
937 return i;938 return i;
938 }939 }
939 }940 }
...@@ -1245,7 +1246,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -1245,7 +1246,7 @@ pub fn ArrayHashMapUnmanaged(
1245 const keys_array = slice.items(.key);1246 const keys_array = slice.items(.key);
1246 for (keys_array) |*item_key, i| {1247 for (keys_array) |*item_key, i| {
1247 const hash_match = if (store_hash) hashes_array[i] == key_hash else true;1248 const hash_match = if (store_hash) hashes_array[i] == key_hash else true;
1248 if (hash_match and key_ctx.eql(key, item_key.*)) {1249 if (hash_match and key_ctx.eql(key, item_key.*, i)) {
1249 const removed_entry: KV = .{1250 const removed_entry: KV = .{
1250 .key = keys_array[i],1251 .key = keys_array[i],
1251 .value = slice.items(.value)[i],1252 .value = slice.items(.value)[i],
...@@ -1286,7 +1287,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -1286,7 +1287,7 @@ pub fn ArrayHashMapUnmanaged(
1286 const keys_array = slice.items(.key);1287 const keys_array = slice.items(.key);
1287 for (keys_array) |*item_key, i| {1288 for (keys_array) |*item_key, i| {
1288 const hash_match = if (store_hash) hashes_array[i] == key_hash else true;1289 const hash_match = if (store_hash) hashes_array[i] == key_hash else true;
1289 if (hash_match and key_ctx.eql(key, item_key.*)) {1290 if (hash_match and key_ctx.eql(key, item_key.*, i)) {
1290 switch (removal_type) {1291 switch (removal_type) {
1291 .swap => self.entries.swapRemove(i),1292 .swap => self.entries.swapRemove(i),
1292 .ordered => self.entries.orderedRemove(i),1293 .ordered => self.entries.orderedRemove(i),
...@@ -1483,8 +1484,9 @@ pub fn ArrayHashMapUnmanaged(...@@ -1483,8 +1484,9 @@ pub fn ArrayHashMapUnmanaged(
14831484
1484 // This pointer survives the following append because we call1485 // This pointer survives the following append because we call
1485 // entries.ensureTotalCapacity before getOrPutInternal.1486 // entries.ensureTotalCapacity before getOrPutInternal.
1486 const hash_match = if (store_hash) h == hashes_array[slot_data.entry_index] else true;1487 const i = slot_data.entry_index;
1487 if (hash_match and checkedEql(ctx, key, keys_array[slot_data.entry_index])) {1488 const hash_match = if (store_hash) h == hashes_array[i] else true;
1489 if (hash_match and checkedEql(ctx, key, keys_array[i], i)) {
1488 return .{1490 return .{
1489 .found_existing = true,1491 .found_existing = true,
1490 .key_ptr = &keys_array[slot_data.entry_index],1492 .key_ptr = &keys_array[slot_data.entry_index],
...@@ -1571,8 +1573,9 @@ pub fn ArrayHashMapUnmanaged(...@@ -1571,8 +1573,9 @@ pub fn ArrayHashMapUnmanaged(
1571 if (slot_data.isEmpty() or slot_data.distance_from_start_index < distance_from_start_index)1573 if (slot_data.isEmpty() or slot_data.distance_from_start_index < distance_from_start_index)
1572 return null;1574 return null;
15731575
1574 const hash_match = if (store_hash) h == hashes_array[slot_data.entry_index] else true;1576 const i = slot_data.entry_index;
1575 if (hash_match and checkedEql(ctx, key, keys_array[slot_data.entry_index]))1577 const hash_match = if (store_hash) h == hashes_array[i] else true;
1578 if (hash_match and checkedEql(ctx, key, keys_array[i], i))
1576 return slot;1579 return slot;
1577 }1580 }
1578 unreachable;1581 unreachable;
...@@ -1624,7 +1627,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -1624,7 +1627,7 @@ pub fn ArrayHashMapUnmanaged(
1624 }1627 }
16251628
1626 inline fn checkedHash(ctx: anytype, key: anytype) u32 {1629 inline fn checkedHash(ctx: anytype, key: anytype) u32 {
1627 comptime std.hash_map.verifyContext(@TypeOf(ctx), @TypeOf(key), K, u32);1630 comptime std.hash_map.verifyContext(@TypeOf(ctx), @TypeOf(key), K, u32, true);
1628 // If you get a compile error on the next line, it means that1631 // If you get a compile error on the next line, it means that
1629 const hash = ctx.hash(key); // your generic hash function doesn't accept your key1632 const hash = ctx.hash(key); // your generic hash function doesn't accept your key
1630 if (@TypeOf(hash) != u32) {1633 if (@TypeOf(hash) != u32) {
...@@ -1633,10 +1636,10 @@ pub fn ArrayHashMapUnmanaged(...@@ -1633,10 +1636,10 @@ pub fn ArrayHashMapUnmanaged(
1633 }1636 }
1634 return hash;1637 return hash;
1635 }1638 }
1636 inline fn checkedEql(ctx: anytype, a: anytype, b: K) bool {1639 inline fn checkedEql(ctx: anytype, a: anytype, b: K, b_index: usize) bool {
1637 comptime std.hash_map.verifyContext(@TypeOf(ctx), @TypeOf(a), K, u32);1640 comptime std.hash_map.verifyContext(@TypeOf(ctx), @TypeOf(a), K, u32, true);
1638 // If you get a compile error on the next line, it means that1641 // If you get a compile error on the next line, it means that
1639 const eql = ctx.eql(a, b); // your generic eql function doesn't accept (self, adapt key, K)1642 const eql = ctx.eql(a, b, b_index); // your generic eql function doesn't accept (self, adapt key, K, index)
1640 if (@TypeOf(eql) != bool) {1643 if (@TypeOf(eql) != bool) {
1641 @compileError("Context " ++ @typeName(@TypeOf(ctx)) ++ " has a generic eql function that returns the wrong type!\n" ++1644 @compileError("Context " ++ @typeName(@TypeOf(ctx)) ++ " has a generic eql function that returns the wrong type!\n" ++
1642 @typeName(bool) ++ " was expected, but found " ++ @typeName(@TypeOf(eql)));1645 @typeName(bool) ++ " was expected, but found " ++ @typeName(@TypeOf(eql)));
...@@ -2255,9 +2258,10 @@ pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K)...@@ -2255,9 +2258,10 @@ pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K)
2255 }.hash;2258 }.hash;
2256}2259}
22572260
2258pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K) bool) {2261pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K, usize) bool) {
2259 return struct {2262 return struct {
2260 fn eql(ctx: Context, a: K, b: K) bool {2263 fn eql(ctx: Context, a: K, b: K, b_index: usize) bool {
2264 _ = b_index;
2261 _ = ctx;2265 _ = ctx;
2262 return meta.eql(a, b);2266 return meta.eql(a, b);
2263 }2267 }
lib/std/builtin.zig+3
...@@ -203,12 +203,14 @@ pub const TypeInfo = union(enum) {...@@ -203,12 +203,14 @@ pub const TypeInfo = union(enum) {
203 /// therefore must be kept in sync with the compiler implementation.203 /// therefore must be kept in sync with the compiler implementation.
204 pub const Int = struct {204 pub const Int = struct {
205 signedness: Signedness,205 signedness: Signedness,
206 /// TODO make this u16 instead of comptime_int
206 bits: comptime_int,207 bits: comptime_int,
207 };208 };
208209
209 /// This data structure is used by the Zig language code generation and210 /// This data structure is used by the Zig language code generation and
210 /// therefore must be kept in sync with the compiler implementation.211 /// therefore must be kept in sync with the compiler implementation.
211 pub const Float = struct {212 pub const Float = struct {
213 /// TODO make this u16 instead of comptime_int
212 bits: comptime_int,214 bits: comptime_int,
213 };215 };
214216
...@@ -218,6 +220,7 @@ pub const TypeInfo = union(enum) {...@@ -218,6 +220,7 @@ pub const TypeInfo = union(enum) {
218 size: Size,220 size: Size,
219 is_const: bool,221 is_const: bool,
220 is_volatile: bool,222 is_volatile: bool,
223 /// TODO make this u16 instead of comptime_int
221 alignment: comptime_int,224 alignment: comptime_int,
222 address_space: AddressSpace,225 address_space: AddressSpace,
223 child: type,226 child: type,
lib/std/hash/auto_hash.zig+2-1
...@@ -81,7 +81,6 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {...@@ -81,7 +81,6 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
81 .NoReturn,81 .NoReturn,
82 .Opaque,82 .Opaque,
83 .Undefined,83 .Undefined,
84 .Void,
85 .Null,84 .Null,
86 .ComptimeFloat,85 .ComptimeFloat,
87 .ComptimeInt,86 .ComptimeInt,
...@@ -91,6 +90,8 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {...@@ -91,6 +90,8 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
91 .Float,90 .Float,
92 => @compileError("unable to hash type " ++ @typeName(Key)),91 => @compileError("unable to hash type " ++ @typeName(Key)),
9392
93 .Void => return,
94
94 // Help the optimizer see that hashing an int is easy by inlining!95 // Help the optimizer see that hashing an int is easy by inlining!
95 // TODO Check if the situation is better after #561 is resolved.96 // TODO Check if the situation is better after #561 is resolved.
96 .Int => {97 .Int => {
lib/std/hash_map.zig+16-7
...@@ -131,7 +131,13 @@ pub const default_max_load_percentage = 80;...@@ -131,7 +131,13 @@ pub const default_max_load_percentage = 80;
131/// If you are passing a context to a *Adapted function, PseudoKey is the type131/// If you are passing a context to a *Adapted function, PseudoKey is the type
132/// of the key parameter. Otherwise, when creating a HashMap or HashMapUnmanaged132/// of the key parameter. Otherwise, when creating a HashMap or HashMapUnmanaged
133/// type, PseudoKey = Key = K.133/// type, PseudoKey = Key = K.
134pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, comptime Key: type, comptime Hash: type) void {134pub fn verifyContext(
135 comptime RawContext: type,
136 comptime PseudoKey: type,
137 comptime Key: type,
138 comptime Hash: type,
139 comptime is_array: bool,
140) void {
135 comptime {141 comptime {
136 var allow_const_ptr = false;142 var allow_const_ptr = false;
137 var allow_mutable_ptr = false;143 var allow_mutable_ptr = false;
...@@ -166,7 +172,9 @@ pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, compti...@@ -166,7 +172,9 @@ pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, compti
166 const prefix = "\n ";172 const prefix = "\n ";
167 const deep_prefix = prefix ++ " ";173 const deep_prefix = prefix ++ " ";
168 const hash_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ") " ++ @typeName(Hash);174 const hash_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ") " ++ @typeName(Hash);
169 const eql_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ", " ++ @typeName(Key) ++ ") bool";175 const index_param = if (is_array) ", b_index: usize" else "";
176 const eql_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ", " ++
177 @typeName(Key) ++ index_param ++ ") bool";
170 const err_invalid_hash_signature = prefix ++ @typeName(Context) ++ ".hash must be " ++ hash_signature ++178 const err_invalid_hash_signature = prefix ++ @typeName(Context) ++ ".hash must be " ++ hash_signature ++
171 deep_prefix ++ "but is actually " ++ @typeName(@TypeOf(Context.hash));179 deep_prefix ++ "but is actually " ++ @typeName(@TypeOf(Context.hash));
172 const err_invalid_eql_signature = prefix ++ @typeName(Context) ++ ".eql must be " ++ eql_signature ++180 const err_invalid_eql_signature = prefix ++ @typeName(Context) ++ ".eql must be " ++ eql_signature ++
...@@ -255,7 +263,8 @@ pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, compti...@@ -255,7 +263,8 @@ pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, compti
255 const info = @typeInfo(@TypeOf(eql));263 const info = @typeInfo(@TypeOf(eql));
256 if (info == .Fn) {264 if (info == .Fn) {
257 const func = info.Fn;265 const func = info.Fn;
258 if (func.args.len != 3) {266 const args_len = if (is_array) 4 else 3;
267 if (func.args.len != args_len) {
259 errors = errors ++ lazy.err_invalid_eql_signature;268 errors = errors ++ lazy.err_invalid_eql_signature;
260 } else {269 } else {
261 var emitted_signature = false;270 var emitted_signature = false;
...@@ -360,7 +369,7 @@ pub fn HashMap(...@@ -360,7 +369,7 @@ pub fn HashMap(
360 comptime Context: type,369 comptime Context: type,
361 comptime max_load_percentage: u64,370 comptime max_load_percentage: u64,
362) type {371) type {
363 comptime verifyContext(Context, K, K, u64);372 comptime verifyContext(Context, K, K, u64, false);
364 return struct {373 return struct {
365 unmanaged: Unmanaged,374 unmanaged: Unmanaged,
366 allocator: Allocator,375 allocator: Allocator,
...@@ -683,7 +692,7 @@ pub fn HashMapUnmanaged(...@@ -683,7 +692,7 @@ pub fn HashMapUnmanaged(
683) type {692) type {
684 if (max_load_percentage <= 0 or max_load_percentage >= 100)693 if (max_load_percentage <= 0 or max_load_percentage >= 100)
685 @compileError("max_load_percentage must be between 0 and 100.");694 @compileError("max_load_percentage must be between 0 and 100.");
686 comptime verifyContext(Context, K, K, u64);695 comptime verifyContext(Context, K, K, u64, false);
687696
688 return struct {697 return struct {
689 const Self = @This();698 const Self = @This();
...@@ -1108,7 +1117,7 @@ pub fn HashMapUnmanaged(...@@ -1108,7 +1117,7 @@ pub fn HashMapUnmanaged(
1108 /// from this function. To encourage that, this function is1117 /// from this function. To encourage that, this function is
1109 /// marked as inline.1118 /// marked as inline.
1110 inline fn getIndex(self: Self, key: anytype, ctx: anytype) ?usize {1119 inline fn getIndex(self: Self, key: anytype, ctx: anytype) ?usize {
1111 comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash);1120 comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash, false);
11121121
1113 if (self.size == 0) {1122 if (self.size == 0) {
1114 return null;1123 return null;
...@@ -1291,7 +1300,7 @@ pub fn HashMapUnmanaged(...@@ -1291,7 +1300,7 @@ pub fn HashMapUnmanaged(
1291 return result;1300 return result;
1292 }1301 }
1293 pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult {1302 pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult {
1294 comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash);1303 comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash, false);
12951304
1296 // If you get a compile error on this line, it means that your generic hash1305 // If you get a compile error on this line, it means that your generic hash
1297 // function is invalid for these parameters.1306 // function is invalid for these parameters.
src/InternArena.zig created+316
...@@ -0,0 +1,316 @@
1map: std.AutoArrayHashMapUnmanaged(void, void) = .{},
2items: std.MultiArrayList(Item) = .{},
3extra: std.ArrayListUnmanaged(u32) = .{},
4
5const InternArena = @This();
6const std = @import("std");
7const Allocator = std.mem.Allocator;
8const assert = std.debug.assert;
9
10const KeyAdapter = struct {
11 intern_arena: *const InternArena,
12
13 pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool {
14 _ = b_void;
15 return ctx.intern_arena.indexToKey(@intToEnum(Index, b_map_index)).eql(a);
16 }
17
18 pub fn hash(ctx: @This(), a: Key) u32 {
19 _ = ctx;
20 return a.hash();
21 }
22};
23
24pub const Key = union(enum) {
25 int_type: struct {
26 signedness: std.builtin.Signedness,
27 bits: u16,
28 },
29 ptr_type: struct {
30 elem_type: Index,
31 sentinel: Index,
32 alignment: u16,
33 size: std.builtin.TypeInfo.Pointer.Size,
34 is_const: bool,
35 is_volatile: bool,
36 is_allowzero: bool,
37 address_space: std.builtin.AddressSpace,
38 },
39 array_type: struct {
40 len: u64,
41 child: Index,
42 sentinel: Index,
43 },
44 vector_type: struct {
45 len: u32,
46 child: Index,
47 },
48 optional_type: struct {
49 payload_type: Index,
50 },
51 error_union_type: struct {
52 error_set_type: Index,
53 payload_type: Index,
54 },
55 simple: Simple,
56
57 pub fn hash(key: Key) u32 {
58 var hasher = std.hash.Wyhash.init(0);
59 switch (key) {
60 .int_type => |int_type| {
61 std.hash.autoHash(&hasher, int_type);
62 },
63 .array_type => |array_type| {
64 std.hash.autoHash(&hasher, array_type);
65 },
66 else => @panic("TODO"),
67 }
68 return @truncate(u32, hasher.final());
69 }
70
71 pub fn eql(a: Key, b: Key) bool {
72 const KeyTag = std.meta.Tag(Key);
73 const a_tag: KeyTag = a;
74 const b_tag: KeyTag = b;
75 if (a_tag != b_tag) return false;
76 switch (a) {
77 .int_type => |a_info| {
78 const b_info = b.int_type;
79 return std.meta.eql(a_info, b_info);
80 },
81 .array_type => |a_info| {
82 const b_info = b.array_type;
83 return std.meta.eql(a_info, b_info);
84 },
85 else => @panic("TODO"),
86 }
87 }
88};
89
90pub const Item = struct {
91 tag: Tag,
92 /// The doc comments on the respective Tag explain how to interpret this.
93 data: u32,
94};
95
96/// Represents an index into `map`. It represents the canonical index
97/// of a `Value` within this `InternArena`. The values are typed.
98/// Two values which have the same type can be equality compared simply
99/// by checking if their indexes are equal, provided they are both in
100/// the same `InternArena`.
101pub const Index = enum(u32) {
102 none = std.math.maxInt(u32),
103 _,
104};
105
106pub const Tag = enum(u8) {
107 /// An integer type.
108 /// data is number of bits
109 type_int_signed,
110 /// An integer type.
111 /// data is number of bits
112 type_int_unsigned,
113 /// An array type.
114 /// data is payload to Array.
115 type_array,
116 /// A type or value that can be represented with only an enum tag.
117 /// data is Simple enum value
118 simple,
119 /// An unsigned integer value that can be represented by u32.
120 /// data is integer value
121 int_u32,
122 /// An unsigned integer value that can be represented by i32.
123 /// data is integer value bitcasted to u32.
124 int_i32,
125 /// A positive integer value that does not fit in 32 bits.
126 /// data is a extra index to BigInt.
127 int_big_positive,
128 /// A negative integer value that does not fit in 32 bits.
129 /// data is a extra index to BigInt.
130 int_big_negative,
131 /// A float value that can be represented by f32.
132 /// data is float value bitcasted to u32.
133 float_f32,
134 /// A float value that can be represented by f64.
135 /// data is payload index to Float64.
136 float_f64,
137 /// A float value that can be represented by f128.
138 /// data is payload index to Float128.
139 float_f128,
140};
141
142pub const Simple = enum(u32) {
143 f16,
144 f32,
145 f64,
146 f80,
147 f128,
148 usize,
149 isize,
150 c_short,
151 c_ushort,
152 c_int,
153 c_uint,
154 c_long,
155 c_ulong,
156 c_longlong,
157 c_ulonglong,
158 c_longdouble,
159 anyopaque,
160 bool,
161 void,
162 type,
163 anyerror,
164 comptime_int,
165 comptime_float,
166 noreturn,
167 @"anyframe",
168 null_type,
169 undefined_type,
170 enum_literal_type,
171 @"undefined",
172 void_value,
173 @"null",
174 bool_true,
175 bool_false,
176};
177
178pub const Array = struct {
179 len: u32,
180 child: Index,
181};
182
183pub fn deinit(ia: *InternArena, gpa: Allocator) void {
184 ia.map.deinit(gpa);
185 ia.items.deinit(gpa);
186 ia.extra.deinit(gpa);
187}
188
189pub fn indexToKey(ia: InternArena, index: Index) Key {
190 const item = ia.items.get(@enumToInt(index));
191 const data = item.data;
192 return switch (item.tag) {
193 .type_int_signed => .{
194 .int_type = .{
195 .signedness = .signed,
196 .bits = @intCast(u16, data),
197 },
198 },
199 .type_int_unsigned => .{
200 .int_type = .{
201 .signedness = .unsigned,
202 .bits = @intCast(u16, data),
203 },
204 },
205 .type_array => {
206 const array_info = ia.extraData(Array, data);
207 return .{ .array_type = .{
208 .len = array_info.len,
209 .child = array_info.child,
210 .sentinel = .none,
211 } };
212 },
213 .simple => .{ .simple = @intToEnum(Simple, data) },
214
215 else => @panic("TODO"),
216 };
217}
218
219pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index {
220 const adapter: KeyAdapter = .{ .intern_arena = ia };
221 const gop = try ia.map.getOrPutAdapted(gpa, key, adapter);
222 if (gop.found_existing) {
223 return @intToEnum(Index, gop.index);
224 }
225 switch (key) {
226 .int_type => |int_type| {
227 const tag: Tag = switch (int_type.signedness) {
228 .signed => .type_int_signed,
229 .unsigned => .type_int_unsigned,
230 };
231 try ia.items.append(gpa, .{
232 .tag = tag,
233 .data = int_type.bits,
234 });
235 },
236 .array_type => |array_type| {
237 const len = @intCast(u32, array_type.len); // TODO have a big_array encoding
238 assert(array_type.sentinel == .none); // TODO have a sentinel_array encoding
239 try ia.items.append(gpa, .{
240 .tag = .type_array,
241 .data = try ia.addExtra(gpa, Array{
242 .len = len,
243 .child = array_type.child,
244 }),
245 });
246 },
247 else => @panic("TODO"),
248 }
249 return @intToEnum(Index, ia.items.len - 1);
250}
251
252fn addExtra(ia: *InternArena, gpa: Allocator, extra: anytype) Allocator.Error!u32 {
253 const fields = std.meta.fields(@TypeOf(extra));
254 try ia.extra.ensureUnusedCapacity(gpa, fields.len);
255 return ia.addExtraAssumeCapacity(extra);
256}
257
258fn addExtraAssumeCapacity(ia: *InternArena, extra: anytype) u32 {
259 const fields = std.meta.fields(@TypeOf(extra));
260 const result = @intCast(u32, ia.extra.items.len);
261 inline for (fields) |field| {
262 ia.extra.appendAssumeCapacity(switch (field.field_type) {
263 u32 => @field(extra, field.name),
264 Index => @enumToInt(@field(extra, field.name)),
265 i32 => @bitCast(u32, @field(extra, field.name)),
266 else => @compileError("bad field type"),
267 });
268 }
269 return result;
270}
271
272fn extraData(ia: InternArena, comptime T: type, index: usize) T {
273 const fields = std.meta.fields(T);
274 var i: usize = index;
275 var result: T = undefined;
276 inline for (fields) |field| {
277 @field(result, field.name) = switch (field.field_type) {
278 u32 => ia.extra.items[i],
279 Index => @intToEnum(Index, ia.extra.items[i]),
280 i32 => @bitCast(i32, ia.extra.items[i]),
281 else => @compileError("bad field type"),
282 };
283 i += 1;
284 }
285 return result;
286}
287
288test "basic usage" {
289 const gpa = std.testing.allocator;
290
291 var ia: InternArena = .{};
292 defer ia.deinit(gpa);
293
294 const i32_type = try ia.get(gpa, .{ .int_type = .{
295 .signedness = .signed,
296 .bits = 32,
297 } });
298 const array_i32 = try ia.get(gpa, .{ .array_type = .{
299 .len = 10,
300 .child = i32_type,
301 .sentinel = .none,
302 } });
303
304 const another_i32_type = try ia.get(gpa, .{ .int_type = .{
305 .signedness = .signed,
306 .bits = 32,
307 } });
308 try std.testing.expect(another_i32_type == i32_type);
309
310 const another_array_i32 = try ia.get(gpa, .{ .array_type = .{
311 .len = 10,
312 .child = i32_type,
313 .sentinel = .none,
314 } });
315 try std.testing.expect(another_array_i32 == array_i32);
316}
src/codegen/spirv/type.zig+2-1
...@@ -157,8 +157,9 @@ pub const Type = extern union {...@@ -157,8 +157,9 @@ pub const Type = extern union {
157 _ = self;157 _ = self;
158 return @truncate(u32, t.hashShallow());158 return @truncate(u32, t.hashShallow());
159 }159 }
160 pub fn eql(self: @This(), a: Type, b: Type) bool {160 pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool {
161 _ = self;161 _ = self;
162 _ = b_index;
162 return a.eqlShallow(b);163 return a.eqlShallow(b);
163 }164 }
164 };165 };
src/type.zig+2-1
...@@ -798,8 +798,9 @@ pub const Type = extern union {...@@ -798,8 +798,9 @@ pub const Type = extern union {
798 _ = self;798 _ = self;
799 return @truncate(u32, t.hash());799 return @truncate(u32, t.hash());
800 }800 }
801 pub fn eql(self: @This(), a: Type, b: Type) bool {801 pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool {
802 _ = self;802 _ = self;
803 _ = b_index;
803 return a.eql(b);804 return a.eql(b);
804 }805 }
805 };806 };
src/value.zig+2-1
...@@ -1765,7 +1765,8 @@ pub const Value = extern union {...@@ -1765,7 +1765,8 @@ pub const Value = extern union {
1765 const other_context: HashContext = .{ .ty = self.ty };1765 const other_context: HashContext = .{ .ty = self.ty };
1766 return @truncate(u32, other_context.hash(val));1766 return @truncate(u32, other_context.hash(val));
1767 }1767 }
1768 pub fn eql(self: @This(), a: Value, b: Value) bool {1768 pub fn eql(self: @This(), a: Value, b: Value, b_index: usize) bool {
1769 _ = b_index;
1769 return a.eql(b, self.ty);1770 return a.eql(b, self.ty);
1770 }1771 }
1771 };1772 };