-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Conversation
Ref #30408 |
I guess this supersedes #16422? |
I see an issue with ambiguity errors:
|
This works you can paste it into your REPL, on this branch. _hasmethod_false(@nospecialize(f), @nospecialize(t)) = false
@generated function static_hasmethod(@nospecialize(f), @nospecialize(t::Type{T})) where T<:Tuple
fi = f.instance #TODO: make this work with constructors and functors.
typ = Base.signature_type(fi, T)
Core.show(typ)
world = 0xffff_ffff_ffff_ffff # can't use type-max, it hasn't been delcared yet
method_doesnot_exist = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), typ, world) === nothing
if method_doesnot_exist
ci = copy(Base.uncompressed_ast(typeof(_hasmethod_false).name.mt.defs.func))
# Now we add the edges so if a method is defined this recompiles
mt = f.name.mt
ci.edges = [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
foo(::Int)=1
tell_of_having_foo(x::T) where T = static_hasmethod(foo, Tuple{T}) ? "It has it" : "It does not have it"
@show tell_of_having_foo(1)
@show tell_of_having_foo('a')
foo(::Char)=2
@show tell_of_having_foo('a')
@code_typed tell_of_having_foo('a') The one I added to Base & Core does not work yet. |
Yeah, that does indeed seem to be an issue. With this PR right now:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worry about how this scales. If this becomes a foundation of traits and you get 1000 methods that call hasmethod
, don't you have to recompile all of them whenever you add a single method to any method table?
Not really, at least not as i understand it. My understanding is: this edge goes from: As i understand it it basically boils down to the same machinery that makes recompilation of things that were triggering
|
There seems like some significant understanding issues here that will sink this approach, sorry. Edges are only the half of the computation, and even so, they still mandate that the result of the generated function must never change for a given set of inputs. For this reason, quoting from https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1: "Generated functions must not...use hasmethod." Can you make the "edges connectable to MethodTables" parts a separate PR? I see no issues with that aspect. For For |
I wrote that line 😀 . But I thought with edges I could be free of that particular rule. Is the remainder because things calling generated functions might not have edges that will be triggered in turn?
Oh yeah, I didn't take that into account. Fair. |
How do we want people to build interfaces? |
Adding
Yeah, that's how we defined |
duck typing |
I've just opened #32774, which @oxinabox, @vchuravy and I worked on together at the hackathon last week. I think that this conversation will be relevant there as well. If there are other things besides edges that make this kind of recompilation still not work, can we think about fixing those as well? If we got that PR to work, does the requirement that these functions don't rely on mutable state like method tables still apply? I would think that this would be enough to lift that restriction? Thanks for your help, Jameson! :) |
As it seems like you note in the tests over there, that approach won't work. I think it's theoretically possible to you could write a pure JuliaInterpreter which only permits side-effects that it can fully reason about and returns the set of operations ("edges") that describe how the answer could be changed by new method definitions. If I had to guess, that's a fairly large project though. |
@vtjnash, for the rest of us who aren't quite as fully suffused with this stuff as you, could you write a bit more about why this is impossible? |
I now understand why this won't work. The short version is: if not fully type inferred then it will become a dynamic dispatch at the call-site. Dynamic dispatches don't need to (and thus don't) set edges. (I suspect this is a bit of a simplification, waiting for full version.) The actual case I care about for traits though always fully infers, since everything is eiter literal types or the results of |
(the full writeup is here: #32774 (comment), and applied to the problematic example, here: #32774 (comment)) |
This plan in its current form isn't going to work out. For now the code is in Tricks.jl |
@oxinabox Did you open a new design for static_hasmethod? |
We can make
hasmethod
able to be used for static dispatch, for truely excellent traits,by making it trigger recompilation of the all functions using it when/if the answer changes.
This is a minor extension of #32237 (in particular addressing this comment)
This will close #32704
TODO:
Very small steps / fine-grained tasks:
hasmethod
delete_method