Looking forward to GCC6 – Many new warnings

Like every new GCC release, GCC6 introduces a lot of new useful warnings. My favorite is still -Wmisleading-indentation. But there are many more that have found various bugs. Not all of them are enabled by default, but it makes sense to enable as many as possible when writing new code.

Duplicate logic

In GCC6 -Wlogical-op (must be enabled explicitly) now also warns when the operands of a logical operator are the same. For example the following typo is detected:

points.c: In function 'distance':
points.c:10:19: warning: logical 'or' of equal expressions [-Wlogical-op]
   if (point.x < 0 || point.x < 0)
                   ^~

Similar logic for detection duplicate conditions in an if-else-if chain has been added with -Wduplicated-cond. It must be enabled explicitly, which I would highly recommend because it found some real bugs like:

elflint.c: In function 'compare_hash_gnu_hash':
elflint.c:2483:34: error: duplicated 'if' condition [-Werror=duplicated-cond]
  else if (hash_shdr->sh_entsize == sizeof (Elf64_Word))
           ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~

elflint.c:2448:29: note: previously used here
  if (hash_shdr->sh_entsize == sizeof (Elf32_Word))
      ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~

GCC is correct, a Word in both an Elf32 and Elf64 file is 4 bytes. We meant to check for sizeof (Elf64_Xword) which is 8 bytes.

And with -Wtautological-compare (enabled by -Wall) GCC6 will also detect comparisons of variables against themselves which will always be true or false. Like in the case where we made a typo:

result.c: In function 'check_fast':
result.c:14:14: warning: self-comparison always evaluates to false [-Wtautological-compare]
  while (res > res)
             ^

Finally -Wswitch-bool (enabled by default) has been improved to only warn about switch statements on a boolean type if any of the case statements is outside the range of the boolean type.

Bit shifting

GCC5 already had warnings for -Wshift-count-negative and -Wshift-count-overflow. Both are enabled by default.

value.c: In function 'calculate':
value.c:7:9: warning: left shift count is negative [-Wshift-count-negative]
  b = a << -3;
        ^~
value.c:8:9: warning: right shift count >= width of type [-Wshift-count-overflow]
  b = a >> 63;
        ^~

GCC6 adds -Wshift-negative-value (enabled by -Wextra) which warns about left shifting a negative value. Such shifts are undefined because they depend on the representation of negative values.

value.c:9:10: warning: left shift of negative value [-Wshift-negative-value]
  b = -1 << 5;
         ^~

Also added in GCC6 is -Wshift-overflow (enabled by default) to detect left shift overflow.

value.c:10:11: warning: result of '10 << 30' requires 35 bits to represent, but 'int' only has 32 bits [-Wshift-overflow=]
 b = 0xa << (14 + 16);
         ^~

You can increase the warnings given with -Wshift-overflow=2 (not enabled by default) which makes GCC also warn if the compiler can detect you are shifting a signed value that would change the sign bit.

value.c:11:10: warning: result of '1 << 31' requires 33 bits to represent, but 'int' only has 32 bits [-Wshift-overflow=]
  b |= 1 << 31;
         ^~				

NULL

The new -Wnull-dereference (must be enabled explicitly) warns when GCC detects you (might) dereference a null pointer that will cause erroneous or undefined behavior (higher optimization levels might catch more cases).

dereference.c: In function 'test2':
dereference.c:30:21: error: null pointer dereference [-Werror=null-dereference]
  if (s == NULL && s->bar > 2)
                   ~^~~~~

-Wnonnull (enabled by -Wall) already warned for passing a null pointer for an argument marked with the nonnull attribute. In GCC6 it has been extended to also warn for comparing an argument marked with nonnull against NULL inside a function.

nonnull.c: In function 'foo':
nonnull.c:8:7: error: nonnull argument 'bar' compared to NULL [-Werror=nonnull]
  if (!bar)
      ^

C++

-Wterminate (enabled by default) warns when a throw will immediate result in a call to terminate like in an noexcept function. In particular it will warn when something is thrown from a C++11 destructor since they default to noexcept, unlike in C++98 (GCC6 defaults to -std=gnu++14).

collect.cxx: In destructor 'area_container::~area_container()':
collect.cxx:23:50: warning: throw will always call terminate() [-Wterminate]
    throw sanity_error ("disposed while negative");
                                                 ^
collect.cxx:23:50: note: in C++11 destructors default to noexcept

The help with some ODR issues GCC6 has -Wlto-type-mismatch and -Wsubobject-linkage.

C++ allows “placement new” of objects at a specified memory location. You are responsible for making sure the memory location provided is of the correct size. This might result in A New Class of Buffer Overflow Attacks. When GCC6 detects the provided buffer is too small it will issue a warning with -Wplacement-new (enabled by default).

placement.C: In function 'S* f(S*)':
placement.C:9:27: warning: placement new constructing an object of type 'S' and size '16' in a region of type 'char [8]' and size '8' [-Wplacement-new=]
     S *t = new (buf) S (*s);
                           ^

And if you actually want less C++ then GCC6 will give you -Wtemplates, -Wmultiple-inheritance, -Wvirtual-inheritance and -Wnamespaces to help enforce coding styles that don’t like those C++ features.

Unused side effects

In GCC6 -Woverride-init-side-effects (enabled by default) is its own warning when you use Designated Initializers multiple times with side effects. If the same field, or array element, is initialized multiple times, it has the value from the last initialization. But if any such overridden initializations has side-effects, it is unspecified whether the side-effect happens or not. So you’ll get a warning for such overrides:

side.c: In function 'foo':
side.c:18:68: warning: initialized field with side-effects overwritten [-Woverride-init-side-effects]
struct Secrets s = { .alpha = count++, .beta = count++, .alpha = count++ };
                                                                 ^~~~~
side.c:18:68: note: (near initialization for 's.alpha')

Before GCC6 -Wunused-variable (enabled by -Wall) didn’t warn for unused static const variables in C. This was because some old code had constructs like: static const char rcs_id[] = "$Id:...";. But this old special use case is not very common anymore. And not warning about such unused variables was hiding real bugs. So GCC6 introduces -Wunused-const-variable (enabled by -Wunused-variable for C, but not for C++). There is still some debate on how to fine tune this warning. So please comment if you find some time to experiment with it before GCC6 is officially released.

framed

Calling __builtin_frame_address or __builtin_return_address with a level other than zero (the current function) is almost always a mistake (it cannot be guaranteed to return a valid value and might even crash the program). So GCC6 now has -Wframe-address (enabled by -Wall) to warn about any such usage.

7 Comments

  1. Anon says:

    With regard to optimisation levels controlling the ability of certain types of errors to be found: is it the case that a “higher” optimization level (e.g. O3 over O2 or O2 over Os) will warn on at least the problems found at lower levels?

    • Mark Wielaard says:

      It is likely the case if the higher optimization uses a super set of optimization passes of the lower level. I don’t know which passes -O2 and -Os have in common.

  2. Stefan says:

    > In GCC6 it has been extended to also warn for comparing an argument marked
    > with nonnull against NULL inside a function.

    This sounds dangerous. If a function has an argument with attribute nonnull, this will only protect from passing an explicit NULL to that function. But if a variable is passed to that function and the variable is NULL and the compiler cannot know that during compile time, compilation will succeed. So the function still has to check for the pointer being NULL. This warning may lead to programmers removing necessary checks.

    • Mark Wielaard says:

      No, it is the other way around. When an argument is tagged with nonnull then the compiler can and will assume that argument is never NULL. So any check of the argument against NULL will be removed as redundant already by the compiler. Which can cause subtle bugs as seen in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=17308#c8. So the new warning essentially says “you promised this check is redundant and it will be removed”.

  3. Ken C says:

    Is there a better way to do something like?

    static const char rcs_id[] = "$Id:...";

    You mentioned that the way above is not common anymore.

    • Mark Wielaard says:

      If you want to use that construct and suppress the warning use:

      static const char rcs_id[] __attribute__ ((unused)) = "$Id:...";

      If you also want the variable and string to not be optimized away use:

      static const char rcs_id[] __attribute__ ((used)) = "$Id:...";

  4. Bruno says:

    When I recently tried GCC 5.3 instead of the usual GCC 4.8, two actual bugs (not found by Coverity) of the kind !size == 1 where found by this warning:

    warning: logical not is only applied to the left hand side of comparison [-Wlogical-not-parentheses]