Proposal to Relax the Rules for Qualification Conversions of Pointers in the C9X Programming Language Document Number: WG14 N754/J11 97-117 C9X Revision Proposal ===================== Title: Relax the Rules for Qualification Conversions Author: J.G.Krom Author Affiliation: Jet Joint Undertaking Postal Address: Abingdon, OX14 3EA, UK E-mail Address: Jon.Krom@jet.uk Telephone Number: +44 1235 464481 Fax Number: +44 1235 464404 Sponsor: (Undecided as yet) Date: 1997-08-26 Proposal Category: __ Editorial change/non-normative contribution X_ Correction __ New feature __ Addition to obsolescent feature list __ Addition to Future Directions __ Other (please specify) Area of Standard Affected: __ Environment X_ Language __ Preprocessor __ Library __ Macro/typedef/tag name __ Function __ Header __ Other (please specify) Prior Art: C++ has similar relaxed rules. C needs them. Target Audience: All programmers interested in const-correct coding. Related Documents (if any): Proposal Attached: X_ Yes __ No, but what's your interest? Abstract: This paper points out a problem using the const qualifier with one the main data-structures of the C language. A proposal is presented that will correct this problem. Proposal: Follows below. %-------------------------------------------------------------------------- 1. BACKGROUND The type qualifier "const" was added to the C language during the standardisation process in the late 1980's. It is often used in function prototypes to indicate that the function will not modify its arguments; e.g. the prototype void f ( const char * p ); indicates that this function f() will not modify the character(s) pointed at by p (i.e. it will not modify *p). However, the current standard C rules for the use of this qualifier are not 100% complete or consistent. These rules conflict with some of the important data structures defined in the C language. %-------------------------------------------------------------------------- 1.1 NO WAY TO HANDLE argv AS A const One of the main problems is that, with the current C standard, it is not possible to write the prototype for a function g() that takes the second argument of main() as its input and that promises that this function will not modify this argv data-structure. void f ( const char * p); void g ( /* some const qualified variant of */ char ** p); int main ( int argc, char * argv[] ) { f(argv[0]); /* argv[0] is not modified by f() */ g(argv); /* Has argv been modified by g() ? */ } What should the prototype of g() be, so that it promises that calling g() will not affect the argv data structure ? Well, within current standard C it is impossible to give g() such a prototype. %-------------------------------------------------------------------------- 1.2 COMMON WAY TO MIMIC argv FAILS Another effect of this is that a closely related, often used and idiomatic data-structure cannot be used in a type-correct way. See the following code fragment: void g ( /* some const qualified variant of */ char ** p); int main ( int argc, char * argv[] ) { const char * dummyargs[] = {"A", "dummy", "argument", "array", 0}; g(argv); g(dummyargs); } In this fragment g() will be called once with a "char **" argument and once with a "const char **". In the current C language, it is impossible to give g() a prototype so that the function could be used in this way. %-------------------------------------------------------------------------- 1.3 THE UNDERLYING TYPE-CHECKING MECHANISM The rules for parameter type-checking during a function call (to a prototyped function) are essentially (by section 6.3.2.2) those that apply to an assignment. The function prototype problems indicated above are the results of the fact that the last of the assignments in the following code fragment is currently type-incorrect: { char * cp char * const * cpcp = &cp; /* legal */ const char * const * ccpcp = &cp; /* illegal */ } There is however little, if any, reason to consider the last assignment type-incorrect. Since ccpcp points to a constant pointer, that points to a const char, it cannot be used to modify any characters, or intermediate pointers, pointed to. The C language could safely allow this assignment. %-------------------------------------------------------------------------- 2. THE PROPOSAL There is a safe way to expand the current assignment rules, that would solve the above mentioned problems. The proposal is a generalisation of the idea to allow the assignment marked "/* illegal */" in the above fragment. The proposal applies not only to a "pointer to a pointer", but also to types representing longer chains of pointers. It further also applies to all qualifiers (although the rules are not the same for "const" and "volatile"). This causes the actual proposal to look rather complicated. The proposal is in three parts. The first (proposal part A) is only an editorial proposal that allows the text of the main proposal to be a written in a slightly more concise notation. The second part (proposal part B) is the main proposal. The last part (proposal part C) specifies some changes required to the existing text of the standard. The text of the current proposal is directly derived from a far larger proposal by Thomas Plum, dated 1994-12-14. Mr. Plum proposed these changes in a different context (that of C++ compatibility), but as indicated above there are also good reasons within the C language to adopt this proposal. %-------------------------------------------------------------------------- 2.1 PROPOSAL PART A [Editorial] Use the abbreviation cv (when possible in italics) for "possibly-qualified" In this document, the notation cv (or cv1, cv1,2, etc.), used in the description of types, represents an arbitrary set of cv-qualifiers, i.e., one of {const}, {volatile}, {restrict}, {const, volatile}, {const, restrict}, {volatile, restrict}, {const, volatile, restrict}, or the empty set. cv-qualifiers applied to an array type attach to the underlying element type, so the notation cvT, where T is an array type, refers to an array whose elements are so-qualified. Such array types can be said to be more (or less) cv-qualified than other types based on the cv-qualification of the underlying element types. Rationale: Brevity. The restrict qualifier has been added, others might be added later. Still, the brevity of cv is attractive. %-------------------------------------------------------------------------- 2.2 PROPOSAL PART B Relax the rules for qualification conversions of pointers Qualification Conversions An rvalue of type pointer to cv1T can be converted to an rvalue of type pointer to cv2T if cv2T is more cv-qualified than cv1T. A conversion can add type qualifiers at levels other than the first in multi-level pointers, subject to the following rules:[*] Two pointer types T1 and T2 are similar if there exists a type T* and integer n>0 such that: T1 is T cv1,n * . . . cv1,1 * cv1,0 and T2 is T cv2,n * . . . cv2,1 * cv2,0 where each cvi,j is a combination of one or more of the qualifiers "const", "volatile", "restrict" or nothing. An expression of type T1 can be converted to type T2 if and only if the following conditions are satisfied: --- the pointer types are similar. --- for every j>0, if const is in cv1,j then "const" is in cv2,j, and similarly for "volatile" and "restrict". --- the cv1,j and cv2,j are different, then "const" is in every cv2,k for 0