2026-6-20
CC BY, see https://creativecommons.org/licenses/by/4.0
Javier A. Múgica (rationale, initial wording, history)
Javier A. Múgica, Joseph Myers, Robert Seacord, Jens Gustedt (JJRJ) (wording)
| number | Title | Authors | Remarks |
|---|---|---|---|
| n3854 | Working Draft | Meneide | base |
| n3724 | Discarded | Múgica | rationale |
| n3890 | What are the operands of Generic, v. 3 | Múgica | online vote before Ottawa |
| n3883 | Wording for “discarded” | JJRJ | wording |
| n3909 | <this paper> | JJRJ | fixes n3883 |
none
| date | meeting | for | against | abstain |
|---|---|---|---|---|
| 2026-03 | teleconference | 12 | 2 | 5 |
date: 2024-10-06
value-discarded: Applies to expressions, type names, and statements. Expressions within a value-discarded construction that are evaluated (ICE in type names or static_assert) are not considered value-discarded
essentially discarded: Right operand in 0 || E2,
etc. These are the constructions explicitly deemed value-discarded, but
not their subexpressions
discarded relative to: An essentially discarded
expression or type name F contained in
an expression E and the
value-discarded constructs contained in F are discarded relative to the expression
E.
regular label: Those that can be the target of a goto.
isolated statement: A statement is isolated if it is not
labeled, does not contain regular labels and if it contains any case or default label it
contains the whole switch body to which
it is associated.
switch label: A case or default label.
Proposed as an optional addition.
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.
Allows implementation to consider value-discarded other expressions that they will know that will never be evaluated.
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])];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.)
Applies the term essentially discarded to describe the places where an identifier without a definition is allowed.
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).
2025-02-07
to evaluate: Applies to expressions
to resolve: Applies to type names
to resolve, partly resolved, fully resolved and runtime-discarded: Type names, both explicit and implicit in declarations, are resolved. This entails determining the type that the type name names. The type of an expression is also resolved, and the term may also be applied to the type named by a type name. Many types are resolved during translation. Other types are only partly resolved at translation. What remains to be resolved are the values of certain expressions on which the type depends. Every time the type name or expression is reached during program execution, and if it is necessary to resolve the type, these expressions are evaluated, or the relevant value retrieved from some previously evaluated expression, whereby the type is fully resolved at runtime. Because the value of those expressions may change every time the type name or expression is reached, the resolved type may be different on each occasion. If it is not necessary to fully resolve the type, those evaluations are not performed, and the type or type name is said to be runtime-discarded. When this term is applied to a type or type name which is resolved during translation, it is devoid of meaning.
Split to a subproposal
Remove the implementation-defined evaluation of the size that is currently present in the standard by mandating evaluation of non-ICE.
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.
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).
2025-02-07
Fixes the previous version. Removes the “Decoupling of value-discarded from runtime-discarded”, leaving it for a future proposal.
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).
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.
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.
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
_Generic,
sizeof etc,
casts, logical, conditional, typeof)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.
It is important to keep the distinction between expressions that the
translator can (and must) discard and those for which it must determine
their value during translation. The former is a stronger property. In
particular, the value of a discarded expression does not affect the
value of the expression of which is it part and caused it to be
discarded. E.g., in 0 && expr,
the value of expr is irrelevant. It is
not even required that expr evaluates
to a value, as in 1/0.
For this reason, named constants that were deemed discarded in the first version of the paper prepared by the Wording Group are no longer considered discarded, because indeed they are not. But the requirement on the translator that their lvalue conversion takes place during translation is kept, and that during runtime there is no access to the underlying object.
The proposed wording has been split, so that both changes can be voted separately.
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.
3 Evaluation of an expression in general includes both value computations and initiation of side effects. Value computation for an lvalue expression includes determining the identity of the designated object. Some expressions and type names are deemed discarded by this document; discarded expressions are not evaluated.
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.FTT) Except as specified later, side effects and value computations of subexpressions are unsequenced.68)
FTT) The value of a discarded expression is determined during translation if it is subject to a constraint or it is needed for a type name as described in 6.7.8.
5 EXAMPLE The following code includes operators that discard operands. The comments note which expressions are discarded by each operator.
sizeof( // sizeof discards: 2 || i, and 2 and i by propagation. 2 || i // || discards: i ) sizeof( // sizeof discards: i || 2, and i and 2 by propagation. i || 2 // || nothing discarded (i is not an ICE) )
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 thedefaultgeneric 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.
Suggestion: If proposal n3890, under online review by the time of this writing, is accepted, the inserted sentence should instead be:
The generic selection discards all its operands except the result expression.
Semantics
5 For a compound literal associated with function prototype scope:
[…]
- if it is not a compound literal constant, neither the compound literal as a whole nor any of the initializers are evaluated and the compound literal is discarded.
sizeof, _Countof and alignof
operatorsSemantics
2 The
sizeofoperator 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 evaluatedthe operator discards its operand and the expression is an integer constant expression.
3 The
alignofoperator 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
_Countofoperator 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 evaluatedthe operator discards its operand and the expression is an integer constant expression.
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.
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.
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.
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)
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 evaluatedappear within or as a discarded subexpression.
NOTE 2 The operand of a typeof (6.7.3.6),sizeof,_Countoforalignofoperator 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.
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 evaluatedthe typeof specifier discards its operand.
7 The first form is equivalent to
alignas(alignof(type-name)). In particular, the alignment specifier discards the type name.
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 orsizeofoperators and changing the value of the array length expression would not affect the result of the operator, it is unspecified whetheror notthe array length expression is evaluated or discarded. Where an array length expression is part of the operand with a_Countofoperator and changing the value of the array length expression would not affect the result of the operator, the array length expression isnot evaluateddiscarded. Where an array length expression is part of the operand of an alignof operator, that expression isnot evaluateddiscarded.
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 expressions1 + (0 && 2+3),1,0 && 2+3and0is determined during translation, but those of2+3,2and3are not. The expressions1+1/0and1/0do not have their values determined during translation because they are not integer constant expressions.
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 asizeofexpression which is an integer constant expression;
— part of the operand of a_Countofexpression which is an integer constant expression;
— part of the operand of analignofoperator;
— 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 awhich 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)sizeof,_Countoforalignofoperator that is an integer constant expression)
3 Evaluation of an expression in general includes both value computations and initiation of side effects. In general, v
Value computation for an lvalue expression includes determining the identity of the designated object. This notwithstanding, named constants undergo lvalue conversion prior to program execution, at which time they have their value determined; when they are evaluated during program execution the identity of any designated object is not determined (and named constants do not have side effects).
12 EXAMPLE 1′
float f; const int n=5; /* ... */ f + n * sizeof(f++);Evaluation of the expression
fcauses an access to the object denoted byfin order to retrieve its value. Evaluation of the named constantndoes not entail any access to any object. Finally, the expressionf++is discarded; hence, it is not evaluated.
Note: “is discarded; hence, it” is to be included only if the wording for “discarded” is accepted.
2
Except when it is the operand of:If an lvalue is a named constant and
- is not the operand the
sizeofoperator,- is not the operand of a typeof operator,
- is not the operand of the unary
&operator,- is not the left operand of the
.operator,- and does not have an array type,
its value is determined once prior to program execution. Otherwise, if it is not the operand of:
- the
sizeofoperator,- the typeof operators,
- the unary
&operator,- the
++operator,- the
--operator,- the left operand of the
.operator,- or, an assignment operator
an lvalue that does not have array type is converted to the value stored in the designated object.
(and isIn both cases the result is no longer an lvalue and);this is called lvalue conversion.
2′ If an lvalue that undergoes conversion has qualified type, the
valueresult has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, thevalueresult has the non-atomic version of the type of the lvalue and the operation synchronizes withmemory_order_seq_cstmemory consistency; otherwise, thevalueresult has the type of the lvalue. …
3 Evaluation of an expression or a type name in general includes both value computations and initiation of side effects. In general, value computation for an lvalue expression includes determining the identity of the designated object. This notwithstanding, named constants undergo lvalue conversion prior to program execution, at which time they have their value determined; when they are evaluated during program execution the identity of any designated object is not determined (and named constants do not have side effects). Further, some expressions and type names are deemed discarded by this document; discarded expressions are not evaluated.