diff --git a/stl/inc/memory b/stl/inc/memory index 7f110c0bc5c..d6d553ddd55 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -533,7 +533,7 @@ namespace ranges { struct _Construct_at_fn { template - requires requires(_Ty* _Ptr, _Types&&... _Args) { + requires (!is_unbounded_array_v<_Ty>) && requires(_Ty* _Ptr, _Types&&... _Args) { ::new (static_cast(_Ptr)) _Ty(static_cast<_Types &&>(_Args)...); // per LWG-3888 } _STATIC_CALL_OPERATOR constexpr _Ty* operator()(_Ty* _Location, _Types&&... _Args) _CONST_CALL_OPERATOR @@ -542,7 +542,26 @@ namespace ranges { #ifdef __EDG__ return _STD construct_at(_Location, _STD forward<_Types>(_Args)...); #else // ^^^ EDG / Other vvv - _MSVC_CONSTEXPR return ::new (static_cast(_Location)) _Ty(_STD forward<_Types>(_Args)...); + if constexpr (is_array_v<_Ty>) { + static_assert(sizeof...(_Types) == 0, "The array is only allowed to be value-initialized by " + "std::ranges::construct_at. (N5032 [specialized.construct]/2)"); +#if defined(__clang__) // TRANSITION, LLVM-117294 + ::new (static_cast(_Location)) _Ty(); + return __builtin_launder(_Location); // per old resolution of LWG-3436 +#elif defined(_MSC_VER) // TRANSITION, DevCom-10798069 + if constexpr (is_trivially_destructible_v<_Ty>) { + _MSVC_CONSTEXPR return ::new (_Secret_placement_new_tag{}, static_cast(_Location)) _Ty[1](); + } else { + // For non-trivially-destructible types, the workaround doesn't work. + // Because additional space is required to record the number of class objects to destroy. + return ::new (static_cast(_Location)) _Ty[1](); + } +#else // ^^^ workaround / no workaround vvv + _MSVC_CONSTEXPR return ::new (static_cast(_Location)) _Ty[1](); +#endif // ^^^ no workaround ^^^ + } else { + _MSVC_CONSTEXPR return ::new (static_cast(_Location)) _Ty(_STD forward<_Types>(_Args)...); + } #endif // ^^^ Other ^^^ } }; diff --git a/stl/inc/xutility b/stl/inc/xutility index 53a64c6c787..7f92ee5c4b4 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -252,6 +252,25 @@ __declspec(noalias) size_t __stdcall __std_mismatch_8(const void* _First1, const } // extern "C" +#if _HAS_CXX20 && !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-10798069 +_STD_BEGIN +struct _Secret_placement_new_tag { + explicit _Secret_placement_new_tag() = default; +}; +_STD_END + +template <_STD same_as<_STD _Secret_placement_new_tag> _Tag> +_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(_Size) + _Post_satisfies_(return == _Where) constexpr void* __CRTDECL operator new[]( + size_t _Size, _Tag, _Writable_bytes_(_Size) void* _Where) noexcept { + (void) _Size; + return _Where; +} + +template <_STD same_as<_STD _Secret_placement_new_tag> _Tag> +constexpr void __CRTDECL operator delete[](void*, _Tag, void*) noexcept {} +#endif // ^^^ workaround ^^^ + _STD_BEGIN template @@ -585,12 +604,31 @@ struct _Get_rebind_alias<_Ty, _Other, void_t - requires requires(_Ty* _Location, _Types&&... _Args) { + requires (!is_unbounded_array_v<_Ty>) && requires(_Ty* _Location, _Types&&... _Args) { ::new (static_cast(_Location)) _Ty(_STD forward<_Types>(_Args)...); // per LWG-3888 } constexpr _Ty* construct_at(_Ty* const _Location, _Types&&... _Args) noexcept(noexcept(::new (static_cast(_Location)) _Ty(_STD forward<_Types>(_Args)...))) /* strengthened */ { - _MSVC_CONSTEXPR return ::new (static_cast(_Location)) _Ty(_STD forward<_Types>(_Args)...); + if constexpr (is_array_v<_Ty>) { + static_assert(sizeof...(_Types) == 0, "The array is only allowed to be value-initialized by std::construct_at. " + "(N5032 [specialized.construct]/2)"); +#if defined(__clang__) || defined(__EDG__) // TRANSITION, LLVM-117294, DevCom-10798145 + ::new (static_cast(_Location)) _Ty(); + return __builtin_launder(_Location); // per old resolution of LWG-3436 +#elif defined(_MSC_VER) // TRANSITION, DevCom-10798069 + if constexpr (is_trivially_destructible_v<_Ty>) { + _MSVC_CONSTEXPR return ::new (_Secret_placement_new_tag{}, static_cast(_Location)) _Ty[1](); + } else { + // For non-trivially-destructible types, the workaround doesn't work. + // Because additional space is required to record the number of class objects to destroy. + return ::new (static_cast(_Location)) _Ty[1](); + } +#else // ^^^ workaround / no workaround vvv + _MSVC_CONSTEXPR return ::new (static_cast(_Location)) _Ty[1](); +#endif // ^^^ no workaround ^^^ + } else { + _MSVC_CONSTEXPR return ::new (static_cast(_Location)) _Ty(_STD forward<_Types>(_Args)...); + } } #endif // _HAS_CXX20 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 8b5299caa1d..5bd21e741ec 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 @@ -67,6 +67,11 @@ static_assert(!can_construct_at); static_assert(!can_construct_at); static_assert(!can_construct_at); +static_assert(can_construct_at); +static_assert(can_construct_at); +static_assert(!can_construct_at); +static_assert(!can_construct_at); + struct X {}; static_assert(!can_construct_at); @@ -619,6 +624,77 @@ static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { ranges::dest static_assert(!CanWellDefinedlyAccessAfterOperation<[](auto& arr) { ranges::destroy_n(arr + 0, 1); }>); #endif // ^^^ no workaround ^^^ +// Test LWG-3436 "std::construct_at should support arrays" +template +constexpr void test_construct_at_array() { + union U { + constexpr U() {} + constexpr ~U() {} + + T a[N]; + }; + U u; + construct_at(&u.a); + for (const auto& elem : u.a) { + assert(elem == T{}); + } + destroy_at(&u.a); +} + +template +constexpr void test_ranges_construct_at_array() { + union U { + constexpr U() {} + constexpr ~U() {} + + T a[N]; + }; + U u; + ranges::construct_at(&u.a); + for (const auto& elem : u.a) { + assert(elem == T{}); + } + ranges::destroy_at(&u.a); +} + +constexpr bool test_construct_at_array() { + test_construct_at_array(); + test_construct_at_array(); + test_ranges_construct_at_array(); + test_ranges_construct_at_array(); + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-10798069 + if (!is_constant_evaluated()) +#endif // ^^^ workaround ^^^ + { +#if !_HAS_CXX23 + if (!is_constant_evaluated()) +#endif // !_HAS_CXX23 + { + test_construct_at_array, 1>(); + test_construct_at_array, 42>(); + test_ranges_construct_at_array, 1>(); + test_ranges_construct_at_array, 42>(); + } + test_construct_at_array(); +#if defined(__EDG__) && _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, DevCom-11012299 + if (!is_constant_evaluated()) +#endif // ^^^ workaround ^^^ + { + test_construct_at_array(); + } + test_ranges_construct_at_array(); +#if defined(__EDG__) && _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, DevCom-11012299 + if (!is_constant_evaluated()) +#endif // ^^^ workaround ^^^ + { + test_ranges_construct_at_array(); + } + } + + return true; +} + int main() { test_runtime(1234); test_runtime(string("hello world")); @@ -637,4 +713,7 @@ int main() { test_array(1234); test_array(string("hello world")); test_array(string("hello to some really long world that certainly doesn't fit in SSO")); + + test_construct_at_array(); + static_assert(test_construct_at_array()); } diff --git a/tests/std/tests/P1004R2_constexpr_vector/test.cpp b/tests/std/tests/P1004R2_constexpr_vector/test.cpp index a50b5383c62..d33fc45aa2d 100644 --- a/tests/std/tests/P1004R2_constexpr_vector/test.cpp +++ b/tests/std/tests/P1004R2_constexpr_vector/test.cpp @@ -657,11 +657,51 @@ constexpr bool test_growth() { return true; } +#pragma warning(push) +#pragma warning(disable : 4582) // '%s': constructor is not implicitly called +#pragma warning(disable : 4583) // '%s': destructor is not implicitly called +template +constexpr void test_vector_of_array() { + vector v(42); + for (const auto& a : v) { + for (const auto& elem : a) { + assert(elem == T{}); + } + } +} + +constexpr bool test_vector_of_array() { + test_vector_of_array(); + test_vector_of_array(); + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-10798069 + if (!is_constant_evaluated()) +#endif // ^^^ workaround ^^^ + { +#if !_HAS_CXX23 + if (!is_constant_evaluated()) +#endif // !_HAS_CXX23 + { + test_vector_of_array>, 1>(); + test_vector_of_array>, 42>(); + } + test_vector_of_array, 1>(); + test_vector_of_array, 42>(); + } + + return true; +} +#pragma warning(pop) + int main() { test_interface(); test_iterators(); test_growth(); + test_vector_of_array(); static_assert(test_interface()); static_assert(test_iterators()); static_assert(test_growth()); +#ifndef __EDG__ // TRANSITION, DevCom-11008487 + static_assert(test_vector_of_array()); +#endif // ^^^ no workaround ^^^ }