Subject: Re: toolchain/22118: make won't compile with -Wcast-qual -Wstrict-prototypes and more
To: NetBSD Toolchain Technical Discussion List <tech-toolchain@NetBSD.org>
From: der Mouse <mouse@Rodents.Montreal.QC.CA>
List: tech-toolchain
Date: 07/18/2003 15:43:10
> The whole purpose of the "const" qualifier in the first place is to
> help the compiler know when to warn the programmer that he or she (or
> a Standard-define API) has declared that a code path _may_ result in
> an attempt to modify a constant, including a string literal constant.

What is your basis for believing you know the intent behind "const"?

Second Edition K&R says (A8.2) that the purpose of const is "to
announce objects that may be placed in read-only memory, and perhaps to
increase opportunities for optimization".

>> You appear to want a language in which the compiler prevents you
>> from doing anything that could ever possibly lead to your writing
>> into read-only storage.
> I have never said any such thing.

Not in so many words, no; as my choice of language implies, that is my
own inference from what you _have_ said.

You appear to believe that it is somehow inherently Wrong to have a
path via which a const qualifier can ever be stripped from a pointer
target type, because (near as I can figure) this introduces an
opportunity for a bug to attempt to write into read-only storage
without any opportunity for the compiler to notice it through "attempt
to modify a const object" detection.

Yes, such a path does introduce such potential.

That does not make it inherently wrong.  It merely means that a coder
using such a thing is doing something risky.  C is not designed to
prevent coders from doing risky things.  (Some compilers attempt to
provide warnings for some of the most commonly problematic risky
things, but that's an entirely different issue.)

> What I do want, and with GCC what I do have, is an implementation of
> Standard C which gives me the ability to know at compile time whether
> or not my code could potentially suffer a bug that might result in an
> attempt to modify a constant.

Only provided you, the coder, have been sufficiently disciplined.  For
example, here's another const-stripper that I believe will work on any
NetBSD system, probably on any current C system, and I don't think will
provoke any gcc warnings (are there any systems with long and pointer
having different sizes?):

	void *deconst(const void *cvp)
	{
	 return((void *)(long int)cvp);
	}

Are conversions between pointers and integers therefore comparably
unsafe and "delenda est"?

>> If you really want the sort of no-escape discipline you seem to
>> think const should provide,
> I do not necessarily want that and I never said I did.

Again, not in so many words, but the venom you heap on anyone who dares
to suggest that stripping const could ever be acceptable certainly
implies that you do.  If you don't, you have been grossly
misrepresenting your own point of view.

> I also have not claimed "const" provides a no-escape discipline.

Just as well, because it doesn't. :-)

> It should be quite clear to all that "const" is merely advice to the
> compiler about what the programmer believes the code will do and that
> the warnings in return are merely advice to the programmer [...]

Right.  So why are you so bent out of shape over using things like
deconst() as additional advice to the compiler that in _this_
particular place, the coder advises the compiler that the apparent
danger is only apparent, not real?

Of course, if the coder mis-advises the compiler, problems can result.
That has always been true and always will be, and is by no means
restricted to inappropriate use of deconst().

> That very first point I tried to make was primarily that any attempt
> to invent a DECONST() macro/function/cast/whatever was clearly and
> blatantly wrong on all counts

It does not appear to be clear to anyone but you.  All you need is a
cast to discard const; deconst() and its relatives exist to _silently_
discard const, ie, to do so without provoking gcc's -Wcast-qual
warning.  I have trouble seeing how this is better than pulling the
affected code into a separate compilation unit and compiling it without
-Wcast-qual, which past messages have implied you find acceptable.

That is, how is

	v[2].iov_base = deconst("\n");

worse than

	extern void set_iov_base(struct iovec *, const void *);
	set_iov_base(&v[2],"\n");
    [and in another file, compiled without -Wcast-qual,]
	void set_iov_base(struct iovec *v, const void *vp)
	{ v->iov_base = (void *)vp; }

The latter seems to be acceptable to you (again, my inference, based on
past messages - I can dig up quotes if you want); I have trouble seeing
any way it is preferable to the former.

> I.e. if you think you need DECONST() then you are mistaken as the
> real problem is elsewhere.  So far nobody has demonstrated even a
> hint of a standard (de facto or otherwise) API which mis-uses "const"
> and thus would require DECONST() tricks to either implement or use it
> without changing the original API

Apparently you missed the message in which I cited four such:

	writev() (the same struct iovec is used for readv()).

	The XClassHint structure ("res_name" and "res_class" fields).

	The XTextProperty structure ("value" field).

	The third arg to XCreatePixmapFromBitmapData().

As near as I can figure, in your view, the latter three mis-use const
by omitting it despite not writing through the pointers; the first, by
using the same struct for read-only and write-only calls.

Like all such, you _can_ use them without deconst() - but then, you
_can_ write code without worrying about any of this by skipping
-Wcast-qual and -Wwrite-strings.

> I'm trying to prevent you and others from inventing and disseminating
> a construct that is blatantly wrong and which will result in far more
> problems than it appears to initially solve,

...specifically...?  I haven't run into any of these "far more
problems", despite using deconst().  You haven't explained any of them,
especially as compared to pulling the affected assignments into a
separate module and building it without -Wcast-qual, which it appears
you find acceptable.

> and if I can't do that then I at least want to make it as obvious as
> possible to others why such a construct is very wrong.

You don't seem to be doing very well at that either.  Everyone I've
seen comment on the issue seems to agree with me.

"If one call thee an ass, pay it no mind.
 If two call thee an ass, give it some thought.
 If three call thee an ass, get thee a saddle."

> I'm also very happy to learn from this discussion that GCC's use of
> writable storage for "char foo[]" initializers is likely to be
> mirrored by other conforming Standard C implementations.

You are so utterly confused on this I'm not sure how to fix it.

I'll try one more time.

Initializers simply _do not have_ storage except for the object they
are initializing.  In
	char foo[] = "bar";
the string "bar" _does not have_ storage except for the object it's
initializing - the array that this declaration causes to be associated
with the name "foo".  This is _exactly_ equivalent to
	char foo[] = { 'b', 'a', 'r', '\0' };
regardless of whether "bar", when written in a non-array-initializer
context, would be in read-only storage or would have const-qualified
type (either Greg Woods style or C Standard style).

This confusion is probably compounded by the difference between that
and
	char *foo = "bar";

The difference, using boxology to describe the memory:

	char foo[] = "bar";
	->
		+-----+ (writable)
	foo:	| 'b' |
		+-----+
		| 'a' |
		+-----+
		| 'r' |
		+-----+
		|  0  |
		+-----+

	char *foo = "bar";
	->
		+-------------+ (writable)
	foo:	|      *      | (however large a char * is)
		+------|------+
		       |
	   ___________/
	  /
	  \	+-----+ (may or may not be writable)
	   +->	| 'b' |
		+-----+
		| 'a' |
		+-----+
		| 'r' |
		+-----+
		|  0  |
		+-----+

/~\ The ASCII				der Mouse
\ / Ribbon Campaign
 X  Against HTML	       mouse@rodents.montreal.qc.ca
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B