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
55 changes: 54 additions & 1 deletion base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1238,12 +1238,65 @@ 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 in the case of false.
_hasmethod_false(@nospecialize(f), @nospecialize(t), @nospecialize(w)) = false

@_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
if method_doesnot_exist
ci_orig = uncompressed_ast(typeof(_hasmethod_false).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
mt = f.name.mt
ci.edges = Core.Compiler.vect(mt, typ)
return ci
else
return true # We are done, it exists, no need to recompile ever
# except if it is deleted. TODO: deal with Base.delete_method
end
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