https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88800
Bug ID: 88800 Summary: Spurious -Werror=array-bounds for non-taken branch Product: gcc Version: 8.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: tgrabiec at gmail dot com Target Milestone: --- Link to godbolt: https://godbolt.org/z/JXfV2y gcc flags: -Wall -Werror Compiles fine with gcc 7.4, errors on 8.2 due to -Warray-bounds. ===== #include <variant> #include <stdint.h> #include <assert.h> #include <memory> #include <unordered_map> #include <type_traits> #include <string_view> #include <cstring> using bytes_view = std::string_view; struct blob_storage { struct [[gnu::packed]] ref_type { blob_storage* ptr; ref_type() {} ref_type(blob_storage* ptr) : ptr(ptr) {} operator blob_storage*() const { return ptr; } blob_storage* operator->() const { return ptr; } blob_storage& operator*() const { return *ptr; } }; using size_type = uint32_t; using char_type = bytes_view::value_type; ref_type* backref; size_type size; size_type frag_size; ref_type next; char_type data[]; blob_storage(ref_type* backref, size_type size, size_type frag_size) noexcept : backref(backref) , size(size) , frag_size(frag_size) , next(nullptr) { *backref = this; } blob_storage(blob_storage&& o) noexcept : backref(o.backref) , size(o.size) , frag_size(o.frag_size) , next(o.next) { *backref = this; o.next = nullptr; if (next) { next->backref = &next; } memcpy(data, o.data, frag_size); } } __attribute__((packed)); class [[gnu::packed]] managed_bytes { static constexpr size_t max_inline_size = 15; struct small_blob { bytes_view::value_type data[max_inline_size]; }; union [[gnu::packed]] u { u() {} ~u() {} blob_storage::ref_type ptr; small_blob small; } _u; int8_t _size; // -1 -> use blob_storage private: bool external() const { return _size < 0; } public: using size_type = blob_storage::size_type; struct initialized_later {}; managed_bytes(initialized_later, size_type size) { if (size <= max_inline_size) { _size = size; } else { _size = -1; auto now = size; void* p = malloc(sizeof(blob_storage) + now); new (p) blob_storage(&_u.ptr, size, now); } } managed_bytes(bytes_view v) : managed_bytes(initialized_later(), v.size()) { if (!external()) { std::copy(v.begin(), v.end(), _u.small.data); // ^^^^^^^^^^^^^^^ HERE ^^^^^^^^^^^^^^^^^^^ return; } auto p = v.data(); auto s = v.size(); auto b = _u.ptr; while (s) { memcpy(b->data, p, b->frag_size); p += b->frag_size; s -= b->frag_size; b = b->next; } assert(!b); } }; static_assert(sizeof(managed_bytes) == 16, "too large"); int main() { char c[16] = { 0, }; bytes_view v(c, 16); managed_bytes b(v); } ===== GCC output: In static member function ‘static _Tp* std::__copy_move<_IsMove, true, std::random_access_iterator_tag>::__copy_m(const _Tp*, const _Tp*, _Tp*) [with _Tp = signed char; bool _IsMove = false]’, inlined from ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false; _II = const signed char*; _OI = signed char*]’ at /usr/include/c++/8/bits/stl_algobase.h:386:30, inlined from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = const signed char*; _OI = signed char*]’ at /usr/include/c++/8/bits/stl_algobase.h:422:45, inlined from ‘_OI std::copy(_II, _II, _OI) [with _II = const signed char*; _OI = signed char*]’ at /usr/include/c++/8/bits/stl_algobase.h:455:8, inlined from ‘managed_bytes::managed_bytes(bytes_view)’ at ./utils/managed_bytes.hh:195:22, inlined from ‘managed_bytes::managed_bytes(const bytes&)’ at ./utils/managed_bytes.hh:162:77, inlined from ‘dht::token dht::bytes_to_token(bytes)’ at dht/random_partitioner.cc:68:57, inlined from ‘dht::token dht::random_partitioner::get_token(bytes)’ at dht/random_partitioner.cc:85:39: /usr/include/c++/8/bits/stl_algobase.h:368:23: error: ‘void* __builtin_memmove(void*, const void*, long unsigned int)’ offset 16 from the object at ‘<anonymous>’ is out of the bounds of referenced subobject ‘managed_bytes::small_blob::data’ with type ‘signed char [15]’ at offset 0 [-Werror=array-bounds] __builtin_memmove(__result, __first, sizeof(_Tp) * _Num); ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cc1plus: all warnings being treated as errors {code} {code}