Subject: Re: toolchain/22118: make won't compile with -Wcast-qual -Wstrict-prototypes and more
To: Simon J. Gerraty <sjg@crufty.net>
From: Greg A. Woods <woods@weird.com>
List: tech-toolchain
Date: 07/14/2003 16:31:09
[ On Monday, July 14, 2003 at 12:17:48 (-0700), Simon J. Gerraty wrote: ]
> Subject: Re: toolchain/22118: make won't compile with -Wcast-qual -Wstrict-prototypes and more 
>
> Sure, no one is talking about that.  We are talking about the pain
> that trying to add const-correctness involves.  The simple fact is
> that there are many existing standard api's that do not take const
> args - even though they do not modify them,

But the point remains -- you said you tried adding the the 'const'
qualifier to the function parameter in question and then you said you
would end up having "to do free(DECONST(pointer))".  Something,
somewhere, is very wrong.  (but you've now given me the hint below to
show what's wrong -- yes, I should have read the code first, but I
didn't)

> Within an application you can analyze all your routines, and for those
> that do not attempt to modify their args, add const to their
> prototypes...   but you eventually come up against api's that are out
> of your control and have to do someting sane when interfacing to them.

Sometimes that's true, but never with free(), for example.  :-)

> Make is full of routines that get passed a pointer and a flag that
> says "free this when you are done".

Such a set of APIs is definitely not friendly to use of 'const' if it
also commonly leads the programmer to sometimes use string constants, at
least not with a compiler that puts string constants in read-only
storage.

The only good solutions are to either: (a) fix the API; or (b) ignore
the compiler warnings; or (c) turn them off using '-fwritable-strings'.

>  The caller is promising that the
> arg in that case has been allocated.  Hence the case of
> fee(DECONST(pointer)).

Ah, I see -- you tried to add a 'const' qualifier to a pointer parameter
that could indeed sometimes be passed to free(), but only if some other
parameter indicated that it should.  Your attempt to const-qualify that
pointer parameter was doomed to failure.  You cannot try to tell the
compiler to 'const'-qualify a parameter which is a pointer to storage
that might be passed to free(), even if only sometiems.  From the
compiler's point of view that storage is either _always_ read-only, or
it's not.  The compiler, nay the language itself, will not allow a
parameter to sometimes be qualified and sometimes not be qualified.
I.e. you can't have it both ways for any paramter(s) to a function that
might sometimes try to modify that storage (e.g. pass it to free(),
which effectively makes it modifiable, but of course the compiler only
knows that free() might modify the storage or pass a pointer to it to
something else that will).

The Standard C 'const' qualifier is indeed a poorly concieved hack with
very limited utility.  However if used with great care it can usually
help the programmer detect at compile-time programming errors which
could result in aborts caused by attempts to write to string constants
or other storage that is in fact truly read-only.  However as it seems
is the case for every attempt to impose compile-time checks on the C
language, this hack also has its costs.  As you've discovered one of
those costs is using '-fwritable-strings' even when it's not strictly
necessary.

The compiler in this case is telling you that it's not going to be
responsible for the correctness of the code that's only ever supposed to
result in a path to free() when the storage is not read-only.

>  The more general point is that at somepoint in
> the call change you need to DECONST() when calling a standard api that
> will not modify the arg but does not take a const arg.

First off I wouldn't call the internal APIs of Pmake "standard".  :-)
(I've not yet encountered any standard API that's really broken
w.r.t. 'const')

Secondly the prototypes for the API are correct already.  The pointer
parameters you're having trouble with CANNOT ever be const-qualified, at
least not from any Standard C compiler's point of view.

Third:  Anything that leads you do need DECONST() means you've gone down
the wrong fork in the path in the first place.  You have to back to the
point you started, remove the 'const' qualifier you added, and make sure
that you never pass a pointer to read-only storage to any function that
might eventually, even just sometimes, try to modify that storage.  If
you have to DECONST() a pointer then that means that pointer should
never have been 'const'- qualified in the first place.  If that means
using '-fwritable-strings' to avoid a de-const on assignment warning
then that's what it means.

Finally your/my hack with ``char foo[] = "string";'' only fools GCC --
it's not a proper or portable fix since if I understand correctly (I may
be wrong as I'm no GCC internals expert!) it doesn't really de-const the
string constant that your "foo" will point to.  Only -fwritable-strings
will do that (at least with GCC), and unfortunately it does it for the
whole module, not just one string constant.

I too wish Standard C had a "writable strings" qualifier for string
constants, just as integer constants have an "unsigned" qualifier, and
perhaps a trailing 'W' would work (provided not too many people confused
it with the meaning of the leading 'L' already provided to declare the
following string constant as containing wide characters).  Adding such a
qualifier to GCC would be a start, but ultimately the standard would
need updating to make such a thing really useful for anything but code
audits within closed systems using only GCC.

-- 
						Greg A. Woods

+1 416 218-0098                  VE3TCP            RoboHack <woods@robohack.ca>
Planix, Inc. <woods@planix.com>          Secrets of the Weird <woods@weird.com>