Document Number: WG14 N676 General wording issues (clauses 1 to 6) Abstract ======== This document is an attempt to identify all the minor issues I can find in clauses 1 to 6 of the Standard. I am using draft 9 pre 3 as my starting point. It omits those items which I have previously proposed, or have been addressed in a DR. It also omits a couple of matters which I am handling separately. There will be one or more companion papers for subclause 7 and the annexes. I hope it is unnecessary to produce formal proposals for all of these, but I will attempt to identify the issue and have usually generated new wording (I hope that, in at least some cases, the new wording will make the problem obvious). ======================================================================= Item 1: The term "access" is not well defined. From context, it sometimes appears to mean "read the value", and sometimes "read or write the value". This ambiguity sometimes makes it hard to understand what is actually meant. There needs to be a definition in clause 3, and all uses of the term need to be checked for the read-only / read-write problem. Probably the best approach is to define it as "read or write", and to find and fix the places where "read" is meant. ==== Item 2: Change the first part of paragraph 1 of subclause 5.1.2.2.1 to: The function called at program startup is named /main/. The implementation declares no prototype for this function. It shall be defined either with no parameters: ... int main (int argc, char *argv[]) { /* ... */ } or equivalent [*], or in some other implementation-defined manner. [*] Thus /int/ can be replaced by a typedef-name defined as /int/, or the type of argv can be written as /char **argv/, and so on. This will make it clear that, while these are the only permitted strictly conforming alternatives, extensions are allowed but must be documented. ==== Item 3: Examples 2 and 6 in subclause 5.1.2.3 need rewording. At present they use the term "exception" to mean something like an overflow trap, whereas 6.3 makes it clear that an "exception" occurs on overflow even when the result is silently wrapped. ==== Item 4: In 5.2.1 paragraph 2, delete the final "literal". The zero character terminates strings, but does not occur in a string literal (which is a syntactic construct). ==== Item 5: Subclause 6.1.2 treats the term "identifier" as representing the sequence of characters. On the other hand, subclause 6.1.2.1 treats the term as representing that sequence within a given scope. Thus in: { int fred; /* fred-1 */ { int fred; /* fred-2 */ } } 6.1.2 paragraph 8 treats fred-1 and fred-2 as being the same identifier, while 6.1.2.1 treats them as different. The term "lexically identical identifiers" appears in 6.1.2.1 paragraph 3. This should be used whenever the meaning of 6.1.2 is intended (this applies in several places throughout the Standard, not just in 6.1.2). ==== Item 6: In 6.1.2.5, delete the last sentence of paragraph 2 and append to paragraph 11: The implementation shall define /char/ to have the same range, representation, and behaviour as one of /signed char/ and /unsigned char/. [*] [*] CHAR_MIN, defined in , will have one of the values 0 or SCHAR_MIN, and this can be used to distinguish the two options. Irrespective of the choice made, /char/ is a separate type from the other two, and is not compatible with either. This clarifies that there are only two differently-behaving types, not three. ==== Item 7: The rules for composite type handle an incomplete array meeting a complete one, but not the equivalent situation with an incomplete structure or union. Replace subclause 6.1.2.6 paragraph 3, first bullet point, with: - If one type is complete and the other type is incomplete, the composite type is a complete type. ==== Item 8: Add the following to the end of subclause 6.2.2.3: An integer may be converted to any pointer type. The result is implementation-defined, and might not be a pointer to an object of that type. [59] Any pointer type may be converted to an integral type; the result is implementation-defined, and need not be in the range of values of any integral type. If the resulting value cannot be represented in the destination type, the behaviour is undefined. [*] [*] Thus if the conversion is to /unsigned int/ but yields a negative value, the behaviour is undefined. A pointer to a complete or incomplete object type may be converted to a pointer to a different complete or incomplete object type. If the resulting pointer is not correctly aligned for the pointed to type, the behaviour is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. [*] [*] All pointers to character types are correctly aligned. In general, the concept "correctly aligned" is transitive: if a pointer to type A is correctly aligned for a pointer to type B, which in turn is correctly aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C. A pointer to a function ... [this paragraph, taken from 6.3.4, remains unchanged]. Note: the idea that implementation-defined behaviour can be required to be undefined is a rather unsettling one. Given the existence of the types intptr_t and uintptr_t in subclause 7.4.4, it may be worth replacing the second of these new paragraphs by: Any pointer type may be converted to an integral type; the result is implementation-defined, but will be in the range of the signed integral type /intptr_t/. If the destination type is any other integral type, the value is converted to /intptr_t/ and then to the destination type. and omitting the relevant footnote. Delete 6.3.4 paragraph 4, and add the following paragraph to the constraints (after paragraph 2): Conversions that involve pointers, other than where permitted by the constraints of 6.3.16.1, shall be specified by means of an explicit cast. ==== Item 9: In 6.3.2.3 paragraph 5, replace: With one exception, if a member of a union object is accessed after a value has been stored in a different member of the object, the behaviour is implementation-defined. [54] One special guarantee is made ... with: With one exception, if the value of a member of a union object is used when the most recent store to the object was to a different member, the behaviour is implementation-defined. [54] One special guarantee is made ... Alternatively, since some implementations apparently have problems with this clause, replace it with: With two exceptions, if the value of a member of a union object is used when the most recent store to the object was to a member whose type does not have the same alignment and representation, the behaviour is undefined. If either member has character type or is an array of character type, the behaviour is implementation-defined. [54]. Furthermore, one special guarantee is made ... ==== Item 10: Replace subclause 6.5.2 paragraph 4 by: Each of the comma-separated sets designate the same type, except that for bit-fields, it is implementation-defined whether the specifier /int/ (or no specifier) is the same type as /signed int/ or is the same type as /unsigned int/. Replace subclause 6.5.2.1 paragraph 8 by: A bit-field shall have a type that is a qualified or unqualified version of /signed int/ or /unsigned int/. A bit field is interpreted as a signed or unsigned integral type consisting of the specified number of bits. [*] [*] As specified in 6.5.2 above, if the actual type specifier used is /int/ or there is no type specifier, or is a typedef-name defined using either of these, then it is implementation-defined whether the bit-field is signed or unsigned. This eliminates the duplicate wording in these two places, and also makes it clear that there is not a potential third signedness of bitfield. If my proposals for representation of types are accepted, there may need to be further wording adjustments in the second alteration. ==== Item 11: In subclause 6.5.2.1, change paragraph 3 to read: ... shall not exceed the number of bits in an object of the type that would be specified if the colon and expression had been omitted. If the value is zero ... The current wording doesn't say *what* the type is compatible with. ==== Item 12: Subclause 6.5.2.2 allows an enumerated type (say /enum e/) to be compatible with /long/ or even /unsigned long long/. On the other hand, subclause 6.2.1.1 states that the type converts to /int/ or /unsigned int/ as part of the integral promotions. This produces the apparent contradiction that two compatible types promote differently ! There are two alternative approaches to solving this. (A) Replace subclause 6.5.2.2 paragraph 4 by: Each enumerated type shall be compatible with one of the following types: signed char unsigned char signed short unsigned short signed int unsigned int The choice of type is inplementation-defined, but shall be capable of representing the values of all the members of the enumeration. (B) Replace subclause 6.2.1.1 first paragraph by: A /char/, a /short int/, or an /int/ bit-field, or their signed or unsigned versions, may be used in an expression wherever an /int/ or /unsigned int/ may be used. If an /int/ can represent all values of the original type, the value is converted to an /int/; otherwise, it is converted to an /unsigned int/. These are called the /integral promotions/. An enumeration type may be used in an expression wherever the type that it is compatible with may be used. The integral promotions cause the value to be converted in the same way as that compatible type would be. All other arithmetic types are unchanged by the integral promotions. and in subclause 6.5.2.2, change the first sentence of paragraph 4 to: Each enumerated type shall be compatible with some signed or unsigned integral type. [At present, enumerated types *are* integral types; the intent is clearly to make them compatible with one of the 10 types named in 6.1.2.5.] ==== Item 13: Delete subclause 6.5.7 paragraph 2: There shall be no more initializers in an initializer list than there are objects to be initialized. Compare example 11. Change paragraph 12 to read: ... the first named member of a union. ... [This isn't strictly necessary, but makes things clearer.] ==== Item 14: If implicit int is to be removed from the Standard, then there is no longer a good rationale for allowing functions with an object return type to execute a return statement without an expression. Change subclause 6.6.6.4 as follows. Add to paragraph 1 (Constraints): A /return/ statement without an expression shall only appear in a function whose return type is /void/. Delete from paragraph 2: with and without expressions Replace paragraph 4 by: If the } that terminates a function is reached, and the value of the function call is used by the caller, the behaviour is undefined. Alternatively and preferably, delete paragraph 4 entirely and insert the following paragraph in the Constraints, after paragraph 1: In a function whose return type is not /void/, the last statement before the terminating } shall have one of the following forms: - a /return/ statement with an expression; - a /goto/ statement; - a block in which the last statement before the terminating } is, recursively, one of these forms; - an /if/ statement with an /else/, in which each substatement is, recursively, one of these forms; - a /switch/ statement which is not the smallest enclosing /switch/ or iteration statement of a /break/ statement, and in which the switch body is, recursively, one of these forms; - an iteration statement which is not the smallest enclosing /switch/ or iteration statement of a /break/ statement, and in which the controlling expression (/expression-2/ for a /for/ statement) is, or is replaced by, a non-zero constant expression. and delete the last sentence of subclause 5.1.2.2.3. If this latter text is not adopted, the last word of 5.1.2.2.3 should be changed to "unspecified" - the concept of undefined value is carefully avoided elsewhere.