aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-02-28 20:05:21 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-02-28 20:05:21 -0700
commitd5131e91eba9324eda3a2ae47eb2aa4530c87e83 (patch)
tree62abf5656b3392f738d48f848d3fc64bbc60c19a
parent157f66ec077ad02f08891bec1a426c0ffef98e09 (diff)
downloadzig-d5131e91eba9324eda3a2ae47eb2aa4530c87e83.tar.gz
zig-d5131e91eba9324eda3a2ae47eb2aa4530c87e83.zip
Sema: complete the Type.hash function
Similar to how Type.eql was reworked in the previous commit, this commit reworks Type.hash to check all the different kinds of tags that a Type can be represented with. It also completes the implementation for all types except error sets, which need to have Type.eql enhanced as well.
-rw-r--r--src/Module.zig4
-rw-r--r--src/type.zig290
-rw-r--r--src/value.zig16
3 files changed, 249 insertions, 61 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 8fed3138e7..847e78f1f2 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -157,8 +157,8 @@ const MonomorphedFuncsContext = struct {
// The generic function Decl is guaranteed to be the first dependency
// of each of its instantiations.
const generic_owner_decl = key.owner_decl.dependencies.keys()[0];
- const generic_func = generic_owner_decl.val.castTag(.function).?.data;
- std.hash.autoHash(&hasher, @ptrToInt(generic_func));
+ const generic_func: *const Fn = generic_owner_decl.val.castTag(.function).?.data;
+ std.hash.autoHash(&hasher, generic_func);
// This logic must be kept in sync with the logic in `analyzeCall` that
// computes the hash.
diff --git a/src/type.zig b/src/type.zig
index 1c8e1bfa50..dbb5eb4ba3 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -847,51 +847,105 @@ pub const Type = extern union {
}
pub fn hashWithHasher(ty: Type, hasher: *std.hash.Wyhash) void {
- const zig_type_tag = ty.zigTypeTag();
- std.hash.autoHash(hasher, zig_type_tag);
- switch (zig_type_tag) {
- .Type,
- .Void,
- .Bool,
- .NoReturn,
- .ComptimeFloat,
- .ComptimeInt,
- .Undefined,
- .Null,
- => {}, // The zig type tag is all that is needed to distinguish.
+ switch (ty.tag()) {
+ .generic_poison => unreachable,
- .Pointer => {
- const info = ty.ptrInfo().data;
- hashWithHasher(info.pointee_type, hasher);
- hashSentinel(info.sentinel, info.pointee_type, hasher);
- std.hash.autoHash(hasher, info.@"align");
- std.hash.autoHash(hasher, info.@"addrspace");
- std.hash.autoHash(hasher, info.bit_offset);
- std.hash.autoHash(hasher, info.host_size);
- std.hash.autoHash(hasher, info.@"allowzero");
- std.hash.autoHash(hasher, info.mutable);
- std.hash.autoHash(hasher, info.@"volatile");
- std.hash.autoHash(hasher, info.size);
+ .usize,
+ .isize,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ => |ty_tag| {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Int);
+ std.hash.autoHash(hasher, ty_tag);
},
- .Int => {
- // Detect that e.g. u64 != usize, even if the bits match on a particular target.
- if (ty.isNamedInt()) {
- std.hash.autoHash(hasher, ty.tag());
- } else {
- // Remaining cases are arbitrary sized integers.
- // The target will not be branched upon, because we handled target-dependent cases above.
- const info = ty.intInfo(@as(Target, undefined));
- std.hash.autoHash(hasher, info.signedness);
- std.hash.autoHash(hasher, info.bits);
- }
+
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .c_longdouble,
+ => |ty_tag| {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Float);
+ std.hash.autoHash(hasher, ty_tag);
},
- .Array, .Vector => {
- const elem_ty = ty.elemType();
- std.hash.autoHash(hasher, ty.arrayLen());
- hashWithHasher(elem_ty, hasher);
- hashSentinel(ty.sentinel(), elem_ty, hasher);
+
+ .bool => std.hash.autoHash(hasher, std.builtin.TypeId.Bool),
+ .void => std.hash.autoHash(hasher, std.builtin.TypeId.Void),
+ .type => std.hash.autoHash(hasher, std.builtin.TypeId.Type),
+ .comptime_int => std.hash.autoHash(hasher, std.builtin.TypeId.ComptimeInt),
+ .comptime_float => std.hash.autoHash(hasher, std.builtin.TypeId.ComptimeFloat),
+ .noreturn => std.hash.autoHash(hasher, std.builtin.TypeId.NoReturn),
+ .@"null" => std.hash.autoHash(hasher, std.builtin.TypeId.Null),
+ .@"undefined" => std.hash.autoHash(hasher, std.builtin.TypeId.Undefined),
+
+ .@"anyopaque" => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Opaque);
+ std.hash.autoHash(hasher, Tag.@"anyopaque");
},
- .Fn => {
+
+ .@"anyframe" => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.AnyFrame);
+ std.hash.autoHash(hasher, Tag.@"anyframe");
+ },
+
+ .enum_literal => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.EnumLiteral);
+ std.hash.autoHash(hasher, Tag.enum_literal);
+ },
+
+ .u1,
+ .u8,
+ .i8,
+ .u16,
+ .i16,
+ .u32,
+ .i32,
+ .u64,
+ .i64,
+ .u128,
+ .i128,
+ .int_signed,
+ .int_unsigned,
+ => {
+ // Arbitrary sized integers. The target will not be branched upon,
+ // because we handled target-dependent cases above.
+ std.hash.autoHash(hasher, std.builtin.TypeId.Int);
+ const info = ty.intInfo(@as(Target, undefined));
+ std.hash.autoHash(hasher, info.signedness);
+ std.hash.autoHash(hasher, info.bits);
+ },
+
+ .error_set,
+ .error_set_single,
+ .anyerror,
+ .error_set_inferred,
+ .error_set_merged,
+ => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.ErrorSet);
+ // TODO implement this after revisiting Type.Eql for error sets
+ },
+
+ .@"opaque" => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Opaque);
+ const opaque_obj = ty.castTag(.@"opaque").?.data;
+ std.hash.autoHash(hasher, opaque_obj);
+ },
+
+ .fn_noreturn_no_args,
+ .fn_void_no_args,
+ .fn_naked_noreturn_no_args,
+ .fn_ccc_void_no_args,
+ .function,
+ => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Fn);
+
const fn_info = ty.fnInfo();
hashWithHasher(fn_info.return_type, hasher);
std.hash.autoHash(hasher, fn_info.alignment);
@@ -906,26 +960,150 @@ pub const Type = extern union {
hashWithHasher(param_ty, hasher);
}
},
- .Optional => {
+
+ .array,
+ .array_u8_sentinel_0,
+ .array_u8,
+ .array_sentinel,
+ => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Array);
+
+ const elem_ty = ty.elemType();
+ std.hash.autoHash(hasher, ty.arrayLen());
+ hashWithHasher(elem_ty, hasher);
+ hashSentinel(ty.sentinel(), elem_ty, hasher);
+ },
+
+ .vector => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Vector);
+
+ const elem_ty = ty.elemType();
+ std.hash.autoHash(hasher, ty.vectorLen());
+ hashWithHasher(elem_ty, hasher);
+ },
+
+ .single_const_pointer_to_comptime_int,
+ .const_slice_u8,
+ .const_slice_u8_sentinel_0,
+ .single_const_pointer,
+ .single_mut_pointer,
+ .many_const_pointer,
+ .many_mut_pointer,
+ .c_const_pointer,
+ .c_mut_pointer,
+ .const_slice,
+ .mut_slice,
+ .pointer,
+ .inferred_alloc_const,
+ .inferred_alloc_mut,
+ .manyptr_u8,
+ .manyptr_const_u8,
+ .manyptr_const_u8_sentinel_0,
+ => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Pointer);
+
+ const info = ty.ptrInfo().data;
+ hashWithHasher(info.pointee_type, hasher);
+ hashSentinel(info.sentinel, info.pointee_type, hasher);
+ std.hash.autoHash(hasher, info.@"align");
+ std.hash.autoHash(hasher, info.@"addrspace");
+ std.hash.autoHash(hasher, info.bit_offset);
+ std.hash.autoHash(hasher, info.host_size);
+ std.hash.autoHash(hasher, info.@"allowzero");
+ std.hash.autoHash(hasher, info.mutable);
+ std.hash.autoHash(hasher, info.@"volatile");
+ std.hash.autoHash(hasher, info.size);
+ },
+
+ .optional,
+ .optional_single_const_pointer,
+ .optional_single_mut_pointer,
+ => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Optional);
+
var buf: Payload.ElemType = undefined;
hashWithHasher(ty.optionalChild(&buf), hasher);
},
- .Float => {
- std.hash.autoHash(hasher, ty.tag());
+
+ .anyerror_void_error_union, .error_union => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.ErrorUnion);
+
+ const set_ty = ty.errorUnionSet();
+ hashWithHasher(set_ty, hasher);
+
+ const payload_ty = ty.errorUnionPayload();
+ hashWithHasher(payload_ty, hasher);
},
- .Struct,
- .ErrorUnion,
- .ErrorSet,
- .Enum,
- .Union,
- .BoundFn,
- .Opaque,
- .Frame,
- .AnyFrame,
- .EnumLiteral,
- => {
- // TODO implement more type hashing
+
+ .anyframe_T => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.AnyFrame);
+ hashWithHasher(ty.childType(), hasher);
+ },
+
+ .empty_struct => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Struct);
+ const namespace: *const Module.Namespace = ty.castTag(.empty_struct).?.data;
+ std.hash.autoHash(hasher, namespace);
+ },
+ .@"struct" => {
+ const struct_obj: *const Module.Struct = ty.castTag(.@"struct").?.data;
+ std.hash.autoHash(hasher, struct_obj);
+ },
+ .tuple, .empty_struct_literal => {
+ std.hash.autoHash(hasher, std.builtin.TypeId.Struct);
+
+ const tuple = ty.tupleFields();
+ std.hash.autoHash(hasher, tuple.types.len);
+
+ for (tuple.types) |field_ty, i| {
+ hashWithHasher(field_ty, hasher);
+ const field_val = tuple.values[i];
+ if (field_val.tag() == .unreachable_value) continue;
+ field_val.hash(field_ty, hasher);
+ }
+ },
+
+ // we can't hash these based on tags because they wouldn't match the expanded version.
+ .call_options,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ => unreachable, // needed to resolve the type before now
+
+ .enum_full, .enum_nonexhaustive => {
+ const enum_obj: *const Module.EnumFull = ty.cast(Payload.EnumFull).?.data;
+ std.hash.autoHash(hasher, std.builtin.TypeId.Enum);
+ std.hash.autoHash(hasher, enum_obj);
+ },
+ .enum_simple => {
+ const enum_obj: *const Module.EnumSimple = ty.cast(Payload.EnumSimple).?.data;
+ std.hash.autoHash(hasher, std.builtin.TypeId.Enum);
+ std.hash.autoHash(hasher, enum_obj);
},
+ .enum_numbered => {
+ const enum_obj: *const Module.EnumNumbered = ty.cast(Payload.EnumNumbered).?.data;
+ std.hash.autoHash(hasher, std.builtin.TypeId.Enum);
+ std.hash.autoHash(hasher, enum_obj);
+ },
+ // we can't hash these based on tags because they wouldn't match the expanded version.
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ => unreachable, // needed to resolve the type before now
+
+ .@"union", .union_tagged => {
+ const union_obj: *const Module.Union = ty.cast(Payload.Union).?.data;
+ std.hash.autoHash(hasher, std.builtin.TypeId.Union);
+ std.hash.autoHash(hasher, union_obj);
+ },
+ // we can't hash these based on tags because they wouldn't match the expanded version.
+ .type_info => unreachable, // needed to resolve the type before now
+
+ .bound_fn => unreachable, // TODO delete from the language
+ .var_args_param => unreachable, // can be any type
}
}
diff --git a/src/value.zig b/src/value.zig
index 51f678aaaa..a740a35b79 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -2040,9 +2040,19 @@ pub const Value = extern union {
}
const fields = ty.structFields().values();
if (fields.len == 0) return;
- const field_values = val.castTag(.@"struct").?.data;
- for (field_values) |field_val, i| {
- field_val.hash(fields[i].ty, hasher);
+ switch (val.tag()) {
+ .empty_struct_value => {
+ for (fields) |field| {
+ field.default_val.hash(field.ty, hasher);
+ }
+ },
+ .@"struct" => {
+ const field_values = val.castTag(.@"struct").?.data;
+ for (field_values) |field_val, i| {
+ field_val.hash(fields[i].ty, hasher);
+ }
+ },
+ else => unreachable,
}
},
.Optional => {