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 16 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 and others added 3 commits July 8, 2025 00:16
@MilesCranmer
Copy link
Member Author

Thanks; added

@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.

@StefanKarpinski
Copy link
Member

The doc string has an unfortunate number of caveats. The keyword handling being broken, ok, we could fix. The comprehension example seems rather unfortunate though and seems to me to very much leak an implementation detail of the language. There's no explicit function in the comprehension, that's just how it happens to be implemented. If we're going to have this it should return the innermost syntactic function.

@MilesCranmer
Copy link
Member Author

MilesCranmer commented Jul 10, 2025

Quick note; callable struct compatibility is added by #58940.

For other cases I prefer these as a developer so I can capture the real innermost enclosing function objects in code analysis. In other words, I see these potential caveats as correct, desirable behavior, rather than things to conceal from the user.

From a practical point of view, when writing macros, I can already grab the innermost syntactic functions by parsing the expression. It is instances where I do not have access to this, like closures, @async, or anonymous functions, where I think a @__FUNCTION__ macro becomes most essential.

On comprehensions: if the implicit closure function might be removed internally in the future, perhaps one idea is to throw an error specifically when @__FUNCTION__ is used in a comprehension. That way, there are no future behavioral changes. But at the moment I think this is still a real implementation detail that gets captured correctly, just as closures generated by a comprehension share properties with regular closure functions. Edit: Fixed below

@adienes
Copy link
Member

adienes commented Jul 10, 2025

rather than @__FUNCTION__ aliasing var"#self#" what if there were a block-level macro replacing all instances of foo with var"#self# ?

fib(n) = n <= 1 ? 1 : (@__FUNCTION__)(n - 1) + (@__FUNCTION__)(n - 2) looks really ugly to me and rather than seeing it as a motivating example it just makes me think "wait, why doesn't it just work with fib"

I understand there are type stability problems and that might be a hard (impossible?) problem for the compiler, but maybe something like

@recursion fib(n) = n <= 1 ? 1 : fib(n - 1) + fib(n - 2)

would be nicer?

@MilesCranmer
Copy link
Member Author

This PR isn't to solve that particular issue, although concrete (or anonymous) self-references is indeed a benefit. I view this PR as moreso adding a public API for a internal that seems to be broadly useful. Some others have used it despite its internal status (GH search) as well as myself in DispatchDoctor.jl (this line). In my library, my macro will self-reference a function inside its own body to look up whether there are any overloads to ignore it from type instability checking - this self-reference itself was causing type instabilities if that function happened to be a closure (due to boxing). Using var"#self#" turned out to be the only solution to this.

But in general I also find the @__FUNCTION__ macro to be an intuitive pair for @__MODULE__ as part of the reflection API. So much so that an identical API had actually been first proposed way back in 2014 (#6733) without me initially seeing it :)

Even if the boxing issue is somehow fixable, I think it will be broadly useful to have an API for function self-references, just as there have been many for @__MODULE__

Copy link
Member Author

@MilesCranmer MilesCranmer left a comment

Choose a reason for hiding this comment

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

Hi @StefanKarpinski,

Addressing your feedback, #58940 now rejects Expr(:thisfunction) when used in a list comprehension or generator. It copies the same logic used for preventing return from being used in these contexts:

julia> f() = [return x for x in 1:5]
ERROR: syntax: "return" not allowed inside comprehension or generator around REPL[4]:1
Stacktrace:
 [1] top-level scope
   @ REPL[4]:1

julia> macro __FUNCTION__(); esc(Expr(:thisfunction)); end;

julia> g() = [(@__FUNCTION__) for x in 1:5]
ERROR: syntax: "thisfunction" not allowed inside comprehension or generator around REPL[6]:1
Stacktrace:
 [1] top-level scope
   @ REPL[6]:1

julia> f2() = [() -> return x for x in 1:5]
f2 (generic function with 1 method)

julia> g2() = [() -> (@__FUNCTION__) for x in 1:5]
g2 (generic function with 1 method)

Copy link
Member Author

@MilesCranmer MilesCranmer left a comment

Choose a reason for hiding this comment

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

The docstring has been updated assuming the fixes of #58940. These PRs are still technically independent and address different parts of the codebase, but let me know if you'd prefer them evaluated together.

Copy link
Member Author

@MilesCranmer MilesCranmer left a comment

Choose a reason for hiding this comment

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

I've also moved the docstring example about type stability in recursive closures to the performance tips page. It seems more suitable there

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
9 participants