1// { dg-options "-std=gnu++14" }
2// { dg-do run }
3
4// Copyright (C) 2013-2015 Free Software Foundation, Inc.
5//
6// This file is part of the GNU ISO C++ Library.  This library is free
7// software; you can redistribute it and/or modify it under the
8// terms of the GNU General Public License as published by the
9// Free Software Foundation; either version 3, or (at your option)
10// any later version.
11
12// This library is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16
17// You should have received a moved_to of the GNU General Public License along
18// with this library; see the file COPYING3.  If not see
19// <http://www.gnu.org/licenses/>.
20
21#include <experimental/optional>
22#include <testsuite_hooks.h>
23
24struct exception {};
25
26int counter = 0;
27
28struct mixin_counter
29{
30  mixin_counter() { ++counter; }
31  mixin_counter(mixin_counter const&) { ++counter; }
32  ~mixin_counter() { --counter; }
33};
34
35struct value_type : private mixin_counter
36{
37  enum state_type
38  {
39    zero,
40    moved_from,
41    throwing_construction,
42    throwing_copy,
43    throwing_copy_assignment,
44    throwing_move,
45    throwing_move_assignment,
46    threw,
47  };
48
49  value_type() = default;
50
51  explicit value_type(state_type state_)
52  : state(state_)
53  {
54    throw_if(throwing_construction);
55  }
56
57  value_type(value_type const& other)
58  : state(other.state)
59  {
60    throw_if(throwing_copy);
61  }
62
63  value_type&
64  operator=(value_type const& other)
65  {
66    state = other.state;
67    throw_if(throwing_copy_assignment);
68    return *this;
69  }
70
71  value_type(value_type&& other)
72  : state(other.state)
73  {
74    other.state = moved_from;
75    throw_if(throwing_move);
76  }
77
78  value_type&
79  operator=(value_type&& other)
80  {
81    state = other.state;
82    other.state = moved_from;
83    throw_if(throwing_move_assignment);
84    return *this;
85  }
86
87  void throw_if(state_type match)
88  {
89    if(state == match)
90    {
91      state = threw;
92      throw exception {};
93    }
94  }
95
96  state_type state = zero;
97};
98
99int main()
100{
101  using O = std::experimental::optional<value_type>;
102  using S = value_type::state_type;
103  auto const make = [](S s = S::zero) { return O { std::experimental::in_place, s }; };
104
105  enum outcome_type { nothrow, caught, bad_catch };
106
107  // Check copy/move assignment for disengaged optional
108
109  // From disengaged optional
110  {
111    O o;
112    VERIFY( !o );
113    O p;
114    o = p;
115    VERIFY( !o );
116    VERIFY( !p );
117  }
118
119  {
120    O o;
121    VERIFY( !o );
122    O p;
123    o = std::move(p);
124    VERIFY( !o );
125    VERIFY( !p );
126  }
127
128  {
129    O o;
130    VERIFY( !o );
131    o = {};
132    VERIFY( !o );
133  }
134
135  // From engaged optional
136  {
137    O o;
138    VERIFY( !o );
139    O p = make(S::throwing_copy_assignment);
140    o = p;
141    VERIFY( o && o->state == S::throwing_copy_assignment );
142    VERIFY( p && p->state == S::throwing_copy_assignment );
143  }
144
145  {
146    O o;
147    VERIFY( !o );
148    O p = make(S::throwing_move_assignment);
149    o = std::move(p);
150    VERIFY( o && o->state == S::throwing_move_assignment );
151    VERIFY( p && p->state == S::moved_from );
152  }
153
154  {
155    outcome_type outcome {};
156    O o;
157    VERIFY( !o );
158    O p = make(S::throwing_copy);
159
160    try
161    {
162      o = p;
163    }
164    catch(exception const&)
165    { outcome = caught; }
166    catch(...)
167    { outcome = bad_catch; }
168
169    VERIFY( outcome == caught );
170    VERIFY( !o );
171    VERIFY( p && p->state == S::throwing_copy );
172  }
173
174  {
175    outcome_type outcome {};
176    O o;
177    VERIFY( !o );
178    O p = make(S::throwing_move);
179
180    try
181    {
182      o = std::move(p);
183    }
184    catch(exception const&)
185    { outcome = caught; }
186    catch(...)
187    { outcome = bad_catch; }
188
189    VERIFY( outcome == caught );
190    VERIFY( !o );
191    VERIFY( p && p->state == S::moved_from );
192  }
193
194  VERIFY( counter == 0 );
195}
196