Skip to content

Commit e2e7171

Browse files
yronglinchencha3
authored andcommitted
[libc++] Implement LWG3528 (make_from_tuple can perform (the equivalent of) a C-style cast) (llvm#85263)
Implement [LWG3528](https://wg21.link/LWG3528). Based on LWG3528(https://wg21.link/LWG3528) and http://eel.is/c++draft/description#structure.requirements-9, the standard allows to impose requirements, we constraint `std::make_from_tuple` to make `std::make_from_tuple` SFINAE friendly and also avoid worse diagnostic messages. We still keep the constraints of `std::__make_from_tuple_impl` so that `std::__make_from_tuple_impl` will have the same advantages when used alone. --------- Signed-off-by: yronglin <[email protected]>
1 parent 55c5cb9 commit e2e7171

File tree

3 files changed

+116
-2
lines changed

3 files changed

+116
-2
lines changed

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
`3523 <https://wg21.link/LWG3523>`__,"``iota_view::sentinel`` is not always ``iota_view``'s sentinel","June 2021","","","|ranges|"
7878
`3526 <https://wg21.link/LWG3526>`__,"Return types of ``uses_allocator_construction_args`` unspecified","June 2021","",""
7979
`3527 <https://wg21.link/LWG3527>`__,"``uses_allocator_construction_args`` handles rvalue pairs of rvalue references incorrectly","June 2021","",""
80-
`3528 <https://wg21.link/LWG3528>`__,"``make_from_tuple`` can perform (the equivalent of) a C-style cast","June 2021","",""
80+
`3528 <https://wg21.link/LWG3528>`__,"``make_from_tuple`` can perform (the equivalent of) a C-style cast","June 2021","|Complete|","19.0"
8181
`3529 <https://wg21.link/LWG3529>`__,"``priority_queue(first, last)`` should construct ``c`` with ``(first, last)``","June 2021","|Complete|","14.0"
8282
`3530 <https://wg21.link/LWG3530>`__,"``BUILTIN-PTR-MEOW`` should not opt the type out of syntactic checks","June 2021","",""
8383
`3532 <https://wg21.link/LWG3532>`__,"``split_view<V, P>::inner-iterator<true>::operator++(int)`` should depend on ``Base``","June 2021","","","|ranges|"

libcxx/include/tuple

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1377,15 +1377,41 @@ inline _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) apply(_Fn&& __f, _Tuple&&
13771377
std::forward<_Tuple>(__t),
13781378
typename __make_tuple_indices<tuple_size_v<remove_reference_t<_Tuple>>>::type{}))
13791379

1380+
#if _LIBCPP_STD_VER >= 20
13801381
template <class _Tp, class _Tuple, size_t... _Idx>
13811382
inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, __tuple_indices<_Idx...>)
1383+
noexcept(noexcept(_Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...)))
1384+
requires is_constructible_v<_Tp, decltype(std::get<_Idx>(std::forward<_Tuple>(__t)))...> {
1385+
return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...);
1386+
}
1387+
#else
1388+
template <class _Tp, class _Tuple, size_t... _Idx>
1389+
inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, __tuple_indices<_Idx...>,
1390+
enable_if_t<is_constructible_v<_Tp, decltype(std::get<_Idx>(std::forward<_Tuple>(__t)))...>> * = nullptr)
13821391
_LIBCPP_NOEXCEPT_RETURN(_Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...))
1392+
#endif // _LIBCPP_STD_VER >= 20
1393+
1394+
template <class _Tp, class _Tuple,
1395+
class _Seq = typename __make_tuple_indices<tuple_size_v<remove_reference_t<_Tuple>>>::type, class = void>
1396+
inline constexpr bool __can_make_from_tuple = false;
13831397

1398+
template <class _Tp, class _Tuple, size_t... _Idx>
1399+
inline constexpr bool __can_make_from_tuple<_Tp, _Tuple, __tuple_indices<_Idx...>,
1400+
enable_if_t<is_constructible_v<_Tp, decltype(std::get<_Idx>(std::declval<_Tuple>()))...>>> = true;
1401+
1402+
// Based on LWG3528(https://wg21.link/LWG3528) and http://eel.is/c++draft/description#structure.requirements-9,
1403+
// the standard allows to impose requirements, we constraint std::make_from_tuple to make std::make_from_tuple
1404+
// SFINAE friendly and also avoid worse diagnostic messages. We still keep the constraints of std::__make_from_tuple_impl
1405+
// so that std::__make_from_tuple_impl will have the same advantages when used alone.
1406+
#if _LIBCPP_STD_VER >= 20
13841407
template <class _Tp, class _Tuple>
1408+
requires __can_make_from_tuple<_Tp, _Tuple> // strengthen
1409+
#else
1410+
template <class _Tp, class _Tuple, class = enable_if_t<__can_make_from_tuple<_Tp, _Tuple>>> // strengthen
1411+
#endif // _LIBCPP_STD_VER >= 20
13851412
inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp make_from_tuple(_Tuple&& __t)
13861413
_LIBCPP_NOEXCEPT_RETURN(std::__make_from_tuple_impl<_Tp>(
13871414
std::forward<_Tuple>(__t), typename __make_tuple_indices<tuple_size_v<remove_reference_t<_Tuple>>>::type{}))
1388-
13891415
# undef _LIBCPP_NOEXCEPT_RETURN
13901416

13911417
# endif // _LIBCPP_STD_VER >= 17

libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,94 @@ void test_noexcept() {
195195
}
196196
}
197197

198+
namespace LWG3528 {
199+
template <class T, class Tuple>
200+
auto test_make_from_tuple(T&&, Tuple&& t) -> decltype(std::make_from_tuple<T>(t), uint8_t()) {
201+
return 0;
202+
}
203+
template <class T, class Tuple>
204+
uint32_t test_make_from_tuple(...) {
205+
return 0;
206+
}
207+
208+
template <class T, class Tuple>
209+
static constexpr bool can_make_from_tuple =
210+
std::is_same_v<decltype(test_make_from_tuple<T, Tuple>(T{}, Tuple{})), uint8_t>;
211+
212+
template <class T, class Tuple>
213+
auto test_make_from_tuple_impl(T&&, Tuple&& t)
214+
-> decltype(std::__make_from_tuple_impl<T>(
215+
t, typename std::__make_tuple_indices< std::tuple_size_v<std::remove_reference_t<Tuple>>>::type{}),
216+
uint8_t()) {
217+
return 0;
218+
}
219+
template <class T, class Tuple>
220+
uint32_t test_make_from_tuple_impl(...) {
221+
return 0;
222+
}
223+
224+
template <class T, class Tuple>
225+
static constexpr bool can_make_from_tuple_impl =
226+
std::is_same_v<decltype(test_make_from_tuple_impl<T, Tuple>(T{}, Tuple{})), uint8_t>;
227+
228+
struct A {
229+
int a;
230+
};
231+
struct B : public A {};
232+
233+
struct C {
234+
C(const B&) {}
235+
};
236+
237+
enum class D {
238+
ONE,
239+
TWO,
240+
};
241+
242+
// Test std::make_from_tuple constraints.
243+
244+
// reinterpret_cast
245+
static_assert(!can_make_from_tuple<int*, std::tuple<A*>>);
246+
static_assert(can_make_from_tuple<A*, std::tuple<A*>>);
247+
248+
// const_cast
249+
static_assert(!can_make_from_tuple<char*, std::tuple<const char*>>);
250+
static_assert(!can_make_from_tuple<volatile char*, std::tuple<const volatile char*>>);
251+
static_assert(can_make_from_tuple<volatile char*, std::tuple<volatile char*>>);
252+
static_assert(can_make_from_tuple<char*, std::tuple<char*>>);
253+
static_assert(can_make_from_tuple<const char*, std::tuple<char*>>);
254+
static_assert(can_make_from_tuple<const volatile char*, std::tuple<volatile char*>>);
255+
256+
// static_cast
257+
static_assert(!can_make_from_tuple<int, std::tuple<D>>);
258+
static_assert(!can_make_from_tuple<D, std::tuple<int>>);
259+
static_assert(can_make_from_tuple<long, std::tuple<int>>);
260+
static_assert(can_make_from_tuple<double, std::tuple<float>>);
261+
static_assert(can_make_from_tuple<float, std::tuple<double>>);
262+
263+
// Test std::__make_from_tuple_impl constraints.
264+
265+
// reinterpret_cast
266+
static_assert(!can_make_from_tuple_impl<int*, std::tuple<A*>>);
267+
static_assert(can_make_from_tuple_impl<A*, std::tuple<A*>>);
268+
269+
// const_cast
270+
static_assert(!can_make_from_tuple_impl<char*, std::tuple<const char*>>);
271+
static_assert(!can_make_from_tuple_impl<volatile char*, std::tuple<const volatile char*>>);
272+
static_assert(can_make_from_tuple_impl<volatile char*, std::tuple<volatile char*>>);
273+
static_assert(can_make_from_tuple_impl<char*, std::tuple<char*>>);
274+
static_assert(can_make_from_tuple_impl<const char*, std::tuple<char*>>);
275+
static_assert(can_make_from_tuple_impl<const volatile char*, std::tuple<volatile char*>>);
276+
277+
// static_cast
278+
static_assert(!can_make_from_tuple_impl<int, std::tuple<D>>);
279+
static_assert(!can_make_from_tuple_impl<D, std::tuple<int>>);
280+
static_assert(can_make_from_tuple_impl<long, std::tuple<int>>);
281+
static_assert(can_make_from_tuple_impl<double, std::tuple<float>>);
282+
static_assert(can_make_from_tuple_impl<float, std::tuple<double>>);
283+
284+
} // namespace LWG3528
285+
198286
int main(int, char**)
199287
{
200288
test_constexpr_construction();

0 commit comments

Comments
 (0)