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. Yet, 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