Search code examples
cgccclangcompiler-warningsgcc-warning

Why do C compilers make it so hard to get -Wwrite-strings warnings?


Consider this C code:

void foo(char *);

void bar(void) {
    foo("");
}

When I compile that with -pedantic -Wall -Wextra with GCC or Clang, or with -Weverything with Clang, it compiles without giving me any relevant warnings. If I add -Wwrite-strings, then GCC gives me this:

<source>:4:9: warning: passing argument 1 of 'foo' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
    4 |     foo("");
      |         ^~
<source>:1:10: note: expected 'char *' but argument is of type 'const char *'
    1 | void foo(char *);
      |          ^~~~~~

And clang gives me this:

<source>:4:9: warning: passing 'const char [1]' to parameter of type 'char *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
    foo("");
        ^~
<source>:1:16: note: passing argument to parameter here
void foo(char *);
               ^

Lots of things seem wrong here to me:

  • This seems like a pretty important warning not only to be off by default, but also to be off even with most of the ways that people enable warnings in practice.
  • GCC's output refers to -Wdiscarded-qualifiers, but if I pass that instead of -Wwrite-strings, I don't get that warning.
  • I thought -Weverything meant "literally every single warning the compiler knows about", but this seems to contradict that.
  • If I compile the same code as C++ instead of as C, then both GCC and Clang give me the warning I want without needing any compiler flags at all. I'm not sure why there's a difference between the languages here, since AFAIK, both C and C++ have undefined behavior if you actually write to the string literal.

What's going on here? Why does this particular warning seem so buggy?


Solution

  • In C, string literals existed before const did. So C string literals were not const-qualified (although the results of attempting to write to them are not defined by the C standard). If string literals were made const-qualified, much old software would break due to type errors. The C committee has decided this change is not worthwhile.

    The switch -Wwrite-strings is not really a warning switch, in spite of -W. It changes the language being compiled to a non-standard C in which string literals are const-qualified. (There is a bug report that it is a mistake for GCC to categorize this as a warning switch.) This explains why GCC shows a -Wdiscarded-qualifier when a string literal is assigned to a char *, and it also explains why -Wdiscarded-qualifier alone does not trigger these warnings—because without -Wwrite-strings, the string literal is not const-qualified, so no qualifier is being discarded by the assignment.

    Presumably Clang’s -Weverything does not include -Wwrite-strings because, as noted above, it is not truly a warning option and because it changes the language to non-standard C.