Skip to content

Commit cdf71bd

Browse files
committed
Correct copy/move for union
By writing separate construction and assignment, plus the new feature of suppressing assignment to a member by writing `member = _ ;` (now allowed only in assignment operators). I do realize that's an "opt-out" which I normally prefer to avoid, but: - I considered and decided against (for now) the alternative of not having assignment be memberwise by default. I want to keep the (new to Cpp2) default of memberwise semantics for assignment as with construction. I think that's a useful feature, and normally if you do assign to a member it doesn't arise, and so I think it makes sense to explicitly call out when we're choosing not to do any assignment at all to a member before doing other assignment processing. We'll get experience with how it goes. - `_` is arguably natural here, since it's pronounced "don't care." There too, we'll see if that is natural generalized, or feels strained. For now it feels natural to me.
1 parent 083c8a0 commit cdf71bd

File tree

7 files changed

+91
-66
lines changed

7 files changed

+91
-66
lines changed

regression-tests/test-results/gcc-13/gcc-version.output

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
gcc (GCC) 13.2.1 20230728 (Red Hat 13.2.1-1)
1+
gcc (GCC) 13.2.1 20231011 (Red Hat 13.2.1-4)
22
Copyright (C) 2023 Free Software Foundation, Inc.
33
This is free software; see the source for copying conditions. There is NO
44
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

regression-tests/test-results/pure2-union.cpp

Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ public: ~name_or_number() noexcept;
3535
public: explicit name_or_number();
3636
public: name_or_number(name_or_number const& that);
3737

38-
public: auto operator=(name_or_number const& that) -> name_or_number& ;
3938
public: name_or_number(name_or_number&& that) noexcept;
39+
public: auto operator=(name_or_number const& that) -> name_or_number& ;
4040
public: auto operator=(name_or_number&& that) noexcept -> name_or_number& ;
4141

4242
#line 5 "pure2-union.cpp2"
@@ -62,8 +62,8 @@ private: auto _destroy() & -> void;
6262
public: ~name_or_other() noexcept;
6363
public: explicit name_or_other();
6464
public: name_or_other(name_or_other const& that);
65-
public: auto operator=(name_or_other const& that) -> name_or_other& ;
6665
public: name_or_other(name_or_other&& that) noexcept;
66+
public: auto operator=(name_or_other const& that) -> name_or_other& ;
6767
public: auto operator=(name_or_other&& that) noexcept -> name_or_other& ;
6868

6969
#line 17 "pure2-union.cpp2"
@@ -101,39 +101,30 @@ auto name_or_number::_destroy() & -> void{
101101
}
102102

103103
name_or_number::~name_or_number() noexcept{_destroy();}
104-
name_or_number::name_or_number()
105-
: _discriminator{ -1 }{}
104+
name_or_number::name_or_number(){}
106105
name_or_number::name_or_number(name_or_number const& that)
107-
: _storage{ that._storage }
108-
, _discriminator{ that._discriminator }{
106+
: _storage{ }
107+
, _discriminator{ -1 }{
109108
if (CPP2_UFCS_0(is_name, that)) {set_name(CPP2_UFCS_0(name, that));}
110109
if (CPP2_UFCS_0(is_num, that)) {set_num(CPP2_UFCS_0(num, that));}
111-
_discriminator = that._discriminator;
110+
}
111+
112+
name_or_number::name_or_number(name_or_number&& that) noexcept
113+
: _storage{ }
114+
, _discriminator{ -1 }{
115+
if (CPP2_UFCS_0(is_name, std::move(that))) {set_name(CPP2_UFCS_0(name, std::move(that)));}
116+
if (CPP2_UFCS_0(is_num, std::move(that))) {set_num(CPP2_UFCS_0(num, std::move(that)));}
112117
}
113118

114119
auto name_or_number::operator=(name_or_number const& that) -> name_or_number& {
115-
_storage = that._storage;
116-
_discriminator = that._discriminator;
117120
if (CPP2_UFCS_0(is_name, that)) {set_name(CPP2_UFCS_0(name, that));}
118121
if (CPP2_UFCS_0(is_num, that)) {set_num(CPP2_UFCS_0(num, that));}
119-
_discriminator = that._discriminator;
120122
return *this;
121123
}
122124

123-
name_or_number::name_or_number(name_or_number&& that) noexcept
124-
: _storage{ std::move(that)._storage }
125-
, _discriminator{ std::move(that)._discriminator }{
126-
if (CPP2_UFCS_0(is_name, std::move(that))) {set_name(CPP2_UFCS_0(name, std::move(that)));}
127-
if (CPP2_UFCS_0(is_num, std::move(that))) {set_num(CPP2_UFCS_0(num, std::move(that)));}
128-
_discriminator = std::move(that)._discriminator;
129-
}
130-
131125
auto name_or_number::operator=(name_or_number&& that) noexcept -> name_or_number& {
132-
_storage = std::move(that)._storage;
133-
_discriminator = std::move(that)._discriminator;
134126
if (CPP2_UFCS_0(is_name, std::move(that))) {set_name(CPP2_UFCS_0(name, std::move(that)));}
135127
if (CPP2_UFCS_0(is_num, std::move(that))) {set_num(CPP2_UFCS_0(num, std::move(that)));}
136-
_discriminator = std::move(that)._discriminator;
137128
return *this;
138129
}
139130
#line 12 "pure2-union.cpp2"
@@ -166,40 +157,31 @@ template <typename T> auto name_or_other<T>::_destroy() & -> void{
166157
}
167158

168159
template <typename T> name_or_other<T>::~name_or_other() noexcept{_destroy();}
169-
template <typename T> name_or_other<T>::name_or_other()
170-
: _discriminator{ -1 }{}
160+
template <typename T> name_or_other<T>::name_or_other(){}
171161
template <typename T> name_or_other<T>::name_or_other(name_or_other const& that)
172-
: _storage{ that._storage }
173-
, _discriminator{ that._discriminator }{
162+
: _storage{ }
163+
, _discriminator{ -1 }{
174164
if (CPP2_UFCS_0(is_name, that)) {set_name(CPP2_UFCS_0(name, that));}
175165
if (CPP2_UFCS_0(is_other, that)) {set_other(CPP2_UFCS_0(other, that));}
176-
_discriminator = that._discriminator;
177166
}
178167

179168

169+
template <typename T> name_or_other<T>::name_or_other(name_or_other&& that) noexcept
170+
: _storage{ }
171+
, _discriminator{ -1 }{
172+
if (CPP2_UFCS_0(is_name, std::move(that))) {set_name(CPP2_UFCS_0(name, std::move(that)));}
173+
if (CPP2_UFCS_0(is_other, std::move(that))) {set_other(CPP2_UFCS_0(other, std::move(that)));}
174+
}
175+
180176
template <typename T> auto name_or_other<T>::operator=(name_or_other const& that) -> name_or_other& {
181-
_storage = that._storage;
182-
_discriminator = that._discriminator;
183177
if (CPP2_UFCS_0(is_name, that)) {set_name(CPP2_UFCS_0(name, that));}
184178
if (CPP2_UFCS_0(is_other, that)) {set_other(CPP2_UFCS_0(other, that));}
185-
_discriminator = that._discriminator;
186179
return *this;
187180
}
188181

189-
template <typename T> name_or_other<T>::name_or_other(name_or_other&& that) noexcept
190-
: _storage{ std::move(that)._storage }
191-
, _discriminator{ std::move(that)._discriminator }{
192-
if (CPP2_UFCS_0(is_name, std::move(that))) {set_name(CPP2_UFCS_0(name, std::move(that)));}
193-
if (CPP2_UFCS_0(is_other, std::move(that))) {set_other(CPP2_UFCS_0(other, std::move(that)));}
194-
_discriminator = std::move(that)._discriminator;
195-
}
196-
197182
template <typename T> auto name_or_other<T>::operator=(name_or_other&& that) noexcept -> name_or_other& {
198-
_storage = std::move(that)._storage;
199-
_discriminator = std::move(that)._discriminator;
200183
if (CPP2_UFCS_0(is_name, std::move(that))) {set_name(CPP2_UFCS_0(name, std::move(that)));}
201184
if (CPP2_UFCS_0(is_other, std::move(that))) {set_other(CPP2_UFCS_0(other, std::move(that)));}
202-
_discriminator = std::move(that)._discriminator;
203185
return *this;
204186
}
205187
#line 19 "pure2-union.cpp2"

regression-tests/test-results/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
cppfront compiler v0.3.0 Build 8A21:1546
2+
cppfront compiler v0.3.0 Build 8A27:1514
33
Copyright(c) Herb Sutter All rights reserved
44

55
SPDX-License-Identifier: CC-BY-NC-ND-4.0

source/build.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"8A21:1546"
1+
"8A27:1514"

source/cppfront.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4812,7 +4812,32 @@ class cppfront
48124812
|| found_default_init
48134813
);
48144814

4815-
// Emit the initializer...
4815+
// Emit the initializer if it it isn't '_' (don't care) and ...
4816+
if (initializer == "_") {
4817+
// I'll walk the walk: I've said publicly that _structured_ goto is perfectly
4818+
// kosher -- it's only _unstructured_ goto that's harmful (and Dijkstra knew it),
4819+
// which means:
4820+
// - jumping into a block or statement (I'm looking at you, Duff's Device)
4821+
// - jumping backwards (that's an unstructured loop == spaghetti control flow)
4822+
// - jumping across a variable declaration (C++ already bans this)
4823+
//
4824+
// But jumping forward-and-outward, skipping no declarations, is righteous.
4825+
//
4826+
// Here, using goto is the right tool for the job, and is better code because:
4827+
// - it avoids a gratuitous extra level of nesting inside an
4828+
// 'if (initializer != "_")' block of the next 100 lines (and please don't
4829+
// start the other diatribe about that the next 100 lines should be a
4830+
// separate named function - no it shouldn't)
4831+
// - which extra indent of identical code would make GitHub's diff for this
4832+
// commit super hard to read (diff does not deal well with blocks of code
4833+
// that simply change indentation level - in fact seeing that diff without
4834+
// the goto was the tipping point to just switch to goto here)
4835+
// ... but sadly I feel the need to defend it.
4836+
//
4837+
// As Scott would say (and has said), "so sue me"
4838+
//
4839+
goto skip_initializer;
4840+
}
48164841

48174842
if (initializer.empty()) {
48184843
initializer = "{}";
@@ -4909,6 +4934,8 @@ class cppfront
49094934
separator = ", ";
49104935
}
49114936

4937+
skip_initializer: ;
4938+
49124939
// And on to the next data member...
49134940
++object;
49144941
}

source/reflect.h

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class alias_declaration;
3838
#line 841 "reflect.h2"
3939
class value_member_info;
4040

41-
#line 1327 "reflect.h2"
41+
#line 1335 "reflect.h2"
4242
}
4343

4444
}
@@ -699,14 +699,14 @@ auto flag_enum(meta::type_declaration& t) -> void;
699699

700700
auto cpp2_union(meta::type_declaration& t) -> void;
701701

702-
#line 1213 "reflect.h2"
702+
#line 1221 "reflect.h2"
703703
//-----------------------------------------------------------------------
704704
//
705705
// print - output a pretty-printed visualization of t
706706
//
707707
auto print(cpp2::in<meta::type_declaration> t) -> void;
708708

709-
#line 1223 "reflect.h2"
709+
#line 1231 "reflect.h2"
710710
//-----------------------------------------------------------------------
711711
//
712712
// apply_metafunctions
@@ -717,7 +717,7 @@ auto print(cpp2::in<meta::type_declaration> t) -> void;
717717
auto const& error
718718
) -> bool;
719719

720-
#line 1327 "reflect.h2"
720+
#line 1335 "reflect.h2"
721721
}
722722

723723
}
@@ -1680,37 +1680,45 @@ std::string destroy = " private _destroy: (inout this) = {\n";
16801680

16811681
// Add the destructor
16821682
#line 1193 "reflect.h2"
1683-
CPP2_UFCS(add_member, t, " operator=: (move this) = { _destroy(); } ");
1683+
CPP2_UFCS(add_member, t, " operator=: (move this) = { _destroy(); }");
16841684

16851685
// Add default constructor
1686-
CPP2_UFCS(add_member, t, " operator=: (out this) = { _discriminator = -1; } ");
1686+
CPP2_UFCS(add_member, t, " operator=: (out this) = { }");
16871687
{
1688-
std::string value_set = " operator=: (out this, that) = {\n";
1688+
std::string value_set = "";
16891689

1690-
// Add value-set
1690+
// Add copy/move construction and assignment
16911691

16921692
#line 1200 "reflect.h2"
16931693
{
16941694
for (
16951695
auto const& a : alternatives ) {
16961696
value_set += " if that.is_" + cpp2::to_string(a.name) + "() { set_" + cpp2::to_string(a.name) + "( that." + cpp2::to_string(a.name) + "() ); }\n";
16971697
}
1698-
1699-
value_set += " _discriminator = that._discriminator;\n";
17001698
value_set += " }\n";
1701-
CPP2_UFCS(add_member, t, std::move(value_set));
1699+
1700+
CPP2_UFCS(add_member, t, std::string(" operator=: (out this, that) = {\n")
1701+
+ " _storage = ();\n"
1702+
+ " _discriminator = -1;\n"
1703+
+ value_set
1704+
);
1705+
CPP2_UFCS(add_member, t, std::string(" operator=: (inout this, that) = {\n")
1706+
+ " _storage = _;\n"
1707+
+ " _discriminator = _;\n"
1708+
+ std::move(value_set)
1709+
);
17021710
}
17031711
}
1704-
#line 1210 "reflect.h2"
1712+
#line 1218 "reflect.h2"
17051713
}
17061714

1707-
#line 1217 "reflect.h2"
1715+
#line 1225 "reflect.h2"
17081716
auto print(cpp2::in<meta::type_declaration> t) -> void
17091717
{
17101718
std::cout << CPP2_UFCS_0(print, t) << "\n";
17111719
}
17121720

1713-
#line 1227 "reflect.h2"
1721+
#line 1235 "reflect.h2"
17141722
[[nodiscard]] auto apply_metafunctions(
17151723
declaration_node& n,
17161724
type_declaration& rtype,
@@ -1810,7 +1818,7 @@ auto print(cpp2::in<meta::type_declaration> t) -> void
18101818
return true;
18111819
}
18121820

1813-
#line 1327 "reflect.h2"
1821+
#line 1335 "reflect.h2"
18141822
}
18151823

18161824
}

source/reflect.h2

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,22 +1190,30 @@ union: (inout t : meta::type_declaration)
11901190
}
11911191

11921192
// Add the destructor
1193-
t.add_member( " operator=: (move this) = { _destroy(); } " );
1193+
t.add_member( " operator=: (move this) = { _destroy(); }" );
11941194

11951195
// Add default constructor
1196-
t.add_member( " operator=: (out this) = { _discriminator = -1; } " );
1196+
t.add_member( " operator=: (out this) = { }" );
11971197

1198-
// Add value-set
1199-
(copy value_set: std::string = " operator=: (out this, that) = {\n")
1198+
// Add copy/move construction and assignment
1199+
(copy value_set: std::string = "")
12001200
{
12011201
for alternatives
12021202
do (a) {
12031203
value_set += " if that.is_(a.name)$() { set_(a.name)$( that.(a.name)$() ); }\n";
12041204
}
1205-
1206-
value_set += " _discriminator = that._discriminator;\n";
12071205
value_set += " }\n";
1208-
t.add_member( value_set );
1206+
1207+
t.add_member( std::string(" operator=: (out this, that) = {\n")
1208+
+ " _storage = ();\n"
1209+
+ " _discriminator = -1;\n"
1210+
+ value_set
1211+
);
1212+
t.add_member( std::string(" operator=: (inout this, that) = {\n")
1213+
+ " _storage = _;\n"
1214+
+ " _discriminator = _;\n"
1215+
+ value_set
1216+
);
12091217
}
12101218
}
12111219

0 commit comments

Comments
 (0)