Skip to content

Commit afa3a4a

Browse files
committed
c++, libstdc++: Implement C++26 P2747R2 - constexpr placement new [PR115744]
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 <[email protected]> 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.
1 parent e3a6dec commit afa3a4a

File tree

9 files changed

+235
-6
lines changed

9 files changed

+235
-6
lines changed

gcc/c-family/c-cppbuiltin.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,7 @@ c_cpp_builtins (cpp_reader *pfile)
10911091
if (cxx_dialect > cxx23)
10921092
{
10931093
/* Set feature test macros for C++26. */
1094-
cpp_define (pfile, "__cpp_constexpr=202306L");
1094+
cpp_define (pfile, "__cpp_constexpr=202406L");
10951095
cpp_define (pfile, "__cpp_static_assert=202306L");
10961096
cpp_define (pfile, "__cpp_placeholder_variables=202306L");
10971097
cpp_define (pfile, "__cpp_structured_bindings=202403L");
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// C++26 P2747R2 - constexpr placement new
2+
// { dg-do compile { target c++26 } }
3+
4+
#include "../cpp2a/construct_at.h"
5+
6+
struct S {
7+
constexpr S () : a (42), b (43) {}
8+
constexpr S (int c, int d) : a (c), b (d) {}
9+
int a, b;
10+
};
11+
struct T {
12+
int a, b;
13+
};
14+
15+
constexpr bool
16+
foo ()
17+
{
18+
std::allocator<int> a;
19+
auto b = a.allocate (3);
20+
::new (b) int ();
21+
::new (b + 1) int (1);
22+
::new (b + 2) int {2};
23+
if (b[0] != 0 || b[1] != 1 || b[2] != 2)
24+
return false;
25+
a.deallocate (b, 3);
26+
std::allocator<S> c;
27+
auto d = c.allocate (4);
28+
::new (d) S;
29+
::new (d + 1) S ();
30+
::new (d + 2) S (7, 8);
31+
::new (d + 3) S { 9, 10 };
32+
if (d[0].a != 42 || d[0].b != 43
33+
|| d[1].a != 42 || d[1].b != 43
34+
|| d[2].a != 7 || d[2].b != 8
35+
|| d[3].a != 9 || d[3].b != 10)
36+
return false;
37+
d[0].~S ();
38+
d[1].~S ();
39+
d[2].~S ();
40+
d[3].~S ();
41+
c.deallocate (d, 4);
42+
std::allocator<T> e;
43+
auto f = e.allocate (3);
44+
::new (f) T ();
45+
::new (f + 1) T (7, 8);
46+
::new (f + 2) T { .a = 9, .b = 10 };
47+
if (f[0].a != 0 || f[0].b != 0
48+
|| f[1].a != 7 || f[1].b != 8
49+
|| f[2].a != 9 || f[2].b != 10)
50+
return false;
51+
f[0].~T ();
52+
f[1].~T ();
53+
f[2].~T ();
54+
e.deallocate (f, 3);
55+
auto g = a.allocate (3);
56+
new (g) int[] {1, 2, 3};
57+
if (g[0] != 1 || g[1] != 2 || g[2] != 3)
58+
return false;
59+
new (g) int[] {4, 5};
60+
if (g[0] != 4 || g[1] != 5)
61+
return false;
62+
a.deallocate (g, 3);
63+
return true;
64+
}
65+
66+
static_assert (foo ());
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// C++26 P2747R2 - constexpr placement new
2+
// { dg-do compile { target c++26 } }
3+
4+
#include <memory>
5+
#include <new>
6+
7+
#ifndef __cpp_lib_constexpr_new
8+
# error "__cpp_lib_constexpr_new"
9+
#elif __cpp_lib_constexpr_new < 202406L
10+
# error "__cpp_lib_constexpr_new < 202406"
11+
#endif
12+
13+
struct S {
14+
constexpr S () : a (42), b (43) {}
15+
constexpr S (int c, int d) : a (c), b (d) {}
16+
int a, b;
17+
};
18+
struct T {
19+
int a, b;
20+
};
21+
22+
constexpr bool
23+
foo ()
24+
{
25+
std::allocator<int> a;
26+
auto b = a.allocate (3);
27+
::new (b) int ();
28+
::new (b + 1) int (1);
29+
::new (b + 2) int {2};
30+
if (b[0] != 0 || b[1] != 1 || b[2] != 2)
31+
return false;
32+
a.deallocate (b, 3);
33+
std::allocator<S> c;
34+
auto d = c.allocate (4);
35+
::new (d) S;
36+
::new (d + 1) S ();
37+
::new (d + 2) S (7, 8);
38+
::new (d + 3) S { 9, 10 };
39+
if (d[0].a != 42 || d[0].b != 43
40+
|| d[1].a != 42 || d[1].b != 43
41+
|| d[2].a != 7 || d[2].b != 8
42+
|| d[3].a != 9 || d[3].b != 10)
43+
return false;
44+
d[0].~S ();
45+
d[1].~S ();
46+
d[2].~S ();
47+
d[3].~S ();
48+
c.deallocate (d, 4);
49+
std::allocator<T> e;
50+
auto f = e.allocate (3);
51+
::new (f) T ();
52+
::new (f + 1) T (7, 8);
53+
::new (f + 2) T { .a = 9, .b = 10 };
54+
if (f[0].a != 0 || f[0].b != 0
55+
|| f[1].a != 7 || f[1].b != 8
56+
|| f[2].a != 9 || f[2].b != 10)
57+
return false;
58+
f[0].~T ();
59+
f[1].~T ();
60+
f[2].~T ();
61+
e.deallocate (f, 3);
62+
auto g = a.allocate (3);
63+
new (g) int[] {1, 2, 3};
64+
if (g[0] != 1 || g[1] != 2 || g[2] != 3)
65+
return false;
66+
new (g) int[] {4, 5};
67+
if (g[0] != 4 || g[1] != 5)
68+
return false;
69+
a.deallocate (g, 3);
70+
return true;
71+
}
72+
73+
static_assert (foo ());
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// C++26 P2747R2 - constexpr placement new
2+
// { dg-do compile { target c++26 } }
3+
4+
#include "../cpp2a/construct_at.h"
5+
6+
struct S {
7+
constexpr S () : a (42), b (43) {}
8+
constexpr S (int c, int d) : a (c), b (d) {}
9+
int a, b;
10+
};
11+
struct T {
12+
int a, b;
13+
};
14+
15+
constexpr bool
16+
foo ()
17+
{
18+
std::allocator<int> a;
19+
auto b = a.allocate (3);
20+
new (b + 1) int[] {2, 3}; // { dg-error "" "" { xfail *-*-* } }
21+
a.deallocate (b, 3);
22+
return true;
23+
}
24+
25+
constexpr bool
26+
bar ()
27+
{
28+
std::allocator<int> a;
29+
auto b = a.allocate (3);
30+
new (b) int[] {1, 2, 3, 4}; // { dg-error "array subscript value '3' is outside the bounds of array 'heap ' of type 'int \\\[3\\\]'" }
31+
a.deallocate (b, 3);
32+
return true;
33+
}
34+
35+
constexpr bool
36+
baz ()
37+
{
38+
std::allocator<int> a;
39+
auto b = a.allocate (2);
40+
new (b) long (42); // { dg-error "accessing value of 'heap ' through a 'long int' glvalue in a constant expression" }
41+
a.deallocate (b, 2);
42+
return true;
43+
}
44+
45+
constexpr bool a = foo ();
46+
constexpr bool b = bar ();
47+
constexpr bool c = baz ();

gcc/testsuite/g++.dg/cpp26/feat-cxx26.C

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@
134134

135135
#ifndef __cpp_constexpr
136136
# error "__cpp_constexpr"
137-
#elif __cpp_constexpr != 202306L
138-
# error "__cpp_constexpr != 202306L"
137+
#elif __cpp_constexpr != 202406L
138+
# error "__cpp_constexpr != 202406L"
139139
#endif
140140

141141
#ifndef __cpp_decltype_auto

gcc/testsuite/g++.dg/cpp2a/construct_at.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,18 @@ namespace std
5858
{ l->~T (); }
5959
}
6060

61-
inline void *operator new (std::size_t, void *p) noexcept
61+
#if __cpp_constexpr >= 202406L
62+
constexpr
63+
#else
64+
inline
65+
#endif
66+
void *operator new (std::size_t, void *p) noexcept
67+
{ return p; }
68+
69+
#if __cpp_constexpr >= 202406L
70+
constexpr
71+
#else
72+
inline
73+
#endif
74+
void *operator new[] (std::size_t, void *p) noexcept
6275
{ return p; }

libstdc++-v3/include/bits/version.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,15 @@ ftms = {
18381838
};
18391839
};
18401840

1841+
ftms = {
1842+
name = constexpr_new;
1843+
values = {
1844+
v = 202406;
1845+
cxxmin = 26;
1846+
extra_cond = "__cpp_constexpr >= 202406L";
1847+
};
1848+
};
1849+
18411850
// Standard test specifications.
18421851
stds[97] = ">= 199711L";
18431852
stds[03] = ">= 199711L";

libstdc++-v3/include/bits/version.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,4 +2033,14 @@
20332033
#endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */
20342034
#undef __glibcxx_want_ranges_concat
20352035

2036+
#if !defined(__cpp_lib_constexpr_new)
2037+
# if (__cplusplus > 202302L) && (__cpp_constexpr >= 202406L)
2038+
# define __glibcxx_constexpr_new 202406L
2039+
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_new)
2040+
# define __cpp_lib_constexpr_new 202406L
2041+
# endif
2042+
# endif
2043+
#endif /* !defined(__cpp_lib_constexpr_new) && defined(__glibcxx_want_constexpr_new) */
2044+
#undef __glibcxx_want_constexpr_new
2045+
20362046
#undef __glibcxx_want_all

libstdc++-v3/libsupc++/new

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#define __glibcxx_want_launder
4444
#define __glibcxx_want_hardware_interference_size
4545
#define __glibcxx_want_destroying_delete
46+
#define __glibcxx_want_constexpr_new
4647
#include <bits/version.h>
4748

4849
#pragma GCC visibility push(default)
@@ -175,12 +176,22 @@ void operator delete[](void*, std::size_t, std::align_val_t)
175176
#endif // __cpp_sized_deallocation
176177
#endif // __cpp_aligned_new
177178

179+
#if __cpp_lib_constexpr_new >= 202406L
180+
# define _GLIBCXX_PLACEMENT_CONSTEXPR constexpr
181+
#else
182+
# define _GLIBCXX_PLACEMENT_CONSTEXPR inline
183+
#endif
184+
178185
// Default placement versions of operator new.
179-
_GLIBCXX_NODISCARD inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
186+
_GLIBCXX_NODISCARD _GLIBCXX_PLACEMENT_CONSTEXPR
187+
void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
180188
{ return __p; }
181-
_GLIBCXX_NODISCARD inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
189+
_GLIBCXX_NODISCARD _GLIBCXX_PLACEMENT_CONSTEXPR
190+
void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
182191
{ return __p; }
183192

193+
#undef _GLIBCXX_PLACEMENT_CONSTEXPR
194+
184195
// Default placement versions of operator delete.
185196
inline void operator delete (void*, void*) _GLIBCXX_USE_NOEXCEPT { }
186197
inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { }

0 commit comments

Comments
 (0)