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