Last month, I embarked on a new project. I set up a new computer with the latest Debian version, installed my favorites tools, and was all set to code. My first task was to migrate all the repositories from C++14 to C++20. While it might seem as straightforward as updating all the CMakeLists.txt
to replace set (CMAKE_CXX_STANDARD 14)
with set (CMAKE_CXX_STANDARD 20)
reality proved otherwise (and I knew it would).
Among the funny things I encountered (such as std::experimental::basic_string_view::to_string()
being missing in std::basic_string_view
), the most cryptic error was something like that:
/usr/include/c++/12/bits/char_traits.h:431:56: warning: ‘void* __builtin_memcpy(void*, const void*, long unsigned int)’ accessing 9223372036854775810 or more bytes at offsets -4611686018427387902 and [-4611686018427387903, 4611686018427387904] may overlap up to 9223372036854775813 bytes at offset -3 [-Wrestrict]
431 | return static_cast<char_type*>(__builtin_memcpy(__s1, __s2, __n));
This is a known bug in GCC and it affects only the 12.x branch. It's (Bug 105329 - [12/13/14 Regression] Bogus restrict warning when assigning 1-char string literal to std::string since r12-3347-g8af8abfbbace49e6).
Encountering this bug might not be an everyday occurrence, but it's quite easy to trigger it. Unfortunately, as you have guessed, Debian Bookworm ships with this problematic version of GCC. Bookworm is the latest stable version of Debian, released in June this year. Previous stables version, Buster and Bulleye, came with GCC 8 and GCC 10 respectively. Since Debian releases a new stable version every two years, if you are using Debian like me, we may have to deal with this bug for an extended period.
In this article, I will explain how you can trigger this bug and how you can avoid it. It's also an opportunity to revisit various techniques for circumventing spurious warnings.
Conditions to Trigger the Bug
Beyond from the source code itself, specific build conditions are necessary to trigger the bug.
- You must use GCC 12, obviously. GCC 11 wasn't affected and the bug is fixed in GCC 13.
- Optimizations must be enabled. You must use either
-O2
or-O3
.-O0
,-O1
and-Os
don't seem to concerned. If you setCMAKE_BUILD_TYPE
toRelease
, CMake will add-O3
. Your release builds will then be susceptible, unlike debug builds. - You must use C++20 and C++23. C++17 is unaffected.
- You must pass the
-Wrestrict
flag since it causes the warning. It is included in-Wall
.
This means your build may break simply by:
- Updating GCC (it happened to GoogleTest).
- Changing from debug to release mode (but I guess your CI always builds in release mode, right?).
- Switching for C++17 or below to C++20 or above (and I encourage you to regularly upgrade to the new standard version).
- Enabling warnings (no, I can't believe you had lived without
-Wall
so far).
I use the word "break" because most release builds also use -Werror
to turn warnings into errors, ensuring they are absolutely noticeable.
The Code Itself
Oh, poor boyz and girlz... The code is so simple.
Here is a CMake CMakeLists.txt
:
cmake_minimum_required(VERSION 3.27)
project(buggy)
add_executable(buggy main.cpp)
target_compile_options(buggy PRIVATE -Wall)
target_compile_features(buggy PRIVATE cxx_std_20)
And here a main.cpp
that will raise the dreaded warning:
#include <string>
std::string s;
int main() {
s = "a"; // oopsi...
}
Here is another case that emits the warning:
#include <string>
std::string s;
int main() {
s = "a" + std::string("b"); // oopsi...
}
The string literal must be single byte and must be the left-hand-side operand. The following lines are fine:
s = "ab";
s = "ab" + std::string("c");
s = std::string("a") + "b";
Circumvention Measures
The discussion for bug 105329 clearly indicates, in comments 26 and 27, than the 12 branch won't be fixed. The solution is hence to move to GCC 13. Alas! Debian will probably not change the major version of GCC in Bookworm. The next release, Trixie, is currently in testing mode and comes with GCC 13, so perhaps one day it will be available in the backports.
Fortunately, there are easy workarounds.
Disable -Wrestrict
globally
As a temporary solution, you may add -Wno-restrict
to your build:
target_compile_options(buggy PRIVATE -Wall -Wno-restrict)
This is extreme and I highly discourage pushing such a change to a develop
or a master
/main
branch.
Disable -Wrestrict
for some files
Another solution I also consider temporary is to disable the warning but only for a particular set of files.
You can use CMake for this, especially if you can't change these sources files:
set_source_files_properties(
main.cpp
PROPERTIES
COMPILE_FLAGS -Wno-restrict)
Alternatively, you can use a pragma at the beginning of each file:
#pragma GCC diagnostic ignored "-Wrestrict"
#include <string>
std::string s;
int main() {
s = "a";
}
Disable -Wrestrict
locally
Disabling the warning only for a couple of lines is a more acceptable solution:
#include <string>
std::string s;
int main() {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wrestrict"
s = "a";
#pragma GCC diagnostic pop
}
I had to use this technique around a #include <boost/test/data/test_case.hpp>
because BOOST_DATA_TEST_CASE
triggered the bug. This solution is very verbose and reduce your code's readability. Think twice before choosing it.
Note about Clang
Clang does support #pragma GCC
but it doesn't support -Wrestrict
... It means that the previous codes will raise a warning or error:
$ clang++ main.cpp -Wall -Werror
main.cpp:7:32: error: unknown warning group '-Wrestrict', ignored [-Werror,-Wunknown-warning-option]
#pragma GCC diagnostic ignored "-Wrestrict"
If you want your code to compile both with GCC and Clang, you will need some extra preprocessor tricks:
#ifndef __clang__
#pragma GCC diagnostic ignored "-Wrestrict"
#endif
The preferred solution: change the code
The solution with set_source_files_properties()
may be acceptable if you plan to switch to GCC 13 soon. Otherwise, if you plan to use GCC 12 for a long time, just change your code.
Remember the two faulty lines:
s = "a";
s = "a" + std::string("b");
Simply change them to:
s = std::string("a");
s = std::string("a") + std::string("b");
This simply makes implicit conversions explicit.
Conclusion
At the end of the day, the warning message is quite scary, but the code can easily be changed to avoid the bug. Apart from changing the code to avoid this particular issue with GCC 12, we also discussed several techniques you may use to mitigate spurious warnings in your code.
This is not the first time I run into a compiler's bug. It's always so much fun (sarcasm indented) 😆
Top comments (4)
I have similar issues with gcc since upgrading to bookworm...
I have posted issue on several forums and am reaching out to see if others have same issues...
After installing all boards/libraries etc with new RPi OS upgrade everything works really smooth. The arduino IDE 1.8.19 runs well...
I can compile and upload for all my different boards except the UNO WIFI Rev2. I have removed board def, reinstalled, rebooted, installed prior versions and nothing seems to get over this error...
It was working before the upgrade.
Adding additional verbose error messages...
Arduino: 1.8.19 (Linux), Board: "Arduino Uno WiFi Rev2, None (ATMEGA4809)"
arduino-builder -dump-prefs -logger=machine -hardware /usr/share/arduino/hardware -hardware /home/rick/.arduino15/packages -hardware /home/rick/Arduino/hardware -tools /usr/share/arduino/hardware/tools/avr -tools /home/rick/.arduino15/packages -libraries /home/rick/Arduino/libraries -fqbn=arduino:megaavr:uno2018:mode=off -vid-pid=03EB_2145 -ide-version=10819 -build-path /tmp/arduino_build_168707 -warnings=none -build-cache /tmp/arduino_cache_356267 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.arduinoOTA.path=/home/rick/.arduino15/packages/arduino/tools/arduinoOTA/1.3.0 -prefs=runtime.tools.arduinoOTA-1.3.0.path=/home/rick/.arduino15/packages/arduino/tools/arduinoOTA/1.3.0 -prefs=runtime.tools.avr-gcc.path=/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5 -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino5.path=/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5 -prefs=runtime.tools.avrdude.path=/home/rick/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17 -prefs=runtime.tools.avrdude-6.3.0-arduino17.path=/home/rick/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17 -verbose /home/rick/Arduino/nRF2401-Transmitter/nRF2401-Transmitter.ino
arduino-builder -compile -logger=machine -hardware /usr/share/arduino/hardware -hardware /home/rick/.arduino15/packages -hardware /home/rick/Arduino/hardware -tools /usr/share/arduino/hardware/tools/avr -tools /home/rick/.arduino15/packages -libraries /home/rick/Arduino/libraries -fqbn=arduino:megaavr:uno2018:mode=off -vid-pid=03EB_2145 -ide-version=10819 -build-path /tmp/arduino_build_168707 -warnings=none -build-cache /tmp/arduino_cache_356267 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.arduinoOTA.path=/home/rick/.arduino15/packages/arduino/tools/arduinoOTA/1.3.0 -prefs=runtime.tools.arduinoOTA-1.3.0.path=/home/rick/.arduino15/packages/arduino/tools/arduinoOTA/1.3.0 -prefs=runtime.tools.avr-gcc.path=/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5 -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino5.path=/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5 -prefs=runtime.tools.avrdude.path=/home/rick/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17 -prefs=runtime.tools.avrdude-6.3.0-arduino17.path=/home/rick/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17 -verbose /home/rick/Arduino/nRF2401-Transmitter/nRF2401-Transmitter.ino
Using board 'uno2018' from platform in folder: /home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8
Using core 'arduino' from platform in folder: /home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8
Warning: platform.txt from core 'Arduino megaAVR Boards' contains deprecated compiler.path={runtime.tools.avr-gcc.path}/bin/, automatically converted to compiler.path=/usr/bin/. Consider upgrading this core.
Detecting libraries used...
"/usr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega4809 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_UNO_WIFI_REV2 -DARDUINO_ARCH_MEGAAVR -DMILLIS_USE_TIMERB3 "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/cores/arduino/api/deprecated" "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/cores/arduino" "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/variants/uno2018" "/tmp/arduino_build_168707/sketch/nRF2401-Transmitter.ino.cpp" -o "/dev/null"
Generating function prototypes...
"/usr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega4809 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_UNO_WIFI_REV2 -DARDUINO_ARCH_MEGAAVR -DMILLIS_USE_TIMERB3 "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/cores/arduino/api/deprecated" "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/cores/arduino" "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/variants/uno2018" "/tmp/arduino_build_168707/sketch/nRF2401-Transmitter.ino.cpp" -o "/tmp/arduino_build_168707/preproc/ctags_target_for_gcc_minus_e.cpp"
avr-g++: error: device-specs/specs-atmega4809: No such file or directory
exit status 1
Error compiling for board Arduino Uno WiFi Rev2.
The issue doesn't seem similar at all. A file is missing in your Arduino toolchain.
You can determine the previous version and current version of the toolchain, and look at the changelog to see if the support as been deprecated.
You can also try to locate the file by hand. Maybe it's just a wrong path. Try to
cd home/rick/.arduino15
and search for the file withfind -name *specs-atmega4809*
.Pierre;
I was able to search and found the file in question...
/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/lib/gcc/avr/7.3.0/device-specs
I am not overly proficient with this...so forgive me if I ask simple questions...Is that the right path for the compiler to find it, if not do you know what the path should be.
Is there something in the file itself which needs to be changed?
Thanks
RIck
I have never used Arduino toolchains, so I can't really tell you. What I would do in your situation: