Skip to content

Commit 22551a2

Browse files
authored
allow specializing Base.hash for enum types without overwriting method (#49777)
Previously `@enum` defined `Base.hash(::MyEnumType, ::UInt)` on the user-defined enum type `MyEnumType`. When the user wants to specialize the hash function for his own enum type he will define exactly that method signature again which overwrites it and leads to the warning WARNING: Method definition hash(TestPackage.MyEnumType, UInt64) in module TestPackage at Enums.jl:210 overwritten at [...] ** incremental compilation may be fatally broken for this module ** This commit changes `@enum` so that an internal method is used instead which is called through a fallback `Base.hash(::Enum, ::UInt)`.
1 parent f44be79 commit 22551a2

File tree

2 files changed

+19
-2
lines changed

2 files changed

+19
-2
lines changed

base/Enums.jl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ Base.cconvert(::Type{T}, x::Enum{T2}) where {T<:Integer,T2<:Integer} = T(x)::T
2121
Base.write(io::IO, x::Enum{T}) where {T<:Integer} = write(io, T(x))
2222
Base.read(io::IO, ::Type{T}) where {T<:Enum} = T(read(io, basetype(T)))
2323

24+
"""
25+
_enum_hash(x::Enum, h::UInt)
26+
27+
Compute hash for an enum value `x`. This internal method will be specialized
28+
for every enum type created through [`@enum`](@ref).
29+
"""
30+
_enum_hash(x::Enum, h::UInt) = hash(x, h)
31+
Base.hash(x::Enum, h::UInt) = _enum_hash(x, h)
2432
Base.isless(x::T, y::T) where {T<:Enum} = isless(basetype(T)(x), basetype(T)(y))
2533

2634
Base.Symbol(x::Enum) = namemap(typeof(x))[Integer(x)]::Symbol
@@ -206,8 +214,12 @@ macro enum(T::Union{Symbol,Expr}, syms...)
206214
Enums.namemap(::Type{$(esc(typename))}) = $(esc(namemap))
207215
Base.typemin(x::Type{$(esc(typename))}) = $(esc(typename))($lo)
208216
Base.typemax(x::Type{$(esc(typename))}) = $(esc(typename))($hi)
209-
let enum_hash = hash($(esc(typename)))
210-
Base.hash(x::$(esc(typename)), h::UInt) = hash(enum_hash, hash(Integer(x), h))
217+
let type_hash = hash($(esc(typename)))
218+
# Use internal `_enum_hash` to allow users to specialize
219+
# `Base.hash` for their own enum types without overwriting the
220+
# method we would define here. This avoids a warning for
221+
# precompilation.
222+
Enums._enum_hash(x::$(esc(typename)), h::UInt) = hash(type_hash, hash(Integer(x), h))
211223
end
212224
let insts = (Any[ $(esc(typename))(v) for v in $values ]...,)
213225
Base.instances(::Type{$(esc(typename))}) = insts

test/enums.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ end
179179
@enum HashEnum2 Enum2_a=1
180180
@test hash(Enum1_a) != hash(Enum2_a)
181181

182+
# PR #49777: Check that `Base.hash` can be specialized by the user without
183+
# overwriting a method definition.
184+
@enum HashEnum3 Enum3_a=1
185+
@test which(hash, (HashEnum3, UInt)).sig != Tuple{typeof(hash), HashEnum3, UInt64}
186+
182187
@test (Vector{Fruit}(undef, 3) .= apple) == [apple, apple, apple]
183188

184189
# long, discongruous

0 commit comments

Comments
 (0)