Introduce storage-class specifiers for compound literals

Alex Gilding (Perforce UK)

Jens Gustedt (INRIA France)

2022-04-08

org: ISO/IEC JCT1/SC22/WG14 document: N2955
target: IS 9899:2023 version: 3
date: 2022-04-08 license: CC BY

Abstract

We propose adding storage-class specifiers such as constexpr or static to compound literals such that this feature gains the full semantic capacities as have declared objects.

Summary of Changes

Rationale

Currently, the feature of compound literals does not have the capacity to express all aspects that ordinary object definitions have.

Recently and independently two papers expressed the idea to extend the current syntax of compound literals such that storage-class specifiers can be used as if for the definition of an object.

N2530
observed that the storage duration of a compound literal is only determined by its syntactic positon; in particular it can not be changed to have static storage duration if found in block scope. That paper provides a good overview over the caveats and restrictions that programmers currently face because of that limitation.
N2917
proposed to extend compound literals with constexpr such that such compound literals potentially can be used anywhere as a constant expression of the appropriate kind. For a rationale for that we refer to that paper and more generally to N2954 which introduces the constexpr feature as such.

For this paper here, we thus propose to extend the feature with a list of possible storage-class specifiers, namely static, register and constexpr. Other possible additions concern thread_local, auto or __auto_type. We leave it to WG14 to determine the list of admissible specifiers.

Syntactically this is just done by allowing to prefix the type name of the compound literal by a list of these storage-class specifiers.

Prior art

GNU C (and followed by IBM XL C for compatibility) already allows the use of compound literals for the initialization of objects with static storage duration such as in

static struct foo  x =  (struct foo) {1, 'a', 'b'}; // initialize with a value, need not be modifiable

If this is found inside a block (and not in file scope) the compound literal has automatic storage duration and would thus not be suitable as initializer for static variables. GNU C gets away with this by extending the definition of constant expression to “compound literals where all initializer expressions are constant expressions”.

This feature does not give syntactic control to the user for this construct; it could just be an error for which the user would like to have a diagnostic. Our proposal, when using constexpr makes this feature explicit.

static struct foo  x =  (constexpr struct foo) {1, 'a', 'b'}; // initialize with a value, need not be modifiable

The same feature for gcc when we take the address of the compound literal is not allowed

static struct foo* p = &(struct foo) {1, 'a', 'b'}; // initialize with an address, target modifiable

and produces an error that the intializer is not constant. So for gcc the expression (struct foo) {1, 'a', 'b'} is constant in some places and others isn’t.

The only way around this in C17 is to use a static variable

static struct foo  Unique =  (struct foo) {1, 'a', 'b'}; // initialize with a value, need not be modifiable
static struct foo* p      =   &Unique;                   // initialize with an address, target modifiable

Our proposal avoids a declaration of a variable that is otherwise unused.

static struct foo* p = &(static struct foo) {1, 'a', 'b'}; // initialize with an address, target modifiable

Proposal

The proposal is based on N2954 and N2819 (already voted into C23).

Postfix operators (6.5.2)

1 postfix-expression:

primary-expression

postfix-expression [ expression ]

postfix-expression ( argument-expression-listopt )

postfix-expression . identifier

postfix-expression -> identifier

postfix-expression ++

postfix-expression --

( type-name ) { initializer-list }

( type-name ) { initializer-list , }

compound-literal

Compound literals (6.5.2.5)

Add a whole new syntax section

Syntax

1’ compound-literal:

( storage-class-specifiersopt type-name ) { initializer-listopt }

( storage-class-specifiersopt type-name ) { initializer-list , }

storage-class-specifiers:

storage-class-specifier

storage-class-specifiers storage-class-specifier

Add a paragraph and footnote at the end of the constraint section.

2’ If the compound literal is evaluated outside the body of a function and outside of any parameter list, it is associated with file scope; otherwise, it is associated with the enclosing block. Depending on this association, the storage-class specifiers SC (possibly empty),FNT0) type name T, and initializer list, if any, shall be such that they are valid specifiers for an object definition in file scope or block scope, respectively, of the following form,

where ID is an identifier that is unique for the whole program and where IL is a (possibly empty) initializer list with nested structure, designators, values and types as the initializer list of the compound literal.

FNT0) If the storage-class specifiers contain the same storage-class specifier more than once, the following constraint is violated.

Remove the textual definition and add words for the storage-class specifiers.

3 A postfix expression that consists of a parenthesized type name followed by a brace-enclosed list of initializers is a compound literal It provides an unnamed object whose value is given by the initializer list, type, storage duration and other properties are as if given by the definition syntax in the constraints; if the storage duration is automatic, the lifetime of the instance of the unnamed object is the current execution of the enclosing block.107) If the storage-class specifiers contain other specifiers than constexpr , static , or register, the behavior is undefined.

4 If the type name specifies an array of unknown size, the size is determined by the initializer list as specified in 6.7.9, and the type of the compound literal is that of the completed array type. Otherwise (when the type name specifies an object type), the type of the compound literal is that specified by the type name. In either case, the result is an lvalue.

5 The value of the compound literal is that of an lvalue corresponding to the an unnamed object initialized by the initializer list. If the compound literal is evaluated outside the body of a function and outside of any parameter list, the object has static storage duration; otherwise, it has automatic storage duration associated with the enclosing block. If it is evaluated for a function call to determine the variably modified type of a parameter it has a lifetime that starts with the evaluation and that ends with the end of the execution of the call.

Constant expressions (6.6)

If the list of admissible storage-class specifiers for compound literals contains constexpr, add to the new paragraph 6

6 An identifier that is declared with storage-class specifier constexpr and an object type is a named constant. A named constant is a constant expression with the type and value of the declared object. A compound literal with storage-class specifier constexpr is a compound literal constant. A compound literal constant is a constant expression with the type and value of the unnamed object.

Then, in the following three paragraphs (integer expressions, initializers, arithmetic expression) where “named constant” appears in the list, replace by “named constant or compound literal constant”.

Questions to WG14

Does WG14 want to add the changes as indicated in N2955 into C23?

Does WG14 want to add the thread_local storage-class specifier to the list of admissible storage-class specifiers for compound literals for integration into C23?

Does WG14 want to add the auto storage-class specifier to the list of admissible storage-class specifiers for compound literals for integration into C23?

Does WG14 want to add the __auto_type storage-class specifier to the list of admissible storage-class specifiers for compound literals for integration into C23?

References