Commit d5131e91eb

Andrew Kelley <andrew@ziglang.org>
2022-03-01 04:05:21
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.
1 parent 157f66e
Changed files (3)
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.
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
         }
     }
 
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 => {