Search code examples
cgccgnunested-functionstatic-initialization

Why is the address of a nested function (GNU extension) in GCC considered "not constant" by the compiler?


The GNU C compiler contains a nice extension to the C language, called Nested Functions. However, the documentation is unclear in some points. For example, it says that

It is possible to call the nested function from outside the scope of its name by storing its address or passing the address to another function [...]

If you try to call the nested function through its address after the containing function exits, all hell breaks loose.

If you try to call it after a containing scope level exits, and if it refers to some of the variables that are no longer in scope, you may be lucky, but it’s not wise to take the risk.

If, however, the nested function does not refer to anything that has gone out of scope, you should be safe.

So on one hand, it says that if you call a nested function after the containing function exits, "all hell breaks loose", but a few sentences ahead it says that there are circumstances in which it is fine to do so.

I imagine that by "things going out of scope" they mean automatic variables, so in particular it should be safe to refactor

static int f() { ... }

int outer() {
    ... // some use of f
}

into

int outer() {
    int f() { ... } // f does not refer to outer's local variables
    ... // some use of f
}

if there are no other uses of f in the module than those in the outer function, even assuming that the outer function somehow leaks the address of f beyond its own scope.

However, I was surprised to find out that the following code doesn't compile

int main(void) {
    void nested_function(void) {}

    static const struct { void (*function_pointer)(void); } s = {
        .function_pointer = nested_function
    };

    return 0;
}

with the complaint that initializer element is not constant (i.e. nested_function).

Is there any reason for this limitation? (I can't imagine the address of a function being non-constant, even if it is nested)


Solution

  • In the current GCC implementation, an unnecessary static chain pointer for nested functions is only omitted during optimization. It is not reflected in the type system (unlike a C++ lambda that does not bind anything). Without optimization, the compiler has to create a trampoline on the stack, so the function address is actually non-constant in that case.