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}