2026-5-23
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 (wording)
| number | Title | Authors | Remarks |
|---|---|---|---|
| n3854 | Working Draft | Meneide | base |
| n3724 | Discarded | Múgica | rationale |
| n3883 | <this paper> | et. al. | wording |
none yet
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.
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. In general, v
Value 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.
2
Except when it is the operand of:If the lvalue is a named constant, and
- 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,
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:
- 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 isThe result is no longer an lvalue); this is called lvalue conversion. …
…
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) )
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.
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.
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.
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/0are not evaluated 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)