Porting to GCC 13

The GCC 13 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 13. This document is an effort to identify common issues and provide solutions. Let us know if you have suggestions for improvements!

C++ language issues

Header dependency changes

Some C++ Standard Library headers have been changed to no longer include other headers that were being used internally by the library. As such, C++ programs that used standard library components without including the right headers will no longer compile.

The following headers are used less widely in libstdc++ and may need to be included explicitly when compiling with GCC 13:

Implicit move rules change

GCC 13 implements C++23 P2266 which simplified the rules for implicit move. As a consequence, valid C++20 code that relies on a returned id-expression's being an lvalue may change behavior or fail to compile in C++23. For example:

   decltype(auto) f(int&& x) { return (x); }  // returns int&&; previously returned int&
   int& g(int&& x) { return x; }  // ill-formed; previously well-formed

Two-stage overload resolution for implicit move removed

GCC 13 removed the two-stage overload resolution when performing implicit move, whereby the compiler does two separate overload resolutions: one treating the operand as an rvalue, and then (if that resolution fails) another one treating the operand as an lvalue. In the standard this was introduced in C++11 and implemented in GCC in r251035. In r11-2412, the fallback overload resolution was disabled in C++20 (but not in C++17). Then C++23 P2266 removed the fallback overload resolution, and changed the implicit move rules once again.

The two overload resolutions approach was complicated and quirky, so users should transition to the newer model. This change means that code that previously didn't compile in C++17 will now compile, for example:

   struct S1 { S1(S1 &&); };
   struct S2 : S1 {};

   f (S2 s)
     return s; // OK, derived-to-base, use S1::S1(S1&&)

Conversely, code that used to work in C++17 may not compile anymore. For example, the following example used to compile in C++11...17 because we performed two separate overload resolutions: one treating the operand as an rvalue, and then (if that resolution failed) another one treating the operand as an lvalue.

   struct W {

   struct F {
     F(W&&) = delete;

   F fn ()
     W w;
     return w; // use w as rvalue -> use of deleted function F::F(W&&)

Excess precision changes

GCC 13 implements excess precision support, which was implemented just in the C front end before, in C++. The new behavior is enabled by default in -std=c++NN modes and when FLT_EVAL_METHOD is 1 or 2 and affects the behavior of floating point constants and expressions.

E.g. for FLT_EVAL_METHOD equal to 2 on ia32

#include <stdlib.h>
void foo (void) { if (1.1f + 3.3f != 1.1L + 3.3L) abort (); }
void bar (void) { double d = 4.2; if (d == 4.2) abort (); }

will not abort with standard excess precision, because constants and expressions in float or double are evaluated in precision of long double and demoted only on casts or assignments, but will abort with fast excess precision, where whether something is evaluated in long double precision depends on what evaluations are done in the i387 floating point stack or are spilled from it.

The -fexcess-precision=fast option can be used to request the previous behavior.

allocator_traits<A>::rebind_alloc<A::value_type> must be A

GCC 13 now checks that allocators used with the standard library can be "rebound" to allocate memory for a different type, as required by the allocator requirements in the C++ standard. If an allocator type Alloc<T> cannot be correctly rebound to another type Alloc<U>, you will get an error like this:

.../bits/alloc_traits.h:70:31: error: static assertion failed: allocator_traits<A>::rebind_alloc<A::value_type> must be A

The assertion checks that rebinding an allocator to its own value type is a no-op, which will be true if its rebind member is defined correctly. If rebinding it to its own value type produces a different type, then the allocator cannot be used with the standard library.

The most common cause of this error is an allocator type Alloc<T> that derives from std::allocator<T> but does not provide its own rebind member. When the standard library attempts to rebind the allocator using Alloc<T>::rebind<U> it finds the std::allocator<T>::rebind<U> member from the base class, and the result is std::allocator<U> instead of Alloc<U>.

The solution is to provide a correct rebind member as shown below. A converting constructor must also be provided, so that that an Alloc<U> can be constructed from an Alloc<T>, and vice versa:

template<class T>
class Alloc
  template<class U> Alloc(const Alloc<U>);

  template<class U> struct rebind { using other = Alloc<U>; };
  // ...

Since C++20, there is no rebind member in std::allocator, so deriving your own allocator types from std::allocator is simpler and does not require the derived allocator to provide its own rebind. For compatibility with previous C++ standards, the member should still be provided. The converting constructor is still required even in C++20.

Fortran language issues

Behavior on integer overflow

GCC 13 includes new optimizations which may change behavior on integer overflow. Traditional code, like linear congruential pseudo-random number generators in old programs and relying on a specific, non-standard behavior may now generate unexpected results. The option -fsanitize=undefined can be used to detect such code at runtime.

It is recommended to use the intrinsic subroutine RANDOM_NUMBER for random number generators or, if the old behavior is desired, to use the -fwrapv option. Note that this option can impact performance.