Skip to content

Make var"#self#" also work for callable structs #58913

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 5 commits into
base: master
Choose a base branch
from

Conversation

MilesCranmer
Copy link
Member

@MilesCranmer MilesCranmer commented Jul 5, 2025

The var"#self#" symbol refers to the enclosing innermost function. This PR defines it for callable structs to help make usage symmetric. This was first mentioned in #6733 (comment) (@c42f).

This is independent of #58909, but would help make a potential @__FUNCTION__ macro more general.

julia> struct Foo end

julia> (foo::Foo)() = var"#self#";

julia> Foo()()
Foo()

julia> bar() = var"#self#";

julia> bar()
bar (generic function with 1 method)

In the current code, var"#self#" does not work with callable structs:

julia> struct Foo end

julia> (foo::Foo)() = var"#self#";

julia> Foo()()
ERROR: UndefVarError: `#self#` not defined in `Main`

If combined with #58909 we can update the tests to use @__FUNCTION__ rather than var"#symbol#".

@c42f
Copy link
Member

c42f commented Jul 7, 2025

Potential @__FUNCTION__ macro

I assume this doesn't propose var"#self#" as a public API, but merely makes it possible to write @__FUNCTION__?

I guess any #-containing names should be compiler internal - if we want normal user code to be able to refer to them we should probably call them something else.

@MilesCranmer
Copy link
Member Author

MilesCranmer commented Jul 7, 2025

@c42f that is done in #58909 – that PR, not this one, exposes @__FUNCTION__ as public API alias for var"#self#".

This PR just makes it var"#self#" available in callable structs. Which by extension would (if #58909 is merged) make @__FUNCTION__ also work in callable structs.

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

@JeffBezanson
Copy link
Member

Yeah this was not meant to be a public API but I guess this is harmless.

Co-authored-by: Jeff Bezanson <jeff.bezanson@gmail.com>
@c42f
Copy link
Member

c42f commented Jul 8, 2025

I have a thought on how to make this a little cleaner.

Can we use Expr(:self) instead of var"#self#", and have @__FUNCTION__ emit that? Then lowering expands Expr(:self) into either var"#self#" or first depending on context?

This might seem equivalent in the current implementation, but in JuliaLowering.jl the internal names like var"#self#" are inaccessible by default (due to hygiene being stronger),

Also we have precedent for this kind of thing in the way that things like @locals expand to special Expr forms.

Using Expr(:self) (or Expr(:function_self) idk?) just gives us a bit more separation between the internals of lowering and the code which uses it.

@MilesCranmer
Copy link
Member Author

MilesCranmer commented Jul 8, 2025

(Sounds like a fine idea to me)

Then lowering expands Expr(:self) into either var"#self#" or first depending on context?

Just wondering, would introducing this split be adding unnecessary complexity? Because of things like:

julia> struct Foo end

julia> (::typeof(sin))(::Foo) = var"#self#";

julia> sin(Foo())
sin (generic function with 15 methods)

Idk I guess I wonder if it’s simpler to normalize to var"#self#" in all cases anyways

@MilesCranmer
Copy link
Member Author

Perhaps you could have Expr(:self, arg) expand to various reflection variables. Like

@__MODULE__ -> Expr(:self, :module) -> __module__
@__FUNCTION__ -> Expr(:self, :function) -> var"#self#"
...

and so on.

@JeffBezanson
Copy link
Member

Just wondering, would introducing this split be adding unnecessary complexity? Because of things like:

What is complex about this case? Actually I'm not sure there really is a split; it just returns the name of the first argument in all cases.

@JeffBezanson
Copy link
Member

We already have (thismodule) so maybe (thisfunction)?

@MilesCranmer
Copy link
Member Author

MilesCranmer commented Jul 8, 2025

Regarding returning the first argument in all cases, are there potential issues with Core.kwcall?

@MilesCranmer
Copy link
Member Author

We already have (thismodule) so maybe (thisfunction)?

Sounds good to me. I'll give it a go

@MilesCranmer
Copy link
Member Author

MilesCranmer commented Jul 8, 2025

Quick questions – (1) should I make a new PR? (2) should I fold #58909 into this as a single PR defining @__FUNCTION__ and also adding Expr(:thisfunction) to the parser?

I'm going to assume (1) yes, (2) no. Let me know otherwise.

New PR in #58940

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants