https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110542
Bug ID: 110542 Summary: use of allocated storage after deallocation in a constant expression: std::array of std::vector<bool> Product: gcc Version: 13.1.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: hal.finkel.oss at gmail dot com Target Milestone: --- For this test case: ``` #include <vector> #include <array> struct s_t { std::vector<std::array<std::vector<int>, 1>> v; constexpr void rz(std::size_t n) { v.resize(n); for (auto &x : v) for (auto &w : x) w.resize(n); } }; constexpr std::size_t test1() { s_t s; s.rz(1); s.rz(2); return 1; } int main() { static_assert(test1() == 1); } ``` g++ -std=c++20 13.1.0 (and GCC trunk as of today on https://gcc.godbolt.org/): ERROR: ``` tc1.cpp: In function ‘int main()’: tc1.cpp:21:25: error: non-constant condition for static assertion 21 | static_assert(test1() == 1); | ~~~~~~~~^~~~ tc1.cpp:21:22: in ‘constexpr’ expansion of ‘test1()’ tc1.cpp:16:7: in ‘constexpr’ expansion of ‘s.s_t::rz(2)’ tc1.cpp:9:49: in ‘constexpr’ expansion of ‘(& w)->std::vector<int>::resize(n)’ /gcc-13.1.0/include/c++/13/bits/stl_vector.h:1011:21: in ‘constexpr’ expansion of ‘((std::vector<int>*)this)->std::vector<int>::_M_default_append((__new_size - ((std::vector<int>*)this)->std::vector<int>::size()))’ /gcc-13.1.0/include/c++/13/bits/vector.tcc:676:16: in ‘constexpr’ expansion of ‘std::vector<int>::_S_relocate(__old_start, __old_finish, __new_start, (* &((std::vector<int>*)this)->std::vector<int>::<anonymous>.std::_Vector_base<int, std::allocator<int> >::_M_get_Tp_allocator()))’ /gcc-13.1.0/include/c++/13/bits/stl_vector.h:504:26: in ‘constexpr’ expansion of ‘std::__relocate_a<int*, int*, allocator<int> >(__first, __last, __result, (* & __alloc))’ /gcc-13.1.0/include/c++/13/bits/stl_uninitialized.h:1142:33: in ‘constexpr’ expansion of ‘std::__relocate_a_1<int, int>(std::__niter_base<int*>(__first), std::__niter_base<int*>(__last), std::__niter_base<int*>(__result), (* & __alloc))’ /gcc-13.1.0/include/c++/13/bits/stl_uninitialized.h:1122:35: in ‘constexpr’ expansion of ‘std::__relocate_a_1<int*, __gnu_cxx::__normal_iterator<int*, void>, allocator<int> >(__first, __last, __out, (* & __alloc))’ /gcc-13.1.0/include/c++/13/bits/stl_uninitialized.h:1100:26: in ‘constexpr’ expansion of ‘std::__relocate_object_a<int, int, allocator<int> >(std::__addressof<int>((* & __cur.__gnu_cxx::__normal_iterator<int*, void>::operator*())), std::__addressof<int>((* __first)), (* & __alloc))’ /gcc-13.1.0/include/c++/13/bits/stl_uninitialized.h:1072:26: in ‘constexpr’ expansion of ‘std::allocator_traits<std::allocator<int> >::construct<int, int>((* & __alloc), __dest, (* & std::move<int&>((*(int*)__orig))))’ /gcc-13.1.0/include/c++/13/bits/alloc_traits.h:539:21: in ‘constexpr’ expansion of ‘std::construct_at<int, int>(__p, (* & std::forward<int>((* & __args#0))))’ tc1.cpp:21:25: error: use of allocated storage after deallocation in a constant expression In file included from /gcc-13.1.0/include/c++/13/vector:63, from tc1.cpp:1: /gcc-13.1.0/include/c++/13/bits/allocator.h:195:52: note: allocated here 195 | return static_cast<_Tp*>(::operator new(__n)); | ~~~~~~~~~~~~~~^~~~~ ``` clang -std=c++20 15, (and 16 and trunk as of today on https://gcc.godbolt.org/, noting that these appear to use headers from libstdc++ gcc-12.2.0; below is from clang 15 using GCC 13.1.0 headers): ERROR: ``` tc1.cpp:21:17: error: static assertion expression is not an integral constant expression static_assert(test1() == 1); ^~~~~~~~~~~~ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_algobase.h:931:11: note: assignment to object outside its lifetime is not allowed in a constant expression *__first = __tmp; ^ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_algobase.h:977:7: note: in call to '__fill_a1(&{*new int[2]#4}[1], &{*new int[2]#4}[2], {*new int[2]#4}[0])' { std::__fill_a1(__first, __last, __value); } ^ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_algobase.h:1128:7: note: in call to '__fill_a(&{*new int[2]#4}[1], &{*new int[2]#4}[2], {*new int[2]#4}[0])' std::__fill_a(__first, __first + __n, __value); ^ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_algobase.h:1157:14: note: in call to '__fill_n_a(&{*new int[2]#4}[1], 1, {*new int[2]#4}[0], {{{{}}}})' return std::__fill_n_a(__first, std::__size_to_integer(__n), __value, ^ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_uninitialized.h:668:18: note: in call to 'fill_n(&{*new int[2]#4}[1], 1, {*new int[2]#4}[0])' __first = std::fill_n(__first, __n - 1, *__val); ^ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_uninitialized.h:704:14: note: in call to '__uninit_default_n(&{*new int[2]#4}[1], 2)' return __uninitialized_default_n_1<__is_trivial(_ValueType) ^ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_uninitialized.h:773:14: note: (skipping 1 call in backtrace; use -fconstexpr-backtrace-limit=0 to see all) { return std::__uninitialized_default_n(__first, __n); } ^ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/vector.tcc:668:9: note: in call to '__uninitialized_default_n_a(&{*new int[2]#4}[0], 2, {*new std::array<std::vector<int>, 1>[2]#2}[1]._M_elems[0]._Vector_base::_M_impl)' std::__uninitialized_default_n_a(__new_start + __size, ^ /gcc-13.1.0/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_vector.h:1011:4: note: in call to '&{*new std::array<std::vector<int>, 1>[2]#2}[1]._M_elems[0]->_M_default_append(2)' _M_default_append(__new_size - size()); ^ tc1.cpp:9:43: note: in call to '&{*new std::array<std::vector<int>, 1>[2]#2}[1]._M_elems[0]->resize(2)' for (auto &x : v) for (auto &w : x) w.resize(n); ^ tc1.cpp:16:5: note: in call to '&s->rz(2)' s.rz(2); ^ tc1.cpp:21:17: note: in call to 'test1()' static_assert(test1() == 1); ^ 1 error generated. ``` clang -std=c++20 -stdlib=libc++ 15, 16, and trunk as of today on https://gcc.godbolt.org/): OK I realize that this might be two compiler bugs, and I see, e.g., GCC bug 101777, but the fact that both GCC and clang provide what seem like they might be the same semantic error, a use after deallocation, albeit pointing at slightly different places in the libstdc++ headers, both on the second resize, and it works with libc++, is why I suspect that it might be an issue in the library (and the error message here is different from the one in 101777). Or, of course, my code has UB and the compilers are correct.