From 489a460df974f744749d25f7d79e6daab0f578b7 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 20 Mar 2025 14:08:41 +0000 Subject: [PATCH] restore method count after redefinition to hide old definition Purely on the external show, since the method does still exist for internals purposes (e.g. method deletion) and is already filtered for inference users (with lim > 0). Close #53814 --- Compiler/test/invalidation.jl | 4 ++-- base/runtime_internals.jl | 37 +++++++++++++++++++++++------------ test/core.jl | 2 +- test/reflection.jl | 2 +- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Compiler/test/invalidation.jl b/Compiler/test/invalidation.jl index b77c7677e6987..db9d3ac06048f 100644 --- a/Compiler/test/invalidation.jl +++ b/Compiler/test/invalidation.jl @@ -142,8 +142,8 @@ begin # this redefinition below should invalidate the cache of `pr48932_callee` but not that of `pr48932_caller` pr48932_callee(x) = (print(GLOBAL_BUFFER, x); nothing) - @test length(Base.methods(pr48932_callee)) == 2 - @test Base.only(Base.methods(pr48932_callee, Tuple{Any})) === first(Base.methods(pr48932_callee)) + @test length(Base.methods(pr48932_callee)) == 1 + @test Base.only(Base.methods(pr48932_callee, Tuple{Any})) === only(Base.methods(pr48932_callee)) @test isempty(Base.specializations(Base.only(Base.methods(pr48932_callee, Tuple{Any})))) let mi = only(Base.specializations(Base.only(Base.methods(pr48932_caller)))) # Base.method_instance(pr48932_callee, (Any,)) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 846c765ff64b7..d77e6db51b063 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1348,6 +1348,24 @@ function MethodList(mt::Core.MethodTable) return MethodList(ms, mt) end +function matches_to_methods(ms::Array{Any,1}, mt::Core.MethodTable, mod) + # Lack of specialization => a comprehension triggers too many invalidations via _collect, so collect the methods manually + ms = Method[(ms[i]::Core.MethodMatch).method for i in 1:length(ms)] + # Remove shadowed methods with identical type signatures + prev = nothing + filter!(ms) do m + l = prev + repeated = (l isa Method && m.sig == l.sig) + prev = m + return !repeated + end + # Remove methods not part of module (after removing shadowed methods) + mod === nothing || filter!(ms) do m + return parentmodule(m) ∈ mod + end + return MethodList(ms, mt) +end + """ methods(f, [types], [module]) @@ -1355,7 +1373,7 @@ Return the method table for `f`. If `types` is specified, return an array of methods whose types match. If `module` is specified, return an array of methods defined in that module. -A list of modules can also be specified as an array. +A list of modules can also be specified as an array or set. !!! compat "Julia 1.4" At least Julia 1.4 is required for specifying a module. @@ -1363,16 +1381,11 @@ A list of modules can also be specified as an array. See also: [`which`](@ref), [`@which`](@ref Main.InteractiveUtils.@which) and [`methodswith`](@ref Main.InteractiveUtils.methodswith). """ function methods(@nospecialize(f), @nospecialize(t), - mod::Union{Tuple{Module},AbstractArray{Module},Nothing}=nothing) + mod::Union{Tuple{Module},AbstractArray{Module},AbstractSet{Module},Nothing}=nothing) world = get_world_counter() world == typemax(UInt) && error("code reflection cannot be used from generated functions") - # Lack of specialization => a comprehension triggers too many invalidations via _collect, so collect the methods manually - ms = Method[] - for m in _methods(f, t, -1, world)::Vector - m = m::Core.MethodMatch - (mod === nothing || parentmodule(m.method) ∈ mod) && push!(ms, m.method) - end - MethodList(ms, typeof(f).name.mt) + ms = _methods(f, t, -1, world)::Vector{Any} + return matches_to_methods(ms, typeof(f).name.mt, mod) end methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,)) @@ -1382,12 +1395,12 @@ function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) world == typemax(UInt) && error("code reflection cannot be used from generated functions") min = RefValue{UInt}(typemin(UInt)) max = RefValue{UInt}(typemax(UInt)) - ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector - return MethodList(Method[(m::Core.MethodMatch).method for m in ms], typeof(f).name.mt) + ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector{Any} + return matches_to_methods(ms, typeof(f).name.mt, nothing) end function methods(@nospecialize(f), - mod::Union{Module,AbstractArray{Module},Nothing}=nothing) + mod::Union{Module,AbstractArray{Module},AbstractSet{Module},Nothing}=nothing) # return all matches return methods(f, Tuple{Vararg{Any}}, mod) end diff --git a/test/core.jl b/test/core.jl index 0e5ccc745ade9..111309ed2b0db 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7691,7 +7691,7 @@ end # issue #31696 foo31696(x::Int8, y::Int8) = 1 foo31696(x::T, y::T) where {T <: Int8} = 2 -@test length(methods(foo31696)) == 2 +@test length(methods(foo31696)) == 1 let T1 = Tuple{Int8}, T2 = Tuple{T} where T<:Int8, a = T1[(1,)], b = T2[(1,)] b .= a @test b[1] == (1,) diff --git a/test/reflection.jl b/test/reflection.jl index a0e9d96f044be..3752bd9c56c88 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -569,7 +569,7 @@ fLargeTable() = 4 fLargeTable(::Union, ::Union) = "a" @test fLargeTable(Union{Int, Missing}, Union{Int, Missing}) == "a" fLargeTable(::Union, ::Union) = "b" -@test length(methods(fLargeTable)) == 206 +@test length(methods(fLargeTable)) == 205 @test fLargeTable(Union{Int, Missing}, Union{Int, Missing}) == "b" # issue #15280