PR middle-end/90904 - vec assignment and copying undefined gcc/ChangeLog: PR middle-end/90904 * vec.c (test_copy_assign): New function. (vec_c_tests): Call it. * vec.h (vec_assign): New function. (auto_vec copy ctor): Define. (auto_vec::operator=): Define. (auto_vec_no_copy): New class template. (auto_string_vec): Disable copying/assignment. diff --git a/gcc/vec.c b/gcc/vec.c index f9dbb2cac31..f15530d1e43 100644 --- a/gcc/vec.c +++ b/gcc/vec.c @@ -317,6 +317,86 @@ test_safe_push () ASSERT_EQ (7, v[2]); } + +/* Verify that auto_vec copy ctor and assignment work correctly. */ + +template +void test_copy_assign () +{ + typedef auto_vec test_vec; + + test_vec a0; + test_vec b0 (0); + test_vec c0 (7); + ASSERT_EQ (0, b0.length ()); + ASSERT_EQ (0, c0.length ()); + + a0 = a0; + ASSERT_EQ (0, a0.length ()); + b0 = a0; + ASSERT_EQ (0, b0.length ()); + c0 = a0; + ASSERT_EQ (0, c0.length ()); + + test_vec a3; + a3.safe_push (5); + a3.safe_push (6); + a3.safe_push (7); + + test_vec b3 (a3); + ASSERT_EQ (3, b3.length ()); + ASSERT_EQ (5, b3[0]); + ASSERT_EQ (6, b3[1]); + ASSERT_EQ (7, b3[2]); + + test_vec c3; + c3 = b3; + ASSERT_EQ (3, c3.length ()); + ASSERT_EQ (5, c3[0]); + ASSERT_EQ (6, c3[1]); + ASSERT_EQ (7, c3[2]); + + test_vec d4; + d4.safe_push (1); + d4.safe_push (2); + d4.safe_push (3); + d4.safe_push (4); + + c3 = d4; + ASSERT_EQ (4, c3.length ()); + ASSERT_EQ (1, c3[0]); + ASSERT_EQ (2, c3[1]); + ASSERT_EQ (3, c3[2]); + ASSERT_EQ (4, c3[3]); + + d4 = a3; + ASSERT_EQ (3, d4.length ()); + ASSERT_EQ (5, d4[0]); + ASSERT_EQ (6, d4[1]); + ASSERT_EQ (7, d4[2]); + + a3 = b0; + ASSERT_EQ (0, a3.length ()); + + b3 = b0; + ASSERT_EQ (0, b3.length ()); + + c3 = c0; + ASSERT_EQ (0, c3.length ()); + + b0 = d4; + ASSERT_EQ (3, b0.length ()); + ASSERT_EQ (5, b0[0]); + ASSERT_EQ (6, b0[1]); + ASSERT_EQ (7, b0[2]); + + c0 = d4; + ASSERT_EQ (3, c0.length ()); + ASSERT_EQ (5, c0[0]); + ASSERT_EQ (6, c0[1]); + ASSERT_EQ (7, c0[2]); +} + /* Verify that vec::truncate works correctly. */ static void @@ -549,6 +629,8 @@ vec_c_tests () { test_quick_push (); test_safe_push (); + test_copy_assign<0> (); + test_copy_assign<2> (); test_truncate (); test_safe_grow_cleared (); test_pop (); diff --git a/gcc/vec.h b/gcc/vec.h index 24df2db0eeb..f5dd5bb329a 100644 --- a/gcc/vec.h +++ b/gcc/vec.h @@ -1509,7 +1509,41 @@ public: want to ask for internal storage for vectors on the stack because if the size of the vector is larger than the internal storage that space is wasted. */ + template +class auto_vec; + +/* Safely assign elements from SRC to DST, invoking the copy assignment + operator on the initial elements and the copy ctor on the excess. */ + +template +auto_vec& vec_assign (auto_vec &dst, const auto_vec &src) +{ + unsigned n0 = dst.length (); + const unsigned n1 = src.length (); + if (n0 < n1) + dst.safe_grow (n1); + else + { + dst.truncate (n1); + n0 = n1; + } + + T *pdst = dst.address (); + const T *psrc = src.address (); + + /* Copy-assign over the first N0 elements. */ + for (unsigned i = 0; i != n0; ++i) + pdst[i] = psrc[i]; + + if (n0 < n1) + /* Copy-construct elements in excess of N0. */ + vec_copy_construct (pdst + n0, psrc + n0, n1 - n0); + + return dst; +} + +template class auto_vec : public vec { public: @@ -1536,11 +1570,34 @@ public: this->release (); } + /* Copy ctor. */ + auto_vec (const auto_vec &); + + /* Copy asssignment. */ + auto_vec& operator= (const auto_vec &r) + { + return vec_assign (*this, r); + } + private: vec m_auto; T m_data[MAX (N - 1, 1)]; }; +/* Copy elements from R to *THIS. */ + +template +auto_vec::auto_vec (const auto_vec& r) +{ + const unsigned n = r.length (); + this->create (n); + if (n) + { + vec_copy_construct (this->address (), r.address (), n); + this->m_vec->m_vecpfx.m_num = n; + } +} + /* auto_vec is a sub class of vec whose storage is released when it is destroyed. */ template @@ -1551,12 +1608,24 @@ public: auto_vec (size_t n CXX_MEM_STAT_INFO) { this->create (n PASS_MEM_STAT); } ~auto_vec () { this->release (); } + /* Copy ctor. */ + auto_vec (const auto_vec&); + + /* Move ctor from vec that's not auto_vec. */ auto_vec (vec&& r) { gcc_assert (!r.using_auto_storage ()); this->m_vec = r.m_vec; r.m_vec = NULL; } + + /* Copy asssignment. */ + auto_vec& operator= (const auto_vec &rhs) + { + return vec_assign (*this, rhs); + } + + /* Move asssignment from vec that's not auto_vec. */ auto_vec& operator= (vec&& r) { gcc_assert (!r.using_auto_storage ()); @@ -1567,6 +1636,35 @@ public: } }; +/* Copy elements from R to *THIS. */ + +template +auto_vec::auto_vec (const auto_vec& r) +{ + const unsigned n = r.length (); + this->create (n); + if (n) + { + vec_copy_construct (this->address (), r.address (), n); + this->m_vec->m_vecpfx.m_num = n; + } +} + +/* Same as auto_vec but with deleted copy ctor and assignment + operator to detect uses of these special functions in contexts + where they may be inefficient. */ + +template +class auto_vec_no_copy: public auto_vec +{ +public: + /* Inherit all ctors. */ + using auto_vec::auto_vec; + +private: + /* Prevent copying and assignment. */ + DISABLE_COPY_AND_ASSIGN (auto_vec_no_copy); +}; /* Allocate heap memory for pointer V and create the internal vector with space for NELEMS elements. If NELEMS is 0, the internal @@ -1587,7 +1685,11 @@ vec_alloc (vec *&v, unsigned nelems CXX_MEM_STAT_INFO) class auto_string_vec : public auto_vec { public: + auto_string_vec (): auto_vec () { } ~auto_string_vec (); + +private: + DISABLE_COPY_AND_ASSIGN (auto_string_vec); }; /* A subclass of auto_vec that deletes all of its elements on