Porting to GCC 8

The GCC 8 release series differs from previous GCC releases in a number of ways. Some of these are a result of bug fixing, and some old behaviors have been intentionally changed to support new standards, or relaxed in standards-conforming ways to facilitate compilation or run-time performance.

Some of these changes are user visible and can cause grief when porting to GCC 8. This document is an effort to identify common issues and provide solutions. Let us know if you have suggestions for improvements!

Preprocessor issues

C language issues

C++ language issues

-Wreturn-type is enabled by default

G++ now assumes that control never reaches the end of a non-void function (i.e. without reaching a return statement). This means that you should always pay attention to -Wreturn-type warnings, as they indicate code that can misbehave when optimized.

To tell the compiler that control can never reach the end of a function (e.g. because all callers enforce its preconditions) you can suppress -Wreturn-type warnings by adding __builtin_unreachable:


      char signchar(int i) // precondition: i != 0
      {
        if (i > 0)
          return '+';
        else if (i < 0)
          return '-';
        __builtin_unreachable();
      }
  

Because -Wreturn-type is now enabled by default, G++ will warn if main is declared with an implicit int return type (which is non-standard but allowed by GCC). To avoid the warning simply add a return type to main, which makes the code more portable anyway.

Stricter rules when using templates

G++ now diagnoses even more cases of ill-formed templates which can never be instantiated (in addition to the stricter rules in GCC 7). The following example will now be diagnosed by G++ because the type of B<T>::a does not depend on T and so the function B<T>::f is ill-formed for every possible instantiation of the template:


      class A { };
      template <typename T> struct B {
        bool f() const { return a; }
        A a;
      };
  
In member function 'bool B<T>::f() const':
error: cannot convert 'const A' to 'bool' in return
   bool f() const { return a; }
                           ^

Ill-formed template code that has never been tested and can never be instantiated should be fixed or removed.

Changes to alignof results

The alignof operator has been changed to return the minimum alignment required by the target ABI, instead of the preferred alignment (consistent with _Alignof in C).

Previously the following assertions could fail on 32-bit x86 but will now pass. GCC's preferred alignment for standalone variables of type double or long long is 8 bytes, but the minimum alignment required by the ABI (and so used for non-static data members) is 4 bytes:


      struct D { double val; };
      static_assert(alignof(D) == alignof(double), "...");
      struct L { long long val; };
      static_assert(alignof(L) == alignof(long long), "...");
  

Code which uses alignof to obtain the preferred alignment can use __alignof__ instead.

Associative containers check the comparison function

The associative containers (std::map, std::multimap, std::set, and std::multiset) now use static assertions to check that their comparison functions support the necessary operations. In C++17 mode this includes enforcing that the function can be called when const-qualified:


      struct Cmp {
        bool operator()(int l, int r) /* not const */ { return l < r; }
      };
      std::set<int, Cmp> s;
  
In member function 'bool B<T>::f() const':
error: static assertion failed: comparison object must be invocable as const
       static_assert(is_invocable_v<const _Compare&, const _Key&, const _Key&>,
                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   bool f() const { return a; }
                           ^

This can be fixed by adding const to the call operator:


      struct Cmp {
        bool operator()(int l, int r) const { return l < r; }
      };
  

Fortran language issues

The library ABI version has been increased, necessitating a recompilation of all Fortran code.

Character lengths are now handled as an INTEGER(kind=C_SIZE_T) variable whose size is dependent on the target system, allowing characters longer than 2**31 on 64-bit targets. Prior to GCC 8, the character length was an INTEGER(kind=4) variable on all targets. If calling a Fortran procedure with character arguments from C (or vice versa) without using the standard ISO_C_BINDING feature, the hidden character length argument at the end of the argument list must thus be modified to be of type size_t rather than of type int. For instance, calling the Fortran subroutine


      subroutine foo (s, a)
      character(len=*) :: s
      integer :: a
      ! Code here
      end subroutine foo
  

from C in a way that is compatible with older GFortran versions can be done by defining the prototype as follows:


      #if __GNUC__ > 7
      typedef size_t fortran_charlen_t;
      #else
      typedef int fortran_charlen_t;
      #endif

      void foo_ (char*, int*, fortran_charlen_t);
  

Versions of gfortran prior to 8.1 wrongly accepted CHARACTER variables with a length type parameter other than one as C interoperable. For example, the code


  module mod
    use iso_c_binding
    type, bind(C) :: a
      character(len=2,kind=c_char) :: b ! Wrong
    end type a
    character(len=2), bind(C) :: c ! Also wrong
  end module mod
  

was accepted. To achieve similar functionality, an array of LEN=1 characters can be used, for example


  module mod
    use iso_c_binding
    type, bind(C) :: a
      character(kind=c_char) :: b(2)
    end type a
    character(kind=c_char), bind(C) :: c(2)
  end module mod