ldionne created this revision.
ldionne added a reviewer: rsmith.
ldionne requested review of this revision.
Herald added projects: clang, libc++.
Herald added subscribers: libcxx-commits, cfe-commits.
Herald added a reviewer: libc++.

LWG3436 adds support for array types in construct_at. That is necessary
for implementing other voted features, such as arrays in make_shared, so
I am extraordinarily implementing the LWG resolution despite it not
being voted into the Standard yet.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D114649

Files:
  clang/lib/AST/ExprConstant.cpp
  clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
  libcxx/include/__memory/construct_at.h
  
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.LWG3436.pass.cpp

Index: libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.LWG3436.pass.cpp
===================================================================
--- /dev/null
+++ libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.LWG3436.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Only recent Clang versions support array placement new in constant expressions.
+// XFAIL: clang-12, clang-13, apple-clang-12, apple-clang-13
+
+// This test ensures that we implement the resolution of https://wg21.link/LWG3436,
+// which mentions that std::construct_at doesn't work with array types.
+//
+// Libc++ currently implements this LWG issue even though it hasn't been voted
+// into the Standard yet, because failure to implement this issue makes it
+// impossible (well, very difficult) to implement other features, such as support
+// for arrays in shared_ptr.
+//
+// Note: libc++ currently implements the proposed resolution as of 2021-01-16.
+//       If the proposed resolution that ends up being adopted changes, we'll
+//       want to update our implementation (and probably this test too).
+//
+// REQUIRES: stdlib=libc++
+
+#include <memory>
+#include <cassert>
+#include <concepts>
+
+struct CountInitializations {
+  CountInitializations() { ++constructions; }
+  static int constructions;
+};
+
+int CountInitializations::constructions = 0;
+
+// Note that we don't test std::construct_at(&array, arguments...) because that
+// is ill-formed. That would call array placement new with a new-initializer
+// like '( expression-list )', which is not allowed (arrays require a braced-init-list).
+constexpr bool test() {
+  {
+    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);
+  }
+
+  return true;
+}
+
+void test_construction_counts() {
+  {
+    using Array = CountInitializations[1];
+    CountInitializations array[1];
+    CountInitializations::constructions = 0;
+    std::construct_at(&array);
+    assert(CountInitializations::constructions == 1);
+  }
+  {
+    using Array = CountInitializations[2];
+    CountInitializations array[2];
+    CountInitializations::constructions = 0;
+    std::construct_at(&array);
+    assert(CountInitializations::constructions == 2);
+  }
+  {
+    using Array = CountInitializations[3];
+    CountInitializations array[3];
+    CountInitializations::constructions = 0;
+    std::construct_at(&array);
+    assert(CountInitializations::constructions == 3);
+  }
+}
+
+int main(int, char**) {
+  test();
+  test_construction_counts();
+  static_assert(test());
+  return 0;
+}
Index: libcxx/include/__memory/construct_at.h
===================================================================
--- libcxx/include/__memory/construct_at.h
+++ libcxx/include/__memory/construct_at.h
@@ -15,6 +15,7 @@
 #include <__iterator/access.h>
 #include <__memory/addressof.h>
 #include <__utility/forward.h>
+#include <new>
 #include <type_traits>
 #include <utility>
 
@@ -34,7 +35,12 @@
 _LIBCPP_INLINE_VISIBILITY
 constexpr _Tp* construct_at(_Tp* __location, _Args&& ...__args) {
     _LIBCPP_ASSERT(__location, "null pointer given to construct_at");
-    return ::new ((void*)__location) _Tp(_VSTD::forward<_Args>(__args)...);
+    auto __ptr = ::new ((void*)__location) _Tp(_VSTD::forward<_Args>(__args)...);
+    // This implements https://wg21.link/LWG3436, which hasn't been voted into the Standard yet.
+    if constexpr (is_array_v<_Tp>)
+        return _VSTD::launder(__location);
+    else
+        return __ptr;
 }
 
 #endif
Index: clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
+++ clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
@@ -96,14 +96,28 @@
   return a == 42;
 }
 
+void *operator new[](std::size_t, void *p) { return p; }
+constexpr bool no_array_placement_new_in_user_code() { // expected-error {{never produces a constant expression}}
+  int a[3];
+  new (&a) int[3](); // expected-note {{call to placement 'operator new[]'}}
+  return a[0] == 0 && a[1] == 0 && a[2] == 0;
+}
+
 namespace std {
   constexpr bool placement_new_in_stdlib() {
     int a;
     new (&a) int(42);
     return a == 42;
   }
+
+  constexpr bool array_placement_new_in_stdlib() {
+    int a[3];
+    new (&a) int[3]{42, 43, 44};
+    return a[0] == 42 && a[1] == 43 && a[2] == 44;
+  }
 }
 static_assert(std::placement_new_in_stdlib());
+static_assert(std::array_placement_new_in_stdlib());
 
 namespace std {
   template<typename T, typename ...Args>
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -9358,8 +9358,7 @@
   bool IsNothrow = false;
   bool IsPlacement = false;
   if (OperatorNew->isReservedGlobalPlacementOperator() &&
-      Info.CurrentCall->isStdFunction() && !E->isArray()) {
-    // FIXME Support array placement new.
+      Info.CurrentCall->isStdFunction()) {
     assert(E->getNumPlacementArgs() == 1);
     if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
       return false;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to