LmCast :: Stay tuned in

Implementation of GCC's Nested Functions (vs. C++ Lambdas)

Recorded: Sept. 8, 2026, 7 p.m.

Original Summarized

Martin Uecker

Blog
Contact/Impressum

Implementation of GCC's Nested Functions (vs. C++ Lambdas)

Martin Uecker, 2026-09-05

Introduction

Here, I want to explain how GCC's nested function are implemented.
I am not going to discuss taking the address of a nested function
that may require the creation of a trampoline. We discussed this
topic - and how to get around it - already in several
previous blog posts. Instead,
I want to describe the basic mechanism that is used to access
variables of a parent function.

Nested Functions

Let us start with a very simple example.

int foo(int k)
{
int bar(int x) { return x + 1; }
return bar(k);
}

Here, the nested function does not access any variable of the parent
function. In this case, it can simply be lifted out of the parent
function and be compiled as a separate function. Such functions can
still can be useful to define small helper functions, or when locally
defining a type that can then be used in the nested function.
WG14 is currently considering proposal N3884 that would allow such
non-capturing local functions when defined with the static
storage class.

But let's consider an example where a nested function accesses a
variable of the parent function.

int foo(int k)
{
int bar(int x) { return x + k; }
return bar(1);
}

When executed the nested function needs to be able to find the variable
k of the parent function (assuming it is not completely optimized
away as would be the case here). Traditionally, this was implemented
by passing it a pointer to the parent's stack frame, where it then can
access the variable at the right stack slot. These techniques were used in
PASCAL and similar languages, and x86 even has special instructions,
i.e. enter and leave, to support this. However, this is not how GCC implement this
feature today.

In GCC, nested functions are lowered in an early middle-end pass.
During this pass, all variables of the parent that are accessed by the nested
function are collected into a single synthetic structure, and a pointer
to this structure is passed to the nested function in a hidden argument.
Accesses to such variables are rewritten to access the corresponding member
of this structure. The resulting code is essentialy the following
(Godbolt Example).

struct frame { int k; };

static int bar(struct frame *f, int x)
{
return x + f->k;
}

int foo(int k)
{
struct frame frame = { k };
return bar(&frame, 1);
}


The main advantage of this approach is that this decouples the
implementation of nested functions from the rest of the compiler, which can
simply treat the static pointer as an additional hidden argument pointing
to a regular structure. Other variables of the parent function that are not
accessed by any child are not affected at all. Also the frame structure itself can be
optimized as any other structure that exists in the program. For example, the
example above is simplified to a simple addition by generic optimizer code
that does not know anything specific about nested functions.

"foo":
lea eax, [rdi+1]
ret

If there are multiple nesting levels, the structure also contains a link
to the frame structure one layer up, creating a list (chain) of frame
structure, but this is rarely needed.


Comparison to C++'s Lambda Feature

It is interesting to compare this to how lambdas work in C++.
There are, of course, some superficial differences in how this feature
is exposed on the language level. Lambdas are function literals which
have no name and are expressions, while GCC's nested functions are
regular function definitions that appear in the nested context.
But this is not a fundamental difference from an implementation
point of view.

Another difference at the language level is that the visible type of the
nested function in GCC is a regular function type. In contrast, in C++ the type
of a lambda is a Voldemort type, an unique anonymous type that can not be named.

Apart from these two differences, the semantics of nested functions are
a subset of C++'s lambda. In fact, the example above can simply be
rewritten into C++ by using a lambda object.

int foo(int k)
{
auto bar = [&](int x) -> int { return x + k; };
return bar(1);
}

If one looks a bit deeper, the implementation mechanism behind GCC's
nested function is also not very different to how a C++ compilers translates
a lambda to a callable object: C++'s lambdas are also converted into structures
(or rather callable objects in C++) that contain a copy or reference to
the captured variables.

struct bar_anonymous {
int &k
int operator() (int x);
};

inline int bar_anonymous::operator() (int x)
{
return x + k;
}

int foo(int k)
{
bar_anonymous bar(k);
return bar(1);
}

There is still one remaining difference, which
can be explained best with an example where there are two nested functions.

int foo(int k)

int bar1(int x) { return x + 2 * k; }
int bar2(int x) { return x + 3 * k; }

return bar1(1) + bar2(1);
}

In this case, GCC will create a single frame structure in the parent function
containing k and both nested functions will receive the exact same
pointer to this shared environment.

struct frame { int k; };

static int bar1(struct frame *f, int x)
{
return x + 2 * f->k;
}

static int bar2(struct frame *f, int x)
{
return x + 3 * f->k;
}

int foo(int k)
{
struct frame frame = { k };
return bar1(&frame, 1) + bar2(&frame, 1);
}

In contrast, a C++ compiler will produce two separate objects for each lambda expression,
each containing a reference to the same k variable on the stack.

struct bar1_anonymous {
int &k
int operator() (int x);
};

inline int bar1_anonymous::operator() (int x)
{
return x + k;
}

struct bar2_anonymous {
int &k
int operator() (int x);
};

inline int bar2_anonymous::operator() (int x)
{
return x + k;
}

int foo(int k)
{
bar1_anonymous bar1(k);
bar2_anonymous bar2(k);

return bar1(1) + bar2(1);
}

Despite this difference in implementation, the GNU C and C++ versions
of this example have the exact same semantics.

Conclusion

GCC's nested function correspond to a small semantic subset of C++'s lambda
and even though they historically evolved from a different approach, their
implementation is not fundamentally different. A compiler that
already implements C++ could expose a feature with the same syntax
and semantics as GCC's nested functions based on its existing support
for lambdas.

Literature

GCC, Nested Functions
Jens Gustedt, N3884: Wording for "Local functions"
Raynmond Chen, The mysterious second parameter to the x86 ENTER instruction

The discussion focuses on the implementation mechanism of nested functions within the GCC compiler and compares this approach to the features provided by C++ lambdas. The initial focus is on how nested functions access variables defined in their parent scope, contrasting it with traditional methods like passing pointers to stack frames used in languages such as PASCAL or x86 assembly instructions.

In GCC, nested functions are implemented by lowering them during an early middle-end pass. During this process, the parent function's variables accessed by the nested function are collected into a single synthetic structure, referred to as a frame, and a pointer to this frame is passed to the nested function as a hidden argument. Accesses to parent variables are then rewritten to access corresponding members within this structure. This method offers significant advantages: it decouples the implementation of nested functions from the general compiler, allowing the compiler to treat the static pointer as an additional hidden argument pointing to a standard structure. Furthermore, only the variables actually accessed by the child are included in the frame structure, ensuring that parent variables not referenced by any nested function remain unaffected and enabling generic optimization across the entire structure. If multiple nesting levels exist, the frame structure also includes links to parent frame structures, forming a chain, although this linkage is rarely necessary in practice.

When comparing this mechanism to C++ lambdas, the superficial differences stem from language-level exposure; lambdas are function literals without names and expressions, whereas GCC's nested functions are regular function definitions. However, semantically, the behavior of nested functions represents a subset of C++ lambda capabilities. The underlying implementation mechanisms share a conceptual similarity: both systems involve translating the concept into structures or callable objects that contain references to captured variables.

A critical distinction emerges when considering multiple nested functions. In GCC's implementation, all related nested functions receive the exact same pointer to a single shared frame structure containing the parent function's variables. In contrast, a C++ compiler typically produces separate objects for each lambda expression, with each object holding its own reference to the captured variables on the stack. Despite this difference in how the environment is managed—shared scope versus separate instances—the resulting semantics of the calculations remain identical. Ultimately, the authors conclude that GCC's nested functions correspond to a small semantic subset of C++ lambdas, and because their implementation techniques are fundamentally analogous, a compiler capable of implementing C++ could expose functionality with the same syntax and semantics as GCC's nested functions by leveraging its existing lambda support.