Skip to content

Commit cd920d2

Browse files
committed
Tweak the operator= generation rule
Only generate other operator= functions from (out this, that) if _none_ of the other three were written - generate all of them or none of them Remove the "A2" generation arrow, which also removes a potential second path to a generated (inout this, move that) via both M2 and A2 where M2 was already preferred - this removes the need for a tie-break and embraces the already-preferred path The previous rule did not allow for expressing copy/move-constructible types that are not assignable (rare, but can happen when there are const members or Cpp1 reference members) - that can now be expressed by writing two operator='s, (out this, that) and (out this, move that)
1 parent 5aa32ae commit cd920d2

14 files changed

+45
-104
lines changed

docs/cpp2/types.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,16 @@ As mentioned above:
148148
149149
This graphic summarizes these generalizations. For convenience I've numbered the (A)ssignment and (M)ove defaults.
150150

151-
![image](https://user-images.githubusercontent.com/1801526/226261443-03125a35-7890-4cc7-bf7d-f23b3a0bb0df.png)
151+
![image](https://github.com/user-attachments/assets/311f27df-17df-48c4-b0c9-b820afb5be0c)
152152

153153
In Cpp1 terms, they can be described as follows:
154154

155155
- **(M)ove, M1, M2:** If you write a copy constructor or assignment operator, but not a corresponding move constructor or assignment operator, the latter is generated.
156156

157-
- **(A)ssignment, A1, A2, A3:** If you write a copy or move or converting constructor, but not a corresponding copy or move or converting assignment operator, the latter is generated.
157+
- **(A)ssignment, A1, A3:** If you write a generalized constructor, but none of the three more-specific copy/move constructor/assignment functions, the latter three get generated. If you write a converting copy constructor, but no converting assignment operator for the same type, the latter is generated.
158158

159159
- **The arrows are transitive.** For example, if you write a copy constructor and nothing else, the move constructor, copy assignment operator, and move assignment operator are generated.
160160

161-
- **M2 is preferred over A2.** Both M2 and A2 can generate a missing `#!cpp (inout this, move that)` function. If both options are available, Cpp2 prefers to use M2 (generate move assignment from copy assignment, which could itself have been generated from copy construction) rather than A2 (generate move assignment from move construction). This is because M2 is a better fit: Move assignment is more like copy assignment than like move construction, because assignments are designed structurally to set the value of an existing `#!cpp this` object.
162-
163161
The most general `#!cpp operator=` with `that` is `#!cpp (out this, that)`. In Cpp1 terms, it generates all four combinations of { copy, move } x { constructor, assignment }. This is often sufficient, so you can write all these value-setting functions just once. If you do want to write a more specific version that does something else, though, you can always write it too.
164162

165163
> Note: Generating `#!cpp inout this` (assignment) from `#!cpp out this` also generates **converting assignment** from converting construction, which is a new thing. Today in Cpp1, if you write a converting constructor from another type `X`, you may or may not write the corresponding assignment from `X`; in Cpp2 you will get that by default, and it sets the object to the same state as the converting constructor from `X` does.
@@ -218,7 +216,7 @@ mytype: type
218216
print();
219217
}
220218

221-
print: (this) = std::cout << "value is [(name)$] [(social_handle)$]\n";
219+
print: (this) = { std::cout << "value is [(name)$] [(social_handle)$]\n"; }
222220
}
223221

224222
// The above definition of mytype allows all of the following...

regression-tests/pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp2

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ main: () = {
5555
z.print(" mv-construct ", " <- ");
5656
x.print("", "\n");
5757

58-
z = y;
59-
z.print(" cp-assign ", " <- ");
60-
y.print("", "\n");
58+
// z = y;
59+
// z.print(" cp-assign ", " <- ");
60+
// y.print("", "\n");
6161

6262
z = (move y);
6363
z.print(" mv-assign ", " <- ");

regression-tests/test-results/clang-12-c++20/pure2-expected-is-as.cpp.output

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,59 +77,59 @@ pure2-expected-is-as.cpp2:34:24: error: use of undeclared identifier 'ex1'; did
7777
__MATHCALL_VEC (exp,, (_Mdouble_ __x));
7878
^
7979
In file included from pure2-expected-is-as.cpp:7:
80-
../../../include/cpp2util.h:469:72: error: invalid application of 'sizeof' to a function type
80+
../../../include/cpp2util.h:475:72: error: invalid application of 'sizeof' to a function type
8181
(std::is_floating_point_v<From> && std::is_floating_point_v<To> && sizeof(From) > sizeof(To)) || // NOLINT(misc-redundant-expression)
8282
^~~~~~~~~~~~
83-
../../../include/cpp2util.h:2924:19: note: in instantiation of variable template specialization 'cpp2::impl::is_narrowing_v' requested here
83+
../../../include/cpp2util.h:2929:19: note: in instantiation of variable template specialization 'cpp2::impl::is_narrowing_v' requested here
8484
if constexpr (is_narrowing_v<C, CPP2_TYPEOF(x)>) {
8585
^
8686
pure2-expected-is-as.cpp2:39:28: note: in instantiation of function template specialization 'cpp2::impl::as_<int, double (&)(double) noexcept>' requested here
8787
auto val1 {cpp2::impl::as_<int>(ex1)};
8888
^
8989
In file included from pure2-expected-is-as.cpp:7:
90-
../../../include/cpp2util.h:2944:12: error: no matching function for call to 'as'
90+
../../../include/cpp2util.h:2949:12: error: no matching function for call to 'as'
9191
return as<C>(CPP2_FORWARD(x));
9292
^~~~~
9393
pure2-expected-is-as.cpp2:39:28: note: in instantiation of function template specialization 'cpp2::impl::as_<int, double (&)(double) noexcept>' requested here
9494
auto val1 {cpp2::impl::as_<int>(ex1)};
9595
^
96-
../../../include/cpp2util.h:1902:16: note: candidate template ignored: constraints not satisfied [with C = int, x:auto = double (&)(double) noexcept]
96+
../../../include/cpp2util.h:1907:16: note: candidate template ignored: constraints not satisfied [with C = int, x:auto = double (&)(double) noexcept]
9797
constexpr auto as(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS) -> decltype(auto)
9898
^
99-
../../../include/cpp2util.h:1908:18: note: because 'std::is_scalar_v<std::remove_cvref_t<decltype(x)> >' evaluated to false
99+
../../../include/cpp2util.h:1913:18: note: because 'std::is_scalar_v<std::remove_cvref_t<decltype(x)> >' evaluated to false
100100
(std::is_scalar_v<CPP2_TYPEOF(x)> && !std::is_enum_v<CPP2_TYPEOF(x)>)
101101
^
102-
../../../include/cpp2util.h:1909:17: note: and 'std::is_floating_point_v<std::remove_cvref_t<decltype(x)> >' evaluated to false
102+
../../../include/cpp2util.h:1914:17: note: and 'std::is_floating_point_v<std::remove_cvref_t<decltype(x)> >' evaluated to false
103103
|| std::is_floating_point_v<CPP2_TYPEOF(x)>
104104
^
105-
../../../include/cpp2util.h:1910:17: note: and 'std::is_base_of_v<int, std::remove_cvref_t<decltype(x)> >' evaluated to false
105+
../../../include/cpp2util.h:1915:17: note: and 'std::is_base_of_v<int, std::remove_cvref_t<decltype(x)> >' evaluated to false
106106
|| std::is_base_of_v<C, CPP2_TYPEOF(x)>
107107
^
108-
../../../include/cpp2util.h:1911:17: note: and 'std::is_base_of_v<std::remove_cvref_t<decltype(x)>, int>' evaluated to false
108+
../../../include/cpp2util.h:1916:17: note: and 'std::is_base_of_v<std::remove_cvref_t<decltype(x)>, int>' evaluated to false
109109
|| std::is_base_of_v<CPP2_TYPEOF(x), C>
110110
^
111-
../../../include/cpp2util.h:1912:30: note: and 'C({std::forward<decltype(x)>(x)})' would be invalid: cannot initialize a value of type 'int' with an lvalue of type 'double (double) noexcept'
111+
../../../include/cpp2util.h:1917:30: note: and 'C({std::forward<decltype(x)>(x)})' would be invalid: cannot initialize a value of type 'int' with an lvalue of type 'double (double) noexcept'
112112
|| requires { C{CPP2_FORWARD(x)}; }
113113
^
114-
../../../include/cpp2util.h:327:37: note: expanded from macro 'CPP2_FORWARD'
114+
../../../include/cpp2util.h:333:37: note: expanded from macro 'CPP2_FORWARD'
115115
#define CPP2_FORWARD(x) std::forward<decltype(x)>(x)
116116
^
117-
../../../include/cpp2util.h:2041:6: note: candidate template ignored: constraints not satisfied [with C = int, X = double (&)(double) noexcept]
117+
../../../include/cpp2util.h:2046:6: note: candidate template ignored: constraints not satisfied [with C = int, X = double (&)(double) noexcept]
118118
auto as(X&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS) -> decltype(auto)
119119
^
120-
../../../include/cpp2util.h:2040:23: note: because 'specialization_of_template<double (&)(double) noexcept, std::variant>' evaluated to false
120+
../../../include/cpp2util.h:2045:23: note: because 'specialization_of_template<double (&)(double) noexcept, std::variant>' evaluated to false
121121
template< typename C, specialization_of_template<std::variant> X >
122122
^
123-
../../../include/cpp2util.h:892:7: note: because 'specialization_of_template_helper<C>(std::forward<X>(x))' would be invalid: no matching function for call to 'specialization_of_template_helper'
123+
../../../include/cpp2util.h:897:7: note: because 'specialization_of_template_helper<C>(std::forward<X>(x))' would be invalid: no matching function for call to 'specialization_of_template_helper'
124124
{ specialization_of_template_helper<C>(std::forward<X>(x)) } -> std::same_as<std::true_type>;
125125
^
126-
../../../include/cpp2util.h:2088:16: note: candidate template ignored: constraints not satisfied [with T = int, X = double (&)(double) noexcept]
126+
../../../include/cpp2util.h:2093:16: note: candidate template ignored: constraints not satisfied [with T = int, X = double (&)(double) noexcept]
127127
constexpr auto as( X && x ) -> decltype(auto) {
128128
^
129-
../../../include/cpp2util.h:2087:22: note: because 'same_type_as<double (&)(double) noexcept, std::any>' evaluated to false
129+
../../../include/cpp2util.h:2092:22: note: because 'same_type_as<double (&)(double) noexcept, std::any>' evaluated to false
130130
template<typename T, same_type_as<std::any> X>
131131
^
132-
../../../include/cpp2util.h:922:29: note: because 'std::same_as<std::remove_cvref_t<double (&)(double) noexcept>, std::remove_cvref_t<any> >' evaluated to false
132+
../../../include/cpp2util.h:927:29: note: because 'std::same_as<std::remove_cvref_t<double (&)(double) noexcept>, std::remove_cvref_t<any> >' evaluated to false
133133
concept same_type_as = std::same_as<std::remove_cvref_t<X>, std::remove_cvref_t<C>>;
134134
^
135135
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/concepts:63:19: note: because '__detail::__same_as<double (double) noexcept, std::any>' evaluated to false
@@ -138,19 +138,19 @@ concept same_type_as = std::same_as<std::remove_cvref_t<X>, std::remove_cvref_t<
138138
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/concepts:57:27: note: because 'std::is_same_v<double (double) noexcept, std::any>' evaluated to false
139139
concept __same_as = std::is_same_v<_Tp, _Up>;
140140
^
141-
../../../include/cpp2util.h:2131:16: note: candidate template ignored: constraints not satisfied [with T = int, X = double (&)(double) noexcept]
141+
../../../include/cpp2util.h:2136:16: note: candidate template ignored: constraints not satisfied [with T = int, X = double (&)(double) noexcept]
142142
constexpr auto as( X&& x ) -> decltype(auto) {
143143
^
144-
../../../include/cpp2util.h:2130:22: note: because 'specialization_of_template<double (&)(double) noexcept, std::optional>' evaluated to false
144+
../../../include/cpp2util.h:2135:22: note: because 'specialization_of_template<double (&)(double) noexcept, std::optional>' evaluated to false
145145
template<typename T, specialization_of_template<std::optional> X>
146146
^
147-
../../../include/cpp2util.h:892:7: note: because 'specialization_of_template_helper<C>(std::forward<X>(x))' would be invalid: no matching function for call to 'specialization_of_template_helper'
147+
../../../include/cpp2util.h:897:7: note: because 'specialization_of_template_helper<C>(std::forward<X>(x))' would be invalid: no matching function for call to 'specialization_of_template_helper'
148148
{ specialization_of_template_helper<C>(std::forward<X>(x)) } -> std::same_as<std::true_type>;
149149
^
150-
../../../include/cpp2util.h:1877:16: note: candidate function template not viable: requires 0 arguments, but 1 was provided
150+
../../../include/cpp2util.h:1882:16: note: candidate function template not viable: requires 0 arguments, but 1 was provided
151151
constexpr auto as() -> auto
152152
^
153-
../../../include/cpp2util.h:1888:16: note: candidate function template not viable: requires 0 arguments, but 1 was provided
153+
../../../include/cpp2util.h:1893:16: note: candidate function template not viable: requires 0 arguments, but 1 was provided
154154
constexpr auto as() -> auto
155155
^
156156
pure2-expected-is-as.cpp2:39:37: error: use of undeclared identifier 'ex1'

regression-tests/test-results/clang-12-c++20/pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp.execution

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ ctor - from string construct [ Henry | 123 Ford Dr. ]
44
ctor - from string assign [ Clara | 123 Ford Dr. ]
55
ctor - copy (GENERAL) cp-construct [ Clara | 123 Ford Dr. ] <- [ Clara | 123 Ford Dr. ]
66
ctor - move mv-construct [ Clara(CM) | 123 Ford Dr. ] <- [ | ]
7-
ctor - copy (GENERAL) cp-assign [ Clara | 123 Ford Dr. ] <- [ Clara | 123 Ford Dr. ]
87
assign - move mv-assign [ Clara | 123 Ford Dr. ] <- [ | ]

regression-tests/test-results/gcc-10-c++20/pure2-expected-is-as.cpp.output

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pure2-expected-is-as.cpp2:85:34: error: parse error in template argument list
3333
pure2-expected-is-as.cpp2:85:159: error: parse error in template argument list
3434
In file included from pure2-expected-is-as.cpp:7:
3535
pure2-expected-is-as.cpp2:85:309: error: parse error in template argument list
36-
../../../include/cpp2util.h:317:66: note: in definition of macro ‘CPP2_TYPEOF’
37-
317 | #define CPP2_TYPEOF(x) std::remove_cvref_t<decltype(x)>
36+
../../../include/cpp2util.h:323:66: note: in definition of macro ‘CPP2_TYPEOF’
37+
323 | #define CPP2_TYPEOF(x) std::remove_cvref_t<decltype(x)>
3838
| ^
3939
pure2-expected-is-as.cpp2:85:430: error: parse error in template argument list

regression-tests/test-results/gcc-10-c++20/pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp.execution

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ ctor - from string construct [ Henry | 123 Ford Dr. ]
44
ctor - from string assign [ Clara | 123 Ford Dr. ]
55
ctor - copy (GENERAL) cp-construct [ Clara | 123 Ford Dr. ] <- [ Clara | 123 Ford Dr. ]
66
ctor - move mv-construct [ Clara(CM) | 123 Ford Dr. ] <- [ | ]
7-
ctor - copy (GENERAL) cp-assign [ Clara | 123 Ford Dr. ] <- [ Clara | 123 Ford Dr. ]
87
assign - move mv-assign [ Clara | 123 Ford Dr. ] <- [ | ]

regression-tests/test-results/gcc-14-c++2b/pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp.execution

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ ctor - from string construct [ Henry | 123 Ford Dr. ]
44
ctor - from string assign [ Clara | 123 Ford Dr. ]
55
ctor - copy (GENERAL) cp-construct [ Clara | 123 Ford Dr. ] <- [ Clara | 123 Ford Dr. ]
66
ctor - move mv-construct [ Clara(CM) | 123 Ford Dr. ] <- [ | ]
7-
ctor - copy (GENERAL) cp-assign [ Clara | 123 Ford Dr. ] <- [ Clara | 123 Ford Dr. ]
87
assign - move mv-assign [ Clara | 123 Ford Dr. ] <- [ | ]

regression-tests/test-results/msvc-2022-c++latest/pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp.execution

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ ctor - from string construct [ Henry | 123 Ford Dr. ]
44
ctor - from string assign [ Clara | 123 Ford Dr. ]
55
ctor - copy (GENERAL) cp-construct [ Clara | 123 Ford Dr. ] <- [ Clara | 123 Ford Dr. ]
66
ctor - move mv-construct [ Clara(CM) | 123 Ford Dr. ] <- [ | ]
7-
ctor - copy (GENERAL) cp-assign [ Clara | 123 Ford Dr. ] <- [ Clara | 123 Ford Dr. ]
87
assign - move mv-assign [ Clara | 123 Ford Dr. ] <- [ | ]

regression-tests/test-results/pure2-last-use.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,6 @@ class issue_857 {
156156
private: std::unique_ptr<int> a;
157157
private: std::unique_ptr<int> b;
158158
public: issue_857(issue_857&& that) noexcept;
159-
#line 172 "pure2-last-use.cpp2"
160-
public: auto operator=(issue_857&& that) noexcept -> issue_857& ;
161159
// operator=: (move this) = {
162160
// f_inout(a); // error, can't pass rvalue to inout param
163161
// f_inout(this.b); // error, can't pass rvalue to inout param
@@ -724,11 +722,6 @@ auto issue_850() -> void{
724722
issue_857::issue_857(issue_857&& that) noexcept
725723
: a{ std::move(that).a }
726724
, b{ std::move(that).b }{}
727-
#line 172 "pure2-last-use.cpp2"
728-
auto issue_857::operator=(issue_857&& that) noexcept -> issue_857& {
729-
a = std::move(that).a;
730-
b = std::move(that).b;
731-
return *this; }
732725

733726
#line 181 "pure2-last-use.cpp2"
734727
[[nodiscard]] auto issue_857::h() & -> decltype(auto) { return f_inout(a); }

regression-tests/test-results/pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ class myclass;
2020
class myclass {
2121

2222
public: myclass(myclass const& that);
23-
#line 4 "pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp2"
24-
public: auto operator=(myclass const& that) -> myclass& ;
2523

2624
#line 8 "pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp2"
2725
public: myclass(myclass&& that) noexcept;
@@ -65,15 +63,6 @@ auto main() -> int;
6563
#line 5 "pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp2"
6664
std::cout << "ctor - copy (GENERAL)";
6765
}
68-
#line 4 "pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp2"
69-
auto myclass::operator=(myclass const& that) -> myclass& {
70-
name = that.name;
71-
addr = that.addr;
72-
#line 5 "pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp2"
73-
std::cout << "ctor - copy (GENERAL)";
74-
return *this;
75-
#line 6 "pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp2"
76-
}
7766

7867
#line 8 "pure2-types-smf-and-that-3-provide-mvconstruct-and-mvassign.cpp2"
7968
myclass::myclass(myclass&& that) noexcept
@@ -140,9 +129,9 @@ auto main() -> int{
140129
CPP2_UFCS(print)(z, " mv-construct ", " <- ");
141130
CPP2_UFCS(print)(cpp2::move(x), "", "\n");
142131

143-
z = y;
144-
CPP2_UFCS(print)(z, " cp-assign ", " <- ");
145-
CPP2_UFCS(print)(y, "", "\n");
132+
// z = y;
133+
// z.print(" cp-assign ", " <- ");
134+
// y.print("", "\n");
146135

147136
z = { std::move(y) };
148137
CPP2_UFCS(print)(cpp2::move(z), " mv-assign ", " <- ");

0 commit comments

Comments
 (0)