]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++, libstdc++: Implement C++26 P2747R2 - constexpr placement new [PR115744]
authorJakub Jelinek <jakub@redhat.com>
Thu, 8 Aug 2024 09:05:36 +0000 (11:05 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 8 Aug 2024 09:07:29 +0000 (11:07 +0200)
With the PR115754 fix in, constexpr placement new mostly just works,
so this patch just adds constexpr keyword to the placement new operators
in <new>, adds FTMs and testsuite coverage.

There is one accepts-invalid though, the
new (p + 1) int[]{2, 3};      // error (in this paper)
case from the paper.  Can we handle that incrementally?
The problem with that is I think calling operator new now that it is
constexpr should be fine even in that case in constant expressions, so
int *p = std::allocator<int>{}.allocate(3);
int *q = operator new[] (sizeof (int) * 2, p + 1);
should be ok, so it can't be easily the placement new operator call
itself on whose constexpr evaluation we try something special, it should
be on the new expression, but constexpr.cc actually sees only
<<< Unknown tree: expr_stmt
  (void) (TARGET_EXPR <D.2640, (void *) TARGET_EXPR <D.2641, VIEW_CONVERT_EXPR<int *>(b) + 4>>, TARGET_EXPR <D.2642, operator new [] (8, NON_LVALUE_EXPR <D.2640>)>,   int * D.2643;
  <<< Unknown tree: expr_stmt
    (void) (D.2643 = (int *) D.2642) >>>;
and that is just fine by the preexisting constexpr evaluation rules.

Should build_new_1 emit some extra cast for the array cases with placement
new in maybe_constexpr_fn (current_function_decl) that the existing P2738
code would catch?

2024-08-08  Jakub Jelinek  <jakub@redhat.com>

PR c++/115744
gcc/c-family/
* c-cppbuiltin.cc (c_cpp_builtins): Change __cpp_constexpr
from 202306L to 202406L for C++26.
gcc/testsuite/
* g++.dg/cpp2a/construct_at.h (operator new, operator new[]):
Use constexpr instead of inline if __cpp_constexpr >= 202406L.
* g++.dg/cpp26/constexpr-new1.C: New test.
* g++.dg/cpp26/constexpr-new2.C: New test.
* g++.dg/cpp26/constexpr-new3.C: New test.
* g++.dg/cpp26/feat-cxx26.C (__cpp_constexpr): Adjust expected
value.
libstdc++-v3/
* libsupc++/new (__glibcxx_want_constexpr_new): Define before
including bits/version.h.
(_GLIBCXX_PLACEMENT_CONSTEXPR): Define.
(operator new, operator new[]): Use it for placement new instead
of inline.
* include/bits/version.def (constexpr_new): New FTM.
* include/bits/version.h: Regenerate.

gcc/c-family/c-cppbuiltin.cc
gcc/testsuite/g++.dg/cpp26/constexpr-new1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-new2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-new3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
gcc/testsuite/g++.dg/cpp2a/construct_at.h
libstdc++-v3/include/bits/version.def
libstdc++-v3/include/bits/version.h
libstdc++-v3/libsupc++/new

index a80372c89914454da944809c3cf539cd69881aba..97c3ecb7d1616c0a3f566d62eb03615e1b9098ea 100644 (file)
@@ -1091,7 +1091,7 @@ c_cpp_builtins (cpp_reader *pfile)
       if (cxx_dialect > cxx23)
        {
          /* Set feature test macros for C++26.  */
-         cpp_define (pfile, "__cpp_constexpr=202306L");
+         cpp_define (pfile, "__cpp_constexpr=202406L");
          cpp_define (pfile, "__cpp_static_assert=202306L");
          cpp_define (pfile, "__cpp_placeholder_variables=202306L");
          cpp_define (pfile, "__cpp_structured_bindings=202403L");
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-new1.C b/gcc/testsuite/g++.dg/cpp26/constexpr-new1.C
new file mode 100644 (file)
index 0000000..131b718
--- /dev/null
@@ -0,0 +1,66 @@
+// C++26 P2747R2 - constexpr placement new
+// { dg-do compile { target c++26 } }
+
+#include "../cpp2a/construct_at.h"
+
+struct S {
+  constexpr S () : a (42), b (43) {}
+  constexpr S (int c, int d) : a (c), b (d) {}
+  int a, b;
+};
+struct T {
+  int a, b;
+};
+
+constexpr bool
+foo ()
+{
+  std::allocator<int> a;
+  auto b = a.allocate (3);
+  ::new (b) int ();
+  ::new (b + 1) int (1);
+  ::new (b + 2) int {2};
+  if (b[0] != 0 || b[1] != 1 || b[2] != 2)
+    return false;
+  a.deallocate (b, 3);
+  std::allocator<S> c;
+  auto d = c.allocate (4);
+  ::new (d) S;
+  ::new (d + 1) S ();
+  ::new (d + 2) S (7, 8);
+  ::new (d + 3) S { 9, 10 };
+  if (d[0].a != 42 || d[0].b != 43
+      || d[1].a != 42 || d[1].b != 43
+      || d[2].a != 7 || d[2].b != 8
+      || d[3].a != 9 || d[3].b != 10)
+    return false;
+  d[0].~S ();
+  d[1].~S ();
+  d[2].~S ();
+  d[3].~S ();
+  c.deallocate (d, 4);
+  std::allocator<T> e;
+  auto f = e.allocate (3);
+  ::new (f) T ();
+  ::new (f + 1) T (7, 8);
+  ::new (f + 2) T { .a = 9, .b = 10 };
+  if (f[0].a != 0 || f[0].b != 0
+      || f[1].a != 7 || f[1].b != 8
+      || f[2].a != 9 || f[2].b != 10)
+    return false;
+  f[0].~T ();
+  f[1].~T ();
+  f[2].~T ();
+  e.deallocate (f, 3);
+  auto g = a.allocate (3);
+  new (g) int[] {1, 2, 3};
+  if (g[0] != 1 || g[1] != 2 || g[2] != 3)
+    return false;
+  new (g) int[] {4, 5};
+  if (g[0] != 4 || g[1] != 5)
+    return false;
+  a.deallocate (g, 3);
+  return true;
+}
+
+static_assert (foo ());
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-new2.C b/gcc/testsuite/g++.dg/cpp26/constexpr-new2.C
new file mode 100644 (file)
index 0000000..e333185
--- /dev/null
@@ -0,0 +1,73 @@
+// C++26 P2747R2 - constexpr placement new
+// { dg-do compile { target c++26 } }
+
+#include <memory>
+#include <new>
+
+#ifndef __cpp_lib_constexpr_new
+# error "__cpp_lib_constexpr_new"
+#elif __cpp_lib_constexpr_new < 202406L
+# error "__cpp_lib_constexpr_new < 202406"
+#endif
+
+struct S {
+  constexpr S () : a (42), b (43) {}
+  constexpr S (int c, int d) : a (c), b (d) {}
+  int a, b;
+};
+struct T {
+  int a, b;
+};
+
+constexpr bool
+foo ()
+{
+  std::allocator<int> a;
+  auto b = a.allocate (3);
+  ::new (b) int ();
+  ::new (b + 1) int (1);
+  ::new (b + 2) int {2};
+  if (b[0] != 0 || b[1] != 1 || b[2] != 2)
+    return false;
+  a.deallocate (b, 3);
+  std::allocator<S> c;
+  auto d = c.allocate (4);
+  ::new (d) S;
+  ::new (d + 1) S ();
+  ::new (d + 2) S (7, 8);
+  ::new (d + 3) S { 9, 10 };
+  if (d[0].a != 42 || d[0].b != 43
+      || d[1].a != 42 || d[1].b != 43
+      || d[2].a != 7 || d[2].b != 8
+      || d[3].a != 9 || d[3].b != 10)
+    return false;
+  d[0].~S ();
+  d[1].~S ();
+  d[2].~S ();
+  d[3].~S ();
+  c.deallocate (d, 4);
+  std::allocator<T> e;
+  auto f = e.allocate (3);
+  ::new (f) T ();
+  ::new (f + 1) T (7, 8);
+  ::new (f + 2) T { .a = 9, .b = 10 };
+  if (f[0].a != 0 || f[0].b != 0
+      || f[1].a != 7 || f[1].b != 8
+      || f[2].a != 9 || f[2].b != 10)
+    return false;
+  f[0].~T ();
+  f[1].~T ();
+  f[2].~T ();
+  e.deallocate (f, 3);
+  auto g = a.allocate (3);
+  new (g) int[] {1, 2, 3};
+  if (g[0] != 1 || g[1] != 2 || g[2] != 3)
+    return false;
+  new (g) int[] {4, 5};
+  if (g[0] != 4 || g[1] != 5)
+    return false;
+  a.deallocate (g, 3);
+  return true;
+}
+
+static_assert (foo ());
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-new3.C b/gcc/testsuite/g++.dg/cpp26/constexpr-new3.C
new file mode 100644 (file)
index 0000000..6a06a6e
--- /dev/null
@@ -0,0 +1,47 @@
+// C++26 P2747R2 - constexpr placement new
+// { dg-do compile { target c++26 } }
+
+#include "../cpp2a/construct_at.h"
+
+struct S {
+  constexpr S () : a (42), b (43) {}
+  constexpr S (int c, int d) : a (c), b (d) {}
+  int a, b;
+};
+struct T {
+  int a, b;
+};
+
+constexpr bool
+foo ()
+{
+  std::allocator<int> a;
+  auto b = a.allocate (3);
+  new (b + 1) int[] {2, 3};    // { dg-error "" "" { xfail *-*-* } }
+  a.deallocate (b, 3);
+  return true;
+}
+
+constexpr bool
+bar ()
+{
+  std::allocator<int> a;
+  auto b = a.allocate (3);
+  new (b) int[] {1, 2, 3, 4};  // { dg-error "array subscript value '3' is outside the bounds of array 'heap ' of type 'int \\\[3\\\]'" }
+  a.deallocate (b, 3);
+  return true;
+}
+
+constexpr bool
+baz ()
+{
+  std::allocator<int> a;
+  auto b = a.allocate (2);
+  new (b) long (42);           // { dg-error "accessing value of 'heap ' through a 'long int' glvalue in a constant expression" }
+  a.deallocate (b, 2);
+  return true;
+}
+
+constexpr bool a = foo ();
+constexpr bool b = bar ();
+constexpr bool c = baz ();
index 8a5fd64d39604b03af738ad9d5f3b826c04cdf51..82be39c996f247576e7f0ac3357bf1b374870dd2 100644 (file)
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 202306L
-#  error "__cpp_constexpr != 202306L"
+#elif __cpp_constexpr != 202406L
+#  error "__cpp_constexpr != 202406L"
 #endif
 
 #ifndef __cpp_decltype_auto
index 27e92cbb28c5bb84a7f21b453a3014e1e1acb090..ed6165610424fc5e289dec20aa59273ede952253 100644 (file)
@@ -58,5 +58,18 @@ namespace std
   { l->~T (); }
 }
 
-inline void *operator new (std::size_t, void *p) noexcept
+#if __cpp_constexpr >= 202406L
+constexpr
+#else
+inline
+#endif
+void *operator new (std::size_t, void *p) noexcept
+{ return p; }
+
+#if __cpp_constexpr >= 202406L
+constexpr
+#else
+inline
+#endif
+void *operator new[] (std::size_t, void *p) noexcept
 { return p; }
index ee0a9e45c441d272ed925bdaa3fc3da35db071ab..6791c6f6f93678ae473648361b6a161db883705d 100644 (file)
@@ -1838,6 +1838,15 @@ ftms = {
   };
 };
 
+ftms = {
+  name = constexpr_new;
+  values = {
+    v = 202406;
+    cxxmin = 26;
+    extra_cond = "__cpp_constexpr >= 202406L";
+  };
+};
+
 // Standard test specifications.
 stds[97] = ">= 199711L";
 stds[03] = ">= 199711L";
index cee497d7443fa2eafde5975696e996d81c0d0533..0b78cb94ecc97dda9ffced2b8fdb1b498e71ea9a 100644 (file)
 #endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */
 #undef __glibcxx_want_ranges_concat
 
+#if !defined(__cpp_lib_constexpr_new)
+# if (__cplusplus >  202302L) && (__cpp_constexpr >= 202406L)
+#  define __glibcxx_constexpr_new 202406L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_new)
+#   define __cpp_lib_constexpr_new 202406L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_constexpr_new) && defined(__glibcxx_want_constexpr_new) */
+#undef __glibcxx_want_constexpr_new
+
 #undef __glibcxx_want_all
index 8b07150fb94941bd29a334cfa178c922255ea394..2e2038e1a82ccbcd2e73becedeb1665b6fb660e8 100644 (file)
@@ -43,6 +43,7 @@
 #define __glibcxx_want_launder
 #define __glibcxx_want_hardware_interference_size
 #define __glibcxx_want_destroying_delete
+#define __glibcxx_want_constexpr_new
 #include <bits/version.h>
 
 #pragma GCC visibility push(default)
@@ -175,12 +176,22 @@ void operator delete[](void*, std::size_t, std::align_val_t)
 #endif // __cpp_sized_deallocation
 #endif // __cpp_aligned_new
 
+#if __cpp_lib_constexpr_new >= 202406L
+# define _GLIBCXX_PLACEMENT_CONSTEXPR constexpr
+#else
+# define _GLIBCXX_PLACEMENT_CONSTEXPR inline
+#endif
+
 // Default placement versions of operator new.
-_GLIBCXX_NODISCARD inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
+_GLIBCXX_NODISCARD _GLIBCXX_PLACEMENT_CONSTEXPR
+void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
 { return __p; }
-_GLIBCXX_NODISCARD inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
+_GLIBCXX_NODISCARD _GLIBCXX_PLACEMENT_CONSTEXPR
+void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
 { return __p; }
 
+#undef _GLIBCXX_PLACEMENT_CONSTEXPR
+
 // Default placement versions of operator delete.
 inline void operator delete  (void*, void*) _GLIBCXX_USE_NOEXCEPT { }
 inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { }