Wording for “discarded”

2026-5-23

Preamble

license

CC BY, see https://creativecommons.org/licenses/by/4.0

contributing

Javier A. Múgica (rationale, initial wording, history)

Javier A. Múgica, Joseph Myers, Robert Seacord, Jens Gustedt (wording)

number Title Authors Remarks
n3854 Working Draft Meneide base
n3724 Discarded Múgica rationale
n3883 <this paper> et. al. wording

LaTeX document branch

none yet

Liaison

none

Relevant polls

Along the lines of n3724

date meeting for against abstain
2026-03 teleconference 12 2 5

Document history

N3351

date: 2024-10-06

Defined terms
Instructions discarded because of control flow

The discarded statements blocks of if/case/while/do statements when the controlling expression is an ICE with the adequate value. In addition, instructions following a goto statement that are impossible to reach are also value-discarded.

Implementation latitude

Allows implementation to consider value-discarded other expressions that they will know that will never be evaluated.

Constant expressions

Applies the term value-discarded to the constraint on constant expressions and to the definition of integer and arithmetic constant expressions. Examples:

int a, *p;
int f(void);
static int i = 2 || 1 / 0;
static int j = 2 || a + f();
static int k = sizeof p[sizeof(int[a])];
Initialization

All the expressions in an initializer for an object that has static or thread storage duration or is declared with the constexpr storage-class specifier shall be constant expressions, string literals or value-discarded. (This only allows value-discarded expressions which are part of a constant expression, since the initializer itself is never value-discarded. Hence, “with respect to the initializer” is not needed.)

External definitions

Applies the term essentially discarded to describe the places where an identifier without a definition is allowed.

N3382

Date: 2024-10-22

A simplification of the original version. Discarded statements are removed. With them the term regular label goes away as well as that of isolated, applied to a statement and the proposal to introduce the term switch label. Another important change is that the previous version delegated the identification of subexpressions that cannot be value-discarded within a value-discarded expression to the rest of the standard, by saying that they are the ones that need to be evaluated (at translation time). Now those subexpressions have been identified as integer constant expressions within type names:

If a type name is value-discarded, expressions within it which are not integer constant expressions are value-discarded. Integer constant expressions not contained in any other expression within the type name are not value-discarded and are evaluated during translation, always, even if the type name is value-discarded. If a type name is not value-discarded, the expressions it contains which are not integer constant expressions are evaluated at runtime when the type name is reached.

EXAMPLE. In the following expression

sizeof(int[1 ? 2 : 1/0]);

the operand int[1 ? 2 : 4-3] is value-discarded, the expression 1 ? 2 : 4-3 within it is not value-discarded, and the subexpression 1/0 is.

A minor change is the application of the term value-discarded to the array length expression that can be replaced by *.

Proposes also the removal of two sentences that now seem superfluous (the one on the evaluation of the type name of a cast operator) or devoid of meaning (the unspecified evaluation of some expressions in sizeof and typeof operators).

N3464

2025-02-07

Terminology
Implementation latitude

Split to a subproposal

Array declarators

Remove the implementation-defined evaluation of the size that is currently present in the standard by mandating evaluation of non-ICE.

Decoupling of value-discarded from runtime-discarded

If the operand of sizeof is an expression its value is not needed for anything. Therefore, it makes sense to say that it is value-discarded, irrespective of its type. We may simply say that the expression is value-discarded and, if the operand is a variable length array, the type is resolved, otherwise it is runtime-discarded.

Split

N3465 Preprocessing integer expressions. To prevent enlarging the class of ICE that the preprocessor needs to handle, such as 1 || &"abcd"[2]. (This now becomes an ICE).

N3504

2025-02-07

Fixes the previous version. Removes the “Decoupling of value-discarded from runtime-discarded”, leaving it for a future proposal.

N3530

2025-04-15

Mostly changes in the exposition, not the wording. ICE within type names are ignored for the relation “discarded relative to”, to simplify the definition of the latter. (According to feedback but not liked by the author).

N3549

2025-05-23

Several points extracted to separate proposals. The subproposal “allowing discarded pieces in constant expressions” has been removed altogether, leaving it for a future proposal once the concept of “discarded” has been integrated into the standard.

Noted that if an expression is needed for its address, it cannot be discarded.

Terminology
Split

N3724

2025-10-08

Fixes a mistake in the previous version that arose as a consequence of splitting part of the proposal to a separate document.

Added the remark that an array length expression that is treated as * at function-prototype scope is discarded.

Observations and choices by the Wording Group

In version N3549 several points where split to separate proposals or altogether removed. This not only left the text with the error noted and corrected by the author in the next version, as pointed above, but also stripped off from the remaining proposal two observations related to type names that should have been kept: that a type name may be discarded and that the operand of typeof is discarded in some cases. These have been reinstated.

The original idea of the “discarded” concept as presented by the author was that the value of the expression is not determined, hence neither its side effects take place. The Wording Group chose instead as the characteristic property defining the concept “discarded” that translation time information is deemed sufficient and no access to the state of the execution is made. This now concerns

This shift in concept solves the conundrum of ICE within type names, that has been present all throughout the history of the proposal since its inception. Now these ICE do not need exceptional treatment. They are discarded normally as part of a larger construct that is discarded, this not implying any longer that their values are not computed.

The author pointed in several of his versions that the list present in “External Definitions” enumerating the places where an identifier with linkage and without a definition is allowed is incomplete, missing the logical || and && operators and some others, and that replacing the list by “discarded” solves the issue, in addition of resulting in a much concise and cleaner text. Although implicit in that observation and explicit in some previous versions of his proposal, we want to point here that this turns some hitherto invalid code (constraint violation or undefined behaviour, depending on whether the identifier has internal or external linkage) into constant expressions, as for example

extern int f(void);
static int a = 0 && f();

where there is no external definition for f.

Proposed wording

Legend

Deletions in the shown standard text are as shown here, additions, as shown here. These may be rendered differently according to the style in which the document is shown by your browser but should always be well distinguishable. In the style provided there are two visual distinctions:

Close to each other proposed changes resemble like this.

Changes to 5.2.2.4 Program semantics

3 Evaluation of an expression in general includes both value computations and initiation of side effects. In general, vValue computation for an lvalue expression includes determining the identity of the designated object. At translation time, some operands, named constants subject to lvalue conversion, and array length expressions are determined to be discarded and consequently not evaluated when they are reached in the execution; the side effects are not produced and the identity of any designated object is not determined.

Changes to 6.3.3.1 Lvalues, arrays, and function designators

2 Except when it is the operand of: If the lvalue is a named constant, and

the value is determined prior to program execution, the type is determined as the unqualified version of the type of the named constant and the expression is discarded. Otherwise, if it is not the operand of:

an lvalue that does not have array type is converted to the value stored in the designated object. (and is The result is no longer an lvalue); this is called lvalue conversion. …

Changes to 6.5.1 General

Semantics

[…]

4 The grouping of operators and operands is indicated by the syntax.67) If an expression is discarded, all its subexpressions and any type names contained within it are also discarded. Discarded expressions do not have their value determined, even during translation, unless otherwise specified.xx) Except as specified later, side effects and value computations of subexpressions are unsequenced.68)

xx) The exceptions are lvalue conversion of some named constants (6.3.3.1) and integer constant expressions within type names (6.7.8).

5 EXAMPLE The following code includes operators that discard operands. The comments note what expressions are discarded by each operator.
sizeof(   // sizeof discards: 2 || i, and 2 and i by propogation.
  2 || i  //    || discards: i
)
sizeof(   // sizeof discards: i || 2, and i and 2 by propogation.
  i || 2  //    || nothing discarded (i is not an ICE)
)

Changes to 6.5.2.1 Generic selection

Semantics

3 The generic controlling operand, array length expressions, and typeof operators contained in the type names of generic associations are not evaluated. If a generic selection has a generic association with a type name that is compatible with the controlling type, then the result expression of the generic selection is the expression in that generic association. Otherwise, the result expression of the generic selection is the expression in the default generic association. None of the expressions from any other generic association of the generic selection is evaluated. The generic selection discards its controlling operand, the type names from all the associations, and the expressions from the associations other than the result expression.

Changes to 6.5.3.6 Compound literals

Semantics

5 For a compound literal associated with function prototype scope:

[…]

Changes to 6.5.4.5 The sizeof, _Countof and alignof operators

Semantics

2 The sizeof operator yields the size (in bytes) of its operand, which can be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand does not have a known constant size, the operand is evaluated; otherwise, the operand is not evaluated the operator discards its operand and the expression is an integer constant expression.

3 The alignof operator yields the alignment requirement of its operand type. The operand is not evaluated and the expression is an integer constant expression. When applied to an array type, the result is the alignment requirement of the element type. The operator discards its operand and the expression is an integer constant expression.

4 […]

5 The _Countof operator yields the number of elements of its operand. The number of elements is determined from the type of the operand. The result is an integer. If the number of elements of the array type is variable, the operand is evaluated; otherwise, the operand is not evaluated the operator discards its operand and the expression is an integer constant expression.

Changes to 6.5.4.6 Static assertions

Add a new note

4′ NOTE Whether the constraint of a given static assertion is violated is determined during translation and does not depend on whether the static assertion occurs in a discarded context. In particular, the constraint is still violated even if the enclosing context is discarded.

Changes to 6.5.5 Cast operators

Semantics

5 Array length expressions and typeof operators contained in a type name used with a cast operator that are not discarded are evaluated whenever the cast expression is evaluated.

Changes to 6.5.14 Logical AND operator

4 Unlike the bitwise binary & operator, the && operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares equal to 0, the second operand is not evaluated; if, in addition, the first operand is an integer constant expression, the operator discards its second operand.

Changes to 6.5.15 Logical OR operator

4 Unlike the bitwise binary | operator, the || operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares unequal to 0, the second operand is not evaluated; if, in addition, the first operand is an integer constant expression, the operator discards its second operand.

Changes to 6.5.16 Conditional operator

6 The first operand is evaluated; there is a sequence point between its evaluation and the evaluation of the second or third operand (whichever is evaluated). The second operand is evaluated only if the first compares unequal to 0; the third operand is evaluated only if the first compares equal to 0;. If the first operand is an integer constant expression, the conditional operator discards its unevaluated operand. tThe result is the value of the second or third operand (whichever is evaluated), converted to the type described subsequently in this subclause.94)

Changes to 6.6.1 General

5 Constant expressions do not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated appear within or as a discarded subexpression.

NOTE 2 The operand of a typeof (6.7.3.6), sizeof, _Countof or alignof operator is usually not evaluated (6.5.4.5).

NOTE 2 For several operators such as typeof (6.7.3.6), sizeof, _Countof, alignof (6.5.4.5), logical and conditional operators (6.5.14, 6.5.15 and 6.5.16) the operand is possibly discarded.

Changes to 6.7.3.6 Typeof specifiers

4 The typeof specifier applies the typeof operators to an expression (6.5.1) or a type name. If the typeof operators are applied to an expression, they yield the type of their operand.117) Otherwise, they designate the same type as the type name with any nested typeof specifier evaluated.118) If the type of the operand is a variably modified type, the operand is evaluated; otherwise, the operand is not evaluated the typeof specifier discards its operand.

Changes to 6.7.6 Alignment specifier

7 The first form is equivalent to alignas(alignof(type-name)). In particular, the alignment specifier discards the type name.

Changes to 6.7.7.3 Array declarators

5 If the array length expression is not an integer constant expression: if it occurs in a declaration at function prototype scope or in a type name of a generic association (as described above), it is treated as if it were replaced by * and the array length expression is discarded; otherwise, each time it is evaluated it shall have a value greater than zero. The size of each instance of a variable length array type does not change during its lifetime. Where an array length expression is part of the operand of the typeof or sizeof operators and changing the value of the array length expression would not affect the result of the operator, it is unspecified whether or not the array length expression is evaluated or discarded. Where an array length expression is part of the operand with a _Countof operator and changing the value of the array length expression would not affect the result of the operator, the array length expression is not evaluated discarded. Where an array length expression is part of the operand of an alignof operator, that expression is not evaluated discarded.

Add a new paragraph to 6.7.8 Type names

4 When a type name is discarded the expressions it contains are discarded. Those of them that are integer constant expressions have their value determined during translation, unless they are discarded within the type name for some other reason.
5 EXAMPLE If the following type names are discarded
int[1 + (0 && 2+3)]
int[1 + 1/0]
the value of the expressions 1 + (0 && 2+3), 1, 0 && 2+3 and 0 is determined during translation, but those of 2+3, 2 and 3 are not. The expressions 1+1/0 and 1/0 are not evaluated because they are not integer constant expressions.

Changes to 6.9.1 General

Constraints

[…]

4 There shall be no more than one external definition for each identifier declared with internal linkage in a translation unit. Moreover, if an identifier declared with internal linkage is used in an expression there shall be exactly one external definition for the identifier in the translation unit, unless it is discarded:
— part of the operand of a sizeof expression which is an integer constant expression;
— part of the operand of a _Countof expression which is an integer constant expression;
— part of the operand of an alignof operator;
— part of the controlling expression of a generic selection;
— part of the expression in a generic association that is not the result expression of its generic selection;
— or, part of the operand of any typeof operator whose result is not a variably modified type.

Semantics

[…]

6 An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a typeof operator whose result is not a variably modified type, part of the controlling expression of a generic selection, part of the expression in a generic association that is not the result expression of its generic selection, or part of a sizeof, _Countof or alignof operator that is an integer constant expression) which is not discarded, somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.167)