diff --git a/stl/inc/memory b/stl/inc/memory index 8791b476763..e959623f7b4 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -584,12 +584,18 @@ namespace ranges { template <_No_throw_input_iterator _It, _No_throw_sentinel_for<_It> _Se> requires destructible> _NODISCARD constexpr _It _Destroy_unchecked(_It _First, _Se _Last) noexcept { - if constexpr (is_trivially_destructible_v>) { - _RANGES advance(_First, _STD move(_Last)); - } else { + if (_STD is_constant_evaluated()) { for (; _First != _Last; ++_First) { _RANGES destroy_at(_STD addressof(*_First)); } + } else { + if constexpr (is_trivially_destructible_v>) { + _RANGES advance(_First, _STD move(_Last)); + } else { + for (; _First != _Last; ++_First) { + _RANGES destroy_at(_STD addressof(*_First)); + } + } } return _First; @@ -630,12 +636,21 @@ _CONSTEXPR20 _NoThrowFwdIt destroy_n(_NoThrowFwdIt _First, const _Diff _Count_ra } auto _UFirst = _STD _Get_unwrapped_n(_First, _Count); - if constexpr (is_trivially_destructible_v<_Iter_value_t<_NoThrowFwdIt>>) { - _STD advance(_UFirst, _Count); - } else { +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { for (; _Count > 0; --_Count, (void) ++_UFirst) { _STD _Destroy_in_place(*_UFirst); } + } else +#endif // _HAS_CXX20 + { + if constexpr (is_trivially_destructible_v<_Iter_value_t<_NoThrowFwdIt>>) { + _STD advance(_UFirst, _Count); + } else { + for (; _Count > 0; --_Count, (void) ++_UFirst) { + _STD _Destroy_in_place(*_UFirst); + } + } } _STD _Seek_wrapped(_First, _UFirst); @@ -658,14 +673,22 @@ namespace ranges { } auto _UFirst = _STD _Get_unwrapped_n(_STD move(_First), _Count); - if constexpr (is_trivially_destructible_v>) { - _RANGES advance(_UFirst, _Count); - } else { + if (_STD is_constant_evaluated()) { do { _RANGES destroy_at(_STD addressof(*_UFirst)); ++_UFirst; --_Count; } while (_Count > 0); + } else { + if constexpr (is_trivially_destructible_v>) { + _RANGES advance(_UFirst, _Count); + } else { + do { + _RANGES destroy_at(_STD addressof(*_UFirst)); + ++_UFirst; + --_Count; + } while (_Count > 0); + } } _STD _Seek_wrapped(_First, _STD move(_UFirst)); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 80041fc71bc..987e534e946 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1111,11 +1111,19 @@ _CONSTEXPR20 void _Destroy_range(_Alloc_ptr_t<_Alloc> _First, const _Alloc_ptr_t template _CONSTEXPR20 void _Destroy_range(_NoThrowFwdIt _First, const _NoThrowSentinel _Last) noexcept { - // note that this is an optimization for debug mode codegen; in release mode the BE removes all of this - if constexpr (!is_trivially_destructible_v<_Iter_value_t<_NoThrowFwdIt>>) { +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { for (; _First != _Last; ++_First) { _STD _Destroy_in_place(*_First); } + } else +#endif // _HAS_CXX20 + { // note that this is an optimization for debug mode codegen; in release mode the BE removes all of this + if constexpr (!is_trivially_destructible_v<_Iter_value_t<_NoThrowFwdIt>>) { + for (; _First != _Last; ++_First) { + _STD _Destroy_in_place(*_First); + } + } } } diff --git a/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp b/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp index 3fdcdca2b3e..8b5299caa1d 100644 --- a/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp +++ b/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp @@ -583,6 +583,42 @@ static_assert(!CanDestroyN); static_assert(!CanDestroyN); static_assert(!CanDestroyN); +#ifdef __clang__ // TRANSITION, DevCom-10642767 (MSVC), DevCom-10896316 (EDG) +// Test that destroy, destroy_at, and destroy_n properly destroy trivially destructible objects +// during constant evaluation. +// After such destruction, further access will cause core language undefined behavior, +// which will in turn cause constant evaluation failure. + +template +struct require_valid_constant; + +template +constexpr int consteval_validate_destruction(Fn op) { + struct S { + int n; + }; + + S arr[1]{{42}}; + op(arr); + return arr[0].n; +} + +template +constexpr bool CanWellDefinedlyAccessAfterOperation = + requires { typename require_valid_constant; }; + +static_assert(CanWellDefinedlyAccessAfterOperation<[](auto&) {}>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { destroy(arr + 0, arr + 1); }>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { destroy_at(arr + 0); }>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { destroy_at(&arr); }>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { destroy_n(arr + 0, 1); }>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { ranges::destroy(arr + 0, arr + 1); }>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { ranges::destroy(arr); }>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { ranges::destroy_at(arr + 0); }>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { ranges::destroy_at(&arr); }>); +static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { ranges::destroy_n(arr + 0, 1); }>); +#endif // ^^^ no workaround ^^^ + int main() { test_runtime(1234); test_runtime(string("hello world"));