Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbea90a2

Browse files
add@__FUNCTION__ andExpr(:thisfunction) as generic function self-reference (#58940)
This PR adds `@__FUNCTION__` to match the naming conventions of existing reflection macros (`@__MODULE__`, `@__FILE__`, etc.).---------Co-authored-by: Jeff Bezanson <jeff.bezanson@gmail.com>
1 parent3b29187 commitbea90a2

File tree

8 files changed

+293
-4
lines changed

8 files changed

+293
-4
lines changed

‎NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ New language features
66

77
- New`Base.@acquire` macro for a non-closure version of`Base.acquire(f, s::Base.Semaphore)`, like`@lock`. ([#56845])
88
- New`nth` function to access the`n`-th element of a generic iterable. ([#56580])
9+
- New`@__FUNCTION__` macro to refer to the innermost enclosing function. ([#58909])
910
- The character U+1F8B2 🢲 (RIGHTWARDS ARROW WITH LOWER HOOK), newly added by Unicode 16,
1011
is now a valid operator with arrow precedence, accessible as`\hookunderrightarrow` at the REPL.
1112
([JuliaLang/JuliaSyntax.jl#525],[#57143])

‎base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ export
10591059
@__DIR__,
10601060
@__LINE__,
10611061
@__MODULE__,
1062+
@__FUNCTION__,
10621063
@int128_str,
10631064
@uint128_str,
10641065
@big_str,

‎base/runtime_internals.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,43 @@ false
173173
"""
174174
ispublic(m::Module, s::Symbol)=ccall(:jl_module_public_p, Cint, (Any, Any), m, s)!=0
175175

176+
"""
177+
@__FUNCTION__
178+
179+
Get the innermost enclosing function object.
180+
181+
!!! note
182+
`@__FUNCTION__` has the same scoping behavior as `return`: when used
183+
inside a closure, it refers to the closure and not the outer function.
184+
Some macros, including [`@spawn`](@ref Threads.@spawn), [`@async`](@ref), etc.,
185+
wrap their input in closures. When `@__FUNCTION__` is used within such code,
186+
it will refer to the closure created by the macro rather than the enclosing function.
187+
188+
# Examples
189+
190+
`@__FUNCTION__` enables recursive anonymous functions:
191+
192+
```jldoctest
193+
julia> factorial = (n -> n <= 1 ? 1 : n * (@__FUNCTION__)(n - 1));
194+
195+
julia> factorial(5)
196+
120
197+
```
198+
199+
`@__FUNCTION__` can be combined with `nameof` to identify a function's
200+
name from within its body:
201+
202+
```jldoctest
203+
julia> bar() = nameof(@__FUNCTION__);
204+
205+
julia> bar()
206+
:bar
207+
```
208+
"""
209+
macro__FUNCTION__()
210+
Expr(:thisfunction)
211+
end
212+
176213
#TODO: this is vaguely broken because it only works for explicit calls to
177214
# `Base.deprecate`, not the @deprecated macro:
178215
isdeprecated(m::Module, s::Symbol)=ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s)!=0

‎doc/src/base/base.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ Base.moduleroot
481481
__module__
482482
__source__
483483
Base.@__MODULE__
484+
Base.@__FUNCTION__
484485
Base.@__FILE__
485486
Base.@__DIR__
486487
Base.@__LINE__

‎doc/src/manual/performance-tips.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,40 @@ In the mean time, some user-contributed packages like
919919
[FastClosures](https://github.com/c42f/FastClosures.jl) automate the
920920
insertion of`let` statements as in`abmult3`.
921921

922+
####Use`@__FUNCTION__` for recursive closures
923+
924+
For recursive closures specifically, the[`@__FUNCTION__`](@ref) macro can avoid both type instability and boxing.
925+
926+
First, let's see the unoptimized version:
927+
928+
```julia
929+
functionmake_fib_unoptimized()
930+
fib(n)= n<=1?1:fib(n-1)+fib(n-2)# fib is boxed
931+
return fib
932+
end
933+
```
934+
935+
The`fib` function is boxed, meaning the return type is inferred as`Any`:
936+
937+
```julia
938+
@code_warntypemake_fib_unoptimized()
939+
```
940+
941+
Now, to eliminate this type instability, we can instead use`@__FUNCTION__` to refer to the concrete function object:
942+
943+
```julia
944+
functionmake_fib_optimized()
945+
fib(n)= n<=1?1: (@__FUNCTION__)(n-1)+ (@__FUNCTION__)(n-2)
946+
return fib
947+
end
948+
```
949+
950+
This gives us a concrete return type:
951+
952+
```julia
953+
@code_warntypemake_fib_optimized()
954+
```
955+
922956

923957
###[Types with values-as-parameters](@id man-performance-value-type)
924958

‎src/ast.scm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@
466466
(define (make-assignment l r) `(= ,l ,r))
467467
(define (assignment? e) (and (pair? e) (eq? (car e)'=)))
468468
(define (return? e) (and (pair? e) (eq? (car e)'return)))
469+
(define (thisfunction? e) (and (pair? e) (eq? (car e)'thisfunction)))
469470

470471
(define (tuple-call? e)
471472
(and (length> e1)

‎src/julia-syntax.scm

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,9 @@
549549
(insert-after-meta `(block
550550
,@stmts)
551551
(cons `(meta nkw ,(+ (length vars) (length restkw)))
552-
annotations))
552+
(if (has-thisfunction? `(block ,@stmts))
553+
(cons `(meta thisfunction-original ,(arg-name (car not-optional))) annotations)
554+
annotations)))
553555
rett)
554556

555557
;; call with no keyword args
@@ -2911,6 +2913,7 @@
29112913
'generator
29122914
(lambda (e)
29132915
(check-no-return e)
2916+
(check-no-thisfunction e)
29142917
(expand-generator e#f'()))
29152918

29162919
'flatten
@@ -2995,6 +2998,13 @@
29952998
(if (has-return? e)
29962999
(error"\"return\" not allowed inside comprehension or generator")))
29973000

3001+
(define (has-thisfunction? e)
3002+
(expr-contains-p thisfunction? e (lambda (x) (not (function-def? x)))))
3003+
3004+
(define (check-no-thisfunction e)
3005+
(if (has-thisfunction? e)
3006+
(error"\"@__FUNCTION__\" not allowed inside comprehension or generator")))
3007+
29983008
(define (has-break-or-continue? e)
29993009
(expr-contains-p (lambda (x) (and (pair? x) (memq (car x)'(break continue))))
30003010
e
@@ -3003,6 +3013,7 @@
30033013

30043014
(define (lower-comprehension ty expr itrs)
30053015
(check-no-return expr)
3016+
(check-no-thisfunction expr)
30063017
(if (has-break-or-continue? expr)
30073018
(error"break or continue outside loop"))
30083019
(let ((result (make-ssavalue))
@@ -3434,7 +3445,7 @@
34343445
vi)
34353446
tab))
34363447

3437-
;; env: list of vinfo (includes any closure #self#;should not include globals)
3448+
;; env: list of vinfo (should not include globals)
34383449
;; captvars: list of vinfo
34393450
;; sp: list of symbol
34403451
;; new-sp: list of symbol (static params declared here)
@@ -3855,7 +3866,7 @@ f(x) = yt(x)
38553866
(Set'(quotetop core lineinfo line inert local-def unnecessary copyast
38563867
meta inbounds boundscheck loopinfo decl aliasscope popaliasscope
38573868
thunk with-static-parameters toplevel-only
3858-
global globalref global-if-global assign-const-if-global isglobal thismodule
3869+
global globalref global-if-global assign-const-if-global isglobal thismodule thisfunction
38593870
const atomic null true false ssavalue isdefined toplevel module lambda
38603871
error gc_preserve_begin gc_preserve_end export public inline noinline purity)))
38613872

@@ -4093,7 +4104,7 @@ f(x) = yt(x)
40934104
((atom? e) e)
40944105
(else
40954106
(case (car e)
4096-
((quotetop core global globalref thismodule lineinfo line break inert module toplevel null true false meta) e)
4107+
((quotetop core global globalref thismodulethisfunctionlineinfo line break inert module toplevel null true false meta) e)
40974108
((toplevel-only)
40984109
;; hack to avoid generating a (method x) expr for struct types
40994110
(if (eq? (cadr e)'struct)
@@ -5133,6 +5144,30 @@ f(x) = yt(x)
51335144

51345145
((error)
51355146
(error (cadr e)))
5147+
5148+
;; thisfunction replaced with first argument name
5149+
((thisfunction)
5150+
(let ((first-arg (and (pair? (lam:args lam)) (car (lam:args lam)))))
5151+
(if first-arg
5152+
(let* ((arg-name (arg-name first-arg))
5153+
;; Check for thisfunction-original metadata in keyword wrapper functions
5154+
(original-name (let ((body (lam:body lam)))
5155+
(and (pair? body) (pair? (cdr body))
5156+
(let loop ((stmts (cdr body)))
5157+
(if (pair? stmts)
5158+
(let ((stmt (car stmts)))
5159+
(if (and (pair? stmt) (eq? (car stmt)'meta)
5160+
(pair? (cdr stmt)) (eq? (cadr stmt)'thisfunction-original)
5161+
(pair? (cddr stmt)))
5162+
(caddr stmt)
5163+
(loop (cdr stmts))))
5164+
#f)))))
5165+
(final-name (or original-name arg-name)))
5166+
(cond (tail (emit-return tail final-name))
5167+
(value final-name)
5168+
(else (emit final-name)#f)))
5169+
(error"\"@__FUNCTION__\" can only be used inside a function"))))
5170+
51365171
(else
51375172
(error (string"invalid syntax" (deparse e)))))))
51385173
;; introduce new slots for assigned arguments

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp