mhauru
October 28, 2025, 2:42pm
1
Base.@locals has this very curious implementation:
macro locals()
return Expr(:locals)
end
How does this work? Is locals some sort of special node type in the AST? I can’t find it documented .
I don’t need to know this for anything, I’m just curious.
You can see what’s happening with Meta.lower, which applies syntax transformations.
julia> Meta.lower(Main, @macroexpand Base.@locals)
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ %1 = Core.apply_type(Base.Dict, Core.Symbol, Core.Any)
│ %2 = (%1)()
└── return %2
))))
julia> Meta.lower(Main, @macroexpand f() = Base.@locals)
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ $(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ return $(Expr(:method, :f))
)))
│ $(Expr(:method, :f))
│ %3 = f
│ %4 = Core.Typeof(%3)
│ %5 = Core.svec(%4)
│ %6 = Core.svec()
│ %7 = Core.svec(%5, %6, $(QuoteNode(:(#= REPL[9]:1 =#))))
│ $(Expr(:method, :f, :(%7), CodeInfo(
@ REPL[9]:1 within `none`
1 ─ %1 = Core.apply_type(Base.Dict, Core.Symbol, Core.Any)
│ %2 = (%1)()
└── return %2
)))
│ %9 = f
└── return %9
))))
julia> Meta.lower(Main, @macroexpand f(x) = Base.@locals)
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ $(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ return $(Expr(:method, :f))
)))
│ $(Expr(:method, :f))
│ %3 = f
│ %4 = Core.Typeof(%3)
│ %5 = Core.svec(%4, Core.Any)
│ %6 = Core.svec()
│ %7 = Core.svec(%5, %6, $(QuoteNode(:(#= REPL[8]:1 =#))))
│ $(Expr(:method, :f, :(%7), CodeInfo(
@ REPL[8]:1 within `none`
1 ─ %1 = Core.apply_type(Base.Dict, Core.Symbol, Core.Any)
│ %2 = (%1)()
│ %3 = $(Expr(:isdefined, :(x)))
└── goto #3 if not %3
2 ─ Base.setindex!(%2, x, :x)
3 ┄ return %2
)))
│ %9 = f
└── return %9
))))
In other words, yes. Expr(:locals) is a special node type that tells the syntax expansion stage to write out the code to generate the locals it knows about.
2 Likes