This page documents longer explanations for diagnostics in the C and C++ front-ends. Contact the gcc-help mailing list to propose new content and/or get access to edit this page yourself (unfortunately due to repeated spam attacks you need to contact a human being to get editing rights, but doing so is easy).

TODO

assuming signed overflow does not occur when simplifying conditional to constant [-Wstrict-overflow]

[ Permalink ]

Signed overflow is undefined behavior according to the C/C++ language. Compilers rely on the fact that a valid program does not contain undefined behavior to optimize code, by assuming that conditions which would lead to undefined behaviour are always false (which is true for valid programs). The -Wstrict-overflow warning does not always mean that the code has a bug, only that some condition that could theoretically happen given the part of the code that the compiler is optimizing (which after inlining and other optimizations may be quite different from what the user wrote) never happens in practice. On the other hand, it may indeed denote a bug, either because the user assumed that signed overflow wraps like unsigned overflow or because the signed overflow does happen in practice.

Given the difficulty of distinguishing, at compilation time, between user errors and valid optimizations, this warning was removed in GCC 8: "-fno-strict-overflow is now mapped to -fwrapv and signed integer overflow is now undefined by default at all optimization levels. Using -fsanitize=signed-integer-overflow is now the preferred way to audit code, -Wstrict-overflow is deprecated."

%D may be used uninitialized [-Wuninitialized]

[ Permalink ]

A variable that has not been initialized is read at some point in the code. Reading a uninitialized variable leads to undefined behaviour. Whether a variable is assigned a value before reading it cannot be decided in general. Depending on the optimization level, the version of GCC and the particular code, GCC may not be able to detect that a variable is actually always initialized and give a false warning. On the other hand, it may be able to detect that the variable is never used apart from uninitialized use and not warn.

See also -Wuninitialized in GCC manual

warning: passing argument %d of %D from incompatible pointer type; note: expected ‘const char **’ but argument is of type ‘char **’

[ Permalink ]

See the C++ FAQ (this question applies to both C and C++):

https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion

In C++ you can use const char* const* to solve the problem, but that will still give a warning in C even though it's actually safe (as described in the C++ FAQ).

warning: switch condition has boolean value [-Wswitch-bool]

[ Permalink ]

Boolean variables in switch conditions are very uncommon and often indicate that the wrong variable is being used. Boolean expressions are even less common, and normally indicate that a bit-wise operator (& instead of &&, or | instead of ||) was intended. There may be valid cases that use a boolean in a switch:

switch (bool)
    {
    case true: X();
    default: Y();
    }

however, all of those cases can be written (arguably, more clearly) using if-else:

if(bool)
  X();
Y();

C++ FE

error: uninitialized const 'd' [-fpermissive], note: 'const class D' has no user-provided default constructor

[ Permalink ]

   1    class C { };
   2    class D : public C { };
   3    const D d;

As mandated by the C++ standard (8.5 [decl.init], para 9 in C++03, para 6 in C++0x), G++ does not allows objects of const-qualified type to be default initialized unless the type has a user-declared default constructor. Code that fails to compile can be fixed by providing an initializer e.g.

   1     const D d = D();

error: there are no arguments to 'f' that depend on a template parameter, so a declaration of 'f' must be available

[ Permalink ]

   1     template<typename T>
   2     struct base
   3     {
   4       void f();
   5     };
   6     template<typename T>
   7     struct derived : public base<T>
   8     {
   9       void g() { f(); }
  10     };

The C++ standard says (14.6.2 [temp.dep] para 3) that lookup of unqualified names (such as f in the example) does not look in a "dependent base", i.e. a base class which depends on a template parameter. To tell the compiler that f is a member and lookup should include dependent bases, it must be qualified as either this->f() or base<T>::f(). See Name lookup, templates, and accessing members of base classes or the C++ templates FAQ or C++ FAQ for further details.

undefined reference to `S::a'

[ Permalink ]

Some people are surprised to get a linker error when using static const members in conditional expressions (i.e. the ?: operator) like so:

   1 struct S
   2 {
   3     static const int a = 1;
   4     static const int b = 2;
   5 };
   6 int main(int argc, char** argv)
   7 {
   8    return argc > 1 ? S::a : S::b;
   9 }

This program is invalid and will fail to link:

/tmp/ccATW9fH.o: In function `main':
n.cc:8: undefined reference to `S::a'
n.cc:8: undefined reference to `S::b'
collect2: error: ld returned 1 exit status

The C++ standard says ([basic.def.odr] 3.2 paragraph 2) "A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied." and (paragraph 3) "Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required."

In the example above the lvalue-to-rvalue conversion cannot be applied, because (x?a:b) is an lvalue when a and b are both lvalues of the same type.

The same link error can happen if the static const member is used as an argument to a function which has reference parameters e.g.

   1 #include <algorithm>
   2 struct S
   3 {
   4     static const int a = 1;
   5     static const int b = 2;
   6 };
   7 int main(int argc, char** argv)
   8 {
   9    return std::max(S::a, S::b);
  10 }

It should be noted that with optimization enabled the variables may not be referenced, due to inlining or constant propagation, and so linking might succeed, but the program is still technically invalid.

The program can be fixed by providing a definition of the variables and/or by forcing either variable to be converted to an rvalue:

   1 struct S
   2 {
   3     static const int a = 1;
   4     static const int b = 2;
   5 };
   6 const int S::a;  // definition
   7 const int S::b;  // definition
   8 int main(int argc, char** argv)
   9 {
  10    return argc > 1 ? S::a : (int)S::b;
  11 }

See also Declare and Define Static Members and How do I define an in-class constant?

N.B. In February 2012 DR 712 updated the language so that the static variables in the conditional expressions above are no longer odr-used, so definitions are no longer required in the conditional expression case. Definitions are still required for cases like the std::max() example. That DR is not implemented by any version of G++ (at the time of writing GCC 4.7.1 is the latest release.)

undefined reference to vtable for X

[ Permalink ]

Every polymorphic class requires a virtual table or vtable describing the type and its virtual functions. Whenever possible the vtable will only be generated and output in a single object file rather than generating the vtable in every object file which refers to the class (which might be hundreds of objects that include a header but don't actually use the class.) The cross-platform C++ ABI states that the vtable will be output in the object file that contains the definition of the key function, which is defined as the first non-inline, non-pure, virtual function declared in the class. If you do not provide a definition for the key function (or fail to link to the file providing the definition) then the linker will not be able to find the class' vtable.

In this example, X::i() is the key function because the destructor is pure virtual, X::f() is not virtual and X::g() and X::h() are inline:

   1 class X {
   2 public:
   3   virtual ~X() = 0;
   4   void f();
   5   virtual void g() { }
   6   virtual void h();
   7   virtual void i();
   8   virtual void j();
   9 };
  10 inline void X::h() { }

To fix the linker error be sure you have provided a definition for the first non-inline non-pure virtual function declared in the class (X::i() in the example above). It may be possible to improve the error message, although this is far from trivial.

For a more detailed description of this error see Missing Key Function in the LLD documentation.

warning: deleting object of polymorphic class type 'Base' which has non-virtual destructor might cause undefined behaviour [-Wdelete-non-virtual-dtor]

[ Permalink ]

Deleting an object through a pointer to its base class will only run the derived class destructor if the base class destructor is virtual. If the destructor of the base class is not virtual then the delete operation has undefined behaviour, often resulting in (but not limited to) leaking memory and other resources managed by the derived class.

G++ will warn when delete is used with a polymorphic type (i.e. a type that has virtual functions) that does not have a virtual destructor e.g.

   1 class Base {
   2 public:
   3   virtual void foo() = 0;
   4 };
   5 
   6 class Derived : public Base {
   7 public:
   8   virtual void foo() {}
   9   ~Derived()         {}
  10 };
  11 
  12 void bar(Base* p)
  13 {
  14   p->foo();
  15   delete p;  // warning!
  16 }
  17 
  18 void baz(Derived* p)
  19 {
  20   p->foo();
  21   delete p;  // warning!
  22 }
  23 
  24 int main() {
  25   Derived* d1 = new Derived();
  26   bar(d1);
  27   Derived* d2 = new Derived();
  28   baz(d2);
  29 }

This code produces two warnings:

d.C: In function 'void bar(Base*)':
d.C:15:10: warning: deleting object of abstract class type 'Base' which has non-virtual destructor will cause undefined behaviour [-Wdelete-non-virtual-dtor]
   delete p;  // warning!
          ^
d.C: In function 'void baz(Derived*)':
d.C:21:10: warning: deleting object of polymorphic class type 'Derived' which has non-virtual destructor might cause undefined behaviour [-Wdelete-non-virtual-dtor]
   delete p;  // warning!
          ^

The first warning says bar will cause undefined behaviour, because Base is an abstract class so the actual object being deleted must be some type derived from Base (because an abstract class can only be used as a base class of another type) and because there is no virtual destructor the derived class destructor will not be called, resulting in undefined behaviour.

The second warning says baz might cause undefined behaviour, because the delete operation is safe if the object being destroyed is a Derived, but unsafe if it is some type derived from Derived. In the code above the call to baz is actually safe, but G++ still issues a warning for baz because a class that has virtual functions suggests it is likely (or at least possible) it will be used as a base class, in which case the delete operation would be unsafe e.g.

   1 // Base, Derived and baz() defined as above
   2 class MoreDerived : public Derived {
   3   std::string str;
   4 public:
   5   virtual void foo() { }
   6   ~MoreDerived() { }
   7 };
   8 
   9 int main() {
  10   MoreDerived* md = new MoreDerived();
  11   baz(md);
  12 }

In this example the MoreDerived destructor will not execute, causing a memory leak.

The will cause undefined behaviour warning always indicates a real bug that should be fixed, either by adding a virtual destructor, or by deleting the correct type instead of the base class. Note that even a pure virtual destructor requires an implementation: [C++11: 12.4/9]: A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual. For example:

   1 class Base {
   2 public:
   3   virtual void foo() = 0;
   4   virtual ~Base() = 0;
   5 };
   6 inline Base::~Base() {}

The might cause undefined behaviour warning indicates a potential bug, but the warning will sometimes be issued for correct code, as in the first example above. Adding a virtual destructor will ensure the code is safe and so prevents the warning. Alternatively, the warning will not be issued if the compiler knows the class cannot be derived from i.e. if the class uses the C++11 final specifier

   1 class Derived final : public Base {
   2   // ...
   3 };

In C++98 and C++03 code the alternative spelling __final can be used instead (this is a GCC extension.)

(The -Wdelete-non-virtual-dtor warning and final specifier are supported by GCC 4.7.0 and later releases.)

error: passing 'const X' as 'this' argument discards qualifiers [-fpermissive]

[ Permalink ]

This error is given when trying to call a non-const member function on a const object or from inside a const member function. A non-const member function might modify the object, which is not allowed through a const access path.

   1 class X {
   2 public:
   3   void update(int n) { m_counter + n; }
   4   void doSomething() const { update(1); }
   5 private:
   6   int m_counter = 0;
   7 };
   8 int main() {
   9   X x;
  10   x.doSomething();
  11 }

The solution might be to add const to the member function (if it doesn't really need to modify anything), or to remove const elsewhere to get a modifiable pointer/reference to the object. For the example above, X::doSomething() should not be const if it needs to call X::update(int)

"control reaches end of non-void function" after a switch using an enum

[ Permalink ]

Many users are surprised to get a warning for code like this:

enum class E { E1, E2 };

int to_int(E e) {
  switch (e) {
  case E::E1:
    return 1;
  case E::E2:
    return 2;
  }
}

e.cc: In function ‘int to_int(E)’:
e.cc:10:1: warning: control reaches end of non-void function [-Wreturn-type]
    10 | }
      | ^

It's a common misunderstanding that variables of type E can only ever have two values, either E::E1 or E::E2, and so the warning is wrong. In fact G++ is correct. You can explicitly create a variable with a different value and pass it to the function, for example to_int(E{3}) or to_int((E)-4). For those calls, neither switch-case will match, and the program's control flow will reach the end of the function, resulting in undefined behaviour.

For an enumeration type with a fixed underlying type any value of the underlying type can be converted to the enum in this way. For an enumeration type without a fixed underlying type, the range of valid values that can be created depends on the enumerators declared for it (but is still not restricted to just the specific values corresponding to the enumerator constants). An enumeration type has a fixed underlying type if it is a scoped enumeration (i.e. enum class) or if it is declared with an enum-base (e.g. enum F : int { ... }).

To prevent the warning, either add a default: case to the switch to handle other values, or inform the compiler that your program will never pass any other values, like so:

int to_int(E e) {
  switch (e) {
  case E::E1:
    return 1;
  case E::E2:
    return 2;
  }
  // promise the compiler that other values will never
  // be passed to this function:
  __builtin_unreachable();
}

None: VerboseDiagnostics (last edited 2023-11-13 20:08:44 by JonathanWakely)