SC22/WG14 N774 J11/97-138 Assorted minor substantive issues Clive D.W. Feather clive@demon.net 1997-09-26 Abstract ======== This paper is assembled from those elements of N720, N735, and N739, and are intended to improve the consistency and understandability of the Standard. In each case there is, in theory, a normative change to the text, but these changes should be uncontroversial and not affect any reasonable program. Items are given a serial number in this paper, but also carry a note statig their origin. Items taken from N720 do not have a rationale; the related DR explains the issues. References are relative to Draft 11 pre 3. Specific items ============== Item 1 [Was N720 item DR 174] ---------------------- In subclause 6.2.1.7 paragraph 1, replace: Many binary operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. with: Many operators cause the same pattern of conversions to be applied to two operands of arithmetic type. The purpose is to yield a common type, which, unless explicitly stated otherwise, is also the type of the operator's result. In subclause 6.3.15, replace paragraphs 4 and 5 with: The first operand is evaluated; there is a sequence point after its evaluation. 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; the result of the operator is the value of the second or third operand (whichever is evaluated), converted to the type described below. If both the second and third operands have arithmetic type, the type that the usual arithmetic conversions would yield if applied to those two operands is the type of the result. If both the operands have structure or union type, the result has that type. If both operands have void type, the result has void type. and change the last sentence of paragraph 6 to end: ... in which case the type of the result is pointer to void. Item 2 [Was N720 item DR 163] ---------------------- Add a new Constraint section to 6.3.1: Constraint Except for function calls as described in 6.3.2.3, there shall be a declaration of any identifier visible at the point it is used as a primary-expression. Item 3 [Was N720 item DR 070] ---------------------- In subclause 6.3.2.3 paragraph 5, after: ... the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behaviour is undefined. insert: with one exception: if the two types are required to have the same representation for some or all values, the behaviour is undefined only if the value of the argument is not such a value. [*] [*] Thus a nonnegative signed integer may be passed to a parameter that has the corresponding unsigned type, and a pointer to void may be passed to a parameter with pointer to a character type. In subclause 6.3.2.4 paragraph 5, change "one exception" to "two exceptions", and append: Secondly, if the two members have types which are required to have the same representation for some or all values, the value stored may be accessed using any member with a type for which that value must have the same representation. Item 4 [Was N720 item DR 115] ---------------------- Change subclause 6.5 paragraph 2 to: A declaration shall declare at least a declarator (excluding the parameters of a function or the members of a structure or union), a tag, or the members of an enumeration. Item 5 [Was N720 item DR 165] ---------------------- Replace subclause 6.5.2.3 by the following wording (taken from the DR): 6.5.2.3 Tags Constraints A specific type shall have its content defined at most once. A type specifier of the form enum identifier without an enumerator list shall only appear when the type it specifies is complete. Semantics All declarations of structure, union, or enumerated types that have the same scope and use the same tag declare the same type. The type is incomplete [F1] until the closing brace of the list defining the content, and complete thereafter. [F1] An incomplete type may only be used when the size of an object of that type is not needed. [Append the present footnote 99, or see below for alternative wording.] Two declarations of structure, union, or enumerated types which are in different scopes or use different tags declare distinct types. Each declaration of a structure, union, or enumerated type which does not include a tag declares a distinct type. A type specifier of the form struct-or-union identifier/opt { struct-declaration-list } or enum identifier/opt { enumerator-list } declares a structure, union, or enumerated type. The list defines the /structure content/, /union content/, or /enumeration content/. If an identifier is provided [F2], the type specifier also declares the identifier to be the tag of that type. [F2] If there is no identifier, the type can, within the translation unit, only be referred to by the declaration of which it is a part. Of course, when the declaration is of a typedef name, subsequent declarations can make use of that typedef name to declare objects having the specified structure, union, or enumerated type. A declaration of the form struct-or-union identifier ; specifies a structure or union type and declares the identifier as the tag of that type [F3]. [F3] A similar construction with /enum/ does not exist. If a type specifier of the form struct-or-union identifier occurs other than as part of one of the above constructions, and no other declaration of the identifier as a tag is visible, then it declares a structure or union type which is incomplete at this point, and declares the identifer as the tag of that type [F3]. If a type specifier of the form struct-or-union identifier or enum identifier occurs other than as part of one of the above constructions, and a declaration of the identifier as a tag is visible, then it specifies the same type as that other declaration, and does not redeclare the tag. [Retain the existing examples.] Note: at the 1993 London meeting I took an action item to rewrite this footnote to explain the issues properly, pursuant to a DR response. The final text was sent to the convenor of the time (P.J.Plauger). Applying that text, the footnote would become: [F1] An incomplete type may only be used when the size of an object of that type is not needed. The size of an object of a specific type is needed, and therefore the type shall be complete, in the following circumstances: - when the size of an object of any of the following types is needed: * a qualified version of that type * a structure or union type with a member of that type * an array type with that type as its element type - when that type, or an expression of that type, appears as the operand of the sizeof operator; - when a variable of that type is defined (this excludes tentative definitions but includes the implicit definition described in 6.7.2); - when an expression of type pointer to that type occurs as either operand of an additive or relational operator; - when a function returning that type is defined. I also recommended that the footnote belonged in 6.1.2.5 rather than here. Item 6 [Was N720 item DRs 096 and 110] ------------------------------- In subclause 6.5.5.2 add a further constraint: The element type shall not be an incomplete or function type. Item 7 [Was N720 item DR 084] ---------------------- In subclause 6.5.5.3 add a further constraint: The parameters in a parameter-type-list that is part of a function definition shall not have incomplete type. and a new Semantics paragraph after paragraph 6: If the function declarator is not part of a function definition, the parameters may have incomplete type. Item 8 [Was N720 item DR 173] ---------------------- In subclause 6.8.4 paragraph 2, change: ... to the current token. to: ... up to an unspecified character within the current token. Item 9 [Was N720 item DR 176] ---------------------- Replace subclause 6.8.5 with the following wording, based on that in the DR but modified to take account of changes since then: 6.8.5 Error directive Constraints A #error preprocessing directive shall not occur in a preprocessing translation unit except as part of a group skipped as part of conditional inclusion. Any diagnostic message generated because of the violation of this constraint [*] shall include the sequence of preprocessing tokens in the directive. [*] The intent of this subclause is that #error that is not skipped indicates that translation should fail. Item 10 [Was N735 item 5] ----------------- Change the first sentence of subclause 7.1.2 paragraph 1 from: Each library function is declared in a /header/, [135] ... to: Each library function is declared, with a type that includes a prototype, in a /header/, [135] ... The as-if rule means that this need not be done literally, provided that the effects of argument assignment rather than default promotion (other than trailing varargs, of course) will happen to all library function calls. Item 11 [Was N735 item 6] ----------------- A careful reading of subclause 7.3.1 shows that, for characters outside the 95-element minimal execution character set, there are two sets of classification macros that are significant. For each set, a character can belong to at most one member of the set. The following table shows these sets, examples of characters within those sets taken from the minimal 95, and cases that cannot happen: isprint() iscntrl() [neither] isalpha() 'A' forbidden =1= isspace() ' ' '\n' =2= [neither] ':' '\b' '\0' The interesting cases are those marked =1= and =2=; any characters with these properties must be locale-specific. The question turns on the intended meaning of "printable". The current definition requires the character to occupy a position on a printing device. Option A -------- If so, such characters do make sense - =1= could be a "dead" character that overprints another one, or =2= could be a hair-thin space. Then attach a footnote to subclauses 7.3.1.2 (isalpha()), 7.3.1.6 (islower()), and 7.3.1.10 (isupper()): [*] The additional characters might not be printing characters; for example, they may be "dead" characters that overprint the preceeding or following character and are thus not "printing". Option B -------- However, it is questionable whether the term "one printing position" still has a meaning in this day of proportional-spaced output devices, and whether there is a need for a better definition of "printable". In this case, change the definition to: The term /printing character/ refers to a member of an implementation-defined set of characters, each of which has a characteristic appearance on a display device and usually occupies one printing position; in subclauses 7.3.1.2 (isalpha()), 7.3.1.6 (islower()), and 7.3.1.10 (isupper()), change: ... locale-specific set of characters ... to: ... locale-specific set of printing characters ... and in 7.3.1.9 (isspace()) change it to: ... locale-specific set of printing or control characters ... Item 12 [Was N735 item 12] ------------------ Change the following C locale values in 7.5 () paragraph 2 from: mon_decimal_point "" negative_sign "" to: mon_decimal_point "." negative_sign "-" Item 13 [Was N720 item DR 066] ---------------------- In subclause 7.5.2.1 paragraph 3, insert after "of zero length": Apart from /grouping/ and /mon_grouping/, the strings shall start and end in the initial shift state. Item 14 [Was N735 item 8] ----------------- In subclause 7.10.1.1 (setjmp()) there is a heading "Environmental constraint". This implies that the sentence is a Constraint, and that violation requires a diagnostic. It is reported that very few implementations generate such a diagnostic, and that most implementations correctly handle other contexts. Change the heading to "Environmental restriction" and add at the end: If the invocation appears in any other context, the behaviour is undefined. Item 15 [Was N720 item DR 140] ---------------------- In subclause 7.13.5.6 (setvbuf()) paragraph 2, change: ... any other operation ... to: ... any other operation (other than an unsuccessful call to /setvbuf/) ... Item 16 [Was N735 item 17] ------------------ Option A -------- Add to subclause 7.14.4.2 (atexit()) paragraph 2: Whether the function is called on abnormal program termination is unspecified. Option B -------- Add to subclause 7.14.4.2 (atexit()) paragraph 2: Whether the function is called on abnormal program termination is implementation-defined. Item 17 [Was N720 item DR 134] ---------------------- In subclause 7.15.6.2 (strerror()), append to paragraph 2: If the argument is not zero, EDOM, ERANGE, or any value that a library function might store in errno, the behaviour is undefined. Item 18 [Was N735 item 18b] ------------------- The Standard provides no way to determine whether realloc() has moved the memory; this is something you want to do if you have pointers to within the block of memory. If it hasn't moved, the returned pointer will compare equal to the pointer argument. But if it has, you cannot make the comparison because a pointer to freed memory (and thus to moved memory) is indeterminate, and the comparison is undefined behaviour (unless you go through hoops like using memcpy()). There is a rationale behind this last part (making a legitimate value suddenly become illegitimate): some implementations may check pointers for validity whenever they are loaded into a register. However, it is a problem. Should the comparison be permitted ? Is it desirable to provide at least some mechanism to determine if the memory has moved ? Item 19 [Was N739 item 9b] ------------------ [This should be deferred until the undefined v implementation-defined issue has been resolved.] If a union is read from a member other than the one last stored into, the result is currently implementation-defined. Because the result might cause a trap of some kind (e.g. invalid pointer), it should be undefined behaviour in most circumstances; the wording should broadly follow 6.3 on this matter. In 6.3.2.4, replace paragraph 5 (either the original or that derived from the changes in item 3) 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 [68]. | Furthermore, a special guarantee is made ... ==== ENDS ====