2026-06-06
Thiago Adams (rationale)
Thiago Adams, Martin Uecker, Jens Gustedt, Joseph Myers, Javier Múgica (wording)
| number | Title | Authors | Remarks |
|---|---|---|---|
| n3679 | Function literals | Thiago R Adams | rationale |
| n3854 | Working Draft | JeanHeyd Meneide | base for diff |
| n3883 | Wording for “discarded” | Wording group | base for access semantics |
| n3884 | Wording for “Local functions” | Wording group | base for local function definitions |
| n3885 | <this paper> | Wording group |
none
none
| meeting | date | for | against | abstain |
|---|---|---|---|---|
| Winter 2026 | Feb 2-6, 2026 | 16 | 5 | 7 |
We chose to orient the wording similar to “Compound literals” in 6.5.3.6
compound-literal:
(storage-class-specifiersopt type-name)braced-initializer
But the proposed syntax has to integrate well with function definitions (not general function declarations) so we use the admissible syntax
function-literal:
(attribute-specifier-sequenceopt declaration-specifiers abstract-declarator)function-body
and describe how this syntax relates to
function-definition:
attribute-specifier-sequenceopt declaration-specifiers declarator function-body
When we do so, we still have an ambiguity, namely whether such a
construct is a compound literal or a function literal. Therefore we
insist that already the part in () distinguishes the
two construct and leads unambiguously to a compound literal or to a
function literal.
Much as for compound literals, this proposal transcribes the definiton of a function literal in block scope to a definition of a local feature, here a local function. Thus this proposal heavily depends on n3884 to be adopted.
Currently, C does not have terminology for the storage duration of functions because the lifetime of functions is always the whole program execution. For the current proposal it is clear that for all function literals that are constructed we want similar properties as for objects with static storage duration. Additionally, where implementations want to add other “storage durations” to that, the new syntax should not prevent such extension.
Also, expectation of users could be that if no such storage class is given, that there are similar rules in place as for compound literals. There, we always have static storage duration in file scope, but in block scope the storage duration can be decided by the compiler. So on a platform with extension for automatic local functions (such as gcc) the expectation of a user could be that a function literal also follows that extension. So we prefer to have a clear syntax for the new feature that leaves no room for ambiguity.
We think that using static as
storage-class specifier should be the minimally supported feature; static specified
functions should work well in file and block scope. Nevertheless, the
possibility for extensions seems important and so we leave for room for
them by making the set of admissible storage-class specifiers
implementation-defined.
For function literals, it seems important that we offer the
possibility to specify attributes that apply to the underlying function
definition, in particular [[noreturn]],
see below. Therefore we add an optional attribute specifier sequence in
front.
Note that this is a feature that is not present for compound literals; we suspect that it simply had been forgotten during the process of integrating attributes into C. There is a pending C23-issue concerning this.
Ordinary functions can be specified with two additional specifiers,
inline and
_Noreturn, but
which are mostly useless for function literals. To not constrain users
and implementations too much, we keep them allowed for function
literals.
The specification of inline for an in
place feature is clearly superfluous: the code is visible everywhere and
it might be used directly. Also, the only normative impact on function
definitions is on the linkage of the function name, which is
pointless for a function without such a name.
_Noreturn
can be replaced by [[noreturn]]
with the same semantics.
Deletions in the shown standard text are as shown here,
additions, as shown here. These may render differently
according to the style in which the document is shown by your browser,
but should always be well distinguishable. In the provided style there
are two visual distinctions:
Close to each other proposed changes resemble like this.
Semantics
1 In the body of a function that is defined with a name,
Tthe identifier__func__shall be implicitly declared by the translator as if, immediately following the opening brace of each function definition, the declaration …
2 This name is encoded as if the implicit declaration had been written in the source character set and then translated into the execution character set as indicated in translation phase 5.
2′ Similarly, in the body of a function literal,
__func__is declared in the same way, only that here"function-name"is an implementation-defined non-empty multi-byte string literal that does not contain control characters; such string literals need not to be unique.
In 1, add function-literal to the end of the derivation of postfix-expression. Then add a new paragraph
Constraints
2 A postfix expression starting
(storage-class-specifiersopt type-name){
such that the type name refers to an object type shall be a compound literal; otherwise, it shall be a function literal.
6.5.3.7 Function literals
Syntax
1 function-literal:
(attribute-specifier-sequenceopt declaration-specifiers abstract-declarator)function-body
Constraints
2 If a function literal
(ASS DS AD) BODYis associated with file scope or block scope (see 6.2.1),SCFSare the storage-class and function specifiers inDS, possibly empty,SPLis the resulting specifier qualifier list where the storage-class and function specifiers are removed fromDS, andIDis an identifier that is unique for the whole program, then
ASS SCFS typeof(SPL AD) ID;
shall be a valid function declaration in the same scope as the function literal. Furthermore, there shall be a declarator
Dsuch that
- the token
IDoccurs exactly once in the token sequence ofD,- removing the token
IDfromDresults in the same token sequence asAD,
and such that
ASS DS D BODY
in the same scope as the function literal shall be a compatible function definition with name
ID.
3 All the constraints for function definitions (6.9.2) also apply to function literals.
Semantics
4 Independently of the scope in which it appears, the type of a function literal is
typeof(SPL AD)as given for the declaration syntax in the constraints. For a function literal associated with function prototype scope the type is determined at translation time and no function is created; the parameter declaration of which it is part discards the function literal. Otherwise, a function literal designates an unnamed function whose type and properties (other than its name and the corresponding linkage) are as if given by the definition syntax in the constraints. It is implementation-defined if and where a function literal with no storage-class specifiers is accepted; the admissible set of such storage-class specifiers containsstaticand is otherwise implementation-defined.
5 The optional attribute specifier sequence in a function literal appertains to the unnamed function. All the semantic rules for function definitions in 6.9.2 also apply to function literals.
6 Function literals are not required to designate distinct functions.
EXAMPLE 1 Function literals are expressions that provide access to anonymous functions. Their specification is kept close to where their usage occurs. If needed, access to an object with automatic storage duration can be enabled by passing a pointer to it as a function parameter. If the lifetime of such an object has ended because the call to the enclosing function has ended, the use of the pointer has undefined behavior. Also, it is implementation-defined if the access to objects with automatic storage duration is limited to the current thread.
#include <stdio.h> void for_each_file(const char* path, void* data, void callback(void* data, const char* filename)); int main() { enum filter { ALL, SMALL } options = ALL; /* automatic storage duration */ ... for_each_file("c:", &options, (static void (void* p, const char* file_name)){ enum filter* captured = p; if (*captured == ALL) { /*do something*/ } }); }
EXAMPLE 2 If the transferred data has allocated storage duration, the call of the function literal is independent of the context in which the function literal occurs, such as whether the enclosing function call already has been terminated or whether the call to the function literal happens within another thread.
#include <stdio.h> #include <stdlib.h> void async(void * data, void callback(void * data)); int main() { int * capture = calloc(1, sizeof *capture); /* allocated storage duration */ *capture = 123; ... async(capture, (static void (void* data)){ int* p = data; printf("value=%d\n",*p); free(p); }); }
EXAMPLE 3 In the following code a type-genetric macro is implemented with a function literal.
SWAP_LITERALproduces an unnamed function designator that has the sought parameter types. This is used to produce a function pointerswapdof typevoid (*)(double*, double*)and a type-generic macroSWAP.
#define SWAP_LITERAL(a, b) \ (static void (typeof(a)* arg1, typeof(b)* arg2)) { \ typeof(*arg1) temp = *arg1; \ *arg1 = *arg2; \ *arg2 = temp; \ } void (*const swapd)(double*, double*) = SWAP_LITERAL(0.0, 0.0); #define SWAP(a, b) SWAP_LITERAL(*(a), *(b))((a), (b)) int main() { double A = 1.0; double B = 2.0; swapd(&A, &B); int a = 1; int b = 2; SWAP(&a, &b); }