aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2020-12-28 01:13:23 +0200
committerGitHub <noreply@github.com>2020-12-28 01:13:23 +0200
commit8aab1e2e8ad1451a22a48109006babf463a09e9e (patch)
tree2daf9dd81e470b7f13562caa592449097c69c5ec /lib/std
parent3fb0288d87b1142bd3d416b0a6df7aa32dd7a943 (diff)
parentf9506e9155d93c14958f9cd559b855a00bf96e11 (diff)
downloadzig-8aab1e2e8ad1451a22a48109006babf463a09e9e.tar.gz
zig-8aab1e2e8ad1451a22a48109006babf463a09e9e.zip
Merge pull request #7483 from indocomsoft/autohashmap
Make hasUniqueRepresentation false for slices
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/hash/auto_hash.zig56
-rw-r--r--lib/std/meta/trait.zig46
2 files changed, 90 insertions, 12 deletions
diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig
index e42d55a0a9..3d1d491675 100644
--- a/lib/std/hash/auto_hash.zig
+++ b/lib/std/hash/auto_hash.zig
@@ -169,21 +169,38 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
}
}
+fn typeContainsSlice(comptime K: type) bool {
+ comptime {
+ if (meta.trait.isSlice(K)) {
+ return true;
+ }
+ if (meta.trait.is(.Struct)(K)) {
+ inline for (@typeInfo(K).Struct.fields) |field| {
+ if (typeContainsSlice(field.field_type)) {
+ return true;
+ }
+ }
+ }
+ if (meta.trait.is(.Union)(K)) {
+ inline for (@typeInfo(K).Union.fields) |field| {
+ if (typeContainsSlice(field.field_type)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
+
/// Provides generic hashing for any eligible type.
/// Only hashes `key` itself, pointers are not followed.
-/// Slices are rejected to avoid ambiguity on the user's intention.
+/// Slices as well as unions and structs containing slices are rejected to avoid
+/// ambiguity on the user's intention.
pub fn autoHash(hasher: anytype, key: anytype) void {
const Key = @TypeOf(key);
- if (comptime meta.trait.isSlice(Key)) {
- comptime assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated
- const extra_help = if (Key == []const u8)
- " Consider std.StringHashMap for hashing the contents of []const u8."
- else
- "";
-
- @compileError("std.auto_hash.autoHash does not allow slices (here " ++ @typeName(Key) ++
- ") because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead." ++
- extra_help);
+ if (comptime typeContainsSlice(Key)) {
+ @compileError("std.auto_hash.autoHash does not allow slices as well as unions and structs containing slices here (" ++ @typeName(Key) ++
+ ") because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead.");
}
hash(hasher, key, .Shallow);
@@ -220,6 +237,23 @@ fn testHashDeepRecursive(key: anytype) u64 {
return hasher.final();
}
+test "typeContainsSlice" {
+ comptime {
+ testing.expect(!typeContainsSlice(@TagType(std.builtin.TypeInfo)));
+
+ testing.expect(typeContainsSlice([]const u8));
+ testing.expect(!typeContainsSlice(u8));
+ const A = struct { x: []const u8 };
+ const B = struct { a: A };
+ const C = struct { b: B };
+ const D = struct { x: u8 };
+ testing.expect(typeContainsSlice(A));
+ testing.expect(typeContainsSlice(B));
+ testing.expect(typeContainsSlice(C));
+ testing.expect(!typeContainsSlice(D));
+ }
+}
+
test "hash pointer" {
const array = [_]u32{ 123, 123, 123 };
const a = &array[0];
diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig
index ae3b77b8cc..180e0b8664 100644
--- a/lib/std/meta/trait.zig
+++ b/lib/std/meta/trait.zig
@@ -480,7 +480,6 @@ pub fn hasUniqueRepresentation(comptime T: type) bool {
.Enum,
.ErrorSet,
.Fn,
- .Pointer,
=> return true,
.Bool => return false,
@@ -489,6 +488,8 @@ pub fn hasUniqueRepresentation(comptime T: type) bool {
.Int => |info| return (info.bits % 8) == 0 and
(info.bits == 0 or std.math.isPowerOfTwo(info.bits)),
+ .Pointer => |info| return info.size != .Slice,
+
.Array => |info| return comptime hasUniqueRepresentation(info.child),
.Struct => |info| {
@@ -529,10 +530,53 @@ test "std.meta.trait.hasUniqueRepresentation" {
testing.expect(hasUniqueRepresentation(TestStruct3));
+ const TestStruct4 = struct {
+ a: []const u8
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestStruct4));
+
+ const TestStruct5 = struct {
+ a: TestStruct4
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestStruct5));
+
+ const TestUnion1 = packed union {
+ a: u32,
+ b: u16,
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestUnion1));
+
+ const TestUnion2 = extern union {
+ a: u32,
+ b: u16,
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestUnion2));
+
+ const TestUnion3 = union {
+ a: u32,
+ b: u16,
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestUnion3));
+
+ const TestUnion4 = union(enum) {
+ a: u32,
+ b: u16,
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestUnion4));
+
inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| {
testing.expect(hasUniqueRepresentation(T));
}
inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| {
testing.expect(!hasUniqueRepresentation(T));
}
+
+ testing.expect(!hasUniqueRepresentation([]u8));
+ testing.expect(!hasUniqueRepresentation([]const u8));
}