Discussion:
-Wcast-qual and casting away
Ian Lance Taylor
2009-05-21 05:10:51 UTC
Permalink
Consider this C/C++ program:

extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }

If I compile this program with g++ -Wcast-qual, I get this:

foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers

If I compile this program with gcc -Wcast-qual, I do not get any
warning.

Let's overlook the fact that the text of the g++ warning does not make
any sense--I am certainly not casting anything away. The warning is
conceptually plausible for the same reason that you can't assign a
char** variable to a const char** variable without a cast. At least, I
think one could make a argument that that is so. But it's not a *very*
strong argument, as -Wcast-qual is documented to warn about cases where
a type qualifier is removed, and that is manifestly not happening here.
-Wcast-qual is useful to catch certain programming errors; I don't think
anybody adding a const qualifier is actually making a mistake.

All that aside, I can't think of any reason that the C and C++ frontends
should be different in this regard. Does anybody want to make an
argument for which of these choices we should adopt?

1) Keep things the same: the C++ frontend warns, the C frontend doesn't.
Consistency is overrated.

2) Change the C frontend to also warn about this case, albeit with a
better message.

3) Change the C++ frontend to not warn about this case.

Of course in all cases the frontends should continue to warn about a
cast from const void** to void**.

Ian
Richard Guenther
2009-05-21 10:20:53 UTC
Permalink
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
If I compile this program with gcc -Wcast-qual, I do not get any
warning.
Let's overlook the fact that the text of the g++ warning does not make
any sense--I am certainly not casting anything away.  The warning is
conceptually plausible for the same reason that you can't assign a
char** variable to a const char** variable without a cast.  At least, I
think one could make a argument that that is so.  But it's not a *very*
strong argument, as -Wcast-qual is documented to warn about cases where
a type qualifier is removed, and that is manifestly not happening here.
-Wcast-qual is useful to catch certain programming errors; I don't think
anybody adding a const qualifier is actually making a mistake.
All that aside, I can't think of any reason that the C and C++ frontends
should be different in this regard.  Does anybody want to make an
argument for which of these choices we should adopt?
1) Keep things the same: the C++ frontend warns, the C frontend doesn't.
  Consistency is overrated.
2) Change the C frontend to also warn about this case, albeit with a
  better message.
3) Change the C++ frontend to not warn about this case.
Of course in all cases the frontends should continue to warn about a
cast from const void** to void**.
As the C++ warning doesn't make any sense I vote for 3).

Richard.
Post by Ian Lance Taylor
Ian
Gabriel Dos Reis
2009-05-21 17:31:46 UTC
Permalink
On Thu, May 21, 2009 at 5:20 AM, Richard Guenther
Post by Richard Guenther
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
If I compile this program with gcc -Wcast-qual, I do not get any
warning.
Let's overlook the fact that the text of the g++ warning does not make
any sense--I am certainly not casting anything away.  The warning is
conceptually plausible for the same reason that you can't assign a
char** variable to a const char** variable without a cast.  At least, I
think one could make a argument that that is so.  But it's not a *very*
strong argument, as -Wcast-qual is documented to warn about cases where
a type qualifier is removed, and that is manifestly not happening here.
-Wcast-qual is useful to catch certain programming errors; I don't think
anybody adding a const qualifier is actually making a mistake.
All that aside, I can't think of any reason that the C and C++ frontends
should be different in this regard.  Does anybody want to make an
argument for which of these choices we should adopt?
1) Keep things the same: the C++ frontend warns, the C frontend doesn't.
  Consistency is overrated.
2) Change the C frontend to also warn about this case, albeit with a
  better message.
3) Change the C++ frontend to not warn about this case.
Of course in all cases the frontends should continue to warn about a
cast from const void** to void**.
As the C++ warning doesn't make any sense I vote for 3).
Why do you think it the warning does not make sense?

The only you can go from void** to const void** is that you actually
remove the const in the middle of const void* const *. That is
precisely what the cast is doing -- so the warning is legitimate.

-- Gaby
Andreas Schwab
2009-05-21 11:50:18 UTC
Permalink
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
In a sense this warning is actually correct: this is storing a const
char * into a void * object, which is where the qualifier is lost. IMHO
having a warning for this questionable operation is a good thing.

Andreas.
--
Andreas Schwab, ***@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
Richard Guenther
2009-05-21 11:58:34 UTC
Permalink
Post by Andreas Schwab
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
In a sense this warning is actually correct: this is storing a const
char * into a void * object, which is where the qualifier is lost.  IMHO
having a warning for this questionable operation is a good thing.
I don't think so.

extern char **f1();
void f(char *p)
{
*(const char **)f1() = p;
}

warns the same. typeof(*(const char **)) should still be const char *.

For

extern const char **f1();
void f(char *p)
{
*(char **)f1() = p;
}

it warns with

t.C: In function ‘void f(char*)’:
t.C:4: warning: cast from type ‘const char**’ to type ‘char**’ casts
away constness

which makes sense.

Richard.
Andreas Schwab
2009-05-21 12:09:38 UTC
Permalink
Post by Richard Guenther
For
extern const char **f1();
void f(char *p)
{
*(char **)f1() = p;
}
it warns with
t.C:4: warning: cast from type ‘const char**’ to type ‘char**’ casts
away constness
which makes sense.
This is actually a safe operation, you can even store safely into
**(char **)f1() afterwards.

Andreas.
--
Andreas Schwab, ***@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
Sebastian Redl
2009-05-21 12:15:56 UTC
Permalink
Post by Richard Guenther
Post by Andreas Schwab
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
In a sense this warning is actually correct: this is storing a const
char * into a void * object, which is where the qualifier is lost. IMHO
having a warning for this questionable operation is a good thing.
I don't think so.
extern char **f1();
void f(char *p)
{
*(const char **)f1() = p;
}
warns the same. typeof(*(const char **)) should still be const char *.
It seems the rules for the warning follow the same rules for whether
such qualifier changes are allowed in implicit conversions (or named
casts other than const_cast).

Let's say char** -> const char** is allowed silently. Then the program
below is silently violating const correctness:
char* pc;
char** ppc = &pc;
const char** cppc = ppc; // Silently allowed?
const char cc = 0;
*ppc = &cc; // Silently allowed, pc now points to cc
*pc = 1; // Silently allowed, but changes cc

The conversion T** -> const T** is unsafe. That's all there is to it.
The warning is correct.
Cast to T const* const* instead.

Sebastian
Ian Lance Taylor
2009-05-21 16:11:38 UTC
Permalink
Post by Richard Guenther
Post by Andreas Schwab
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
In a sense this warning is actually correct: this is storing a const
char * into a void * object, which is where the qualifier is lost.  IMHO
having a warning for this questionable operation is a good thing.
I don't think so.
extern char **f1();
void f(char *p)
{
*(const char **)f1() = p;
}
warns the same. typeof(*(const char **)) should still be const char *.
For
extern const char **f1();
void f(char *p)
{
*(char **)f1() = p;
}
it warns with
t.C:4: warning: cast from type ‘const char**’ to type ‘char**’ casts
away constness
which makes sense.
Let's not focus too much on the operation (the indirection and the
assignment). The warning is about the cast itself. Should we issue
that warning or not? Others have explained the cases where the cast can
lead to unsafe code.

The indirection + assignment operation is easy to do in all cases by
moving the cast to the other side.

Ian
Gabriel Dos Reis
2009-05-21 17:36:10 UTC
Permalink
Post by Ian Lance Taylor
Post by Richard Guenther
Post by Andreas Schwab
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
In a sense this warning is actually correct: this is storing a const
char * into a void * object, which is where the qualifier is lost.  IMHO
having a warning for this questionable operation is a good thing.
I don't think so.
extern char **f1();
void f(char *p)
{
  *(const char **)f1() = p;
}
warns the same. typeof(*(const char **)) should still be const char *.
For
extern const char **f1();
void f(char *p)
{
  *(char **)f1() = p;
}
it warns with
t.C:4: warning: cast from type ‘const char**’ to type ‘char**’ casts
away constness
which makes sense.
Let's not focus too much on the operation (the indirection and the
assignment).  The warning is about the cast itself.  Should we issue
that warning or not?  Others have explained the cases where the cast can
lead to unsafe code.
The particular cast in question is not a safe operation. Should we warn
about it when -Wcast-qual, I think so -- that is one of the purposes of
the switch.

-- Gaby

Joseph S. Myers
2009-05-21 11:58:27 UTC
Permalink
Post by Ian Lance Taylor
All that aside, I can't think of any reason that the C and C++ frontends
should be different in this regard. Does anybody want to make an
There's the fairly obvious reason that C and C++ have different rules on
implicit conversions involving const, so const at higher levels of
indirection is irrelevant in C in a way that it is not in C++. (Adoption
of the C++ rules was considered and rejected for C99.)
--
Joseph S. Myers
***@codesourcery.com
Ian Lance Taylor
2009-05-21 15:47:19 UTC
Permalink
Post by Joseph S. Myers
Post by Ian Lance Taylor
All that aside, I can't think of any reason that the C and C++ frontends
should be different in this regard. Does anybody want to make an
There's the fairly obvious reason that C and C++ have different rules on
implicit conversions involving const, so const at higher levels of
indirection is irrelevant in C in a way that it is not in C++. (Adoption
of the C++ rules was considered and rejected for C99.)
Fair enough, but I don't see any particular reason these rather esoteric
rules should affect the -Wcast-qual option.

Ian
Dave Korn
2009-05-21 12:49:30 UTC
Permalink
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
If I compile this program with gcc -Wcast-qual, I do not get any
warning.
2) Change the C frontend to also warn about this case, albeit with a
better message.
Of course in all cases the frontends should continue to warn about a
cast from const void** to void**.
Well, they should warn about this case also then.

extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }

void *x;

void **f1()
{
return &x
}

void f2(char *foo, int n)
{
memcpy (x, foo, n); // BOOM!
}

This looks like a disguised way of casting a const char* to a (non-const)
void* to me, isn't it? Sure, the warning is being tripped by the cast rather
than the conversion that the cast is hiding, it needs to be improved, but I'm
glad we got at least some noise from such a dubious construction.

cheers,
DaveK
Gabriel Dos Reis
2009-05-21 17:29:34 UTC
Permalink
Post by Ian Lance Taylor
extern void **f1();
void f2(const char *p) { *(const void **)f1() = p; }
foo2.cc:2: warning: cast from type ‘void**’ to type ‘const void**’ casts away qualifiers
If I compile this program with gcc -Wcast-qual, I do not get any
warning.
Let's overlook the fact that the text of the g++ warning does not make
any sense--I am certainly not casting anything away.  The warning is
conceptually plausible for the same reason that you can't assign a
char** variable to a const char** variable without a cast.  At least, I
think one could make a argument that that is so.  But it's not a *very*
strong argument, as -Wcast-qual is documented to warn about cases where
a type qualifier is removed, and that is manifestly not happening here.
-Wcast-qual is useful to catch certain programming errors; I don't think
anybody adding a const qualifier is actually making a mistake.
In fact, the contrary is true. The warning speaks the truth.
The implicit conversion is unsafe and the cast is explicitly
by-passing that const-safety built into the type system.

-- Gaby
Loading...