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