On Wed, 3 Jul 2024 at 15:37, Jakub Jelinek <ja...@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 <ja...@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 >