Wording for “Function literals”

2026-06-06

Preamble

contributing

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

LaTeX document branch

none

Liaison

none

Relevant polls

Does WG14 think a proposal along the lines of N3678 and N3679 without captures is useful? 16-5-7

meeting date for against abstain
Winter 2026 Feb 2-6, 2026 16 5 7

Choices proposed for the wording

Principal syntax

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.

Storage-class specifier

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.

Attributes

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.

Function specifiers

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.

Proposed wording

Legend

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.

Add to clause 6.4.3.2 “Predefined identifiers”

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 6.5.3.1 for postfix operators

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.

Add a new subclause 6.5.3.7

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) BODY is associated with file scope or block scope (see 6.2.1), SCFS are the storage-class and function specifiers in DS, possibly empty, SPL is the resulting specifier qualifier list where the storage-class and function specifiers are removed from DS, and ID is 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 D such that

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 contains static and 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_LITERAL produces an unnamed function designator that has the sought parameter types. This is used to produce a function pointer swapd of type void (*)(double*, double*) and a type-generic macro SWAP.

#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);
}