Implementation of GCC's Nested Functions (vs. C++ Lambdas)
Recorded: Sept. 8, 2026, 7 p.m.
| Original | Summarized |
Martin Uecker
Blog 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. Let us start with a very simple example. Here, the nested function does not access any variable of the parent But let's consider an example where a nested function accesses a int foo(int k) When executed the nested function needs to be able to find the variable In GCC, nested functions are lowered in an early middle-end pass. struct frame { int k; }; static int bar(struct frame *f, int x) int foo(int k) "foo": If there are multiple nesting levels, the structure also contains a link It is interesting to compare this to how lambdas work in C++. Another difference at the language level is that the visible type of the Apart from these two differences, the semantics of nested functions are int foo(int k) If one looks a bit deeper, the implementation mechanism behind GCC's struct bar_anonymous { inline int bar_anonymous::operator() (int x) int foo(int k) There is still one remaining difference, which int foo(int k) return bar1(1) + bar2(1); In this case, GCC will create a single frame structure in the parent function struct frame { int k; }; static int bar1(struct frame *f, int x) static int bar2(struct frame *f, int x) int foo(int k) In contrast, a C++ compiler will produce two separate objects for each lambda expression, struct bar1_anonymous { inline int bar1_anonymous::operator() (int x) struct bar2_anonymous { inline int bar2_anonymous::operator() (int x) int foo(int k) return bar1(1) + bar2(1); Despite this difference in implementation, the GNU C and C++ versions GCC's nested function correspond to a small semantic subset of C++'s lambda GCC, Nested Functions |
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. |