Skip to content

Commit

Permalink
improve effects of objectid and getindex(::Dict) (#49447)
Browse files Browse the repository at this point in the history
This commit also marks `Module` type as `identityfree`.

Co-authored-by: Shuhei Kadowaki <aviatesk@gmail.com>
  • Loading branch information
oscardssmith and aviatesk authored Apr 25, 2023
1 parent c70e0fa commit 86b819c
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 12 deletions.
3 changes: 2 additions & 1 deletion base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
33 changes: 22 additions & 11 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -3244,6 +3244,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;
Expand Down
19 changes: 19 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7987,3 +7987,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}},)))
8 changes: 8 additions & 0 deletions test/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 86b819c

Please sign in to comment.