Skip to content

Commit 51e4ccd

Browse files
stev47KristofferC
authored and
KristofferC
committed
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)`. (cherry picked from commit 22551a2)
1 parent 0857574 commit 51e4ccd

File tree

2 files changed

+19
-2
lines changed

2 files changed

+19
-2
lines changed

base/Enums.jl

+14-2
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

+5
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)