I’m looping over method instances in a module using @tim.holy’s marvelous MethodAnalysis.jl. I need to run @code_typed for those instances to extract some type information. For that I first resolve the function using getfield on MethodInstance.def.module and MethodInstance.def.name. The issue is that a method can be defined in other modules than the original function, so MethodInstance.def.module is not quite right.
Is there a way to get the module where the function was introduced using MethodInstance interface?
I often do it that way, too. However, I’ve also noticed an alternative, which I sometimes remember to use:
julia> m = @which sum((1,2))
sum(x::Tuple{Any, Vararg{Any, N} where N}) in Base at tuple.jl:474
julia> ft = Base.unwrap_unionall(m.sig).parameters[1]
typeof(sum)
julia> ft.instance
sum (generic function with 14 methods)
I don’t remember whether the last line is always safe; maybe give it a try and let us know of any corrections you discover?
EDIT: and I’d be happy to have something like this wrapped up nicely and added to MethodAnalysis, if you want to submit a PR.
@tim.holy neat! There’s one problem with instance (as I figured running your approach on a bunch of instances): it’s not defined for constructors. E.g.
julia> h() = 1
julia> (@which h()).sig.parameters[1].instance
h (generic function with 1 method)
julia> struct S end;
julia> (@which S()).sig.parameters[1].instance
ERROR: UndefRefError: access to undefined reference
Stacktrace:
[1] getproperty(::Type{T} where T, ::Symbol) at ./Base.jl:28
[2] top-level scope at REPL[95]:1
(And the error looks a bit bizzare.) So, instead of typeof(f) we have Type{SomeType} at this spot.
This is not too hard to work around, though, assuming constructor’s function defined in the same module as the corresponding type. So, currently, I have the following to convert a method instance to a function:
instance_to_function(mi :: MethodInstance) = begin
sig = Base.unwrap_unionall(mi.specTypes).types
if sig[1] <: Function # typeof(func)
sig[1].instance
else # constructor
getfield(
parentmodule(sig[1].parameters[1]),
mi.def.name)
end
end
I’m yet to see how it works on a larger data set (it could reveal some more problems). But already this is good progress, thanks a lot!
My understanding is that without the actual value of T not much I can do with the signature I have (e.g. I use code_typed on the result but it fails for such signature). So I only analyze the case of typ <: Function right now.
I’ve played with this a bit in the meantime too. (Your question was useful since it made me think about this a bit more.) Here’s how SnoopCompile now deals with this:
ft2f (“function type to function”) is only used to print stuff to the REPL, so all that was needed was a good fallback for the case of a closure. And yes, just as you say I do have to Base.unwrap_unionall it before calling ft2f.