15a83710eSEric Fiselier //===----------------------------------------------------------------------===//
25a83710eSEric Fiselier //
357b08b09SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
457b08b09SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
557b08b09SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65a83710eSEric Fiselier //
75a83710eSEric Fiselier //===----------------------------------------------------------------------===//
85a83710eSEric Fiselier
95a83710eSEric Fiselier // <tuple>
105a83710eSEric Fiselier
115a83710eSEric Fiselier // template <class... Types> class tuple;
125a83710eSEric Fiselier
135a83710eSEric Fiselier // tuple& operator=(tuple&& u);
145a83710eSEric Fiselier
1531cbe0f2SLouis Dionne // UNSUPPORTED: c++03
160a52cd79SEric Fiselier
175a127cdcSMarshall Clow #include <memory>
185a83710eSEric Fiselier #include <tuple>
19e21582e7SMarshall Clow #include <utility>
205a83710eSEric Fiselier #include <cassert>
215a83710eSEric Fiselier
227fc6a556SMarshall Clow #include "test_macros.h"
23949389c3SMarshall Clow #include "MoveOnly.h"
245a83710eSEric Fiselier
25aedcbf89SEric Fiselier struct NonAssignable {
26aedcbf89SEric Fiselier NonAssignable& operator=(NonAssignable const&) = delete;
27aedcbf89SEric Fiselier NonAssignable& operator=(NonAssignable&&) = delete;
28aedcbf89SEric Fiselier };
29aedcbf89SEric Fiselier struct CopyAssignable {
30aedcbf89SEric Fiselier CopyAssignable& operator=(CopyAssignable const&) = default;
31aedcbf89SEric Fiselier CopyAssignable& operator=(CopyAssignable&&) = delete;
32aedcbf89SEric Fiselier };
33aedcbf89SEric Fiselier static_assert(std::is_copy_assignable<CopyAssignable>::value, "");
34aedcbf89SEric Fiselier struct MoveAssignable {
35aedcbf89SEric Fiselier MoveAssignable& operator=(MoveAssignable const&) = delete;
36aedcbf89SEric Fiselier MoveAssignable& operator=(MoveAssignable&&) = default;
37aedcbf89SEric Fiselier };
38a0839b14SLouis Dionne struct NothrowMoveAssignable {
operator =NothrowMoveAssignable39a0839b14SLouis Dionne NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; }
40a0839b14SLouis Dionne };
41a0839b14SLouis Dionne struct PotentiallyThrowingMoveAssignable {
operator =PotentiallyThrowingMoveAssignable42a0839b14SLouis Dionne PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; }
43a0839b14SLouis Dionne };
44aedcbf89SEric Fiselier
45aedcbf89SEric Fiselier struct CountAssign {
46aedcbf89SEric Fiselier static int copied;
47aedcbf89SEric Fiselier static int moved;
resetCountAssign48aedcbf89SEric Fiselier static void reset() { copied = moved = 0; }
49aedcbf89SEric Fiselier CountAssign() = default;
operator =CountAssign50aedcbf89SEric Fiselier CountAssign& operator=(CountAssign const&) { ++copied; return *this; }
operator =CountAssign51aedcbf89SEric Fiselier CountAssign& operator=(CountAssign&&) { ++moved; return *this; }
52aedcbf89SEric Fiselier };
53aedcbf89SEric Fiselier int CountAssign::copied = 0;
54aedcbf89SEric Fiselier int CountAssign::moved = 0;
55aedcbf89SEric Fiselier
56*06e2b737SArthur O'Dwyer TEST_CONSTEXPR_CXX20
test()57*06e2b737SArthur O'Dwyer bool test()
585a83710eSEric Fiselier {
595a83710eSEric Fiselier {
605a83710eSEric Fiselier typedef std::tuple<> T;
615a83710eSEric Fiselier T t0;
625a83710eSEric Fiselier T t;
635a83710eSEric Fiselier t = std::move(t0);
645a83710eSEric Fiselier }
655a83710eSEric Fiselier {
665a83710eSEric Fiselier typedef std::tuple<MoveOnly> T;
675a83710eSEric Fiselier T t0(MoveOnly(0));
685a83710eSEric Fiselier T t;
695a83710eSEric Fiselier t = std::move(t0);
705a83710eSEric Fiselier assert(std::get<0>(t) == 0);
715a83710eSEric Fiselier }
725a83710eSEric Fiselier {
735a83710eSEric Fiselier typedef std::tuple<MoveOnly, MoveOnly> T;
745a83710eSEric Fiselier T t0(MoveOnly(0), MoveOnly(1));
755a83710eSEric Fiselier T t;
765a83710eSEric Fiselier t = std::move(t0);
775a83710eSEric Fiselier assert(std::get<0>(t) == 0);
785a83710eSEric Fiselier assert(std::get<1>(t) == 1);
795a83710eSEric Fiselier }
805a83710eSEric Fiselier {
815a83710eSEric Fiselier typedef std::tuple<MoveOnly, MoveOnly, MoveOnly> T;
825a83710eSEric Fiselier T t0(MoveOnly(0), MoveOnly(1), MoveOnly(2));
835a83710eSEric Fiselier T t;
845a83710eSEric Fiselier t = std::move(t0);
855a83710eSEric Fiselier assert(std::get<0>(t) == 0);
865a83710eSEric Fiselier assert(std::get<1>(t) == 1);
875a83710eSEric Fiselier assert(std::get<2>(t) == 2);
885a83710eSEric Fiselier }
89aedcbf89SEric Fiselier {
90aedcbf89SEric Fiselier // test reference assignment.
91aedcbf89SEric Fiselier using T = std::tuple<int&, int&&>;
92aedcbf89SEric Fiselier int x = 42;
93aedcbf89SEric Fiselier int y = 100;
94aedcbf89SEric Fiselier int x2 = -1;
95aedcbf89SEric Fiselier int y2 = 500;
96aedcbf89SEric Fiselier T t(x, std::move(y));
97aedcbf89SEric Fiselier T t2(x2, std::move(y2));
98aedcbf89SEric Fiselier t = std::move(t2);
99aedcbf89SEric Fiselier assert(std::get<0>(t) == x2);
100aedcbf89SEric Fiselier assert(&std::get<0>(t) == &x);
101aedcbf89SEric Fiselier assert(std::get<1>(t) == y2);
102aedcbf89SEric Fiselier assert(&std::get<1>(t) == &y);
103aedcbf89SEric Fiselier }
104*06e2b737SArthur O'Dwyer return true;
105*06e2b737SArthur O'Dwyer }
106*06e2b737SArthur O'Dwyer
main(int,char **)107*06e2b737SArthur O'Dwyer int main(int, char**)
108*06e2b737SArthur O'Dwyer {
109*06e2b737SArthur O'Dwyer test();
110*06e2b737SArthur O'Dwyer #if TEST_STD_VER >= 20
111*06e2b737SArthur O'Dwyer static_assert(test());
112*06e2b737SArthur O'Dwyer #endif
113*06e2b737SArthur O'Dwyer
114aedcbf89SEric Fiselier {
115aedcbf89SEric Fiselier // test that the implicitly generated move assignment operator
116aedcbf89SEric Fiselier // is properly deleted
117aedcbf89SEric Fiselier using T = std::tuple<std::unique_ptr<int>>;
118aedcbf89SEric Fiselier static_assert(std::is_move_assignable<T>::value, "");
119aedcbf89SEric Fiselier static_assert(!std::is_copy_assignable<T>::value, "");
120aedcbf89SEric Fiselier }
121aedcbf89SEric Fiselier {
122aedcbf89SEric Fiselier using T = std::tuple<int, NonAssignable>;
123aedcbf89SEric Fiselier static_assert(!std::is_move_assignable<T>::value, "");
124aedcbf89SEric Fiselier }
125aedcbf89SEric Fiselier {
126aedcbf89SEric Fiselier using T = std::tuple<int, MoveAssignable>;
127aedcbf89SEric Fiselier static_assert(std::is_move_assignable<T>::value, "");
128aedcbf89SEric Fiselier }
129aedcbf89SEric Fiselier {
130aedcbf89SEric Fiselier // The move should decay to a copy.
131aedcbf89SEric Fiselier CountAssign::reset();
132aedcbf89SEric Fiselier using T = std::tuple<CountAssign, CopyAssignable>;
133aedcbf89SEric Fiselier static_assert(std::is_move_assignable<T>::value, "");
134aedcbf89SEric Fiselier T t1;
135aedcbf89SEric Fiselier T t2;
136aedcbf89SEric Fiselier t1 = std::move(t2);
137aedcbf89SEric Fiselier assert(CountAssign::copied == 1);
138aedcbf89SEric Fiselier assert(CountAssign::moved == 0);
139aedcbf89SEric Fiselier }
140a0839b14SLouis Dionne {
141a0839b14SLouis Dionne using T = std::tuple<int, NonAssignable>;
142a0839b14SLouis Dionne static_assert(!std::is_move_assignable<T>::value, "");
143a0839b14SLouis Dionne }
144a0839b14SLouis Dionne {
145a0839b14SLouis Dionne using T = std::tuple<int, MoveAssignable>;
146a0839b14SLouis Dionne static_assert(std::is_move_assignable<T>::value, "");
147a0839b14SLouis Dionne }
148a0839b14SLouis Dionne {
149a0839b14SLouis Dionne using T = std::tuple<NothrowMoveAssignable, int>;
150a0839b14SLouis Dionne static_assert(std::is_nothrow_move_assignable<T>::value, "");
151a0839b14SLouis Dionne }
152a0839b14SLouis Dionne {
153a0839b14SLouis Dionne using T = std::tuple<PotentiallyThrowingMoveAssignable, int>;
154a0839b14SLouis Dionne static_assert(!std::is_nothrow_move_assignable<T>::value, "");
155a0839b14SLouis Dionne }
156618862e8SLouis Dionne {
157618862e8SLouis Dionne // We assign through the reference and don't move out of the incoming ref,
158618862e8SLouis Dionne // so this doesn't work (but would if the type were CopyAssignable).
159618862e8SLouis Dionne using T1 = std::tuple<MoveAssignable&, int>;
160618862e8SLouis Dionne static_assert(!std::is_move_assignable<T1>::value, "");
161618862e8SLouis Dionne
162618862e8SLouis Dionne // ... works if it's CopyAssignable
163618862e8SLouis Dionne using T2 = std::tuple<CopyAssignable&, int>;
164618862e8SLouis Dionne static_assert(std::is_move_assignable<T2>::value, "");
165618862e8SLouis Dionne
166618862e8SLouis Dionne // For rvalue-references, we can move-assign if the type is MoveAssignable
167618862e8SLouis Dionne // or CopyAssignable (since in the worst case the move will decay into a copy).
168618862e8SLouis Dionne using T3 = std::tuple<MoveAssignable&&, int>;
169618862e8SLouis Dionne using T4 = std::tuple<CopyAssignable&&, int>;
170618862e8SLouis Dionne static_assert(std::is_move_assignable<T3>::value, "");
171618862e8SLouis Dionne static_assert(std::is_move_assignable<T4>::value, "");
172618862e8SLouis Dionne
173618862e8SLouis Dionne // In all cases, we can't move-assign if the types are not assignable,
174618862e8SLouis Dionne // since we assign through the reference.
175618862e8SLouis Dionne using T5 = std::tuple<NonAssignable&, int>;
176618862e8SLouis Dionne using T6 = std::tuple<NonAssignable&&, int>;
177618862e8SLouis Dionne static_assert(!std::is_move_assignable<T5>::value, "");
178618862e8SLouis Dionne static_assert(!std::is_move_assignable<T6>::value, "");
179618862e8SLouis Dionne }
1802df59c50SJF Bastien
1812df59c50SJF Bastien return 0;
1825a83710eSEric Fiselier }
183