diff options
author | Jakub Jelinek <jakub@redhat.com> | 2024-08-08 11:05:36 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2024-08-08 11:07:29 +0200 |
commit | afa3a4a52cf91485477e4aaa5f05987ec7ff869d (patch) | |
tree | 28eefc7d2a4da71312abcb0e0eecc05ac8e6af96 | |
parent | libgomp.c++/static-aggr-constructor-destructor-{1,2}.C: Fix scan-tree-dump (diff) |
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 <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.
-rw-r--r-- | gcc/c-family/c-cppbuiltin.cc | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp26/constexpr-new1.C | 66 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp26/constexpr-new2.C | 73 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp26/constexpr-new3.C | 47 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/construct_at.h | 15 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/version.def | 9 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/version.h | 10 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/new | 15 |
9 files changed, 235 insertions, 6 deletions
diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index a80372c89914..97c3ecb7d161 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc | |||
@@ -1091,7 +1091,7 @@ c_cpp_builtins (cpp_reader *pfile) | |||
1091 | if (cxx_dialect > cxx23) | 1091 | if (cxx_dialect > cxx23) |
1092 | { | 1092 | { |
1093 | /* Set feature test macros for C++26. */ | 1093 | /* Set feature test macros for C++26. */ |
1094 | cpp_define (pfile, "__cpp_constexpr=202306L"); | 1094 | cpp_define (pfile, "__cpp_constexpr=202406L"); |
1095 | cpp_define (pfile, "__cpp_static_assert=202306L"); | 1095 | cpp_define (pfile, "__cpp_static_assert=202306L"); |
1096 | cpp_define (pfile, "__cpp_placeholder_variables=202306L"); | 1096 | cpp_define (pfile, "__cpp_placeholder_variables=202306L"); |
1097 | cpp_define (pfile, "__cpp_structured_bindings=202403L"); | 1097 | 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 index 000000000000..131b718efa5b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-new1.C | |||
@@ -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 ()); | ||
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-new2.C b/gcc/testsuite/g++.dg/cpp26/constexpr-new2.C new file mode 100644 index 000000000000..e33318542537 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-new2.C | |||
@@ -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 ()); | ||
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-new3.C b/gcc/testsuite/g++.dg/cpp26/constexpr-new3.C new file mode 100644 index 000000000000..6a06a6e6f32e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-new3.C | |||
@@ -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 (); | ||
diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index 8a5fd64d3960..82be39c996f2 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | |||
@@ -134,8 +134,8 @@ | |||
134 | 134 | ||
135 | #ifndef __cpp_constexpr | 135 | #ifndef __cpp_constexpr |
136 | # error "__cpp_constexpr" | 136 | # error "__cpp_constexpr" |
137 | #elif __cpp_constexpr != 202306L | 137 | #elif __cpp_constexpr != 202406L |
138 | # error "__cpp_constexpr != 202306L" | 138 | # error "__cpp_constexpr != 202406L" |
139 | #endif | 139 | #endif |
140 | 140 | ||
141 | #ifndef __cpp_decltype_auto | 141 | #ifndef __cpp_decltype_auto |
diff --git a/gcc/testsuite/g++.dg/cpp2a/construct_at.h b/gcc/testsuite/g++.dg/cpp2a/construct_at.h index 27e92cbb28c5..ed6165610424 100644 --- a/gcc/testsuite/g++.dg/cpp2a/construct_at.h +++ b/gcc/testsuite/g++.dg/cpp2a/construct_at.h | |||
@@ -58,5 +58,18 @@ namespace std | |||
58 | { l->~T (); } | 58 | { l->~T (); } |
59 | } | 59 | } |
60 | 60 | ||
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 | ||
62 | { return p; } | 75 | { return p; } |
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index ee0a9e45c441..6791c6f6f936 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def | |||
@@ -1838,6 +1838,15 @@ ftms = { | |||
1838 | }; | 1838 | }; |
1839 | }; | 1839 | }; |
1840 | 1840 | ||
1841 | ftms = { | ||
1842 | name = constexpr_new; | ||
1843 | values = { | ||
1844 | v = 202406; | ||
1845 | cxxmin = 26; | ||
1846 | extra_cond = "__cpp_constexpr >= 202406L"; | ||
1847 | }; | ||
1848 | }; | ||
1849 | |||
1841 | // Standard test specifications. | 1850 | // Standard test specifications. |
1842 | stds[97] = ">= 199711L"; | 1851 | stds[97] = ">= 199711L"; |
1843 | stds[03] = ">= 199711L"; | 1852 | stds[03] = ">= 199711L"; |
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index cee497d7443f..0b78cb94ecc9 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h | |||
@@ -2033,4 +2033,14 @@ | |||
2033 | #endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */ | 2033 | #endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */ |
2034 | #undef __glibcxx_want_ranges_concat | 2034 | #undef __glibcxx_want_ranges_concat |
2035 | 2035 | ||
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 | |||
2036 | #undef __glibcxx_want_all | 2046 | #undef __glibcxx_want_all |
diff --git a/libstdc++-v3/libsupc++/new b/libstdc++-v3/libsupc++/new index 8b07150fb949..2e2038e1a82c 100644 --- a/libstdc++-v3/libsupc++/new +++ b/libstdc++-v3/libsupc++/new | |||
@@ -43,6 +43,7 @@ | |||
43 | #define __glibcxx_want_launder | 43 | #define __glibcxx_want_launder |
44 | #define __glibcxx_want_hardware_interference_size | 44 | #define __glibcxx_want_hardware_interference_size |
45 | #define __glibcxx_want_destroying_delete | 45 | #define __glibcxx_want_destroying_delete |
46 | #define __glibcxx_want_constexpr_new | ||
46 | #include <bits/version.h> | 47 | #include <bits/version.h> |
47 | 48 | ||
48 | #pragma GCC visibility push(default) | 49 | #pragma GCC visibility push(default) |
@@ -175,12 +176,22 @@ void operator delete[](void*, std::size_t, std::align_val_t) | |||
175 | #endif // __cpp_sized_deallocation | 176 | #endif // __cpp_sized_deallocation |
176 | #endif // __cpp_aligned_new | 177 | #endif // __cpp_aligned_new |
177 | 178 | ||
179 | #if __cpp_lib_constexpr_new >= 202406L | ||
180 | # define _GLIBCXX_PLACEMENT_CONSTEXPR constexpr | ||
181 | #else | ||
182 | # define _GLIBCXX_PLACEMENT_CONSTEXPR inline | ||
183 | #endif | ||
184 | |||
178 | // Default placement versions of operator new. | 185 | // 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 | ||
180 | { return __p; } | 188 | { 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 | ||
182 | { return __p; } | 191 | { return __p; } |
183 | 192 | ||
193 | #undef _GLIBCXX_PLACEMENT_CONSTEXPR | ||
194 | |||
184 | // Default placement versions of operator delete. | 195 | // Default placement versions of operator delete. |
185 | inline void operator delete (void*, void*) _GLIBCXX_USE_NOEXCEPT { } | 196 | inline void operator delete (void*, void*) _GLIBCXX_USE_NOEXCEPT { } |
186 | inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { } | 197 | inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { } |