(This version is expanded and revised from those previously sent to some mailing lists and comp.std.c.) Problems with constant expressions in C99 ========================================= (1) There are the problems in DR#261 the UK C Panel submitted a couple of years ago. This is where the Oxford minutes say "DR 261. Clive had potential sticking points and will converse with interested parties." and "ACTION: CF to draft words describing the four uses of the term constant to be considered in the response to DR 261. DONE merged in here or a separate document? I don't have them.". The actual wording proposed in that DR about "translation-time values" is problematic because expressions with translation-time values don't seem to be restricted to those that would be *integer* constant expressions. Null pointer constants require *integer* constant expressions. Arrays require *integer* constant expressions not to be VLAs, but *any* type of constant expression suffices to require the constraint on the size being > 0 to be checked (this may be a defect, but should be separately noted if being fixed in the course of fixing another defect). int x[(int)(double)1.0]; is a VLA while int x[(int)(double)0.0]; int y[(int)-1.0]; are both constraint violations, although the expressions are only arithmetic constant expressions (presuming no extensions to make them integer constant expressions). Problems with this arise because in general it is not possible for an arithmetic constant expression to be evaluated at translation time: if __STDC_IEC_559__ is defined and the FENV_ACCESS pragma is on, the execution-time rounding mode may affect the value of the expression (F.7.4). Discussions at a UK C Panel meeting suggested that the problem might be that such expressions are still considered constant expressions in the presence of the pragma, and that some wording that apparently was written on the blackboard at Oxford to deal with DR#261 might help. (2) The UK C Panel couldn't agree when we discussed the issue about whether the changes in C99 (where comma expressions may appear in an unevaluated subexpression of a constant expression outside sizeof - including possibly in the preprocessor) were intended or not (so that draft DR didn't get submitted). This may not be a defect, but the effect is that there are in effect several flags on subexpressions (and on types appearing within expressions) that must maintained by an implementation in order to determine whether an expression is a constant expression and, if so, what sort of constant expression it is; some non-constant subexpressions are OK in integer constant expressions if they are within an unevaluated subexpression, while some are only OK within sizeof. Furthermore, a sizeof expression whose result is not an integer constant may be OK within an unevaluated part of an arithmetic constant expression - as long as it does not contain a cast of prohibited form - but not within an unevaluated part of an integer constant expression. It isn't even immediately obvious from the definitions that all integer constant expressions are also arithmetic constant expressions (although I believe this to be so). (3) Address constants can contain casts to variably modified types, which might be a defect . If you cast to such a type (while keeping the expression in fact a constant), any nonconstants directly in the sequence of tokens for the type (as opposed to in a typedef) are also subject to the constraints on what may only appear in unevaluated subexpressions, and probably to those on permitted operands (see also item 5 below). (4) When exactly can "other forms" of constant expressions (6.6 paragraph 10) be accepted, and what restrictions apply to such "other forms"? One view on comp.std.c, e.g. ), is that they can't be accepted as integer constant expressions (or one of the other types enumerated), only as some type in addition to those, that can be accepted in initializers (or, I suppose, allowed in constraint checking for VLAs having positive size), and that any such forms must follow the Syntax, Description and Constraints sections of 6.6. DR#032 suggests they can be accepted as integer constant expressions, but still must satisfy the constraints. The precise restrictions on "other forms" should be made much clearer. (With regard to each type of constant expression, a given expression might be required by the standard to be a constant expression of that type, permitted to be such (as an "other form"), or prohibited to be such, and the standard should make clear which of the three alternatives applies in each case.) (5) What are the "operands" (6.6 paragraphs 6 and 8) of a constant expression? Presumably it means the "ultimate" operands in some sense, recursing down various operators to their operands, but this isn't specified. Presumably compound literals such as (const int){0} are not meant to be constant expressions (being references to anonymous variables), but it is hardly clear from the text that 0 isn't the operand here. When compound literals appear in sizeof expressions whose results are not integer constants in unevaluated parts of expressions, whether the expressions are arithmetic constant expressions may depend on what casts are present in the compound literals. Also, one would expect that parentheses are meant to be purely syntactic rather than having "operands", so that (int)(0.0) is an integer constant expression just as (int)0.0 is, and ((void *)0) is a null pointer constant, but this is nowhere stated. (6) We know from 6.5.3.4 paragraph 2 that sizeof of a non-VLA is an integer constant. Is it also the case that sizeof of a VLA is *never* an integer constant, or indeed never counts as a constant? Can it be shown from the standard that sizeof(int [(int)(double)1.0]) isn't an arithmetic constant expression - or is it? (7) May address constants use array indices that are arithmetic constant expressions but not integer constant expressions? May they use such expressions as conditions in conditional expressions? (8) 6.6 paragraph 7 says that a constant expression in an initializer "shall be, or evaluate to" one of four possibilities. When does "be" apply, and when does "evaluate to" apply? (Expressions *are* expressions (pieces of source code), but *evaluate to* values (something more abstract, but with no intersection with pieces of source code), so only one can apply in each case.) * an arithmetic constant expression - this is a type of expression, so "be" applies. * a null pointer constant - a type of expression, so "be" applies. * an address constant - a type of value (with some associated restrictions on the expression behind the value), so "evaluate to" applied. * an address constant for an object type plus or minus an integer constant expression - some sort of hybrid to which neither applies. (The change made following C90 DR#145 is inadequate for this issue, as the last listed form is still a strange compound of "be" and "evaluate to".) What of static int x[10]; static int *p = 1 + x + 9 - 0 + 0; ? Is this valid? Pointing one past the end of an array, it clearly falls outside the definition of "address constant". But the syntax doesn't make it clearly syntactically of the form (address constant) +/- (integer constant expression). What about the null pointer (void *)(void *)(int)0? That's created by casting an integer constant expression to pointer type - but the rules for address constants refer to "an integer *constant* cast to pointer type". I think a proper fix for this last issue is to specify all standard types of constant expression syntactically, with a more precise (unified?) definition of the last two. (9) What is the significance of the constraint that "Each constant expression shall evaluate to a constant that is in the range of representable values for its type."? It clearly applies to floating point arithmetic done in greater range than the underlying type, but does it apply otherwise? Also, does the requirement apply in any way to subexpressions? (The response to C90 DR#031 says so, but the example given in a footnote shows this is only evaluated subexpressions, not "each subexpression" as suggested in that DR.) Supposing DBL_MAX + DBL_MAX is evaluated to a finite value using a range bigger than that of double, is 0 * (DBL_MAX + DBL_MAX) a valid arithmetic constant expression? Considering integer types, unsigned arithmetic always yields results within the range of the type. INT_MAX + 1, however, yields undefined behavior at runtime. Is it nevertheless considered to have a value, outside the range of values of int, and so required not to be a constant expression, or is it unspecified whether it is a constant expression? What of 0 * (INT_MAX + 1)? What of 0 << (CHAR_BIT * sizeof(int))? Undefined behavior is OK in unevaluated parts of constant expressions, but what about in evaluated parts as here? What about a cast to signed integer type of a value not representable in that type? If the implementation defines this to raise a signal, is it then not an integer constant expression, while if it gives an implementation-defined value is it then an integer constant expression? What of (-1 << 0), which C99 changed from implementation-defined to undefined? Does having undefined behavior mean it is prohibited in static initializers?