The new test is currently marked as XFAIL because PR c++/102284 means that GCC doesn't notice that the lifetimes have ended.
libstdc++-v3/ChangeLog: PR libstdc++/121024 * include/bits/ranges_uninitialized.h (ranges::destroy): Do not optimize away trivial destructors during constant evaluation. (ranges::destroy_n): Likewise. * testsuite/20_util/specialized_algorithms/destroy/121024.cc: New test. --- Tested powerpc64le-linux. GCC doesn't care about this change, but it makes Clang happy. .../include/bits/ranges_uninitialized.h | 26 +++---- .../specialized_algorithms/destroy/121024.cc | 77 +++++++++++++++++++ 2 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/121024.cc diff --git a/libstdc++-v3/include/bits/ranges_uninitialized.h b/libstdc++-v3/include/bits/ranges_uninitialized.h index 12a714b68aa0..3f9a07fdf5c6 100644 --- a/libstdc++-v3/include/bits/ranges_uninitialized.h +++ b/libstdc++-v3/include/bits/ranges_uninitialized.h @@ -556,13 +556,12 @@ namespace ranges __destroy_fn::operator()(_Iter __first, _Sent __last) const noexcept { if constexpr (is_trivially_destructible_v<iter_value_t<_Iter>>) - return ranges::next(std::move(__first), __last); - else - { - for (; __first != __last; ++__first) - ranges::destroy_at(std::__addressof(*__first)); - return __first; - } + if (!is_constant_evaluated()) + return ranges::next(std::move(__first), __last); + + for (; __first != __last; ++__first) + ranges::destroy_at(std::__addressof(*__first)); + return __first; } template<__detail::__nothrow_input_range _Range> @@ -581,13 +580,12 @@ namespace ranges operator()(_Iter __first, iter_difference_t<_Iter> __n) const noexcept { if constexpr (is_trivially_destructible_v<iter_value_t<_Iter>>) - return ranges::next(std::move(__first), __n); - else - { - for (; __n > 0; ++__first, (void)--__n) - ranges::destroy_at(std::__addressof(*__first)); - return __first; - } + if (!is_constant_evaluated()) + return ranges::next(std::move(__first), __n); + + for (; __n > 0; ++__first, (void)--__n) + ranges::destroy_at(std::__addressof(*__first)); + return __first; } }; diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/121024.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/121024.cc new file mode 100644 index 000000000000..781dd404750c --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/121024.cc @@ -0,0 +1,77 @@ +// { dg-do compile { target c++26 } } + +// Bug 121024 +// ranges::destroy and ranges::destroy_n do not end lifetime of trivial types + +#include <memory> + +consteval bool is_within_lifetime(const auto* p) noexcept +{ + return __builtin_constant_p(*p); +} + +template<typename T> +struct Buf +{ + constexpr Buf() : p(std::allocator<T>().allocate(2)) { } + constexpr ~Buf() { std::allocator<T>().deallocate(p, 2); } + T* p; +}; + +template<typename T> +consteval bool +test_destroy() +{ + Buf<T> buf; + std::uninitialized_value_construct(buf.p, buf.p + 2); + std::destroy(buf.p, buf.p + 2); + return not is_within_lifetime(buf.p) && not is_within_lifetime(buf.p + 1); +} + +template<typename T> +consteval bool +test_destroy_n() +{ + Buf<T> buf; + std::uninitialized_value_construct_n(buf.p, 2); + std::destroy_n(buf.p, 2); + return not is_within_lifetime(buf.p) && not is_within_lifetime(buf.p + 1); +} + +template<typename T> +consteval bool +test_ranges_destroy() +{ + Buf<T> buf; + std::uninitialized_value_construct(buf.p, buf.p + 2); + std::ranges::destroy(buf.p, buf.p + 2); + return not is_within_lifetime(buf.p) && not is_within_lifetime(buf.p + 1); +} + +template<typename T> +consteval bool +test_ranges_destroy_n() +{ + Buf<T> buf; + std::uninitialized_value_construct_n(buf.p, 2); + std::ranges::destroy_n(buf.p, 2); + return not is_within_lifetime(buf.p) && not is_within_lifetime(buf.p + 1); +} + +struct O +{ + constexpr O() { } + constexpr ~O() { } +}; + +// These all fail for GCC because is_within_lifetime still returns true +// after the lifetime has been ended. +// { dg-xfail-if "PR c++/102284" { *-*-* } } +static_assert( test_destroy<int>() ); +static_assert( test_destroy<O>() ); +static_assert( test_destroy_n<int>() ); +static_assert( test_destroy_n<O>() ); +static_assert( test_ranges_destroy<int>() ); +static_assert( test_ranges_destroy<O>() ); +static_assert( test_ranges_destroy_n<int>() ); +static_assert( test_ranges_destroy_n<O>() ); -- 2.50.1