diff --git a/base/dict.jl b/base/dict.jl index 359016bd3c2a8..55a364dc97e6a 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -258,11 +258,12 @@ function empty!(h::Dict{K,V}) where V where K end # get the index where a key is stored, or -1 if not present -function ht_keyindex(h::Dict{K,V}, key) where V where K +@assume_effects :terminates_locally function ht_keyindex(h::Dict{K,V}, key) where V where K isempty(h) && return -1 sz = length(h.keys) iter = 0 maxprobe = h.maxprobe + maxprobe < sz || throw(AssertionError()) # This error will never trigger, but is needed for terminates_locally to be valid index, sh = hashindex(key, sz) keys = h.keys diff --git a/base/reflection.jl b/base/reflection.jl index ac8d2752a4719..6740ce8044068 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -334,17 +334,6 @@ macro locals() return Expr(:locals) end -""" - objectid(x) -> UInt - -Get a hash value for `x` based on object identity. - -If `x === y` then `objectid(x) == objectid(y)`, and usually when `x !== y`, `objectid(x) != objectid(y)`. - -See also [`hash`](@ref), [`IdDict`](@ref). -""" -objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) - # concrete datatype predicates datatype_fieldtypes(x::DataType) = ccall(:jl_get_fieldtypes, Core.SimpleVector, (Any,), x) @@ -600,6 +589,28 @@ Return `true` if `x` is an instance of an [`isbitstype`](@ref) type. """ isbits(@nospecialize x) = isbitstype(typeof(x)) +""" + objectid(x) -> UInt + +Get a hash value for `x` based on object identity. + +If `x === y` then `objectid(x) == objectid(y)`, and usually when `x !== y`, `objectid(x) != objectid(y)`. + +See also [`hash`](@ref), [`IdDict`](@ref). +""" +function objectid(x) + # objectid is foldable iff it isn't a pointer. + if isidentityfree(typeof(x)) + return _foldable_objectid(x) + end + return _objectid(x) +end +function _foldable_objectid(@nospecialize(x)) + @_foldable_meta + _objectid(x) +end +_objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) + """ isdispatchtuple(T) diff --git a/src/jltypes.c b/src/jltypes.c index 482f21a14c76e..820e5c517e374 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3137,6 +3137,8 @@ void jl_init_types(void) JL_GC_DISABLED // Technically not ismutationfree, but there's a separate system to deal // with mutations for global state. jl_module_type->ismutationfree = 1; + // Module object identity is determined by its name and parent name. + jl_module_type->isidentityfree = 1; // Array's mutable data is hidden, so we need to override it ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_array_type))->ismutationfree = 0; diff --git a/test/core.jl b/test/core.jl index a89d206182dbf..18f9fa64e85e9 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7985,3 +7985,22 @@ f48950(::Union{Int,d}, ::Union{c,Nothing}...) where {c,d} = 1 # Module as tparam in unionall struct ModTParamUnionAll{A, B}; end @test isa(objectid(ModTParamUnionAll{Base}), UInt) + +# effects for objectid +for T in (Int, String, Symbol, Module) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (T,))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (T,))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Some{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Some{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Some{Some{T}},))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Some{Some{T}},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Tuple{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Tuple{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Tuple{T,T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Tuple{T,T},))) +end +@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Ref{Int},))) +@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Tuple{Ref{Int}},))) +# objectid for datatypes is inconsistant for types that have unbound type parameters. +@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (DataType,))) +@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Tuple{Vector{Int}},))) diff --git a/test/dict.jl b/test/dict.jl index 65f8939bc6dfc..6a47c3c6eea8b 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1363,3 +1363,11 @@ end sizehint!(d, 10) @test length(d.slots) < 100 end + +# getindex is :effect_free and :terminates but not :consistent +for T in (Int, Float64, String, Symbol) + @test !Core.Compiler.is_consistent(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test Core.Compiler.is_effect_free(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test !Core.Compiler.is_nothrow(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test Core.Compiler.is_terminates(Base.infer_effects(getindex, (Dict{T,Any}, T))) +end