1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4//                     The LLVM Compiler Infrastructure
5//
6// This file is dual licensed under the MIT and the University of Illinois Open
7// Source Licenses. See LICENSE.TXT for details.
8//
9//===----------------------------------------------------------------------===//
10
11// UNSUPPORTED: c++98, c++03, c++11, c++14
12
13// <variant>
14
15// template <class ...Types> class variant;
16
17// template <class T>
18// variant& operator=(T&&) noexcept(see below);
19
20#include <cassert>
21#include <string>
22#include <type_traits>
23#include <variant>
24
25#include "test_macros.h"
26#include "variant_test_helpers.hpp"
27
28namespace MetaHelpers {
29
30struct Dummy {
31  Dummy() = default;
32};
33
34struct ThrowsCtorT {
35  ThrowsCtorT(int) noexcept(false) {}
36  ThrowsCtorT &operator=(int) noexcept { return *this; }
37};
38
39struct ThrowsAssignT {
40  ThrowsAssignT(int) noexcept {}
41  ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
42};
43
44struct NoThrowT {
45  NoThrowT(int) noexcept {}
46  NoThrowT &operator=(int) noexcept { return *this; }
47};
48
49} // namespace MetaHelpers
50
51namespace RuntimeHelpers {
52#ifndef TEST_HAS_NO_EXCEPTIONS
53
54struct ThrowsCtorT {
55  int value;
56  ThrowsCtorT() : value(0) {}
57  ThrowsCtorT(int) noexcept(false) { throw 42; }
58  ThrowsCtorT &operator=(int v) noexcept {
59    value = v;
60    return *this;
61  }
62};
63
64struct ThrowsAssignT {
65  int value;
66  ThrowsAssignT() : value(0) {}
67  ThrowsAssignT(int v) noexcept : value(v) {}
68  ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
69};
70
71struct NoThrowT {
72  int value;
73  NoThrowT() : value(0) {}
74  NoThrowT(int v) noexcept : value(v) {}
75  NoThrowT &operator=(int v) noexcept {
76    value = v;
77    return *this;
78  }
79};
80
81#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
82} // namespace RuntimeHelpers
83
84void test_T_assignment_noexcept() {
85  using namespace MetaHelpers;
86  {
87    using V = std::variant<Dummy, NoThrowT>;
88    static_assert(std::is_nothrow_assignable<V, int>::value, "");
89  }
90  {
91    using V = std::variant<Dummy, ThrowsCtorT>;
92    static_assert(!std::is_nothrow_assignable<V, int>::value, "");
93  }
94  {
95    using V = std::variant<Dummy, ThrowsAssignT>;
96    static_assert(!std::is_nothrow_assignable<V, int>::value, "");
97  }
98}
99
100void test_T_assignment_sfinae() {
101  {
102    using V = std::variant<long, unsigned>;
103    static_assert(!std::is_assignable<V, int>::value, "ambiguous");
104  }
105  {
106    using V = std::variant<std::string, std::string>;
107    static_assert(!std::is_assignable<V, const char *>::value, "ambiguous");
108  }
109  {
110    using V = std::variant<std::string, void *>;
111    static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
112  }
113#if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
114  {
115    using V = std::variant<int, int &&>;
116    static_assert(!std::is_assignable<V, int>::value, "ambiguous");
117  }
118  {
119    using V = std::variant<int, const int &>;
120    static_assert(!std::is_assignable<V, int>::value, "ambiguous");
121  }
122#endif
123}
124
125void test_T_assignment_basic() {
126  {
127    std::variant<int> v(43);
128    v = 42;
129    assert(v.index() == 0);
130    assert(std::get<0>(v) == 42);
131  }
132  {
133    std::variant<int, long> v(43l);
134    v = 42;
135    assert(v.index() == 0);
136    assert(std::get<0>(v) == 42);
137    v = 43l;
138    assert(v.index() == 1);
139    assert(std::get<1>(v) == 43);
140  }
141#if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
142  {
143    using V = std::variant<int &, int &&, long>;
144    int x = 42;
145    V v(43l);
146    v = x;
147    assert(v.index() == 0);
148    assert(&std::get<0>(v) == &x);
149    v = std::move(x);
150    assert(v.index() == 1);
151    assert(&std::get<1>(v) == &x);
152    // 'long' is selected by FUN(const int &) since 'const int &' cannot bind
153    // to 'int&'.
154    const int &cx = x;
155    v = cx;
156    assert(v.index() == 2);
157    assert(std::get<2>(v) == 42);
158  }
159#endif
160}
161
162void test_T_assignment_performs_construction() {
163  using namespace RuntimeHelpers;
164#ifndef TEST_HAS_NO_EXCEPTIONS
165  {
166    using V = std::variant<std::string, ThrowsCtorT>;
167    V v(std::in_place_type<std::string>, "hello");
168    try {
169      v = 42;
170    } catch (...) { /* ... */
171    }
172    assert(v.valueless_by_exception());
173  }
174  {
175    using V = std::variant<ThrowsAssignT, std::string>;
176    V v(std::in_place_type<std::string>, "hello");
177    v = 42;
178    assert(v.index() == 0);
179    assert(std::get<0>(v).value == 42);
180  }
181#endif
182}
183
184void test_T_assignment_performs_assignment() {
185  using namespace RuntimeHelpers;
186#ifndef TEST_HAS_NO_EXCEPTIONS
187  {
188    using V = std::variant<ThrowsCtorT>;
189    V v;
190    v = 42;
191    assert(v.index() == 0);
192    assert(std::get<0>(v).value == 42);
193  }
194  {
195    using V = std::variant<ThrowsCtorT, std::string>;
196    V v;
197    v = 42;
198    assert(v.index() == 0);
199    assert(std::get<0>(v).value == 42);
200  }
201  {
202    using V = std::variant<ThrowsAssignT>;
203    V v(100);
204    try {
205      v = 42;
206      assert(false);
207    } catch (...) { /* ... */
208    }
209    assert(v.index() == 0);
210    assert(std::get<0>(v).value == 100);
211  }
212  {
213    using V = std::variant<std::string, ThrowsAssignT>;
214    V v(100);
215    try {
216      v = 42;
217      assert(false);
218    } catch (...) { /* ... */
219    }
220    assert(v.index() == 1);
221    assert(std::get<1>(v).value == 100);
222  }
223#endif
224}
225
226int main() {
227  test_T_assignment_basic();
228  test_T_assignment_performs_construction();
229  test_T_assignment_performs_assignment();
230  test_T_assignment_noexcept();
231  test_T_assignment_sfinae();
232}
233