[comp.lang.c] qualifications in prototypes

diamond@tkou02.enet.dec.com (diamond@tkovoa) (08/07/90)

In article <476@mtndew.UUCP> friedl@mtndew.UUCP (Stephen J. Friedl) writes:
SF>	extern int stat(char *filename, struct stat *stptr);
SF>Shouldn't the "filename" argument be const qualified?

In article <1893@tkou02.enet.dec.com> diamond@tkou02.enet.dec.com (diamond@tkovoa) writes:
ND>But if const is included, it would break a lot of code that presently
ND>works under Unix(tm) operating system.

In article <386@taumet.com>, steve@taumet.com (Stephen Clamage) writes:
SC>No, it wouldn't break any code.  A const-qualified parameter type may
SC>be passed a non-const argument, but the reverse is not true.

Section 3.3.2.2:  "Each argument shall have a type such that its value
may be assigned to an object with the unqualified version of the type of
its corresponding parameter."

Consider:
 typedef const char * const const_ptr_to_const_char;            /* (1) */
 typedef const char *       modifiable_ptr_to_const_char;       /* (2) */
 typedef       char * const const_ptr_to_modifiable_char;       /* (3) */
 typedef       char *       modifiable_ptr_to_modifiable_char;  /* (4) */
I interpret that the "unqualified version" of a type only removes the
top-level qualifiers.  For example, the unqualified version of (1) is (2),
not (4).  And that (2) is already unqualified.

Section 3.1.2.5 supports this interpretation.  "Any type so far mentioned
is an unqualified type.  Each unqualified type has three corresponding
qualified versions of its type:  a const-qualified version, a volatile-
qualified version, and a version having both qualifications."
A const-qualified version does not seem to be one which propagates a
"const" qualification down the chain of indirection.

If this is true, then an argument of type (1) or (2) can be passed to
a parameter of type (1) or (2), and an argument of type (3) or (4) can
be passed to a parameter of type (3) or (4), but not (2) to (4).

SC>If the parameter is not const-qualified, you cannot pass it a literal
SC>string without a cast.  So the *failure* to const-qualify will break
SC>existing code.  That is, with the above prototype,
SC>	r = stat("myfile", &data);
SC>is illegal under ANSI.

Section 3.1.4:  "For character string literals, the array elements have
type char, and are initialized ..."  Not type const char.  The Rationale
emphasizes this.

Capability to change the elements from their initialized values has
usually been considered implementation-dependent.

The Rationale for section 3.1.4 further emphasizes the matter of what
cannot be assigned to what, which seems to support my opinion on the
function arguments.

SC>But given
SC>	extern int stat(const char *, struct stat *);
SC>			^^^^^
SC>the following are legal:
SC>	char *fname; /* not const */
SC>	stat(fname, &data);
SC>	stat("myfile", &data);

Exactly backwards.  I think these become illegal.

In article <17240@haddock.ima.isc.com> karl@kelp.ima.isc.com (Karl Heuer) writes:

KH>Sorry, 3.2.2.3 agrees with me.  It doesn't break existing code, because it's
KH>perfectly legal to assign from `char *' to `char const *'.  (Only *removing*
KH>the qualifier requires an explicit cast, and even then you'd better know what
KH>you're doing.)

Section 3.2.2.3 specifies what pointers may be converted and still compare
equal.  It does not specify that conversions are applied automatically
without a cast.  Sorry, but I believe it is irrelevant to the topic.
-- 
Norman Diamond, Nihon DEC     diamond@tkou02.enet.dec.com
This is me speaking.  If you want to hear the company speak, you need DECtalk.

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/07/90)

In article <1896@tkou02.enet.dec.com> diamond@tkou02.enet.dec.com (diamond@tkovoa) writes:
>In article <476@mtndew.UUCP> friedl@mtndew.UUCP (Stephen J. Friedl) writes:
>SF>	extern int stat(char *filename, struct stat *stptr);
>SF>Shouldn't the "filename" argument be const qualified?

More accurately, *filename should be const-qualified.

The relevant constraints are given in 3.3.16.1, in particular:
	both operands are pointers to qualified or unqualified versions
	of compatible types, and the type pointed to by the left [in
	this context, the declared function parameter] has all the
	qualifiers of the type pointed to by the right [the actual
	function argument].
Certainly, const char * has all the qualifiers of char *, as well as
one additional qualifier.

For practical examples, see many of the standard library functions in
section 4 of the standard.

karl@haddock.ima.isc.com (Karl Heuer) (08/07/90)

In article <1896@tkou02.enet.dec.com> diamond@tkou02.enet.dec.com (diamond@tkovoa) writes:
>[Can you pass an unqualified `char *' to a function whose prototype declares
>a parameter of type `char const *'?]
>
>Section 3.3.2.2:  "Each argument shall have a type such that its value
>may be assigned to an object with the unqualified version of the type of
>its corresponding parameter."

I agree the the question is equivalent to that of simple assignment.

>I interpret that the "unqualified version" of a type only removes the
>top-level qualifiers.  [3.1.2.5 supports this.]

I'm willing to grant that, too.

>The Rationale for section 3.1.4 further emphasizes the matter of what
>cannot be assigned to what, which seems to support my opinion on the
>function arguments.

It mentions only the assignment *from* qualified *to* unqualified.  Nobody
disputes that this is illegal without a cast.

>Section 3.2.2.3 specifies what pointers may be converted and still compare
>equal.  It does not specify that conversions are applied automatically
>without a cast.  Sorry, but I believe it is irrelevant to the topic.

Okay, then, let's try 3.3.16.1 Simple Assignment: "Constraints: One of the
following shall hold: ... both operands 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 to by the right."

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint

diamond@tkou02.enet.dec.com (diamond@tkovoa) (08/08/90)

In article <17247@haddock.ima.isc.com> karl@kelp.ima.isc.com (Karl Heuer) writes:

[Can you pass an unqualified `char *' to a function whose prototype declares
a parameter of type `char const *'?]

>Okay, then, let's try 3.3.16.1 Simple Assignment: "Constraints: One of the
>following shall hold: ... both operands 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 to by the right."

Looks like you got it.  Thank you, and you win.  IBM's prototypes should
be changed.  (But just like using the real lint, we had to wade through a
misleading message before finding the correct one!  :-)

Mr. Heuer, I'm curious why you considered this discussion inappropriate for
comp.std.c.  Please advise by e-mail (or posting if you think it appropriate.)
-- 
Norman Diamond, Nihon DEC     diamond@tkou02.enet.dec.com
This is me speaking.  If you want to hear the company speak, you need DECtalk.

richard@aiai.ed.ac.uk (Richard Tobin) (08/08/90)

>>[Can you pass an unqualified `char *' to a function whose prototype declares
>>a parameter of type `char const *'?]

The idea (presumably) is that declaring a parameter `char const *'
doesn't mean that the function *requires* the argument to be constant,
but rather promises not to change it.  Similarly with assignement; you
can assign something to a variable that promises to be more careful
with it, but need a cast if the variable might be less careful.

-- Richard
-- 
Richard Tobin,                       JANET: R.Tobin@uk.ac.ed             
AI Applications Institute,           ARPA:  R.Tobin%uk.ac.ed@nsfnet-relay.ac.uk
Edinburgh University.                UUCP:  ...!ukc!ed.ac.uk!R.Tobin