Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions stl/inc/memory
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ namespace ranges {

struct _Construct_at_fn {
template <class _Ty, class... _Types>
requires requires(_Ty* _Ptr, _Types&&... _Args) {
requires (!is_unbounded_array_v<_Ty>) && requires(_Ty* _Ptr, _Types&&... _Args) {
::new (static_cast<void*>(_Ptr)) _Ty(static_cast<_Types &&>(_Args)...); // per LWG-3888
}
_STATIC_CALL_OPERATOR constexpr _Ty* operator()(_Ty* _Location, _Types&&... _Args) _CONST_CALL_OPERATOR
Expand All @@ -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<void*>(_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<void*>(_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<void*>(_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<void*>(_Location)) _Ty[1]();
}
#else // ^^^ workaround / no workaround vvv
_MSVC_CONSTEXPR return ::new (static_cast<void*>(_Location)) _Ty[1]();
#endif // ^^^ no workaround ^^^
} else {
_MSVC_CONSTEXPR return ::new (static_cast<void*>(_Location)) _Ty(_STD forward<_Types>(_Args)...);
}
#endif // ^^^ Other ^^^
}
};
Expand Down
42 changes: 40 additions & 2 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class _Target, class _Source>
Expand Down Expand Up @@ -585,12 +604,31 @@ struct _Get_rebind_alias<_Ty, _Other, void_t<typename _Ty::template rebind<_Othe

#if _HAS_CXX20
_EXPORT_STD template <class _Ty, class... _Types>
requires requires(_Ty* _Location, _Types&&... _Args) {
requires (!is_unbounded_array_v<_Ty>) && requires(_Ty* _Location, _Types&&... _Args) {
::new (static_cast<void*>(_Location)) _Ty(_STD forward<_Types>(_Args)...); // per LWG-3888
}
constexpr _Ty* construct_at(_Ty* const _Location, _Types&&... _Args)
noexcept(noexcept(::new (static_cast<void*>(_Location)) _Ty(_STD forward<_Types>(_Args)...))) /* strengthened */ {
_MSVC_CONSTEXPR return ::new (static_cast<void*>(_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<void*>(_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<void*>(_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<void*>(_Location)) _Ty[1]();
}
#else // ^^^ workaround / no workaround vvv
_MSVC_CONSTEXPR return ::new (static_cast<void*>(_Location)) _Ty[1]();
#endif // ^^^ no workaround ^^^
} else {
_MSVC_CONSTEXPR return ::new (static_cast<void*>(_Location)) _Ty(_STD forward<_Types>(_Args)...);
}
}
#endif // _HAS_CXX20

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ static_assert(!can_construct_at<const volatile int>);
static_assert(!can_construct_at<const volatile int, int>);
static_assert(!can_construct_at<const volatile int, int&>);

static_assert(can_construct_at<int[1]>);
static_assert(can_construct_at<int[1][42]>);
static_assert(!can_construct_at<int[]>);
static_assert(!can_construct_at<int[][42]>);

struct X {};

static_assert(!can_construct_at<int, X>);
Expand Down Expand Up @@ -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 <class T, size_t N>
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 <class T, size_t N>
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<int, 1>();
test_construct_at_array<int, 42>();
test_ranges_construct_at_array<int, 1>();
test_ranges_construct_at_array<int, 42>();

#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<unique_ptr<string>, 1>();
test_construct_at_array<unique_ptr<string>, 42>();
test_ranges_construct_at_array<unique_ptr<string>, 1>();
test_ranges_construct_at_array<unique_ptr<string>, 42>();
}
test_construct_at_array<string, 1>();
#if defined(__EDG__) && _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, DevCom-11012299
if (!is_constant_evaluated())
#endif // ^^^ workaround ^^^
{
test_construct_at_array<string, 42>();
}
test_ranges_construct_at_array<string, 1>();
#if defined(__EDG__) && _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, DevCom-11012299
if (!is_constant_evaluated())
#endif // ^^^ workaround ^^^
{
test_ranges_construct_at_array<string, 42>();
}
}

return true;
}

int main() {
test_runtime(1234);
test_runtime(string("hello world"));
Expand All @@ -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());
}
40 changes: 40 additions & 0 deletions tests/std/tests/P1004R2_constexpr_vector/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class T, size_t N>
constexpr void test_vector_of_array() {
vector<T[N]> 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<int, 1>();
test_vector_of_array<int, 42>();

#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<unique_ptr<vector<char>>, 1>();
test_vector_of_array<unique_ptr<vector<char>>, 42>();
}
test_vector_of_array<vector<long>, 1>();
test_vector_of_array<vector<long>, 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 ^^^
}