Skip to content

Commit 96f154a

Browse files
Speculatively implement LWG-4273 Standard execution policy types should be conventional tag class types (#5578)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent 72d8110 commit 96f154a

File tree

5 files changed

+233
-25
lines changed

5 files changed

+233
-25
lines changed

stl/inc/execution

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,36 +73,42 @@ namespace execution {
7373
_EXPORT_STD class sequenced_policy {
7474
// indicates support for only sequential execution, and requests termination on exceptions
7575
public:
76+
explicit sequenced_policy() = default; // per LWG-4273
77+
7678
using _Standard_execution_policy = int;
7779
static constexpr bool _Parallelize = false;
7880
static constexpr bool _Ivdep = false;
7981
};
8082

81-
_EXPORT_STD inline constexpr sequenced_policy seq{/* unspecified */};
83+
_EXPORT_STD inline constexpr sequenced_policy seq{};
8284

8385
_EXPORT_STD class parallel_policy {
8486
// indicates support by element access functions for parallel execution with parallel forward progress
8587
// guarantees, and requests termination on exceptions
8688
public:
89+
explicit parallel_policy() = default; // per LWG-4273
90+
8791
using _Standard_execution_policy = int;
8892
static constexpr bool _Parallelize = true;
8993
static constexpr bool _Ivdep = true;
9094
};
9195

92-
_EXPORT_STD inline constexpr parallel_policy par{/* unspecified */};
96+
_EXPORT_STD inline constexpr parallel_policy par{};
9397

9498
_EXPORT_STD class parallel_unsequenced_policy {
9599
// indicates support by element access functions for parallel execution with weakly parallel forward progress
96100
// guarantees, and requests termination on exceptions
97101
//
98102
// (at this time, equivalent to parallel_policy)
99103
public:
104+
explicit parallel_unsequenced_policy() = default; // per LWG-4273
105+
100106
using _Standard_execution_policy = int;
101107
static constexpr bool _Parallelize = true;
102108
static constexpr bool _Ivdep = true;
103109
};
104110

105-
_EXPORT_STD inline constexpr parallel_unsequenced_policy par_unseq{/* unspecified */};
111+
_EXPORT_STD inline constexpr parallel_unsequenced_policy par_unseq{};
106112

107113
#if _HAS_CXX20
108114
_EXPORT_STD class unsequenced_policy {
@@ -112,12 +118,14 @@ namespace execution {
112118
// (at this time, equivalent to sequenced_policy except for for_each(_n), destroy(_n),
113119
// uninitialized_default_construct(_n), and uninitialized_value_construct(_n))
114120
public:
121+
explicit unsequenced_policy() = default; // per LWG-4273
122+
115123
using _Standard_execution_policy = int;
116124
static constexpr bool _Parallelize = false;
117125
static constexpr bool _Ivdep = true;
118126
};
119127

120-
_EXPORT_STD inline constexpr unsequenced_policy unseq{/* unspecified */};
128+
_EXPORT_STD inline constexpr unsequenced_policy unseq{};
121129
#endif // _HAS_CXX20
122130

123131
} // namespace execution

tests/std/test.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ tests\GH_005421_vector_algorithms_integer_class_type_iterator
268268
tests\GH_005472_do_not_overlap
269269
tests\GH_005553_regex_character_translation
270270
tests\LWG2381_num_get_floating_point
271+
tests\LWG2510_tag_classes
271272
tests\LWG2597_complex_branch_cut
272273
tests\LWG3018_shared_ptr_function
273274
tests\LWG3121_constrained_tuple_forwarding_ctor
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\usual_matrix.lst
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include <cstddef>
5+
#include <functional>
6+
#include <list>
7+
#include <memory>
8+
#include <new>
9+
#include <scoped_allocator>
10+
#include <string>
11+
#include <tuple>
12+
#include <type_traits>
13+
#include <utility>
14+
#include <vector>
15+
16+
#ifndef _M_CEE_PURE
17+
#include <future>
18+
#include <mutex>
19+
#endif // _M_CEE_PURE
20+
21+
#if _HAS_CXX17
22+
#include <execution>
23+
#endif // _HAS_CXX17
24+
25+
#if _HAS_CXX20
26+
#include <chrono>
27+
#include <format>
28+
#include <iterator>
29+
#include <ranges>
30+
#include <stop_token>
31+
#endif // _HAS_CXX20
32+
33+
#if _HAS_CXX23
34+
#include <expected>
35+
#include <generator>
36+
#include <mdspan>
37+
#include <optional>
38+
#endif // _HAS_CXX23
39+
40+
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
41+
42+
using namespace std;
43+
44+
// LWG-2510 "Tag types should not be DefaultConstructible"
45+
46+
template <class T>
47+
void check_implicit_default_construction(const T&); // not defined
48+
49+
template <class T, class = void>
50+
constexpr bool implicitly_default_constructible = false;
51+
template <class T>
52+
constexpr bool implicitly_default_constructible<T, void_t<decltype(check_implicit_default_construction<T>({}))>> = true;
53+
54+
STATIC_ASSERT(implicitly_default_constructible<int>);
55+
STATIC_ASSERT(implicitly_default_constructible<void*>);
56+
57+
STATIC_ASSERT(!implicitly_default_constructible<void>);
58+
STATIC_ASSERT(!implicitly_default_constructible<int&>);
59+
STATIC_ASSERT(!implicitly_default_constructible<void()>);
60+
61+
template <class T>
62+
void check_tag_class() { // COMPILE-ONLY
63+
STATIC_ASSERT(is_default_constructible_v<T>);
64+
STATIC_ASSERT(!implicitly_default_constructible<T>);
65+
66+
STATIC_ASSERT(is_copy_constructible_v<T>);
67+
STATIC_ASSERT(is_move_constructible_v<T>);
68+
STATIC_ASSERT(is_constructible_v<T, T&>);
69+
STATIC_ASSERT(is_constructible_v<T, const T>);
70+
STATIC_ASSERT(is_convertible_v<T&, T>);
71+
STATIC_ASSERT(is_convertible_v<const T&, T>);
72+
STATIC_ASSERT(is_convertible_v<T, T>);
73+
STATIC_ASSERT(is_convertible_v<const T, T>);
74+
STATIC_ASSERT(is_copy_assignable_v<T>);
75+
STATIC_ASSERT(is_move_assignable_v<T>);
76+
STATIC_ASSERT(is_assignable_v<T&, T&>);
77+
STATIC_ASSERT(is_assignable_v<T&, const T>);
78+
79+
STATIC_ASSERT(is_nothrow_destructible_v<T>);
80+
81+
STATIC_ASSERT(!is_polymorphic_v<T>);
82+
83+
constexpr T constant_argument;
84+
(void) constant_argument;
85+
86+
// desired properties not yet guaranteed by the Standard
87+
STATIC_ASSERT(is_nothrow_default_constructible_v<T>);
88+
STATIC_ASSERT(is_nothrow_copy_constructible_v<T>);
89+
STATIC_ASSERT(is_nothrow_move_constructible_v<T>);
90+
STATIC_ASSERT(is_nothrow_constructible_v<T, T&>);
91+
STATIC_ASSERT(is_nothrow_constructible_v<T, const T>);
92+
#if _HAS_CXX20
93+
STATIC_ASSERT(is_nothrow_convertible_v<T&, T>);
94+
STATIC_ASSERT(is_nothrow_convertible_v<const T&, T>);
95+
STATIC_ASSERT(is_nothrow_convertible_v<T, T>);
96+
STATIC_ASSERT(is_nothrow_convertible_v<const T, T>);
97+
#endif // _HAS_CXX20
98+
STATIC_ASSERT(is_nothrow_copy_assignable_v<T>);
99+
STATIC_ASSERT(is_nothrow_move_assignable_v<T>);
100+
STATIC_ASSERT(is_nothrow_assignable_v<T&, T&>);
101+
STATIC_ASSERT(is_nothrow_assignable_v<T&, const T>);
102+
103+
STATIC_ASSERT(is_trivially_default_constructible_v<T>);
104+
STATIC_ASSERT(is_trivially_copy_constructible_v<T>);
105+
STATIC_ASSERT(is_trivially_move_constructible_v<T>);
106+
STATIC_ASSERT(is_trivially_constructible_v<T, T&>);
107+
STATIC_ASSERT(is_trivially_constructible_v<T, const T>);
108+
STATIC_ASSERT(is_trivially_copy_assignable_v<T>);
109+
STATIC_ASSERT(is_trivially_move_assignable_v<T>);
110+
STATIC_ASSERT(is_trivially_assignable_v<T&, T&>);
111+
STATIC_ASSERT(is_trivially_assignable_v<T&, const T>);
112+
STATIC_ASSERT(is_trivially_destructible_v<T>);
113+
STATIC_ASSERT(is_trivially_copyable_v<T>);
114+
115+
STATIC_ASSERT(is_standard_layout_v<T>);
116+
STATIC_ASSERT(is_empty_v<T>); // only guaranteed for allocator_arg_t and piecewise_construct_t
117+
118+
// ABI-specific, seemingly agreed among mainstream implementations
119+
STATIC_ASSERT(sizeof(T) == 1);
120+
}
121+
122+
void check_standard_tags() { // COMPILE-ONLY
123+
check_tag_class<nothrow_t>();
124+
check_tag_class<allocator_arg_t>();
125+
check_tag_class<piecewise_construct_t>();
126+
#ifndef _M_CEE_PURE
127+
check_tag_class<adopt_lock_t>();
128+
check_tag_class<defer_lock_t>();
129+
check_tag_class<try_to_lock_t>();
130+
#endif // _M_CEE_PURE
131+
132+
#if _HAS_CXX17
133+
check_tag_class<in_place_t>();
134+
check_tag_class<in_place_index_t<0>>();
135+
check_tag_class<in_place_index_t<42>>();
136+
check_tag_class<in_place_index_t<static_cast<size_t>(-1)>>();
137+
check_tag_class<in_place_type_t<int>>();
138+
check_tag_class<in_place_type_t<in_place_type_t<int>>>();
139+
check_tag_class<in_place_type_t<void>>();
140+
check_tag_class<in_place_type_t<void()>>();
141+
142+
// LWG-4273 "Standard execution policy types should be conventional tag class types"
143+
check_tag_class<execution::sequenced_policy>();
144+
check_tag_class<execution::parallel_policy>();
145+
check_tag_class<execution::parallel_unsequenced_policy>();
146+
#endif // _HAS_CXX17
147+
148+
#if _HAS_CXX20
149+
check_tag_class<destroying_delete_t>();
150+
check_tag_class<nostopstate_t>();
151+
check_tag_class<chrono::last_spec>();
152+
153+
// LWG-4273 "Standard execution policy types should be conventional tag class types"
154+
check_tag_class<execution::unsequenced_policy>();
155+
#endif // _HAS_CXX20
156+
157+
#if _HAS_CXX23
158+
check_tag_class<from_range_t>();
159+
check_tag_class<unexpect_t>();
160+
// TODO: Cover sorted_equivalent_t and sorted_unique_t once flat_meow are implemented.
161+
#endif // _HAS_CXX23
162+
163+
// TODO: Update to cover newly introduced tag class types.
164+
}
165+
166+
// We intentionally implement internal disambiguation tag types like standard ones.
167+
void check_implementation_details() { // COMPILE-ONLY
168+
// TODO: Synchronize the check list with actual implementation details.
169+
check_tag_class<_Alloc_exact_args_t>();
170+
check_tag_class<_Alloc_unpack_tuple_t>();
171+
check_tag_class<_Exact_args_t>();
172+
check_tag_class<_Leave_proxy_unbound>();
173+
check_tag_class<_Move_allocator_tag>();
174+
check_tag_class<_One_then_variadic_args_t>();
175+
check_tag_class<_String_constructor_concat_tag>();
176+
check_tag_class<_Secret_copyability_ignoring_tag>();
177+
check_tag_class<_Secret_scoped_allocator_construct_tag>();
178+
check_tag_class<_Unforced>();
179+
check_tag_class<_Unpack_tuple_t>();
180+
check_tag_class<_Value_init_tag>();
181+
check_tag_class<_Zero_then_variadic_args_t>();
182+
#ifndef _M_CEE_PURE
183+
check_tag_class<_From_raw_state_tag>();
184+
#endif // _M_CEE_PURE
185+
186+
#if _HAS_CXX17
187+
check_tag_class<_No_init_tag>();
188+
check_tag_class<_Not_fn_tag>();
189+
#endif // _HAS_CXX17
190+
191+
#if _HAS_CXX20
192+
check_tag_class<_Auto_id_tag>();
193+
check_tag_class<_For_overwrite_tag>();
194+
check_tag_class<_Variantish_empty_tag>();
195+
check_tag_class<chrono::_Secret_time_zone_construct_tag>();
196+
check_tag_class<chrono::_Secret_time_zone_link_construct_tag>();
197+
check_tag_class<chrono::_Secret_tzdb_list_construct_tag>();
198+
check_tag_class<ranges::_Construct_tag>();
199+
#endif // _HAS_CXX20
200+
201+
#if _HAS_CXX23
202+
check_tag_class<_Construct_expected_from_invoke_result_tag>();
203+
check_tag_class<_Construct_from_invoke_result_tag>();
204+
check_tag_class<_Extents_from_tuple>();
205+
check_tag_class<_Gen_detail::_Secret_tag>();
206+
#endif // _HAS_CXX23
207+
}

tests/std/tests/VSO_0000000_more_pair_tuple_sfinae/test.cpp

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33

44
#include <cassert>
55
#include <memory>
6-
#ifndef _M_CEE_PURE
7-
#include <mutex>
8-
#endif // _M_CEE_PURE
9-
#include <new>
106
#include <string>
117
#include <tuple>
128
#include <type_traits>
@@ -42,39 +38,31 @@ STATIC_ASSERT(!is_constructible_v<tuple<NoDefault, NoDefault>, allocator_arg_t,
4238

4339

4440
// LWG-2510 "Tag types should not be DefaultConstructible"
45-
template <typename T, typename = void>
46-
struct IsImplicitlyDefaultConstructible : false_type {};
41+
template <class T, class = void>
42+
constexpr bool implicitly_default_constructible = false;
4743

48-
template <typename T>
49-
void ImplicitlyDefaultConstruct(const T&);
44+
template <class T>
45+
void check_implicit_default_construction(const T&); // not defined
5046

51-
template <typename T>
52-
struct IsImplicitlyDefaultConstructible<T, void_t<decltype(ImplicitlyDefaultConstruct<T>({}))>> : true_type {};
47+
template <class T>
48+
constexpr bool implicitly_default_constructible<T, void_t<decltype(check_implicit_default_construction<T>({}))>> = true;
5349

5450
struct ExplicitDefault {
5551
explicit ExplicitDefault() = default;
5652
};
5753

5854
template <typename T>
59-
constexpr bool OrdinaryDC = is_default_constructible_v<T> && IsImplicitlyDefaultConstructible<T>::value;
55+
constexpr bool OrdinaryDC = is_default_constructible_v<T> && implicitly_default_constructible<T>;
6056

6157
template <typename T>
62-
constexpr bool ExplicitDC = is_default_constructible_v<T> && !IsImplicitlyDefaultConstructible<T>::value;
58+
constexpr bool ExplicitDC = is_default_constructible_v<T> && !implicitly_default_constructible<T>;
6359

6460
template <typename T>
65-
constexpr bool VerbotenDC = !is_default_constructible_v<T> && !IsImplicitlyDefaultConstructible<T>::value;
61+
constexpr bool VerbotenDC = !is_default_constructible_v<T> && !implicitly_default_constructible<T>;
6662

6763
STATIC_ASSERT(OrdinaryDC<int>);
6864
STATIC_ASSERT(VerbotenDC<NoDefault>);
6965
STATIC_ASSERT(ExplicitDC<ExplicitDefault>);
70-
STATIC_ASSERT(ExplicitDC<nothrow_t>);
71-
STATIC_ASSERT(ExplicitDC<piecewise_construct_t>);
72-
STATIC_ASSERT(ExplicitDC<allocator_arg_t>);
73-
#ifndef _M_CEE_PURE
74-
STATIC_ASSERT(ExplicitDC<defer_lock_t>);
75-
STATIC_ASSERT(ExplicitDC<try_to_lock_t>);
76-
STATIC_ASSERT(ExplicitDC<adopt_lock_t>);
77-
#endif // _M_CEE_PURE
7866

7967
using Expl = ExplicitDefault;
8068
using NOPE = NoDefault;

0 commit comments

Comments
 (0)