diff mbox series

c++, libstdc++: Implement C++26 P2747R2 - constexpr placement new [PR115744]

Message ID ZoViDDKrpczHeWxm@tucnak
State New
Headers show
Series c++, libstdc++: Implement C++26 P2747R2 - constexpr placement new [PR115744] | expand

Commit Message

Jakub Jelinek July 3, 2024, 2:37 p.m. UTC
Hi!

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?

Anyway, bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-07-03  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.


	Jakub

Comments

Jonathan Wakely July 3, 2024, 2:50 p.m. UTC | #1
On Wed, 3 Jul 2024 at 15:37, Jakub Jelinek <jakub@redhat.com> wrote:
>
> Hi!
>
> 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?
>
> Anyway, bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

I have a mild preference for #undef _GLIBCXX_PLACEMENT_CONSTEXPR after
we're finished using it, but the libstdc++ parts are OK either way.


>
> 2024-07-03  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.jj     2024-07-02 22:06:21.343875948 +0200
> +++ gcc/c-family/c-cppbuiltin.cc        2024-07-03 10:18:00.311324004 +0200
> @@ -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");
> --- gcc/testsuite/g++.dg/cpp2a/construct_at.h.jj        2024-07-02 22:06:22.138865784 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/construct_at.h   2024-07-03 10:18:00.312323991 +0200
> @@ -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; }
> --- gcc/testsuite/g++.dg/cpp26/constexpr-new1.C.jj      2024-07-03 10:18:00.312323991 +0200
> +++ gcc/testsuite/g++.dg/cpp26/constexpr-new1.C 2024-07-03 10:18:00.312323991 +0200
> @@ -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 ());
> --- gcc/testsuite/g++.dg/cpp26/constexpr-new2.C.jj      2024-07-03 10:57:14.936113640 +0200
> +++ gcc/testsuite/g++.dg/cpp26/constexpr-new2.C 2024-07-03 10:58:34.268063259 +0200
> @@ -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 ());
> --- gcc/testsuite/g++.dg/cpp26/constexpr-new3.C.jj      2024-07-03 11:03:44.848951067 +0200
> +++ gcc/testsuite/g++.dg/cpp26/constexpr-new3.C 2024-07-03 11:20:19.850776541 +0200
> @@ -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 ();
> --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj  2024-07-02 22:06:22.081866513 +0200
> +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C     2024-07-03 10:18:00.312323991 +0200
> @@ -134,8 +134,8 @@
>
>  #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
> --- libstdc++-v3/libsupc++/new.jj       2024-01-03 12:07:51.070049086 +0100
> +++ libstdc++-v3/libsupc++/new  2024-07-03 10:36:17.728769550 +0200
> @@ -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,10 +176,18 @@ void operator delete[](void*, std::size_
>  #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; }
>
>  // Default placement versions of operator delete.
> --- libstdc++-v3/include/bits/version.def.jj    2024-07-01 11:28:23.642225952 +0200
> +++ libstdc++-v3/include/bits/version.def       2024-07-03 10:33:56.996636092 +0200
> @@ -1814,6 +1814,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";
> --- libstdc++-v3/include/bits/version.h.jj      2024-07-01 11:28:23.643225939 +0200
> +++ libstdc++-v3/include/bits/version.h 2024-07-03 10:34:28.487052774 +0200
> @@ -2023,4 +2023,14 @@
>  #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
>
>         Jakub
>
Jason Merrill July 3, 2024, 3:41 p.m. UTC | #2
On 7/3/24 10:37 AM, Jakub Jelinek wrote:
> +#if __cpp_lib_constexpr_new >= 202406L
> +# define _GLIBCXX_PLACEMENT_CONSTEXPR constexpr
> +#else
> +# define _GLIBCXX_PLACEMENT_CONSTEXPR inline
> +#endif

I'm a bit curious why you want constexpr *or* inline rather than leaving 
the inline keyword on the declaration and maybe adding constexpr.  The 
effect should be the same either way, so just wondering.

Jason
Jakub Jelinek July 3, 2024, 3:51 p.m. UTC | #3
On Wed, Jul 03, 2024 at 11:41:58AM -0400, Jason Merrill wrote:
> On 7/3/24 10:37 AM, Jakub Jelinek wrote:
> > +#if __cpp_lib_constexpr_new >= 202406L
> > +# define _GLIBCXX_PLACEMENT_CONSTEXPR constexpr
> > +#else
> > +# define _GLIBCXX_PLACEMENT_CONSTEXPR inline
> > +#endif
> 
> I'm a bit curious why you want constexpr *or* inline rather than leaving the
> inline keyword on the declaration and maybe adding constexpr.  The effect
> should be the same either way, so just wondering.

Just that the inline is then redundant.
But I'll do whatever Jonathan wants (already added #undef of the macro after
uses).

	Jakub
Jonathan Wakely July 3, 2024, 4:35 p.m. UTC | #4
On Wed, 3 Jul 2024 at 16:51, Jakub Jelinek wrote:
>
> On Wed, Jul 03, 2024 at 11:41:58AM -0400, Jason Merrill wrote:
> > On 7/3/24 10:37 AM, Jakub Jelinek wrote:
> > > +#if __cpp_lib_constexpr_new >= 202406L
> > > +# define _GLIBCXX_PLACEMENT_CONSTEXPR constexpr
> > > +#else
> > > +# define _GLIBCXX_PLACEMENT_CONSTEXPR inline
> > > +#endif
> >
> > I'm a bit curious why you want constexpr *or* inline rather than leaving the
> > inline keyword on the declaration and maybe adding constexpr.  The effect
> > should be the same either way, so just wondering.
>
> Just that the inline is then redundant.
> But I'll do whatever Jonathan wants (already added #undef of the macro after
> uses).

I have a mild preference (again :-) for what Jakub's patch does. Those
declarations are getting more and more verbose, so if we don't have
the 'inline' there (because it's part of the macro) then that seems a
little less cluttered.
Jakub Jelinek Aug. 7, 2024, 12:36 p.m. UTC | #5
Hi!

I'd like to ping the
https://gcc.gnu.org/pipermail/gcc-patches/2024-July/thread.html#656299
patch.

Jonathan has acked the libstdc++ side thereof (I've added the
requested #undef on my side), is the c-cppbuiltin.cc side ok for trunk?

And, shall we (incrementally or right away) add some new tree to represent
the new expressions so that constant evaluation can do the required
diagnostics?

Thanks.

On Wed, Jul 03, 2024 at 04:37:00PM +0200, Jakub Jelinek wrote:
> 2024-07-03  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.

	Jakub
Jason Merrill Aug. 7, 2024, 10:23 p.m. UTC | #6
On 7/3/24 10:37 AM, Jakub Jelinek wrote:
> Hi!
> 
> 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?

I'm not inclined to do anything about that in the short term, at least; 
the paper implies that it is only prohibited for now because they didn't 
feel like figuring out how to specify allowing it.

> Anyway, bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

> 2024-07-03  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.jj	2024-07-02 22:06:21.343875948 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2024-07-03 10:18:00.311324004 +0200
> @@ -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");
> --- gcc/testsuite/g++.dg/cpp2a/construct_at.h.jj	2024-07-02 22:06:22.138865784 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/construct_at.h	2024-07-03 10:18:00.312323991 +0200
> @@ -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; }
> --- gcc/testsuite/g++.dg/cpp26/constexpr-new1.C.jj	2024-07-03 10:18:00.312323991 +0200
> +++ gcc/testsuite/g++.dg/cpp26/constexpr-new1.C	2024-07-03 10:18:00.312323991 +0200
> @@ -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 ());
> --- gcc/testsuite/g++.dg/cpp26/constexpr-new2.C.jj	2024-07-03 10:57:14.936113640 +0200
> +++ gcc/testsuite/g++.dg/cpp26/constexpr-new2.C	2024-07-03 10:58:34.268063259 +0200
> @@ -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 ());
> --- gcc/testsuite/g++.dg/cpp26/constexpr-new3.C.jj	2024-07-03 11:03:44.848951067 +0200
> +++ gcc/testsuite/g++.dg/cpp26/constexpr-new3.C	2024-07-03 11:20:19.850776541 +0200
> @@ -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 ();
> --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2024-07-02 22:06:22.081866513 +0200
> +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2024-07-03 10:18:00.312323991 +0200
> @@ -134,8 +134,8 @@
>   
>   #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
> --- libstdc++-v3/libsupc++/new.jj	2024-01-03 12:07:51.070049086 +0100
> +++ libstdc++-v3/libsupc++/new	2024-07-03 10:36:17.728769550 +0200
> @@ -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,10 +176,18 @@ void operator delete[](void*, std::size_
>   #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; }
>   
>   // Default placement versions of operator delete.
> --- libstdc++-v3/include/bits/version.def.jj	2024-07-01 11:28:23.642225952 +0200
> +++ libstdc++-v3/include/bits/version.def	2024-07-03 10:33:56.996636092 +0200
> @@ -1814,6 +1814,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";
> --- libstdc++-v3/include/bits/version.h.jj	2024-07-01 11:28:23.643225939 +0200
> +++ libstdc++-v3/include/bits/version.h	2024-07-03 10:34:28.487052774 +0200
> @@ -2023,4 +2023,14 @@
>   #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
> 
> 	Jakub
>
diff mbox series

Patch

--- gcc/c-family/c-cppbuiltin.cc.jj	2024-07-02 22:06:21.343875948 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2024-07-03 10:18:00.311324004 +0200
@@ -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");
--- gcc/testsuite/g++.dg/cpp2a/construct_at.h.jj	2024-07-02 22:06:22.138865784 +0200
+++ gcc/testsuite/g++.dg/cpp2a/construct_at.h	2024-07-03 10:18:00.312323991 +0200
@@ -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; }
--- gcc/testsuite/g++.dg/cpp26/constexpr-new1.C.jj	2024-07-03 10:18:00.312323991 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-new1.C	2024-07-03 10:18:00.312323991 +0200
@@ -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 ());
--- gcc/testsuite/g++.dg/cpp26/constexpr-new2.C.jj	2024-07-03 10:57:14.936113640 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-new2.C	2024-07-03 10:58:34.268063259 +0200
@@ -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 ());
--- gcc/testsuite/g++.dg/cpp26/constexpr-new3.C.jj	2024-07-03 11:03:44.848951067 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-new3.C	2024-07-03 11:20:19.850776541 +0200
@@ -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 ();
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2024-07-02 22:06:22.081866513 +0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2024-07-03 10:18:00.312323991 +0200
@@ -134,8 +134,8 @@ 
 
 #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
--- libstdc++-v3/libsupc++/new.jj	2024-01-03 12:07:51.070049086 +0100
+++ libstdc++-v3/libsupc++/new	2024-07-03 10:36:17.728769550 +0200
@@ -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,10 +176,18 @@  void operator delete[](void*, std::size_
 #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; }
 
 // Default placement versions of operator delete.
--- libstdc++-v3/include/bits/version.def.jj	2024-07-01 11:28:23.642225952 +0200
+++ libstdc++-v3/include/bits/version.def	2024-07-03 10:33:56.996636092 +0200
@@ -1814,6 +1814,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";
--- libstdc++-v3/include/bits/version.h.jj	2024-07-01 11:28:23.643225939 +0200
+++ libstdc++-v3/include/bits/version.h	2024-07-03 10:34:28.487052774 +0200
@@ -2023,4 +2023,14 @@ 
 #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