# How does \`Base.@locals\` work?

**URL:** <https://discourse.julialang.org/t/how-does-base-locals-work/133483>\
**Category:** General Usage\
**Tags:** internals\
**Created:** [October 28, 2025, 2:42pm UTC](https://discourse.julialang.org/t/how-does-base-locals-work/133483 "2025-10-28T14:42:34Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)\
**Post date:** [October 28, 2025, 2:42pm UTC](https://discourse.julialang.org/t/how-does-base-locals-work/133483/1 "2025-10-28T14:42:34Z")

</div>

`Base.@locals` has [this](https://github.com/JuliaLang/julia/blob/065ec06a5fa937ff9748bde2c558f4983b472f13/base/runtime_internals.jl#L552) very curious implementation:

```julia
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](https://github.com/JuliaLang/julia/blob/065ec06a5fa937ff9748bde2c558f4983b472f13/doc/src/devdocs/ast.md).

I don’t need to know this for anything, I’m just curious.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [October 28, 2025, 3:25pm UTC](https://discourse.julialang.org/t/how-does-base-locals-work/133483/2 "2025-10-28T15:25:08Z")

</div>

You can see what’s happening with `Meta.lower`, which applies syntax transformations.

```julia-auto
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.
