Skip to content

Commit

Permalink
Define parentmodule(::Method) (JuliaLang#47548)
Browse files Browse the repository at this point in the history
Among the methods of `parentmodule` is one that looks for a matching
method and retrieves the parent module of that method. However, there
was no method of `parentmodule` for `Method`s themselves. The change
made here introduces such a method and modifies code elsewhere to make
use of it.
  • Loading branch information
ararslan authored Nov 15, 2022
1 parent 626f3b2 commit 9413050
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 47 deletions.
2 changes: 1 addition & 1 deletion base/Enums.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function Base.show(io::IO, x::Enum)
sym = _symbol(x)
if !(get(io, :compact, false)::Bool)
from = get(io, :module, Base.active_module())
def = typeof(x).name.module
def = parentmodule(typeof(x))
if from === nothing || !Base.isvisible(sym, def, from)
show(io, def)
print(io, ".")
Expand Down
9 changes: 4 additions & 5 deletions base/errorshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,7 @@ function showerror(io::IO, ex::MethodError)
elseif isempty(methods(f)) && !isa(f, Function) && !isa(f, Type)
print(io, "objects of type ", ft, " are not callable")
else
if ft <: Function && isempty(ft.parameters) &&
isdefined(ft.name.module, name) &&
ft == typeof(getfield(ft.name.module, name))
if ft <: Function && isempty(ft.parameters) && _isself(ft)
f_is_function = true
end
print(io, "no method matching ")
Expand Down Expand Up @@ -541,7 +539,7 @@ function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=()
end
println(iob)

m = parentmodule_before_main(method.module)
m = parentmodule_before_main(method)
modulecolor = get!(() -> popfirst!(STACKTRACE_MODULECOLORS), STACKTRACE_FIXEDCOLORS, m)
print_module_path_file(iob, m, string(file), line; modulecolor, digit_align_width = 3)

Expand Down Expand Up @@ -696,14 +694,15 @@ function print_stackframe(io, i, frame::StackFrame, n::Int, ndigits_max, modulec
end

# Gets the topmost parent module that isn't Main
function parentmodule_before_main(m)
function parentmodule_before_main(m::Module)
while parentmodule(m) !== m
pm = parentmodule(m)
pm == Main && break
m = pm
end
m
end
parentmodule_before_main(x) = parentmodule_before_main(parentmodule(x))

# Print a stack frame where the module color is set manually with `modulecolor`.
function print_stackframe(io, i, frame::StackFrame, n::Int, ndigits_max, modulecolor)
Expand Down
19 changes: 9 additions & 10 deletions base/methodshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,12 @@ function show_method(io::IO, m::Method; modulecolor = :light_black, digit_align_
show_method_params(io, tv)
end

# module & file, re-using function from errorshow.jl
if get(io, :compact, false)::Bool # single-line mode
print_module_path_file(io, m.module, string(file), line; modulecolor, digit_align_width)
else
if !(get(io, :compact, false)::Bool) # single-line mode
println(io)
print_module_path_file(io, m.module, string(file), line; modulecolor, digit_align_width=digit_align_width+4)
digit_align_width += 4
end
# module & file, re-using function from errorshow.jl
print_module_path_file(io, parentmodule(m), string(file), line; modulecolor, digit_align_width)
end

function show_method_list_header(io::IO, ms::MethodList, namefmt::Function)
Expand Down Expand Up @@ -312,10 +311,10 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru

print(io, " ", lpad("[$n]", digit_align_width + 2), " ")

modulecolor = if meth.module == modul
modulecolor = if parentmodule(meth) == modul
nothing
else
m = parentmodule_before_main(meth.module)
m = parentmodule_before_main(meth)
get!(() -> popfirst!(STACKTRACE_MODULECOLORS), STACKTRACE_FIXEDCOLORS, m)
end
show_method(io, meth; modulecolor)
Expand Down Expand Up @@ -358,7 +357,7 @@ end
fileurl(file) = let f = find_source_file(file); f === nothing ? "" : "file://"*f; end

function url(m::Method)
M = m.module
M = parentmodule(m)
(m.file === :null || m.file === :string) && return ""
file = string(m.file)
line = m.line
Expand Down Expand Up @@ -402,7 +401,7 @@ function show(io::IO, ::MIME"text/html", m::Method)
sig = unwrap_unionall(m.sig)
if sig === Tuple
# Builtin
print(io, m.name, "(...) in ", m.module)
print(io, m.name, "(...) in ", parentmodule(m))
return
end
print(io, decls[1][2], "(")
Expand All @@ -426,7 +425,7 @@ function show(io::IO, ::MIME"text/html", m::Method)
show_method_params(io, tv)
print(io,"</i>")
end
print(io, " in ", m.module)
print(io, " in ", parentmodule(m))
if line > 0
file, line = updated_methodloc(m)
u = url(m)
Expand Down
21 changes: 16 additions & 5 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ function methods(@nospecialize(f), @nospecialize(t),
ms = Method[]
for m in _methods(f, t, -1, world)::Vector
m = m::Core.MethodMatch
(mod === nothing || m.method.module mod) && push!(ms, m.method)
(mod === nothing || parentmodule(m.method) mod) && push!(ms, m.method)
end
MethodList(ms, typeof(f).name.mt)
end
Expand Down Expand Up @@ -1561,16 +1561,27 @@ parentmodule(f::Function) = parentmodule(typeof(f))
"""
parentmodule(f::Function, types) -> Module
Determine the module containing a given definition of a generic function.
Determine the module containing the first method of a generic function `f` matching
the specified `types`.
"""
function parentmodule(@nospecialize(f), @nospecialize(types))
m = methods(f, types)
if isempty(m)
error("no matching methods")
end
return first(m).module
return parentmodule(first(m))
end

"""
parentmodule(m::Method) -> Module
Return the module in which the given method `m` is defined.
!!! compat "Julia 1.9"
Passing a `Method` as an argument requires Julia 1.9 or later.
"""
parentmodule(m::Method) = m.module

"""
hasmethod(f, t::Type{<:Tuple}[, kwnames]; world=get_world_counter()) -> Bool
Expand Down Expand Up @@ -1632,7 +1643,7 @@ as written, called after all missing keyword-arguments have been assigned defaul
`basemethod` is the method you obtain via [`which`](@ref) or [`methods`](@ref).
"""
function bodyfunction(basemethod::Method)
fmod = basemethod.module
fmod = parentmodule(basemethod)
# The lowered code for `basemethod` should look like
# %1 = mkw(kwvalues..., #self#, args...)
# return %1
Expand All @@ -1652,7 +1663,7 @@ function bodyfunction(basemethod::Method)
fsym = callexpr.args[3]
end
if isa(fsym, Symbol)
return getfield(basemethod.module, fsym)::Function
return getfield(fmod, fsym)::Function
elseif isa(fsym, GlobalRef)
return getfield(fsym.mod, fsym.name)::Function
elseif isa(fsym, Core.SSAValue)
Expand Down
26 changes: 14 additions & 12 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,27 @@ function show(io::IO, ::MIME"text/plain", r::LinRange)
print_range(io, r)
end

function _isself(@nospecialize(ft))
name = ft.name.mt.name
mod = parentmodule(ft) # NOTE: not necessarily the same as ft.name.mt.module
return isdefined(mod, name) && ft == typeof(getfield(mod, name))
end

function show(io::IO, ::MIME"text/plain", f::Function)
get(io, :compact, false)::Bool && return show(io, f)
ft = typeof(f)
mt = ft.name.mt
name = ft.name.mt.name
if isa(f, Core.IntrinsicFunction)
print(io, f)
id = Core.Intrinsics.bitcast(Int32, f)
print(io, " (intrinsic function #$id)")
elseif isa(f, Core.Builtin)
print(io, mt.name, " (built-in function)")
print(io, name, " (built-in function)")
else
name = mt.name
isself = isdefined(ft.name.module, name) &&
ft == typeof(getfield(ft.name.module, name))
n = length(methods(f))
m = n==1 ? "method" : "methods"
sname = string(name)
ns = (isself || '#' in sname) ? sname : string("(::", ft, ")")
ns = (_isself(ft) || '#' in sname) ? sname : string("(::", ft, ")")
what = startswith(ns, '@') ? "macro" : "generic function"
print(io, ns, " (", what, " with $n $m)")
end
Expand Down Expand Up @@ -570,7 +573,7 @@ modulesof!(s::Set{Module}, x::TypeVar) = modulesof!(s, x.ub)
function modulesof!(s::Set{Module}, x::Type)
x = unwrap_unionall(x)
if x isa DataType
push!(s, x.name.module)
push!(s, parentmodule(x))
elseif x isa Union
modulesof!(s, x.a)
modulesof!(s, x.b)
Expand Down Expand Up @@ -2401,11 +2404,10 @@ end
# `io` should contain the UnionAll env of the signature
function show_signature_function(io::IO, @nospecialize(ft), demangle=false, fargname="", html=false, qualified=false)
uw = unwrap_unionall(ft)
if ft <: Function && isa(uw, DataType) && isempty(uw.parameters) &&
isdefined(uw.name.module, uw.name.mt.name) &&
ft == typeof(getfield(uw.name.module, uw.name.mt.name))
if qualified && !is_exported_from_stdlib(uw.name.mt.name, uw.name.module) && uw.name.module !== Main
print_within_stacktrace(io, uw.name.module, '.', bold=true)
if ft <: Function && isa(uw, DataType) && isempty(uw.parameters) && _isself(uw)
uwmod = parentmodule(uw)
if qualified && !is_exported_from_stdlib(uw.name.mt.name, uwmod) && uwmod !== Main
print_within_stacktrace(io, uwmod, '.', bold=true)
end
s = sprint(show_sym, (demangle ? demangle_function_name : identity)(uw.name.mt.name), context=io)
print_within_stacktrace(io, s, bold=true)
Expand Down
6 changes: 1 addition & 5 deletions base/stacktraces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,7 @@ function Base.parentmodule(frame::StackFrame)
linfo = frame.linfo
if linfo isa MethodInstance
def = linfo.def
if def isa Module
return def
else
return (def::Method).module
end
return def isa Module ? def : parentmodule(def::Method)
else
# The module is not always available (common reasons include inlined
# frames and frames arising from the interpreter)
Expand Down
2 changes: 1 addition & 1 deletion base/summarysize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ function (ss::SummarySize)(obj::Module)
if isa(value, UnionAll)
value = unwrap_unionall(value)
end
if isa(value, DataType) && value.name.module === obj && value.name.name === binding
if isa(value, DataType) && parentmodule(value) === obj && nameof(value) === binding
# charge a TypeName to its module (but not to the type)
size += ss(value.name)::Int
end
Expand Down
6 changes: 2 additions & 4 deletions stdlib/Serialization/src/Serialization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -566,10 +566,8 @@ function serialize_type_data(s, @nospecialize(t::DataType))
serialize(s, t.name)
else
writetag(s.io, DATATYPE_TAG)
tname = t.name.name
serialize(s, tname)
mod = t.name.module
serialize(s, mod)
serialize(s, nameof(t))
serialize(s, parentmodule(t))
end
if !isempty(t.parameters)
if iswrapper
Expand Down
8 changes: 4 additions & 4 deletions stdlib/Test/src/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1850,7 +1850,7 @@ function detect_ambiguities(mods::Module...;
function examine(mt::Core.MethodTable)
for m in Base.MethodList(mt)
m.sig == Tuple && continue # ignore Builtins
is_in_mods(m.module, recursive, mods) || continue
is_in_mods(parentmodule(m), recursive, mods) || continue
world = Base.get_world_counter()
ambig = Ref{Int32}(0)
ms = Base._methods_by_ftype(m.sig, nothing, -1, world, true, Ref(typemin(UInt)), Ref(typemax(UInt)), ambig)::Vector
Expand Down Expand Up @@ -1883,7 +1883,7 @@ function detect_ambiguities(mods::Module...;
f = Base.unwrap_unionall(getfield(mod, n))
if isa(f, Module) && f !== mod && parentmodule(f) === mod && nameof(f) === n
push!(work, f)
elseif isa(f, DataType) && isdefined(f.name, :mt) && f.name.module === mod && f.name.name === n && f.name.mt !== Symbol.name.mt && f.name.mt !== DataType.name.mt
elseif isa(f, DataType) && isdefined(f.name, :mt) && parentmodule(f) === mod && nameof(f) === n && f.name.mt !== Symbol.name.mt && f.name.mt !== DataType.name.mt
examine(f.name.mt)
end
end
Expand Down Expand Up @@ -1922,7 +1922,7 @@ function detect_unbound_args(mods...;
mods = collect(mods)::Vector{Module}
function examine(mt::Core.MethodTable)
for m in Base.MethodList(mt)
is_in_mods(m.module, recursive, mods) || continue
is_in_mods(parentmodule(m), recursive, mods) || continue
has_unbound_vars(m.sig) || continue
tuple_sig = Base.unwrap_unionall(m.sig)::DataType
if Base.isvatuple(tuple_sig)
Expand Down Expand Up @@ -1954,7 +1954,7 @@ function detect_unbound_args(mods...;
f = Base.unwrap_unionall(getfield(mod, n))
if isa(f, Module) && f !== mod && parentmodule(f) === mod && nameof(f) === n
push!(work, f)
elseif isa(f, DataType) && isdefined(f.name, :mt) && f.name.module === mod && f.name.name === n && f.name.mt !== Symbol.name.mt && f.name.mt !== DataType.name.mt
elseif isa(f, DataType) && isdefined(f.name, :mt) && parentmodule(f) === mod && nameof(f) === n && f.name.mt !== Symbol.name.mt && f.name.mt !== DataType.name.mt
examine(f.name.mt)
end
end
Expand Down
2 changes: 2 additions & 0 deletions test/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ let
@test parentmodule(foo9475, (Any,)) == TestMod7648.TestModSub9475
@test parentmodule(foo9475) == TestMod7648.TestModSub9475
@test parentmodule(Foo7648) == TestMod7648
@test parentmodule(first(methods(foo9475))) == TestMod7648.TestModSub9475
@test parentmodule(first(methods(foo7648))) == TestMod7648
@test nameof(Foo7648) === :Foo7648
@test basename(functionloc(foo7648, (Any,))[1]) == "reflection.jl"
@test first(methods(TestMod7648.TestModSub9475.foo7648)) == which(foo7648, (Int,))
Expand Down

0 comments on commit 9413050

Please sign in to comment.