Skip to content

Commit 9cc926b

Browse files
authored
Add MethodInstance subtyping (#27)
Add MethodInstance subtyping Demo: ``` julia> methodinstances(convert, (Type{Bool}, Real)) 2-element Vector{Core.MethodInstance}: MethodInstance for convert(::Type{Bool}, ::Bool) MethodInstance for convert(::Type{Bool}, ::Int64) ``` This also: * Improves the implementation of `call_type` * Fixes tests on 1.7 (inference got better and transcendental functions are implemented in Julia, so values got inferred as `Const`) * Runs CI on 1.6 LTS
1 parent 899ae12 commit 9cc926b

File tree

3 files changed

+90
-15
lines changed

3 files changed

+90
-15
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
matrix:
1717
version:
1818
- '1.0'
19+
- '1.6'
1920
- '1'
2021
- 'nightly'
2122
os:

src/MethodAnalysis.jl

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ Split a signature type like `Tuple{typeof(f),ArgTypes...}` back out to `(f, Tupl
2424
function call_type(tt)
2525
ft = tt.parameters[1]
2626
argt = Tuple{tt.parameters[2:end]...}
27-
name = Symbol(String(ft.name.name)[2:end]) # strip off leading '#'
28-
return (getfield(ft.name.module, name), argt)
27+
name = Symbol(String(ft.name.name))
28+
return (getfield(ft.name.module, name).instance, argt)
2929
end
3030

3131
"""
@@ -87,34 +87,69 @@ julia> methodinstance(Tuple{typeof(f), Int, String})
8787
MethodInstance for f(::Int64, ::String)
8888
```
8989
"""
90-
function methodinstance(@nospecialize(f), @nospecialize(types))
90+
methodinstance(@nospecialize(f), @nospecialize(types)) =
91+
_methodinstance(f, types, false)
92+
93+
function methodinstance(@nospecialize(types))
94+
f, argt = call_type(types)
95+
return methodinstance(f, types)
96+
end
97+
98+
function _methodinstance(@nospecialize(f), @nospecialize(types), multi::Bool)
9199
if types isa Tuple
92100
tt = Tuple{typeof(f), types...}
93-
return methodinstance(f, tt)
101+
return _methodinstance(f, tt, multi)
94102
end
95-
inst = nothing
103+
kept = MethodInstance[]
96104
visit(f) do mi
97105
if isa(mi, MethodInstance)
98-
if mi.specTypes === types
99-
inst = mi
106+
if multi ? (mi.specTypes <: types) : (mi.specTypes === types)
107+
push!(kept, mi)
100108
end
101109
return false
102110
end
103111
true
104112
end
105-
return inst
106-
end
107-
function methodinstance(@nospecialize(types))
108-
f, argt = call_type(types)
109-
return methodinstance(f, types)
113+
multi && return kept
114+
length(kept) == 1 && return kept[1]
115+
length(kept) == 0 && return nothing
116+
error(length(kept), " MethodInstances matched the specified types")
110117
end
111118

112119
"""
113120
methodinstances()
114-
methodinstances(mod::Module)
115-
methodinstances(f)
121+
methodinstances(top)
116122
117123
Collect all `MethodInstance`s, optionally restricting them to a particular module, function, method, or methodlist.
124+
125+
# Examples
126+
127+
```
128+
julia> sin(π/2)
129+
1.0
130+
131+
julia> sin(0.8f0)
132+
0.7173561f0
133+
134+
julia> methodinstances(sin)
135+
2-element Vector{Core.MethodInstance}:
136+
MethodInstance for sin(::Float64)
137+
MethodInstance for sin(::Float32)
138+
139+
julia> m = which(convert, (Type{Bool}, Real))
140+
convert(::Type{T}, x::Number) where T<:Number in Base at number.jl:7
141+
142+
julia> methodinstances(m)
143+
68-element Vector{Core.MethodInstance}:
144+
MethodInstance for convert(::Type{UInt128}, ::Int64)
145+
MethodInstance for convert(::Type{Int128}, ::Int64)
146+
MethodInstance for convert(::Type{Int64}, ::Int32)
147+
MethodInstance for convert(::Type{UInt64}, ::Int64)
148+
149+
```
150+
151+
Note the method `m` was broader than the signature we queried with, and the returned `MethodInstance`s reflect that breadth.
152+
See [`methodinstances`](@ref) for a more restrictive subset.
118153
"""
119154
function methodinstances(top=())
120155
if isa(top, Module) || isa(top, Function) || isa(top, Type) || isa(top, Method) || isa(top, Base.MethodList)
@@ -129,6 +164,32 @@ function methodinstances(top=())
129164
return mis
130165
end
131166

167+
"""
168+
methodinstances(f, types)
169+
methodinstances(tt::Type{<:Tuple})
170+
171+
Return all MethodInstances whose signature is a subtype of `types`.
172+
173+
# Example
174+
175+
```
176+
julia> methodinstances(convert, (Type{Bool}, Real))
177+
2-element Vector{Core.MethodInstance}:
178+
MethodInstance for convert(::Type{Bool}, ::Bool)
179+
MethodInstance for convert(::Type{Bool}, ::Int64)
180+
```
181+
182+
Compare this to the result from [`methodinstance`](@ref).
183+
"""
184+
methodinstances(@nospecialize(f), @nospecialize(types)) =
185+
_methodinstance(f, types, true)
186+
187+
function methodinstances(@nospecialize(types::Type))
188+
f, argt = call_type(types)
189+
return methodinstances(f, types)
190+
end
191+
192+
132193
if isdefined(Core, :MethodMatch)
133194
include("findcallers.jl")
134195
end

test/runtests.jl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ end
8787
mis = methodinstances(which(convert, (Type{String}, AbstractString)))
8888
@test length(mis) > 2
8989
@test mi mis # that's covered by a different Method
90+
91+
f(x) = 1
92+
f(1)
93+
f(1.0)
94+
@test methodinstance(f, (Real,)) === nothing
95+
mi = methodinstance(f, (Float64,))
96+
@test mi.specTypes == Tuple{typeof(f),Float64}
97+
for mis in (methodinstances(f, (Real,)), methodinstances(Tuple{typeof(f),Real}))
98+
@test length(mis) == 2
99+
@test all(mis) do mi
100+
mi.specTypes (Tuple{typeof(f),Float64}, Tuple{typeof(f),Int})
101+
end
102+
end
90103
end
91104

92105
@testset "Backedges" begin
@@ -189,7 +202,7 @@ module Callers
189202

190203
f(x) = rand()
191204
function g()
192-
x = sin(π/2)
205+
x = sin(rand())
193206
y = f(x)
194207
z = round(Int, x)
195208
y = f(z)

0 commit comments

Comments
 (0)