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
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cIssues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"`LWG4106 <https://wg21.link/LWG4106>`__","``basic_format_args`` should not be default-constructible","2024-06 (St. Louis)","|Complete|","19",""
"","","","","",""
"`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","",""
"`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","",""
"`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","|Complete|","21",""
"`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","","",""
"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","",""
"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","",""
Expand Down
18 changes: 15 additions & 3 deletions libcxx/include/__memory/construct_at.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <__new/placement_new_delete.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_array.h>
#include <__type_traits/is_unbounded_array.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
#include <__utility/move.h>
Expand All @@ -34,15 +35,26 @@ _LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 20

template <class _Tp, class... _Args, class = decltype(::new(std::declval<void*>()) _Tp(std::declval<_Args>()...))>
template <class _Tp,
class... _Args,
class = decltype(::new(std::declval<void*>()) _Tp(std::declval<_Args>()...)),
__enable_if_t<!is_unbounded_array_v<_Tp>, int> = 0>
Comment on lines +40 to +41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to use requires (ditto for `ranges::construct_at)?

_LIBCPP_HIDE_FROM_ABI constexpr _Tp* construct_at(_Tp* __location, _Args&&... __args) {
_LIBCPP_ASSERT_NON_NULL(__location != nullptr, "null pointer given to construct_at");
return ::new (static_cast<void*>(__location)) _Tp(std::forward<_Args>(__args)...);
if constexpr (is_array_v<_Tp>) {
static_assert(sizeof...(_Args) == 0, "construction arguments cannot be passed to construct_at with an array type");
return ::new (static_cast<void*>(__location)) _Tp[1]();
} else {
return ::new (static_cast<void*>(__location)) _Tp(std::forward<_Args>(__args)...);
}
}

#endif

template <class _Tp, class... _Args, class = decltype(::new(std::declval<void*>()) _Tp(std::declval<_Args>()...))>
template <class _Tp,
class... _Args,
class = decltype(::new(std::declval<void*>()) _Tp(std::declval<_Args>()...)),
__enable_if_t<!is_unbounded_array_v<_Tp>, int> = 0>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_unbounded_array_v is introduced in C++20 so we shouldn't use it for __construct_at. Moreover, should we make the internal __construct_at have the same SFINAE constraints as std::construct_at?

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp* __construct_at(_Tp* __location, _Args&&... __args) {
#if _LIBCPP_STD_VER >= 20
return std::construct_at(__location, std::forward<_Args>(__args)...);
Expand Down
6 changes: 5 additions & 1 deletion libcxx/include/__memory/ranges_construct_at.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/dangling.h>
#include <__type_traits/is_unbounded_array.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
#include <__utility/move.h>
Expand All @@ -38,7 +39,10 @@ namespace ranges {
// construct_at

struct __construct_at {
template <class _Tp, class... _Args, class = decltype(::new(std::declval<void*>()) _Tp(std::declval<_Args>()...))>
template <class _Tp,
class... _Args,
class = decltype(::new(std::declval<void*>()) _Tp(std::declval<_Args>()...)),
__enable_if_t<!is_unbounded_array_v<_Tp>, int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator()(_Tp* __location, _Args&&... __args) const {
return std::construct_at(__location, std::forward<_Args>(__args)...);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that we can use the new style (ditto below):

Suggested change
// UNSUPPORTED: c++03, c++11, c++14, c++17
// REQUIRES: std-at-least-c++20

// REQUIRES: stdlib=libc++

// <memory>

// Test that std::construct_at provides a meaningful diagnostic when used with an
// array type and construction arguments are provided. See LWG3436.

#include <memory>

using Array = int[3];
void test(Array* a) {
std::construct_at(a, 1, 2, 3);
// expected-error-re@*:* {{static assertion failed {{.*}}construction arguments cannot be passed to construct_at with an array type}}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
// constexpr T* construct_at(T* location, Args&& ...args);

#include <cassert>
#include <concepts>
#include <cstddef>
#include <memory>
#include <utility>

#include "test_iterators.h"
#include "test_macros.h"

struct Foo {
constexpr Foo() {}
Expand All @@ -39,25 +41,31 @@ struct Counted {
constexpr ~Counted() { --count_; }
};

struct CountDefaultInitializations {
CountDefaultInitializations() { ++constructions; }
static int constructions;
};
int CountDefaultInitializations::constructions = 0;

constexpr bool test() {
{
int i = 99;
int* res = std::construct_at(&i);
int i = 99;
std::same_as<int*> auto res = std::construct_at(&i);
assert(res == &i);
assert(*res == 0);
}

{
int i = 0;
int* res = std::construct_at(&i, 42);
int i = 0;
std::same_as<int*> auto res = std::construct_at(&i, 42);
assert(res == &i);
assert(*res == 42);
}

{
Foo foo = {};
int count = 0;
Foo* res = std::construct_at(&foo, 42, 'x', 123.89, &count);
Foo foo = {};
int count = 0;
std::same_as<Foo*> auto res = std::construct_at(&foo, 42, 'x', 123.89, &count);
assert(res == &foo);
assert(*res == Foo(42, 'x', 123.89));
assert(count == 1);
Expand All @@ -78,12 +86,70 @@ constexpr bool test() {
a.deallocate(p, 2);
}

// Test LWG3436, std::construct_at with array types
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC Clang didn't support placement new for array in constant evaluation until Clang 20. (The ability was added via #104586.) So, should we skip "Clang 19 + constant evaluation" here?

{
{
using Array = int[1];
Array array;
std::same_as<Array*> auto result = std::construct_at(&array);
assert(result == &array);
assert(array[0] == 0);
}
{
using Array = int[2];
Array array;
std::same_as<Array*> auto result = std::construct_at(&array);
assert(result == &array);
assert(array[0] == 0);
assert(array[1] == 0);
}
{
using Array = int[3];
Array array;
std::same_as<Array*> auto result = std::construct_at(&array);
assert(result == &array);
assert(array[0] == 0);
assert(array[1] == 0);
assert(array[2] == 0);
}

// Make sure we initialize the right number of elements. This can't be done inside
// constexpr since it requires a global variable.
if (!TEST_IS_CONSTANT_EVALUATED) {
{
using Array = CountDefaultInitializations[1];
CountDefaultInitializations array[1];
CountDefaultInitializations::constructions = 0;
std::construct_at(&array);
assert(CountDefaultInitializations::constructions == 1);
}
{
using Array = CountDefaultInitializations[2];
CountDefaultInitializations array[2];
CountDefaultInitializations::constructions = 0;
std::construct_at(&array);
assert(CountDefaultInitializations::constructions == 2);
}
{
using Array = CountDefaultInitializations[3];
CountDefaultInitializations array[3];
CountDefaultInitializations::constructions = 0;
std::construct_at(&array);
assert(CountDefaultInitializations::constructions == 3);
}
}
}

return true;
}

template <class... Args>
constexpr bool can_construct_at = requires { std::construct_at(std::declval<Args>()...); };

struct NoDefault {
NoDefault() = delete;
};

// Check that SFINAE works.
static_assert(can_construct_at<int*, int>);
static_assert(can_construct_at<Foo*, int, char, double>);
Expand All @@ -96,6 +162,11 @@ static_assert(!can_construct_at<contiguous_iterator<Foo*>, int, char, double>);
static_assert(!can_construct_at<int (*)()>);
static_assert(!can_construct_at<int (*)(), std::nullptr_t>);

// LWG3436
static_assert(can_construct_at<int (*)[3]>); // test the test
static_assert(!can_construct_at<int (*)[]>); // unbounded arrays should SFINAE away
static_assert(!can_construct_at<NoDefault (*)[1]>); // non default constructible shouldn't work

int main(int, char**) {
test();
static_assert(test());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17
// REQUIRES: stdlib=libc++

// <memory>

// Test that std::ranges::construct_at provides a meaningful diagnostic when used with an
// array type and construction arguments are provided. See LWG3436.

#include <memory>

using Array = int[3];
void test(Array* a) {
std::ranges::construct_at(a, 1, 2, 3);
// expected-error-re@*:* {{static assertion failed {{.*}}construction arguments cannot be passed to construct_at with an array type}}
}
Loading
Loading