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}

Reply via email to