Skip to content

Define @__FUNCTION__ as an alias to var"#self#" #58909

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

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

MilesCranmer
Copy link
Member

@MilesCranmer MilesCranmer commented Jul 5, 2025

Just as @__MODULE__ refers to the enclosing module object, the internal variable var"#self#" can be used to refer to the enclosing function object.

This PR creates an alias @__FUNCTION__ for this variable to match the naming conventions of existing reflection macros (@__MODULE__, @__FILE__, etc.).

Fixes #58908 Fixes #6733

@nsajko nsajko added feature Indicates new feature / enhancement requests macros @macros labels Jul 5, 2025
@KristofferC
Copy link
Member

KristofferC commented Jul 5, 2025

Referencing the discussion about implementations of this in #6733.

@giordano
Copy link
Member

giordano commented Jul 5, 2025

This should also be added to the docs. I'm surprised this isn't caught by CI.

@giordano giordano added the needs news A NEWS entry is required for this change label Jul 5, 2025
@MilesCranmer
Copy link
Member Author

Thanks; done

@nsajko nsajko removed the needs news A NEWS entry is required for this change label Jul 6, 2025
@LilithHafner LilithHafner added the triage This should be discussed on a triage call label Jul 7, 2025
@LilithHafner
Copy link
Member

tagging triage to talk about an expansion of the public API

Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

Could you add some tests for kwarg handling too, since those functions are tricky (even if you have to make them test_broken for now)

@MilesCranmer
Copy link
Member Author

Thanks; added

@@ -131,6 +131,11 @@ end
test_do_block()
end

@testset "Compatibility with kwargs" begin
foo(; n) = n <= 1 ? 1 : n * (@__FUNCTION__)(; n = n - 1)
@test foo(n = 5) == 120
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is getting a closure captured #self# value copy instead of using the argument. It is hard to distinguish, since they are the identical value, but it appears to result in the self value getting passed to the inner kwarg function twice (and so it might appear as more of an issue once the other related function is merged).

julia> methods(ans)[1]
var"#f#5"(y, ::typeof(f))
     @ Main REPL[8]:1

julia> Base.method_argnames(ans)
3-element Vector{Symbol}:
 Symbol("#f#5")
 :y
 Symbol("") # expected `#self#` here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the first ans there?

With a build on #58913 I see this:

julia> mutable struct Bar
           x::Int
       end

julia> b = Bar(1)
Bar(1)

julia> (::Bar)(; n=2) = @__FUNCTION__

julia> b === Bar(1)
false

julia> b === b()
true

julia> methods(b)[1]
(::Bar)(; n)
     @ Main REPL[21]:1

julia> Base.method_argnames(ans)
1-element Vector{Symbol}:
 Symbol("#self#")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh from this I guess?

julia> @code_warntype foo(n=3)
MethodInstance for Core.kwcall(::@NamedTuple{n::Int64}, ::typeof(foo))
  from kwcall(::NamedTuple, ::typeof(foo)) @ Main REPL[26]:1
Arguments
  _::Core.Const(Core.kwcall)
  @_2::@NamedTuple{n::Int64}
  @_3::Core.Const(Main.foo)
Locals
  @_4::Int64
  n::Int64
Body::Int64
1 ─       Core.NewvarNode(:(@_4))
│   %2  = Core.isdefined(@_2, :n)::Core.Const(true)
└──       goto #3 if not %2
2 ─       (@_4 = Core.getfield(@_2, :n))
└──       goto #4
3 ─       Core.Const(:(Core.UndefKeywordError(:n)))
└──       Core.Const(:(@_4 = Core.throw(%6)))
4%8  = @_4::Int64
│         (n = %8)
│   %10 = Base.keys(@_2)::Core.Const((:n,))
│   %11 = (:n,)::Core.Const((:n,))
│   %12 = Base.diff_names(%10, %11)::Core.Const(())
│   %13 = Base.isempty(%12)::Core.Const(true)
└──       goto #6 if not %13
5 ─       goto #7
6 ─       Core.Const(:(Base.kwerr(@_2, @_3)))
7%17 = Main.:(var"#foo#13")::Core.Const(Main.var"#foo#13")
│   %18 = n::Int64%19 = (%17)(%18, @_3)::Int64
└──       return %19


julia> methods(var"#foo#13")
# 1 method for generic function "#foo#13" from Main:
 [1] var"#foo#13"(n, ::typeof(foo))
     @ REPL[26]:1

julia> Base.method_argnames(ans[1])
3-element Vector{Symbol}:
 Symbol("#foo#13")
 :n
 Symbol("")

@MilesCranmer
Copy link
Member Author

MilesCranmer commented Jul 8, 2025

x-ref #58940 which defines Expr(:thisfunction) that gets lowered to var"#self#". Suggested by @JeffBezanson and @c42f to make pre-lowering analysis easier. With that merged, we could have @__FUNCTION__ return that instead of var"#self#". That PR also makes it work for callable structs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Indicates new feature / enhancement requests macros @macros triage This should be discussed on a triage call
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Marking var"#self#" as stable? add builtin @__FUNCTION__ macros
8 participants