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
12 changes: 2 additions & 10 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function store_backedges(frame::InferenceState)
toplevel = !isa(frame.linfo.def, Method)
if !toplevel && (frame.cached || frame.parent !== nothing)
caller = frame.result.linfo
for edges in frame.stmt_edges
for edges in (frame.stmt_edges..., frame.src.edges)
edges === nothing && continue
i = 1
while i <= length(edges)
Expand All @@ -191,15 +191,7 @@ function store_backedges(frame::InferenceState)
end
end
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
end
frame.src.edges = nothing
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
return UnionAll(u.var, rewrap_unionall(t, u.body))
end

# replace TypeVars in all enclosing UnionAlls with fresh TypeVars
Expand Down
50 changes: 50 additions & 0 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,56 @@ julia> hasmethod(g, Tuple{}, (:a, :b, :c, :d)) # g accepts arbitrary kwargs
true
```
"""
function hasmethod(@nospecialize(f), @nospecialize(t); world=typemax(UInt))
hasmethod(f, to_tuple_type(t); world=world)
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
fi = f.instance #TODO: make this work with constructors and functors.
typ = signature_type(fi, 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

# TODO: delete this and rename static_hasmethod above
function hasmethod(@nospecialize(f), @nospecialize(t); world=typemax(UInt))
t = to_tuple_type(t)
t = signature_type(f, t)
Expand Down