Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Make hasmethod able to be used for static dispatch #32732

Closed
wants to merge 9 commits into from
41 changes: 19 additions & 22 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -176,29 +176,26 @@ function store_backedges(frame::InferenceState)
if !toplevel && (frame.cached || frame.parent !== nothing)
caller = frame.result.linfo
for edges in frame.stmt_edges
edges === nothing && continue
i = 1
while i <= length(edges)
to = edges[i]
if isa(to, MethodInstance)
ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any), to, caller)
i += 1
else
typeassert(to, Core.MethodTable)
typ = edges[i + 1]
ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), to, typ, caller)
i += 2
end
end
store_backedges(caller, edges)
end
edges = frame.src.edges
if edges !== nothing
edges = edges::Vector{MethodInstance}
for edge in edges
@assert isa(edge, MethodInstance)
ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any), edge, caller)
end
frame.src.edges = nothing
store_backedges(caller, frame.src.edges)
frame.src.edges = nothing
end
end

store_backedges(caller, edges::Nothing) = nothing
function store_backedges(caller, edges::Vector)
i = 1
while i <= length(edges)
to = edges[i]
if isa(to, MethodInstance)
ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any), to, caller)
i += 1
else
typeassert(to, Core.MethodTable)
typ = edges[i + 1]
oxinabox marked this conversation as resolved.
Show resolved Hide resolved
ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), to, typ, caller)
i += 2
end
end
end
Expand Down
5 changes: 3 additions & 2 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,11 @@ function unwrap_unionall(@nospecialize(a))
end

function rewrap_unionall(@nospecialize(t), @nospecialize(u))
if !isa(u, UnionAll)
if isa(u, UnionAll)
return UnionAll(u.var, rewrap_unionall(t, u.body))
else
return t
end
oxinabox marked this conversation as resolved.
Show resolved Hide resolved
return UnionAll(u.var, rewrap_unionall(t, u.body))
end

# replace TypeVars in all enclosing UnionAlls with fresh TypeVars
Expand Down
58 changes: 57 additions & 1 deletion base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1238,12 +1238,68 @@ julia> hasmethod(g, Tuple{}, (:a, :b, :c, :d)) # g accepts arbitrary kwargs
true
```
"""
function hasmethod(@nospecialize(f), @nospecialize(t); world=typemax(UInt))
function hasmethod end

# This is just declaring a simplified version of `@generated` earlier in bootstrappping
# to be used by hasmethod. TODO: Replace this with just using @eval ?
macro _early_generated(headsig, body)
return Expr(:escape,
Expr(:function, headsig,
Expr(:block,
Expr(:if, Expr(:generated),
body,
Expr(:block,
Expr(:meta, :generated_only),
Expr(:return, nothing))))))
end

# This is used to create the CodeInfo returned by hasmethod.
_hasmethod_false(@nospecialize(f), @nospecialize(t), @nospecialize(w)) = false
_hasmethod_true(@nospecialize(f), @nospecialize(t), @nospecialize(w)) = true

@_early_generated(
static_hasmethod(
@nospecialize(f),
@nospecialize(t::Type{T}),
@nospecialize(world::Val{W})
) where {T<:Tuple, W},
begin
# The signature type:
typ = rewrap_unionall(Tuple{f, unwrap_unionall(T).parameters...}, T)

method_doesnot_exist = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), typ, W) === nothing
ret_func = method_doesnot_exist ? _hasmethod_false : _hasmethod_true
ci_orig = uncompressed_ast(typeof(ret_func).name.mt.defs.func)
ci = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), ci_orig)

# Now we add the edges so if a method is defined this recompiles
if method_doesnot_exist
# No method so attach to method table
mt = f.name.mt
ci.edges = Core.Compiler.vect(mt, typ)
else # There is a method so need to attach to MethodInstance
# TODO: this case

end
return ci
end
)

function static_hasmethod(@nospecialize(f), @nospecialize(t); world=typemax(UInt))
return static_hasmethod(f, t, Val{world}())
end


function dynamic_hasmethod(@nospecialize(f), @nospecialize(t); world=typemax(UInt))
t = to_tuple_type(t)
t = signature_type(f, t)
return ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), t, world) !== nothing
end

function hasmethod(@nospecialize(f), @nospecialize(t); world=typemax(UInt))
oxinabox marked this conversation as resolved.
Show resolved Hide resolved
return static_hasmethod(f, t; world=world)
end

function hasmethod(@nospecialize(f), @nospecialize(t), kwnames::Tuple{Vararg{Symbol}}; world=typemax(UInt))
# TODO: this appears to be doing the wrong queries
hasmethod(f, t, world=world) || return false
Expand Down