-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Create Expr(:thisfunction)
as generic function self-reference
#58940
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
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: Jeff Bezanson <jeff.bezanson@gmail.com>
Unless I'm missing something, I think we can remove all the code for |
Does core kwcall need special treatment? |
Yes, good point, it should return the object the user code called I think. So it should expand to |
Ok I think I found a possible way to make it work. I put the function in meta and then read it back during compilation for kwcalls. This seems to let it work for kwcalls. And otherwise we just take the first arg which lets it work for callable structs and functions alike: julia> struct CallableStruct
value::Int
end
julia> @eval (obj::CallableStruct)() = $(Expr(:thisfunction))
julia> CallableStruct(1)()
CallableStruct(1)
julia> @eval f(; n=1) = n <= 1 ? 1 : n * $(Expr(:thisfunction))(; n = n - 1)
f (generic function with 1 method)
julia> f(n=5)
120
julia> @eval foo() = n -> n<=1 ? n : n * $(Expr(:thisfunction))(n-1) # anonymous closure
foo (generic function with 1 method)
julia> Test.@inferred foo(); Test.@inferred foo()(3)
6 This also seems to fix the kwcall issue @vtjnash pointed out in #58909 (comment) julia> methods(var"#f#3")
# 1 method for generic function "#f#3" from Main:
[1] var"#f#3"(n, f::typeof(f))
@ REPL[8]:1
julia> Base.method_argnames(ans[1])
3-element Vector{Symbol}:
Symbol("#f#3")
:n
:f ^Before, it wasn't able to extract Is this the right way to do it? |
Expr(:thisfunction)
as pre-lowering form of var"#self#"
Expr(:thisfunction)
as generic function self-reference
Co-authored-by: Jeff Bezanson <jeff.bezanson@gmail.com>
(if first-arg | ||
(let* ((arg-name (arg-name first-arg)) | ||
;; Check for struct constructor by looking for |#ctor-self#| in args | ||
(ctor-self-arg (let ((args (lam:args lam))) |
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 think this is handled by the other cases; it always starts as the first argument.
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.
This one is written to address some edgecases with parametric type constructors such as
@eval struct Cols{T<:Tuple}
cols::T
operator
Cols(args...; operator=union) = (new{typeof(args)}(args, operator); string($(Expr(:thisfunction))))
end
Cols(1, 2, 3)
Without this logic, this constructor will returns "_". With it, it correctly returns "Cols"
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.
Correction: that was actually my REPL acting up. Actually, without this block logic, it gives:
ERROR: syntax: malformed expression
I do love how this lets you write recursive anonymous functions! |
(let ((e1 (cond (original-name `(globalref (thismodule) ,final-name)) | ||
((and arg-map (symbol? final-name)) | ||
(get arg-map final-name final-name)) | ||
(else final-name)))) |
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.
This part seems necessary for handling kwcall correctly
This is v2 of #58913 which introduces a directive
Expr(:thisfunction)
that gets expanded tovar"#self#"
.Expr(:thisfunction)
was recommended by @JeffBezanson to complementExpr(:thismodule)
.After this PR, the
@__FUNCTION__
macro in #58909 could be updated to emitExpr(:thisfunction)
. Part of the motivation is to ease analysis in lowering tools like JuliaLowering.jl, especially because@__FUNCTION__
would be part of the public API.@c42f @JeffBezanson let me know if this is close to what you had in mind. I'm not too familiar with this level of Julia so please let me know if I'm doing something wrong.