Using GCC's Nested Functions with Wide Pointers and no Trampolines II
13 points by accelbread
13 points by accelbread
When I implemented functions with capture, in https://myrlang.org, I found one of the most common mistakes users made was keeping a function pointer around longer than its captured variables.
Basically, the classic holding on to a stack variable longer than the stack frame problem, but it's quite a bit easier to miss if you have a large function body that accidentally captures an enclosing variable.
Htab*
mk_htab(void)
{
/* code here */
int i = ...;
/* code here */
uint32_t hash(Thing *t) {
/* oops: should have been 'int i =...' */
for(i = 0; i < t->len; i++)
}
ht->hashfn = hash;
/* oops, use after free */
return ht;
}
I'm not sure C needs this footgun too.
This feature makes GCC 17 very exciting. I'm looking forward to using this in my personal projects to clean up callback APIs.
In general, I think public library APIs still need to take a function pointer and context arg, and pass the context to the function, since otherwise updating to function pointer and static chain would prevent non-nested function passing. There would still be reasons to use non-nested functions, such as when the callback is expected to outlive the stack frame and the context is on static or heap memory with appropriate lifetime.
I'm interested if those APIs can not only pass the context to the context argument, but also pass it in the static chain register, enabling it to handle both regular functions and nesting functions using surrounding variables. That would depend on it being ok to pass some arbitrary value in the static chain register to a function that doesnt need the static chain.
Nice. I think I may have been involved in the warning about returning local nested capturing function pointers; colleague and I reported this as a missing stack pointer return/silent disabling of NX back in GCC 13 or so. I always found the implementation of this extension terrifying, this looks significantly better.