JTC1/SC22/WG14
N754
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<k<j,
and similarly for "restrict" (but not for "volatile").
-----------------
Footnote [*]: These rules ensure that const-safety is preserved
by the conversion. This conversion never applies to non-static
member functions because there is no way to obtain an lvalue for
a non-static member function.
The text of parts A and B of the proposal can be combined into one,
numbered, paragraph. This paragraph could be located immediately after
section 6.1.2.6, because of its relationship with compatible types.
Alternatively it could be placed close to section 6.5.3 because of its
relationship with qualified types.
%--------------------------------------------------------------------------
2.3 PROPOSAL PART C
Adapt the text of section 6.3.16.1 in order to use the relaxed
qualification conversions.
In section 6.3.16.1, "Simple assignment", the third constraint
currently states:
--- Both operands [of a simple assignment] are pointers to
qualified or unqualified versions of compatible types, and the
type pointed to by the left has all the qualifiers of the type
pointed at by the right.
This constraint should at least be depreciated, it probably can
be removed altogether.
A new constraint should be added to this section:
--- Both operands [of a simple assignment] are pointers to
compatible types, and the qualifiers of the type pointed at by
the right operand can be converted to the type pointed at by the
left operand, according to the rules in section Qualification
Conversions [to be replaced by the appropriate section number].
%--------------------------------------------------------------------------
3. SAFETY
The proposal would allow assignments to be made that are currently
forbidden. This means that no existing code uses these assignments.
Even if some code exist that ignores any compiler warnings and uses
such an assignments, it could only meaningfully do so to obtain the
semantics currently proposed.
The assignments that this proposal would allow are completely safe with
respect to the "const" and "volatile" qualifiers. (It only changes the
rules for "const"; the rule for "volatile" is essentially still what it
was in C89.)
[[A further elaborated version of this proposal should include some
support for this statement.]]
I do not have enough information to judge the effects on the "restrict"
qualifier. Although the first impression is that handling "restrict"
in the same way as "const" ought to be safe, this is better checked by
someone with more detailed knowledge of "restrict".
%--------------------------------------------------------------------------
4. PREVIOUS ART
The C++ language already successfully allows assignments as are proposed
in this paper for C. The actual wording of the proposal is derived from
the wording in the C++ (draft) standard.
However, this proposal is not presented as an attempt to make C and
C++ more compatible, but as attempt to correct a feature of the C
language.