Wrong -Wstringop-overflow warning for std::array of vectors?

Georg-Johann Lay via Gcc-help <[email protected]> Fri, 13 Feb 2026 13:51:39 +0100
Newsgroups gmane.comp.gcc.help
Message-ID <[email protected]>
I am getting warnings like

main.cpp:45:15: warning: writing 1 byte into a region of size 0 
[-Wstringop-overflow=]
    45 |         v_[i] = (Point::value_t) val;
       |         ~~~~~~^~~~~~~~~~~~~~~~~~~~~~


and am unsure whether it is a known PR or worth reporting as PR.

Attached is as C++17 test case that's compiled like:

$ g++ -c main.cpp -std=c++17 -fno-exceptions -W -Wall -O3 -fmax-errors=1 
  -o main.obj

In member function 'void Point::set(int, int)',
     inlined from 'void Cluster::shift(int, int)' at main.cpp:135:19,
     inlined from 'void Cluster::add(Point, bool)' at main.cpp:128:23,
     inlined from 'Cluster::Cluster(const Cluster*, Point)' at 
main.cpp:81:13,
     inlined from 'int main()' at main.cpp:201:61:
main.cpp:45:15: warning: writing 1 byte into a region of size 0 
[-Wstringop-overflow=]
    45 |         v_[i] = (Point::value_t) val;
       |         ~~~~~~^~~~~~~~~~~~~~~~~~~~~~
main.cpp: In function 'int main()':
main.cpp:201:61: note: at offset 8 into destination object '<anonymous>' 
of size 8
   201 |                 set[i].emplace (Cluster (&cs, Point::rand ()));
       |                                                             ^

and more similar warnings.

I see these warnings with g++ 14 and also with current trunk from today 
on x86_64 gnu/linux.
The test case is not preprocessed and includes only std headers.
When a pre-processed test case is needed I can provide one.

The test case defines a struct Point that's a vector type with 2 int8_t 
components. It then defines a struct Cluster that's a std::array<Point, 
1+CELLS> that can hold 0...CELLS Points.  The number of Points in the 
Cluster is stored in the last array element.

Some observations:

* I only see the warnings with -O3, with lower optimizations it goes away.

* The warning goes away when I add -fsanitize=address.

* The main function iterated over a std::set<Cluster>.  When I adjust
the code to iterate over std::vector<Cluster> then the warning
also goes away.


best regards

Johann
main.cpp (text/x-c++src, 5.3 KB)
#include <vector>
#include <set>
#include <array>
#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <cassert>

#define DIM 2
#define CELLS 3

struct Point ///////////////////////////////////////////////////////////////////
{
    using value_t = int8_t;
#if DIM == 2
    using vector_t = value_t __attribute__((vector_size(2)));
    using int_t = uint16_t;
#elif DIM > 2 && DIM <= 4
    using vector_t = value_t __attribute__((vector_size(4)));
    using int_t = uint32_t;
#elif DIM > 4 && DIM <= 8
    using vector_t = value_t __attribute__((vector_size(8)));
    using int_t = uint64_t;
#elif DIM > 8 && DIM <= 16
    using vector_t = value_t __attribute__((vector_size(16)));
    using int_t = unsigned __int128;
#else
#error DIM=?
#endif

    vector_t v_ = (vector_t) (int_t) 0;

    int size () const
    {
        return DIM;
    }
    void set (int i, int val)
    {
#if 0
        // ??? v_[i] = Gives a (wrong?) warning.  Work around that.
        vector_t w (v_);
        w[i] = (Point::value_t) val;
        v_ = w;
#else
        v_[i] = (Point::value_t) val;
#endif
    }
    int operator [] (int i) const
    {
        return v_[i];
    }
    int cmp (Point d) const
    {
        for (int i = 0; i < size (); ++i)
            if (v_[i] != d[i])
                return v_[i] - d[i];
        return 0;
    }
    static Point rand ()
    {
        Point d;
        for (int j = 0; j < d.size (); ++j)
            d.set (j, std::rand() % 100);
        return d;
    }
};


struct Cluster //////////////////////////////////////////////////////////////
{
    struct Iterator;
    struct CIterator;

    std::array<Point, 1 + CELLS> a_;

    Cluster () {}
    Cluster (const Cluster *dad, Point d)
    {
        if (dad)
            a_ = dad->a_;
        add (d, 1);
    }
    Point operator [] (int i) const
    {
        return a_[i];
    }
    int size () const
    {
        int sz = (int) a_[CELLS][0];
        assert (sz >= 0 && sz <= CELLS);
        return sz;
    }
    void set_size (int sz)
    {
        assert (sz >= 0 && sz <= CELLS);
        a_[CELLS].set (0, sz);
    }
    void insert (const Iterator &it, Point d)
    {
        const int pos = (int) (& (*it) - & a_[0]);
        assert (pos >= 0 && pos <= size ());
        assert (pos < CELLS);
        // Get a vacant Point at pos...
        for (int i = size (); i > pos; --i)
            a_[i] = a_[i - 1];
        // ...for insertion.
        a_[pos] = d;
        set_size (1 + size ());
    }
    void add (Point d, bool assert_add)
    {
        for (auto it = begin (); ; ++it)
            if (int i; it == end () || (i = d.cmp (*it)) < 0)
            {
                insert (it, d);
                break;
            }
            else if (i == 0)
            {
                if (assert_add)
                    assert (i != 0 && "Assume we always increase cells");
                break;
            }
        // Normalize again.  Max one component of d is negative.
        for (int j = 0; j < d.size (); ++j)
            if (d[j] < 0)
            {
                shift (j, -d[j]);
                break;
            }
    }
    void shift (int i, int off)
    {
        for (Point &d : *this)
            d.set (i, d[i] + off);
    }
    int cmp (const Cluster &c) const
    {
        auto &&p2 = c.begin ();
        for (Point d : *this)
            if (p2 == c.end ())
                return 1;
            else if (int i = d.cmp (*p2); i != 0)
                return i;
            else
                ++p2;
        return p2 == c.end () ? 0 : -1;
    }
    bool operator == (const Cluster &r) const { return cmp (r) == 0; }
    bool operator <  (const Cluster &r) const { return cmp (r) < 0;  }
    bool contains (Point d) const
    {
        for (Point c : *this)
            if (int i = c.cmp (d); i == 0)
                return true;
            else if (i > 0)
                break;
        return false;
    }

    struct Iterator
    {
        friend Cluster;
        Point *ptr;
        Iterator () = delete;
        Iterator (Point *ptr) : ptr(ptr) {}
        void operator ++ () { ++ptr; };
        bool operator == (const Iterator &i) const { return ptr == i.ptr; }
        bool operator != (const Iterator &i) const { return ptr != i.ptr; }
        Point& operator * () const { return *ptr; }
    };
    struct CIterator
    {
        friend Cluster;
        const Point *ptr;
        CIterator () = delete;
        CIterator (const Point *ptr) : ptr(ptr) {}
        void operator ++ () { ++ptr; };
        bool operator == (const CIterator &i) const { return ptr == i.ptr; }
        bool operator != (const CIterator &i) const { return ptr != i.ptr; }
        Point operator * () const { return *ptr; }
    };
    Iterator begin () { return Iterator (&a_[0]); }
    Iterator end ()   { return Iterator (&a_[size ()]); }
    CIterator begin () const { return CIterator (&a_[0]); }
    CIterator end ()   const { return CIterator (&a_[size ()]); }
};

int main () /////////////////////////////////////////////////////////////////
{
    std::vector<std::set<Cluster>> set (1 + CELLS);

    for (int i = 1; i <= CELLS; ++i)
    {
        std::cout << "== " << i << " ==\n";

        if (i == 1)
            set[1].emplace (Cluster (nullptr, Point ()));
        else
            for (const Cluster &cs : set[i - 1])
                set[i].emplace (Cluster (&cs, Point::rand ()));

        std::cout << "#clusters = " << set[i].size() << "\n";
    }
}