Signatures and renaming
We can demonstrate some of this package's functionality with the following simple example:
julia> ex = :(f(x; color::Symbol=:green) = 2x)
:(f(x; color::Symbol = :green) = begin
          #= REPL[1]:1 =#
          2x
      end)
julia> eval(ex)
f (generic function with 1 method)
julia> f(3)
6Things get more interesting (and complicated) when we examine the lowered code:
julia> lwr = Meta.lower(Main, ex)
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─       $(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─     return $(Expr(:method, :f))
)))
│         $(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─     return $(Expr(:method, Symbol("#f#2")))
)))
│         $(Expr(:method, :f))
│         $(Expr(:method, Symbol("#f#2")))
│   %5  = Core.typeof(var"#f#2")
│   %6  = Core.Typeof(f)
│   %7  = Core.svec(%5, Symbol, %6, Core.Any)
│   %8  = Core.svec()
│   %9  = Core.svec(%7, %8, $(QuoteNode(:(#= REPL[1]:1 =#))))
│         $(Expr(:method, Symbol("#f#2"), :(%9), CodeInfo(quote
    $(Expr(:meta, :nkw, 1))
    2 * x
    return %2
end)))
│         $(Expr(:method, :f))
│   %12 = Core.Typeof(f)
│   %13 = Core.svec(%12, Core.Any)
│   %14 = Core.svec()
│   %15 = Core.svec(%13, %14, $(QuoteNode(:(#= REPL[1]:1 =#))))
│         $(Expr(:method, :f, :(%15), CodeInfo(quote
    var"#f#2"(:green, #self#, x)
    return %1
end)))
│         $(Expr(:method, :f))
│   %18 = Core.Typeof(f)
│   %19 = Core.kwftype(%18)
│   %20 = Core.Typeof(f)
│   %21 = Core.svec(%19, Core.Any, %20, Core.Any)
│   %22 = Core.svec()
│   %23 = Core.svec(%21, %22, $(QuoteNode(:(#= REPL[1]:1 =#))))
│         $(Expr(:method, :f, :(%23), CodeInfo(quote
    Base.haskey(@_2, :color)
    unless %1 goto %11
    Base.getindex(@_2, :color)
    %3 isa Symbol
    unless %4 goto %7
    goto %9
    %new(Core.TypeError, Symbol("keyword argument"), :color, Symbol, %3)
    Core.throw(%7)
    @_6 = %3
    goto %12
    @_6 = :green
    color = @_6
    Core.tuple(:color)
    Core.apply_type(Core.NamedTuple, %13)
    Base.structdiff(@_2, %14)
    Base.pairs(%15)
    Base.isempty(%16)
    unless %17 goto %20
    goto %21
    Base.kwerr(@_2, @_3, x)
    var"#f#2"(color, @_3, x)
    return %21
end)))
│   %25 = f
│   %26 = Core.ifelse(false, false, %25)
└──       return %26
))))This reveals the three methods actually got defined:
- one method of fwith a single positional argument (this is the second 3-argument:methodexpression)
- a keyword-handling method that checks the names of supplied keyword arguments and fills in defaults (this is the third 3-argument :methodexpression). This method can be obtained fromCore.kwfunc(f), which returns a function namedf##kw.
- a "keyword-body" method that actually does the work specifies by our function definition. This method gets called by the other two. (This is the first 3-argument :methodexpression.)
From examining the lowered code we might guess that this function is called #f#2. What happens if we try to get it?
julia> fbody = var"#f#2"
ERROR: UndefVarError: #f#2 not defined
Stacktrace:
 [1] top-level scope at REPL[6]:1Curiously, however, there is a closely-related function, and looking at its body code we see it is the one we wanted:
julia> fbody = var"#f#1"
#f#1 (generic function with 1 method)
julia> mbody = first(methods(fbody))
#f#1(color::Symbol, ::typeof(f), x) in Main at REPL[1]:1
julia> Base.uncompressed_ast(mbody)
CodeInfo(
    @ REPL[1]:1 within `#f#1'
1 ─      nothing
│   %2 = 2 * x
└──      return %2
)It's named #f#1, rather than #f#2, because it was actually defined by that eval(ex) command at the top of this page. That eval caused it to be lowered once, and calling Meta.lower causes it to be lowered a second time, with different generated names.
We can obtain the running version more directly (without having to guess) via the following:
julia> m = first(methods(f))
f(x; color) in Main at REPL[1]:1
julia> using LoweredCodeUtils
julia> bodymethod(m)
#f#1(color::Symbol, ::typeof(f), x) in Main at REPL[1]:1We can also rename these methods, if we first turn it into a frame:
julia> using JuliaInterpreter
julia> frame = Frame(Main, lwr.args[1])
Frame for Main
   1 0  1 ─       $(Expr(:thunk, CodeInfo(
   2 0  1 ─     return $(Expr(:method, :f))
   3 0  )))
⋮
julia> rename_framemethods!(frame)
Dict{Symbol,LoweredCodeUtils.MethodInfo} with 3 entries:
  :f             => MethodInfo(11, 24, [1])
  Symbol("#f#2") => MethodInfo(4, 10, [2])
  Symbol("#f#1") => MethodInfo(4, 10, [2])
julia> frame.framecode.src
CodeInfo(
    @ none within `top-level scope'
1 ─     $(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─     return $(Expr(:method, :f))
)))
│       $(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─     return $(Expr(:method, Symbol("#f#1")))
)))
│       $(Expr(:method, :f))
│       $(Expr(:method, Symbol("#f#1")))
│       ($(QuoteNode(typeof)))(var"#f#1")
│       ($(QuoteNode(Core.Typeof)))(f)
│       ($(QuoteNode(Core.svec)))(%J5, Symbol, %J6, $(QuoteNode(Any)))
│       ($(QuoteNode(Core.svec)))()
│       ($(QuoteNode(Core.svec)))(%J7, %J8, $(QuoteNode(:(#= REPL[1]:1 =#))))
│       $(Expr(:method, Symbol("#f#1"), %J9, CodeInfo(quote
    $(Expr(:meta, :nkw, 1))
    2 * x
    return %2
end)))
│       $(Expr(:method, :f))
│       ($(QuoteNode(Core.Typeof)))(f)
│       ($(QuoteNode(Core.svec)))(%J12, $(QuoteNode(Any)))
│       ($(QuoteNode(Core.svec)))()
│       ($(QuoteNode(Core.svec)))(%J13, %J14, $(QuoteNode(:(#= REPL[1]:1 =#))))
│       $(Expr(:method, :f, %J15, CodeInfo(quote
    var"#f#1"(:green, #self#, x)
    return %1
end)))
│       $(Expr(:method, :f))
│       ($(QuoteNode(Core.Typeof)))(f)
│       ($(QuoteNode(Core.kwftype)))(%J18)
│       ($(QuoteNode(Core.Typeof)))(f)
│       ($(QuoteNode(Core.svec)))(%J19, $(QuoteNode(Any)), %J20, $(QuoteNode(Any)))
│       ($(QuoteNode(Core.svec)))()
│       ($(QuoteNode(Core.svec)))(%J21, %J22, $(QuoteNode(:(#= REPL[1]:1 =#))))
│       $(Expr(:method, :f, %J23, CodeInfo(quote
    Base.haskey(@_2, :color)
    unless %1 goto %11
    Base.getindex(@_2, :color)
    %3 isa Symbol
    unless %4 goto %7
    goto %9
    %new(Core.TypeError, Symbol("keyword argument"), :color, Symbol, %3)
    Core.throw(%7)
    @_6 = %3
    goto %12
    @_6 = :green
    color = @_6
    Core.tuple(:color)
    Core.apply_type(Core.NamedTuple, %13)
    Base.structdiff(@_2, %14)
    Base.pairs(%15)
    Base.isempty(%16)
    unless %17 goto %20
    goto %21
    Base.kwerr(@_2, @_3, x)
    var"#f#1"(color, @_3, x)
    return %21
end)))
│       f
│       ($(QuoteNode(ifelse)))(false, false, %J25)
└──     return %J26
)While there are a few differences in representation stemming from converting it to a frame, you can see that the #f#2s have been changed to #f#1s to match the currently-running names.