https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127625
Bug ID: 127625
Summary: Possible false positive -Warray-bounds in
std::vector::insert with a custom allocator at -O2
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: hello at davidmolnar dot dev
Target Milestone: ---
Host: x86_64-w64-mingw32
Target: x86_64-w64-mingw32
Build: x86_64-w64-mingw32
Created attachment 65693
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65693&action=edit
Preprocessed C++20 reproducer generated with GCC 16.2.0 (WinLibs/MinGW-w64)
I can reproduce a possible false positive -Warray-bounds diagnostic when
appending byte ranges to a std::vector using a custom allocator at -O2.
The standalone example fails with both tested Windows toolchains:
- GCC 16.2.0, WinLibs r1, MinGW-W64 x86_64-ucrt-posix-seh.
- GCC 15.2.0, WinLibs r7, MinGW-W64 x86_64-ucrt-posix-seh.
Host OS: Windows
Build, host and target: x86_64-w64-mingw32
The GCC 16.2.0 distribution uses MinGW-w64 14.0.0, UCRT and POSIX threads. The
complete compiler configuration and diagnostic are included below.
Save the following as repro.cpp. It uses only C++20 standard-library headers
and needs no external function definitions for this compile-only test.
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <span>
#include <vector>
template <class T> struct Allocator {
using value_type = T;
Allocator() = default;
template <class U> Allocator(const Allocator<U>&) {}
T* allocate(std::size_t n) { return std::allocator<T>{}.allocate(n); }
void deallocate(T* p, std::size_t n) { std::allocator<T>{}.deallocate(p,
n); }
template <class U> bool operator==(const Allocator<U>&) const { return
true; }
};
struct Stream {
std::vector<std::byte, Allocator<std::byte>> bytes;
void write(std::span<const std::byte> src) {
bytes.insert(bytes.end(), src.begin(), src.end());
}
};
struct Record {
std::uint8_t tag{};
std::array<unsigned char, 4> id{};
std::uint32_t index{};
void serialize(Stream& s) const {
s.write(std::as_bytes(std::span{&tag, 1}));
s.write(std::as_bytes(std::span{id}));
s.write(std::as_bytes(std::span{&index, 1}));
const std::uint8_t prefix = 0;
s.write(std::as_bytes(std::span{&prefix, 1}));
}
};
void reproduce(const Record& record)
{
Stream stream;
record.serialize(stream);
}
Command:
g++ -std=c++20 -O2 -Werror=array-bounds -c repro.cpp
Expected: compilation without an array-bounds diagnostic.
Actual: compilation exits with code 1 and reports:
bits/stl_construct.h:133:7: error: 'void* __builtin_memcpy(void*, const void*,
long long unsigned int)' offset [5, 8] is out of the bounds [0, 5]
[-Werror=array-bounds=]
The diagnostic is emitted through std::vector::_M_range_insert and
Stream::write, inlined into the append of Record::index.
The source spans refer to live objects and do not overlap the destination
vector. The allocator delegates allocation and deallocation to std::allocator
without changing the requested size. The four appends contain 1, 4, 4 and 1
bytes on this target; vector::insert manages the destination capacity.
The same source also fails with GCC 16.2.0 using:
g++ -std=c++20 -O2 -Wall -Wextra -Werror -c repro.cpp
The attached preprocessed source was generated with:
g++ -v -save-temps=obj -std=c++20 -O2 -Werror=array-bounds -c repro.cpp -o
report-gcc16.o
Compiling the preprocessed file independently reproduces the same diagnostic
and exits with code 1:
g++ -std=c++20 -O2 -Werror=array-bounds -c report-gcc16.ii -o
report-gcc16-preprocessed.o
Additional checks on the same source with WinLibs GCC 15.2.0,
x86_64-w64-mingw32 (all use -std=c++20 -Werror=array-bounds unless stated
otherwise):
- -O2: the same diagnostic.
- -O0: compiles.
- -O2 -fno-inline: compiles.
- -O2 -fno-tree-vrp: the diagnostic remains.
- -O2 -fno-strict-aliasing -fwrapv -fno-aggressive-loop-optimizations: the
diagnostic remains.
- -O2 -D_GLIBCXX_ASSERTIONS: the diagnostic remains.
- -O2 -D_GLIBCXX_DEBUG: compiles.
- With -O2 -Wall -Wextra and without -Werror, compilation succeeds with the
corresponding warning.
With Ubuntu GCC 13.3.0 on x86_64 Linux, the source compiles with -std=c++20 -O2
-Wall -Wextra -Werror. This comparison changes both the compiler/library
version and the platform, so I have not established where a regression started.
A separate runtime smoke test calling the function with 10,000 valid records
passed with GCC 15.2.0 and libstdc++ assertions/debug mode, and with Ubuntu GCC
13.3.0 and ASan/UBSan. These checks do not establish the compiler's root cause
or prove the absence of all undefined behavior.
The example was reduced while investigating a similar diagnostic in Bitcoin
Core PR #35440:
https://github.com/bitcoin/bitcoin/pull/35440
The original PR also encountered the same diagnostic in CI with GCC 14.2 on
32-bit ARM and GCC 14.3 in the MinGW cross-compilation jobs (MSVCRT and UCRT).
I have not tested this standalone reproducer with those GCC 14 toolchains;
these are observations from the original PR's CI, not confirmed results for the
reduced example. This reproducer has no Bitcoin Core or third-party
dependencies.
I am reporting a suspected false-positive diagnostic, not a demonstrated
wrong-code bug. I found potentially related reports 107252, 117983 and 114945,
but have not established that this is a duplicate of any of them. PR117983
concerns -Wstringop-overflow and has a fix; its testcase differs from this one.
I have not tested a GCC development snapshot or a self-built upstream GCC. The
recorded standalone checks were performed on September 8-9, 2026.
Full GCC 16.2.0 verbose compiler output:
Using built-in specs.
COLLECT_GCC=D:\work\bitcoin-wallet\gcc-array-bounds-repro\toolchains\gcc16.2\mingw64\bin\g++.exe
OFFLOAD_TARGET_NAMES=nvptx-none
Target: x86_64-w64-mingw32
Configured with: ../configure
--prefix=/c/Prog/winlibs_staging_ucrt/home64/inst_gcc-16.2.0/share/gcc
--build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32
--enable-offload-targets=nvptx-none --with-pkgversion='MinGW-W64
x86_64-ucrt-posix-seh, built by Brecht Sanders, r1' --with-tune=generic
--enable-checking=release --enable-threads=posix --disable-sjlj-exceptions
--disable-libunwind-exceptions --disable-serial-configure --disable-bootstrap
--enable-host-shared --enable-plugin --disable-default-ssp --disable-rpath
--disable-libstdcxx-debug --disable-version-specific-runtime-libs
--disable-symvers --enable-languages=c,c++,fortran,lto,objc,obj-c++
--disable-gold --disable-nls --disable-stage1-checking --disable-win32-registry
--disable-multilib --enable-ld --enable-libquadmath --enable-libada
--enable-libssp --enable-libstdcxx --enable-lto --enable-fully-dynamic-string
--enable-libgomp --enable-graphite --enable-mingw-wildcard
--enable-libstdcxx-time --enable-libstdcxx-pch
--with-mpc=/c/Prog/winlibs_staging_ucrt/custombuilt64
--with-mpfr=/c/Prog/winlibs_staging_ucrt/custombuilt64
--with-gmp=/c/Prog/winlibs_staging_ucrt/custombuilt64
--with-isl=/c/Prog/winlibs_staging_ucrt/custombuilt64
--disable-libstdcxx-backtrace --enable-install-libiberty --enable-__cxa_atexit
--without-included-gettext --with-diagnostics-color=auto
--enable-clocale=generic --enable-libgdiagnostics --with-libiconv
--with-system-zlib
--with-build-sysroot=/c/Prog/winlibs_staging_ucrt/home64/gcc-16.2.0/build_mingw/mingw-w64
CFLAGS='-I/c/Prog/winlibs_staging_ucrt/custombuilt64/include/libdl-win32
-march=nocona -msahf -mtune=generic -O2 -Wno-error=format'
CXXFLAGS='-Wno-int-conversion -march=nocona -msahf -mtune=generic -O2'
LDFLAGS='-pthread -Wl,--no-insert-timestamp -Wl,--dynamicbase
-Wl,--high-entropy-va -Wl,--nxcompat -Wl,--tsaware'
LD=/c/Prog/winlibs_staging_ucrt/custombuilt64/share/binutils/bin/ld.exe
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 16.2.0 (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders,
r1)
COLLECT_GCC_OPTIONS='-v' '-save-temps=obj' '-std=c++20' '-O2'
'-Werror=array-bounds' '-c' '-o' 'report-gcc16.o' '-shared-libgcc'
'-mtune=generic' '-march=x86-64'
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/16.2.0/cc1plus.exe
-E -quiet -v -iprefix
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/16.2.0/
-D_REENTRANT repro.cpp -mtune=generic -march=x86-64 -std=c++20
-Werror=array-bounds -O2 -fpch-preprocess -o report-gcc16.ii
ignoring duplicate directory
"D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/lib/gcc/../../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../include/c++/16.2.0"
ignoring duplicate directory
"D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/lib/gcc/../../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../include/c++/16.2.0/x86_64-w64-mingw32"
ignoring duplicate directory
"D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/lib/gcc/../../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../include/c++/16.2.0/backward"
ignoring duplicate directory
"D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/lib/gcc/../../lib/gcc/x86_64-w64-mingw32/16.2.0/include"
ignoring nonexistent directory
"C:/Prog/winlibs_staging_ucrt/home64/inst_gcc-16.2.0/share/gcc/include"
ignoring nonexistent directory
"/c/Prog/winlibs_staging_ucrt/home64/inst_gcc-16.2.0/share/gcc/include"
ignoring duplicate directory
"D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/lib/gcc/../../lib/gcc/x86_64-w64-mingw32/16.2.0/include-fixed"
ignoring duplicate directory
"D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/lib/gcc/../../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../x86_64-w64-mingw32/include"
ignoring nonexistent directory "/mingw/include"
#include "..." search starts here:
#include <...> search starts here:
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../include/c++/16.2.0
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../include/c++/16.2.0/x86_64-w64-mingw32
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../include/c++/16.2.0/backward
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/16.2.0/include
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../include
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/16.2.0/include-fixed
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/16.2.0/../../../../x86_64-w64-mingw32/include
End of search list.
COLLECT_GCC_OPTIONS='-v' '-save-temps=obj' '-std=c++20' '-O2'
'-Werror=array-bounds' '-c' '-o' 'report-gcc16.o' '-shared-libgcc'
'-mtune=generic' '-march=x86-64'
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/16.2.0/cc1plus.exe
-fpreprocessed report-gcc16.ii -quiet -dumpbase report-gcc16.cpp -dumpbase-ext
.cpp -mtune=generic -march=x86-64 -O2 -Werror=array-bounds -std=c++20 -version
-o report-gcc16.s
GNU C++20 (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r1)
version 16.2.0 (x86_64-w64-mingw32)
compiled by GNU C version 16.2.0, GMP version 6.3.0, MPFR version
4.2.2, MPC version 1.4.1, isl version isl-0.28-GMP
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: b99e3589a9faf7626d8db5f4c017c742
In file included from
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/include/c++/16.2.0/bits/stl_iterator.h:78,
from
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/include/c++/16.2.0/bits/stl_algobase.h:66,
from
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/include/c++/16.2.0/array:45,
from repro.cpp:1:
In function 'constexpr void std::_Construct(_Tp*, _Args&& ...) [with _Tp =
byte; _Args = {const byte&}]',
inlined from 'static constexpr void std::allocator_traits<
<template-parameter-1-1> >::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp =
std::byte; _Args = {const std::byte&}; _Alloc = Allocator<std::byte>]' at
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/include/c++/16.2.0/bits/alloc_traits.h:467:21,
inlined from 'constexpr _ForwardIterator
std::__uninitialized_copy_a(_InputIterator, _Sentinel, _ForwardIterator,
_Allocator&) [with _InputIterator = __gnu_cxx::__normal_iterator<const byte*,
span<const byte>::__iter_tag>; _Sentinel = __gnu_cxx::__normal_iterator<const
byte*, span<const byte>::__iter_tag>; _ForwardIterator = byte*; _Allocator =
Allocator<byte>]' at
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/include/c++/16.2.0/bits/stl_uninitialized.h:627:21,
inlined from 'constexpr void std::vector<_Tp,
_Alloc>::_M_range_insert(iterator, _ForwardIterator, _ForwardIterator,
std::forward_iterator_tag) [with _ForwardIterator =
__gnu_cxx::__normal_iterator<const std::byte*, std::span<const
std::byte>::__iter_tag>; _Tp = std::byte; _Alloc = Allocator<std::byte>]' at
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/include/c++/16.2.0/bits/vector.tcc:984:34,
inlined from 'constexpr std::vector<_Tp, _Alloc>::iterator std::vector<_Tp,
_Alloc>::insert(const_iterator, _InputIterator, _InputIterator) [with
_InputIterator = __gnu_cxx::__normal_iterator<const std::byte*, std::span<const
std::byte>::__iter_tag>; <template-parameter-2-2> = void; _Tp = std::byte;
_Alloc = Allocator<std::byte>]' at
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/include/c++/16.2.0/bits/stl_vector.h:1611:19,
inlined from 'void Stream::write(std::span<const std::byte>)' at
repro.cpp:20:21,
inlined from 'void Record::serialize(Stream&) const' at repro.cpp:31:16,
inlined from 'void reproduce(const Record&)' at repro.cpp:40:21:
D:/work/bitcoin-wallet/gcc-array-bounds-repro/toolchains/gcc16.2/mingw64/include/c++/16.2.0/bits/stl_construct.h:133:7:
error: 'void* __builtin_memcpy(void*, const void*, long long unsigned int)'
offset [5, 8] is out of the bounds [0, 5] [-Werror=array-bounds=]
133 | ::new(static_cast<void*>(__p))
_Tp(std::forward<_Args>(__args)...);
|
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus.exe: some warnings being treated as errors