Skip to content

Commit 3d41600

Browse files
[libc++] Fix tuple_cat for element with unconstrained constructor
Currently, when the result type is 1-`tuple`, `tuple_cat` possibly tests an undesired constructor of the element, due to conversion from the reference tuple to the result type. If the element type has an unconstrained constructor template, there can be extraneous hard error which shouldn't happen. This patch introduces a helper function template `__tuple_cat_select_element_wise` to select the element-wise constructor template of `tuple`, which can avoid such error.
1 parent 98e5962 commit 3d41600

File tree

2 files changed

+89
-4
lines changed

2 files changed

+89
-4
lines changed

libcxx/include/tuple

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,12 +1338,25 @@ struct __tuple_cat<tuple<_Types...>, __tuple_indices<_I0...>, __tuple_indices<_J
13381338
}
13391339
};
13401340

1341+
template <class _TupleDst, class _TupleSrc, size_t... _Indices>
1342+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _TupleDst
1343+
__tuple_cat_select_element_wise(_TupleSrc&& __src, __tuple_indices<_Indices...>) {
1344+
static_assert(tuple_size<_TupleDst>::value == tuple_size<_TupleSrc>::value,
1345+
"misuse of __tuple_cat_select_element_wise with tuples of different sizes");
1346+
return _TupleDst(std::get<_Indices>(std::forward<_TupleSrc>(__src))...);
1347+
}
1348+
13411349
template <class _Tuple0, class... _Tuples>
13421350
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 typename __tuple_cat_return<_Tuple0, _Tuples...>::type
13431351
tuple_cat(_Tuple0&& __t0, _Tuples&&... __tpls) {
1344-
using _T0 _LIBCPP_NODEBUG = __libcpp_remove_reference_t<_Tuple0>;
1345-
return __tuple_cat<tuple<>, __tuple_indices<>, typename __make_tuple_indices<tuple_size<_T0>::value>::type>()(
1346-
tuple<>(), std::forward<_Tuple0>(__t0), std::forward<_Tuples>(__tpls)...);
1352+
using _T0 _LIBCPP_NODEBUG = __libcpp_remove_reference_t<_Tuple0>;
1353+
using _TRet _LIBCPP_NODEBUG = typename __tuple_cat_return<_Tuple0, _Tuples...>::type;
1354+
using _T0Indices _LIBCPP_NODEBUG = typename __make_tuple_indices<tuple_size<_T0>::value>::type;
1355+
using _TRetIndices _LIBCPP_NODEBUG = typename __make_tuple_indices<tuple_size<_TRet>::value>::type;
1356+
return std::__tuple_cat_select_element_wise<_TRet>(
1357+
__tuple_cat<tuple<>, __tuple_indices<>, _T0Indice>()(
1358+
tuple<>(), std::forward<_Tuple0>(__t0), std::forward<_Tuples>(__tpls)...),
1359+
_TRetIndice());
13471360
}
13481361

13491362
template <class... _Tp, class _Alloc>

libcxx/test/std/utilities/tuple/tuple.tuple/tuple.creation/tuple_cat.pass.cpp

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,70 @@ template<typename ...Ts>
3131
void forward_as_tuple(Ts...) = delete;
3232
}
3333

34+
// https://github.com/llvm/llvm-project/issues/41034
35+
struct Unconstrained {
36+
int data;
37+
template <typename Arg>
38+
TEST_CONSTEXPR_CXX14 Unconstrained(Arg arg) : data(arg) {}
39+
};
40+
41+
TEST_CONSTEXPR_CXX14 std::tuple<Unconstrained> test_cat_unary_lvalue() {
42+
auto tup = std::tuple<Unconstrained>(Unconstrained(5));
43+
return std::tuple_cat(tup);
44+
}
45+
46+
TEST_CONSTEXPR_CXX14 std::tuple<Unconstrained> test_cat_unary_rvalue() {
47+
return std::tuple_cat(std::tuple<Unconstrained>(Unconstrained(6)));
48+
}
49+
50+
TEST_CONSTEXPR_CXX14 std::tuple<Unconstrained> test_cat_unary_and_nullary() {
51+
return std::tuple_cat(std::tuple<Unconstrained>(Unconstrained(7)), std::tuple<>());
52+
}
53+
54+
#if TEST_STD_VER >= 17
55+
constexpr auto test_cat_unary_lvalue_ctad() {
56+
auto tup = std::tuple(Unconstrained(8));
57+
return std::tuple_cat(tup);
58+
}
59+
60+
constexpr auto test_cat_unary_rvalue_ctad() { return std::tuple_cat(std::tuple(Unconstrained(9))); }
61+
62+
constexpr auto test_cat_unary_and_nullary_ctad() { return std::tuple_cat(std::tuple(Unconstrained(10)), std::tuple()); }
63+
#endif
64+
65+
TEST_CONSTEXPR_CXX14 bool test_tuple_cat_with_unconstrained_constructor() {
66+
{
67+
auto tup = test_cat_unary_lvalue();
68+
assert(std::get<0>(tup).data == 5);
69+
}
70+
{
71+
auto tup = test_cat_unary_rvalue();
72+
assert(std::get<0>(tup).data == 6);
73+
}
74+
{
75+
auto tup = test_cat_unary_and_nullary();
76+
assert(std::get<0>(tup).data == 7);
77+
}
78+
#if TEST_STD_VER >= 17
79+
{
80+
auto tup = test_cat_unary_lvalue_ctad();
81+
ASSERT_SAME_TYPE(decltype(tup), std::tuple<Unconstrained>);
82+
assert(std::get<0>(tup).data == 8);
83+
}
84+
{
85+
auto tup = test_cat_unary_rvalue_ctad();
86+
ASSERT_SAME_TYPE(decltype(tup), std::tuple<Unconstrained>);
87+
assert(std::get<0>(tup).data == 9);
88+
}
89+
{
90+
auto tup = test_cat_unary_and_nullary_ctad();
91+
ASSERT_SAME_TYPE(decltype(tup), std::tuple<Unconstrained>);
92+
assert(std::get<0>(tup).data == 10);
93+
}
94+
#endif
95+
return true;
96+
}
97+
3498
int main(int, char**)
3599
{
36100
{
@@ -270,5 +334,13 @@ int main(int, char**)
270334
assert(std::get<0>(t).i == 1);
271335
assert(std::get<0>(t2).i == 1);
272336
}
273-
return 0;
337+
// See https://github.com/llvm/llvm-project/issues/41034
338+
{
339+
test_tuple_cat_with_unconstrained_constructor();
340+
#if TEST_STD_VER >= 14
341+
static_assert(test_tuple_cat_with_unconstrained_constructor(), "");
342+
#endif
343+
}
344+
345+
return 0;
274346
}

0 commit comments

Comments
 (0)