Primary expressions and constant expressions, clarification request

Jens Gustedt (INRIA France)

2022-06-14

org: ISO/IEC JCT1/SC22/WG14 document: N3010
target: IS 9899:2023 version: 2
date: 2022-06-14 license: CC BY

Summary of Changes

Introduction

When preparing the recent proposals for changes to constant expressions (keywords false and true, constexpr and nullptr) it came to me that the text on primary expressions is not completely clear concerning the specialized properties of expressions that the standard knows about. The general idea is that these propagate well through primary expressions, but the current text only mentions lvalues, function designators and void expressions explicitly.

Recently, this ambiguity has also led some uncertainty in the clang/LLVM community and from there to a lengthy discussion on the WG14 reflector and social media.

There are more properties that give special status to expressions, namely being a constant expression, an integer constant expression, an arithmetic constant expression, an address constant, or a null pointer constant. I don’t think that anybody gets these wrong, but nevertheless it might be worth to add words to the text that make this explicit.

Besides constant expression, these special properties are not defined as syntax terms but only as derived terms. In particular, all appearances (except one) in the syntax of the term constant-expression then have explicit constraints that these in fact are integer constant expressions. It appeared to me that the understanding of the text could be much easier if that property would immediately follow from the syntax.

Impact

This is intended for clarification; the answers to the first two questions, the first three possible modification of the text, and the optional syntax addition should have no normative impact. Only one, separated, normative change is suggested. A lot of polls are taken separately; of most importance for the resultion of questioning from the field are the first two.

Proposed wording

Changes are proposed against the wording in C23 draft n2912. Some of the other recent proposal may textually interact or even conflict with the proposal, here, but these should be resolvable by editorial decisions, only.

Primary expressions (6.5.1)

change p5 and p 6, and move text into a footnote

5 A parenthesized expression is a primary expression. Its type and, value and semantics are identical to those of the unparenthesized expression. It is an lvalue, a function designator, or a void expression if the unparenthesized expression is, respectively, an lvalue, a function designator, or a void expression.

6 A generic selection is a primary expression. Its type and, value and semantics depend on the selected generic association, as detailed in the following subclause.

Add a new paragraph p7

7 A parenthesized expression or a generic selection are said to be derived from the unparenthesized expression or from the selected generic association, respectively. FNT1)

FNT1) Such a primary expression is a constant expression, an integer constant expression, an arithmetic constant expression, an address constant, a null pointer constant, an lvalue, a function designator, or a void expression if the expression from which it is derived is, respectively, a constant expression, an integer constant expression, an arithmetic constant expression, an address constant, a null pointer constant, an lvalue, a function designator, or a void expression.

Generic selection (6.5.1.1)

change p4 and move text into a footnote

4 The type and, value and semantics of a generic selection are identical to those of its result expression. It is an lvalue, a function designator, or a void expression if its result expression is, respectively, an lvalue, a function designator, or a void expression.

Constant expressions (6.6)

Emphasize that all constants may also appear inside primary expressions. Also make it clear that only evaluated parts account for the property.

6 An integer constant expression130) shall have integer type and shall only have evaluated operands that are integer constants, enumeration constants, character constants, predefined constants, or other primary expressions that are derived from these, or sizeof expressions whose results are integer constants, alignof expressions, and floating constants (or derived primary expressions) that are the immediate operands of casts. Cast operators in an integer constant expression that are evaluated shall only convert arithmetic types to integer types, except as part of an operand to the typeof operators, sizeof operator, or alignof operator.

7 More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:

or a primary expression that is derived from these.

8 An arithmetic constant expression shall have arithmetic type and shall only have evaluated operands that are integer constants, floating constants, enumeration constants, character constants, predefined constants,or other primary expressions that are derived from these, or sizeof expressions whose results are integer constants, and alignof expressions. Cast operators in an arithmetic constant expression that are evaluated shall only convert arithmetic types to arithmetic types, except as part of an operand to the typeof operators, sizeof operator, or alignof operator.

9 An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or the cast of an integer constant cast(or derived primary expression) to pointer type, or implicitly by the use of an expression of array or function type. The array-subscript [] and member-access . and -> operators, the address & and indirection * unary operators, and pointer casts may be used in the creation of an address constant, but the value of an object shall not be accessed by use of these operators.

Optional changes, introduce new syntax terms

Currently, terms are introduced in clause 6.6 that are then implicitly used in syntax derivations. A common pattern is for example to have “constant-expression” in a syntax rule and then to have a constraint that the particular constant expression must be an integer constant expression. We think that this would be easier to read (and less verbose at the same time) if we introduce the terms directly as syntax terms and then use these syntax terms consequently in the sequel.

Constant expressions (6.6)

Syntax

1 constant-expression:

conditional-expression

integer-constant-expression:

constant-expression

arithmetic-constant-expression:

constant-expression

address-constant:

constant-expression

null-pointer-constant:

constant-expression

Description

2 A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be. Null pointer constants have been described in 6.3.2.3.

Bitfields (6.7.2.1)

member-declarator:

declarator

declaratoropt : integer-constant-expression

Enumerator specifiers (6.7.2.2)

enumerator:

enumeration-constant attribute-specifier-sequenceopt

enumeration-constant attribute-specifier-sequenceopt = integer-constant-expression

Constraints

2 The integer constant expression that defines the value of an enumeration constant shall be an integer constant expression that hashave a value representable as an int.

Alignment specifier (6.7.5)

Syntax

1 alignment-specifier:

_Alignas ( type-name )

_Alignas ( integer-constant-expression )

Constraints

2 An alignment specifier shall appear only in the declaration specifiers of a declaration, or in the specifier-qualifier list of a member declaration, or in the type name of a compound literal. An alignment specifier shall not be used in conjunction with either of the storage-class specifiers typedef or register, nor in a declaration of a function or bit-field.

3 The integer constant expression shall be an integer constant expression. It shall evaluate to a valid fundamental alignment, or to a valid extended alignment supported by the implementation for an object of the storage duration (if any) being declared, or to zero.

Initialization (6.7.9)

in p1 modify

designator:

[ integer-constant-expression ]

. identifier

modify p6

6 If a designator has the form

[ integer-constant-expression ]

then the current object (defined below) shall have array type and the expression shall be an integer constant expression. If the array is of unknown size, any nonnegative value is valid.

Case labels (6.8.1)

Syntax

1 label:

attribute-specifier-sequenceopt identifier :

attribute-specifier-sequenceopt case integer-constant-expression :

attribute-specifier-sequenceopt default :

labeled-statement:

label statement

The switch statement (6.8.4.2)

change p3

3 The expression of each case label shall be an integer constant expression and n No two of the case integer constant expressions in the same switch statement shall have the same value after conversion. There may be at most one default label in a switch statement. (Any enclosed switch statement may have a default label or case integer constant expressions with values that duplicate case integer constant expressions in the enclosing switch statement.)

change p5

5 The integer promotions are performed on the controlling expression. The integer constant expression in each case label is converted to the promoted type of the controlling expression. If a converted value matches that of the promoted controlling expression, control jumps to the statement following the matched case label. Otherwise, if there is a default label, control jumps to the statement following the default label. If no converted case constant expression matches and there is no default label, no part of the switch body is executed.

Preprocessing directives (6.10)

change in p1

if-group:

# if integer-constant-expression new-line groupopt

# ifdef identifier new-line groupopt

# ifndef identifier new-line groupopt

elif-groups:

elif-group

elif-groups elif-group

elif-group:

# elif integer-constant-expression new-line groupopt

# elifdef identifier new-line groupopt

# elifndef identifier new-line groupopt

Conditional inclusion (6.10.1)

change p6

6 Preprocessing directives of the forms

# if integer-constant-expression new-line groupopt

# elif integer-constant-expression new-line groupopt

check whether the controlling integer constant expression evaluates to nonzero.

and change the term

controlling integer constant expression

where it occurs.

Static assertion (6.7.10), normative change

For static assertions the situation is a bit different because the requirement to be an integer constant expression is not formulated as a constraint; if the expression is not an integer constant expression the behavior is undefined.

This a divergence with C++, which allows all constant expressions that are convertible to bool. If WG14 would aggree to this change, this would be a normative change, although this would only affect existing implementations, not existing code.

On the other hand, it would reduce the number of cases for which C and C++ are different. This would a win, since this is a feature that C copied from C++.

Syntax

1 static_assert-declaration:

static_assert ( constant-expression , string-literal ) ;

static_assert ( constant-expression ) ;

Constraints

2 The constant expression shall compare unequal to 0.

Semantics

3 The constant expression shall be an integer constant expression. If the value of the constant expression compares unequal to 0, the declaration has no effect. Otherwise, the constraint is violated and the implementation shall produce a diagnostic message which should include the text of the string literal, if present.

Questions to WG14

  1. Is there an intended semantic difference between the use of an expression expr and its parametrized version (expr)?

  2. Is there an intended semantic difference between the use of the result expression of a generic selection and the generic selection itself?

  3. Does WG14 want to integrate the changes for the Sematics clause of primary expressions, 3.1 of N3010, into C23?

  4. Does WG14 want to integrate the changes for generic expressions, 3.2 of N3010, into C23?

  5. Does WG14 want to integrate the changes for primary expressions in constant expressions, 3.3 of N3010, into C23?

  6. Does WG14 want to integrate the syntax additions for constant expression, 4.1 to 4.9 of N3010, into C23?

  7. Does WG14 want to integrate the syntax additions for constant expression, 4.1 to 4.9 of N3010, into a future version of C?

  8. Does WG14 want to integrate the change for static_assert, 5 of N3010, into C23?

  9. Does WG14 want to integrate the change for static_assert, 5 of N3010, into a future version of C?