authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-12-28 01:13:23+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-12-28 01:13:23+02:00
log8aab1e2e8ad1451a22a48109006babf463a09e9e
tree2daf9dd81e470b7f13562caa592449097c69c5ec
parent3fb0288d87b1142bd3d416b0a6df7aa32dd7a943
parentf9506e9155d93c14958f9cd559b855a00bf96e11
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7483 from indocomsoft/autohashmap

Make hasUniqueRepresentation false for slices

2 files changed, 90 insertions(+), 12 deletions(-)

lib/std/hash/auto_hash.zig+45-11
...@@ -169,21 +169,38 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {...@@ -169,21 +169,38 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
169 }169 }
170}170}
171171
172fn typeContainsSlice(comptime K: type) bool {
173 comptime {
174 if (meta.trait.isSlice(K)) {
175 return true;
176 }
177 if (meta.trait.is(.Struct)(K)) {
178 inline for (@typeInfo(K).Struct.fields) |field| {
179 if (typeContainsSlice(field.field_type)) {
180 return true;
181 }
182 }
183 }
184 if (meta.trait.is(.Union)(K)) {
185 inline for (@typeInfo(K).Union.fields) |field| {
186 if (typeContainsSlice(field.field_type)) {
187 return true;
188 }
189 }
190 }
191 return false;
192 }
193}
194
172/// Provides generic hashing for any eligible type.195/// Provides generic hashing for any eligible type.
173/// Only hashes `key` itself, pointers are not followed.196/// Only hashes `key` itself, pointers are not followed.
174/// Slices are rejected to avoid ambiguity on the user's intention.197/// Slices as well as unions and structs containing slices are rejected to avoid
198/// ambiguity on the user's intention.
175pub fn autoHash(hasher: anytype, key: anytype) void {199pub fn autoHash(hasher: anytype, key: anytype) void {
176 const Key = @TypeOf(key);200 const Key = @TypeOf(key);
177 if (comptime meta.trait.isSlice(Key)) {201 if (comptime typeContainsSlice(Key)) {
178 comptime assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated202 @compileError("std.auto_hash.autoHash does not allow slices as well as unions and structs containing slices here (" ++ @typeName(Key) ++
179 const extra_help = if (Key == []const u8)203 ") because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead.");
180 " Consider std.StringHashMap for hashing the contents of []const u8."
181 else
182 "";
183
184 @compileError("std.auto_hash.autoHash does not allow slices (here " ++ @typeName(Key) ++
185 ") because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead." ++
186 extra_help);
187 }204 }
188205
189 hash(hasher, key, .Shallow);206 hash(hasher, key, .Shallow);
...@@ -220,6 +237,23 @@ fn testHashDeepRecursive(key: anytype) u64 {...@@ -220,6 +237,23 @@ fn testHashDeepRecursive(key: anytype) u64 {
220 return hasher.final();237 return hasher.final();
221}238}
222239
240test "typeContainsSlice" {
241 comptime {
242 testing.expect(!typeContainsSlice(@TagType(std.builtin.TypeInfo)));
243
244 testing.expect(typeContainsSlice([]const u8));
245 testing.expect(!typeContainsSlice(u8));
246 const A = struct { x: []const u8 };
247 const B = struct { a: A };
248 const C = struct { b: B };
249 const D = struct { x: u8 };
250 testing.expect(typeContainsSlice(A));
251 testing.expect(typeContainsSlice(B));
252 testing.expect(typeContainsSlice(C));
253 testing.expect(!typeContainsSlice(D));
254 }
255}
256
223test "hash pointer" {257test "hash pointer" {
224 const array = [_]u32{ 123, 123, 123 };258 const array = [_]u32{ 123, 123, 123 };
225 const a = &array[0];259 const a = &array[0];
lib/std/meta/trait.zig+45-1
...@@ -480,7 +480,6 @@ pub fn hasUniqueRepresentation(comptime T: type) bool {...@@ -480,7 +480,6 @@ pub fn hasUniqueRepresentation(comptime T: type) bool {
480 .Enum,480 .Enum,
481 .ErrorSet,481 .ErrorSet,
482 .Fn,482 .Fn,
483 .Pointer,
484 => return true,483 => return true,
485484
486 .Bool => return false,485 .Bool => return false,
...@@ -489,6 +488,8 @@ pub fn hasUniqueRepresentation(comptime T: type) bool {...@@ -489,6 +488,8 @@ pub fn hasUniqueRepresentation(comptime T: type) bool {
489 .Int => |info| return (info.bits % 8) == 0 and488 .Int => |info| return (info.bits % 8) == 0 and
490 (info.bits == 0 or std.math.isPowerOfTwo(info.bits)),489 (info.bits == 0 or std.math.isPowerOfTwo(info.bits)),
491490
491 .Pointer => |info| return info.size != .Slice,
492
492 .Array => |info| return comptime hasUniqueRepresentation(info.child),493 .Array => |info| return comptime hasUniqueRepresentation(info.child),
493494
494 .Struct => |info| {495 .Struct => |info| {
...@@ -529,10 +530,53 @@ test "std.meta.trait.hasUniqueRepresentation" {...@@ -529,10 +530,53 @@ test "std.meta.trait.hasUniqueRepresentation" {
529530
530 testing.expect(hasUniqueRepresentation(TestStruct3));531 testing.expect(hasUniqueRepresentation(TestStruct3));
531532
533 const TestStruct4 = struct {
534 a: []const u8
535 };
536
537 testing.expect(!hasUniqueRepresentation(TestStruct4));
538
539 const TestStruct5 = struct {
540 a: TestStruct4
541 };
542
543 testing.expect(!hasUniqueRepresentation(TestStruct5));
544
545 const TestUnion1 = packed union {
546 a: u32,
547 b: u16,
548 };
549
550 testing.expect(!hasUniqueRepresentation(TestUnion1));
551
552 const TestUnion2 = extern union {
553 a: u32,
554 b: u16,
555 };
556
557 testing.expect(!hasUniqueRepresentation(TestUnion2));
558
559 const TestUnion3 = union {
560 a: u32,
561 b: u16,
562 };
563
564 testing.expect(!hasUniqueRepresentation(TestUnion3));
565
566 const TestUnion4 = union(enum) {
567 a: u32,
568 b: u16,
569 };
570
571 testing.expect(!hasUniqueRepresentation(TestUnion4));
572
532 inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| {573 inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| {
533 testing.expect(hasUniqueRepresentation(T));574 testing.expect(hasUniqueRepresentation(T));
534 }575 }
535 inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| {576 inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| {
536 testing.expect(!hasUniqueRepresentation(T));577 testing.expect(!hasUniqueRepresentation(T));
537 }578 }
579
580 testing.expect(!hasUniqueRepresentation([]u8));
581 testing.expect(!hasUniqueRepresentation([]const u8));
538}582}