Skip to content

Commit 2e23597

Browse files
authored
Fix default argument lowering (#1262)
There should now be enough test cases to exercise all the places you can put default arguments Also, only emit Cpp1 lambdas as 'mutable' if there are captures (when it matters) so that pure function expressions are not 'mutable' Closes #1235 Signed-off-by: Herb Sutter <[email protected]>
1 parent c599f41 commit 2e23597

File tree

48 files changed

+302
-156
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+302
-156
lines changed

regression-tests/pure2-default-arguments.cpp2

+36-3
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,52 @@ my_function_name: (
1111

1212
f: (x: i32 = 0) = { std::cout << x; }
1313

14+
combine_maps:
15+
<
16+
AssocContainer,
17+
Func: type = std::plus<>
18+
>
19+
(
20+
inout map1: AssocContainer,
21+
map2: AssocContainer,
22+
func: Func = ()
23+
)
24+
= {
25+
for map2 do(kv) {
26+
map1[kv.first] = func(map1[kv.first], kv.second);
27+
}
28+
}
29+
30+
myclass: <T: type = int, N: int = 42> type = {
31+
memfunc: <TT: type = int, NN: int = 42> (MM: int = 43) = { _ = MM; }
32+
}
33+
myfunc: <T: type = int, N: int = 42> (M: int = 43) = {
34+
_ = M;
35+
: <TT: type = int, NN: int = 42> (MM: int = 43) = { _ = MM; };
36+
}
37+
1438
main: (args) = {
1539
my_function_name();
1640
f();
1741
f(1);
1842
f(2);
1943

20-
: <V: bool = gcc_clang_msvc_min_versions( 1400, 1600, 1920)> () = {
44+
: <V: bool = gcc_clang_msvc_min_versions( 1400, 1600, 1920 )> () = {
2145
if constexpr V {
22-
std::cout << "a newer compiler\n";
46+
std::cout << "\na newer compiler\n";
2347
}
2448
else {
25-
std::cout << "an older compiler\n";
49+
std::cout << "\nan older compiler\n";
2650
}
2751
} ();
2852

53+
m1: std::map<int, int> = ();
54+
m1[1] = 11;
55+
56+
m2: std::map<int, int> = ();
57+
m2[1] = 22;
58+
59+
combine_maps( m1, m2, :(x,y) x+y+33 );
60+
61+
std::cout << "(m1.size())$, (m2.size())$, (m1[1])$\n";
2962
}

regression-tests/test-results/clang-12-c++20/mixed-bugfix-for-ufcs-non-local.cpp.output

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ mixed-bugfix-for-ufcs-non-local.cpp2:27:29: error: a lambda expression cannot ap
107107
#define CPP2_UFCS_(LAMBDADEFCAPT,SFINAE,MVFWD,QUALID,TEMPKW,...) \
108108
^
109109
mixed-bugfix-for-ufcs-non-local.cpp2:41:84: error: lambda expression in an unevaluated operand
110-
inline CPP2_CONSTEXPR bool u::c{ [](cpp2::impl::in<std::type_identity_t<decltype(CPP2_UFCS_NONLOCAL(f)(o))>> x) mutable -> auto { return x; }(true) };// Fails on Clang 12 (lambda in unevaluated context).
110+
inline CPP2_CONSTEXPR bool u::c{ [](cpp2::impl::in<std::type_identity_t<decltype(CPP2_UFCS_NONLOCAL(f)(o))>> x) -> auto { return x; }(true) };// Fails on Clang 12 (lambda in unevaluated context).
111111
^
112112
../../../include/cpp2util.h:2137:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL'
113113
#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__)
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
pure2-bugfix-for-non-local-function-expression.cpp2:5:34: error: lambda expression in an unevaluated operand
2-
template<typename T> concept v = []() mutable -> bool { return true; }();
2+
template<typename T> concept v = []() -> bool { return true; }();
33
^
44
pure2-bugfix-for-non-local-function-expression.cpp2:7:41: error: lambda expression in an unevaluated operand
5-
using u = std::type_identity_t<decltype([]() mutable -> void{})>;
5+
using u = std::type_identity_t<decltype([]() -> void{})>;
66
^
77
pure2-bugfix-for-non-local-function-expression.cpp2:9:47: error: lambda expression in an unevaluated operand
8-
class t: public std::type_identity_t<decltype([]() mutable -> void{})> {
8+
class t: public std::type_identity_t<decltype([]() -> void{})> {
99
^
1010
3 errors generated.
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
pure2-last-use.cpp2:945:44: error: a lambda expression cannot appear in this context
2-
static_cast<void>([_0 = std::array<int,[](auto const& x) mutable -> auto { return identity(x); }(0)>()]() mutable -> auto { return _0; });// Fails on Clang 12 (lambda in unevaluated context).
2+
static_cast<void>([_0 = std::array<int,[](auto const& x) -> auto { return identity(x); }(0)>()]() mutable -> auto { return _0; });// Fails on Clang 12 (lambda in unevaluated context).
33
^
44
1 error generated.
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
calling: int main(int, char**)
2-
012an older compiler
2+
012
3+
a newer compiler
4+
1, 1, 66

regression-tests/test-results/mixed-bugfix-for-ufcs-non-local.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ auto g() -> void{
9595

9696
#line 40 "mixed-bugfix-for-ufcs-non-local.cpp2"
9797
inline CPP2_CONSTEXPR bool u::b{ CPP2_UFCS_NONLOCAL(f)(o) };
98-
inline CPP2_CONSTEXPR bool u::c{ [](cpp2::impl::in<std::type_identity_t<decltype(CPP2_UFCS_NONLOCAL(f)(o))>> x) mutable -> auto { return x; }(true) };// Fails on Clang 12 (lambda in unevaluated context).
98+
inline CPP2_CONSTEXPR bool u::c{ [](cpp2::impl::in<std::type_identity_t<decltype(CPP2_UFCS_NONLOCAL(f)(o))>> x) -> auto { return x; }(true) };// Fails on Clang 12 (lambda in unevaluated context).
9999
auto u::g(auto const& s, auto const& sz) -> void{
100100
if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(sz)(s) != 0) ) { cpp2::cpp2_default.report_violation(""); }}
101101

regression-tests/test-results/mixed-function-expression-and-std-for-each.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@
3232
// Passing a function expression
3333
std::ranges::for_each(
3434
vec,
35-
[](auto& x) mutable -> void { x += "-ish"; }
35+
[](auto& x) -> void { x += "-ish"; }
3636
);
3737

3838
// Initializing from a function expression
39-
auto callback {[](auto& x) mutable -> void { x += " maybe"; }};
39+
auto callback {[](auto& x) -> void { x += " maybe"; }};
4040
std::ranges::for_each(
4141
vec,
4242
cpp2::move(callback)

regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each-with-capture.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
std::ranges::for_each
3535
(vec, [_0 = cpp2::move(y)](auto const& x) mutable -> void { std::cout << x << _0; });
3636

37-
auto callback {[](auto& x) mutable -> void { x += "-ish"; }};
37+
auto callback {[](auto& x) -> void { x += "-ish"; }};
3838
std::ranges::for_each(vec, cpp2::move(callback));
3939

4040
for ( auto const& str : cpp2::move(vec) )

regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
"hello", "2022"};
3232

3333
std::ranges::for_each
34-
(vec, [](auto const& x) mutable -> void { std::cout << x << "\n"; });
34+
(vec, [](auto const& x) -> void { std::cout << x << "\n"; });
3535

36-
auto callback {[](auto& x) mutable -> void { x += "-ish"; }};
36+
auto callback {[](auto& x) -> void { x += "-ish"; }};
3737
std::ranges::for_each(vec, cpp2::move(callback));
3838

3939
for ( auto const& str : cpp2::move(vec) )

regression-tests/test-results/mixed-function-expression-with-pointer-capture.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
std::cout << CPP2_UFCS(c_str)((*cpp2::impl::assert_not_null(_0))) << x << *cpp2::impl::assert_not_null(_0); }
3636
);
3737

38-
auto callback {[](auto& x) mutable -> void { x += "-ish"; }};
38+
auto callback {[](auto& x) -> void { x += "-ish"; }};
3939
std::ranges::for_each(vec, cpp2::move(callback));
4040

4141
for ( auto const& str : cpp2::move(vec) )

regression-tests/test-results/mixed-function-expression-with-repeated-capture.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
std::ranges::for_each
3535
(vec, [_0 = cpp2::move(y)](auto const& x) mutable -> void { std::cout << _0 << x << _0; });
3636

37-
auto callback {[](auto& x) mutable -> void { x += "-ish"; }};
37+
auto callback {[](auto& x) -> void { x += "-ish"; }};
3838
std::ranges::for_each(vec, cpp2::move(callback));
3939

4040
for ( auto const& str : cpp2::move(vec) )
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
calling: int __cdecl main(const int,char **)
2-
012a newer compiler
2+
012
3+
a newer compiler
4+
1, 1, 66

regression-tests/test-results/pure2-bugfix-for-non-local-function-expression.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ class t;
2020
// Standalone Cpp1 repro: https://godbolt.org/z/dznnYTvc6
2121

2222
#line 5 "pure2-bugfix-for-non-local-function-expression.cpp2"
23-
template<typename T> concept v = []() mutable -> bool { return true; }();
23+
template<typename T> concept v = []() -> bool { return true; }();
2424

25-
using u = std::type_identity_t<decltype([]() mutable -> void{})>;
25+
using u = std::type_identity_t<decltype([]() -> void{})>;
2626

27-
class t: public std::type_identity_t<decltype([]() mutable -> void{})> {
27+
class t: public std::type_identity_t<decltype([]() -> void{})> {
2828

2929
};
3030

regression-tests/test-results/pure2-bugfix-for-ufcs-arguments.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ auto main() -> int{
151151
static_cast<void>(CPP2_UFCS_TEMPLATE(f<t,t>)(n, 0, 0));
152152
static_cast<void>(CPP2_UFCS_TEMPLATE(f<t,t>)(a<t,t>, 0, 0));
153153

154-
static_cast<void>([](auto const& a, auto const& f) mutable -> void{static_cast<void>(CPP2_UFCS(f)(CPP2_UFCS(f)(a, a))); });
154+
static_cast<void>([](auto const& a, auto const& f) -> void{static_cast<void>(CPP2_UFCS(f)(CPP2_UFCS(f)(a, a))); });
155155
// _ = 0.std::min<int>(0);
156156
static_cast<void>(CPP2_UFCS_QUALIFIED_TEMPLATE((ns::t<0,0>::),f<0>)(0));
157157
}

regression-tests/test-results/pure2-bugfix-for-ufcs-name-lookup.cpp

+11-11
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ auto const& f{t().f()};
9898
static_assert(f == 0);
9999
}
100100
{
101-
auto f {[](auto const& f) mutable -> auto{
101+
auto f {[](auto const& f) -> auto{
102102
if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(f)(t()) == 0) ) { cpp2::cpp2_default.report_violation(""); }
103103
return CPP2_UFCS(f)(u());
104104
}(identity())};
@@ -112,7 +112,7 @@ auto const& f{t().f()};
112112
// _ = f;
113113
}
114114
{
115-
auto f {[]() mutable -> void{
115+
auto f {[]() -> void{
116116
{
117117
cpp2::impl::in<identity> f{identity()};
118118
#line 59 "pure2-bugfix-for-ufcs-name-lookup.cpp2"
@@ -128,26 +128,26 @@ cpp2::impl::in<identity> f{identity()};
128128
static_cast<void>(cpp2::move(f));
129129
}
130130
{
131-
auto f {[]() mutable -> void{
131+
auto f {[]() -> void{
132132
using ns::f;
133133
static_assert(CPP2_UFCS(f)(t()) == 0);
134134
// static_assert(u().f() == 1);
135135
}};
136136
static_cast<void>(cpp2::move(f));
137137
}
138138
{
139-
auto f {[]() mutable -> void{
139+
auto f {[]() -> void{
140140
static_assert(t().f() == 0);
141-
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> std::void_t<decltype(T().f())>{}};
141+
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) -> std::void_t<decltype(T().f())>{}};
142142
static_assert(!(std::is_invocable_v<decltype(cpp2::move(g)),u>));
143143
using ns::f;
144144
}};
145145
static_cast<void>(cpp2::move(f));
146146
}
147147
{
148-
auto f {[]() mutable -> void{
148+
auto f {[]() -> void{
149149
using ns::f;
150-
static_cast<void>([]() mutable -> void{
150+
static_cast<void>([]() -> void{
151151
static_assert(CPP2_UFCS(f)(t()) == 0);
152152
// static_assert(u().f() == 1);
153153
});
@@ -159,15 +159,15 @@ cpp2::impl::in<identity> f{identity()};
159159
static_cast<void>(cpp2::move(f));
160160
}
161161
{
162-
auto f {[]() mutable -> void{
163-
static_cast<void>([]() mutable -> void{
162+
auto f {[]() -> void{
163+
static_cast<void>([]() -> void{
164164
static_assert(t().f() == 0);
165-
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> std::void_t<decltype(T().f())>{}};
165+
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) -> std::void_t<decltype(T().f())>{}};
166166
static_assert(!(std::is_invocable_v<decltype(cpp2::move(g)),u>));
167167
});
168168
{
169169
static_assert(t().f() == 0);
170-
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> std::void_t<decltype(T().f())>{}};
170+
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) -> std::void_t<decltype(T().f())>{}};
171171
static_assert(!(std::is_invocable_v<decltype(cpp2::move(g)),u>));
172172
}
173173
using ns::f;

regression-tests/test-results/pure2-bugfix-for-ufcs-sfinae.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ template<typename T> [[nodiscard]] auto f() -> std::type_identity_t<decltype(CPP
3434

3535
#line 5 "pure2-bugfix-for-ufcs-sfinae.cpp2"
3636
auto main() -> int{
37-
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> std::void_t<decltype(f<T>())>{}};
37+
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) -> std::void_t<decltype(f<T>())>{}};
3838
static_assert(!(std::is_invocable_v<decltype(cpp2::move(g)),B>));
3939
}
4040

regression-tests/test-results/pure2-bugfix-for-unbraced-function-expression.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ extern cpp2::i32 y;
4242
{
4343
auto const& x{t()};
4444
#line 6 "pure2-bugfix-for-unbraced-function-expression.cpp2"
45-
{CPP2_ASSERT_IN_BOUNDS(x, []() mutable -> auto { return 0; }); }
45+
{CPP2_ASSERT_IN_BOUNDS(x, []() -> auto { return 0; }); }
4646
}
4747
{
4848
auto const& x{t()};
4949
#line 7 "pure2-bugfix-for-unbraced-function-expression.cpp2"
50-
{CPP2_ASSERT_IN_BOUNDS(x, []() mutable -> auto { return 0; }); }
50+
{CPP2_ASSERT_IN_BOUNDS(x, []() -> auto { return 0; }); }
5151
}
5252

5353
#line 9 "pure2-bugfix-for-unbraced-function-expression.cpp2"
54-
if (cpp2::cpp2_default.is_active() && !(!((cpp2::impl::is<int>([]() mutable -> auto { return 0; })))) ) { cpp2::cpp2_default.report_violation(""); }
54+
if (cpp2::cpp2_default.is_active() && !(!((cpp2::impl::is<int>([]() -> auto { return 0; })))) ) { cpp2::cpp2_default.report_violation(""); }
5555

5656
return cpp2::i32{0};
5757
}

regression-tests/test-results/pure2-default-arguments.cpp

+65-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
#line 1 "pure2-default-arguments.cpp2"
1010

11+
#line 30 "pure2-default-arguments.cpp2"
12+
template<typename T = int, int N = 42> class myclass;
13+
1114

1215
//=== Cpp2 type definitions and function declarations ===========================
1316

@@ -24,6 +27,29 @@ auto my_function_name(
2427
#line 12 "pure2-default-arguments.cpp2"
2528
auto f(cpp2::impl::in<cpp2::i32> x = 0) -> void;
2629

30+
template
31+
<
32+
typename AssocContainer,
33+
typename Func = std::plus<>
34+
> auto combine_maps
35+
(
36+
AssocContainer& map1,
37+
AssocContainer const& map2,
38+
Func const& func = {}
39+
) -> void;
40+
41+
#line 30 "pure2-default-arguments.cpp2"
42+
template<typename T, int N> class myclass {
43+
public: template<typename TT = int, int NN = 42> static auto memfunc(cpp2::impl::in<int> MM = 43) -> void;
44+
public: myclass() = default;
45+
public: myclass(myclass const&) = delete; /* No 'that' constructor, suppress copy */
46+
public: auto operator=(myclass const&) -> void = delete;
47+
48+
#line 32 "pure2-default-arguments.cpp2"
49+
};
50+
template <typename T = int, int N = 42> auto myfunc(cpp2::impl::in<int> M = 43) -> void;
51+
52+
#line 38 "pure2-default-arguments.cpp2"
2753
auto main(int const argc_, char** argv_) -> int;
2854

2955
//=== Cpp2 function definitions =================================================
@@ -42,22 +68,57 @@ auto my_function_name(
4268
auto f(cpp2::impl::in<cpp2::i32> x) -> void{std::cout << x; }
4369

4470
#line 14 "pure2-default-arguments.cpp2"
71+
template
72+
<
73+
typename AssocContainer,
74+
typename Func
75+
> auto combine_maps
76+
(
77+
AssocContainer& map1,
78+
AssocContainer const& map2,
79+
Func const& func
80+
) -> void
81+
{
82+
for ( auto const& kv : map2 ) {
83+
CPP2_ASSERT_IN_BOUNDS(map1, kv.first) = func(CPP2_ASSERT_IN_BOUNDS(map1, kv.first), kv.second);
84+
}
85+
}
86+
87+
#line 31 "pure2-default-arguments.cpp2"
88+
template <typename T, int N> template<typename TT, int NN> auto myclass<T,N>::memfunc(cpp2::impl::in<int> MM) -> void{static_cast<void>(MM); }
89+
90+
#line 33 "pure2-default-arguments.cpp2"
91+
template <typename T, int N> auto myfunc(cpp2::impl::in<int> M) -> void{
92+
static_cast<void>(M);
93+
[]<typename TT = int, int NN = 42>(cpp2::impl::in<int> MM = 43) -> void{static_cast<void>(MM); };
94+
}
95+
96+
#line 38 "pure2-default-arguments.cpp2"
4597
auto main(int const argc_, char** argv_) -> int{
4698
auto const args = cpp2::make_args(argc_, argv_);
47-
#line 15 "pure2-default-arguments.cpp2"
99+
#line 39 "pure2-default-arguments.cpp2"
48100
my_function_name();
49101
f();
50102
f(1);
51103
f(2);
52104

53-
[]<bool V = gcc_clang_msvc_min_versions(1400, 1600, 1920)>() mutable -> void{
105+
[]<bool V = gcc_clang_msvc_min_versions(1400, 1600, 1920)>() -> void{
54106
if constexpr (V) {
55-
std::cout << "a newer compiler\n";
107+
std::cout << "\na newer compiler\n";
56108
}
57109
else {
58-
std::cout << "an older compiler\n";
110+
std::cout << "\nan older compiler\n";
59111
}
60112
}();
61113

114+
std::map<int,int> m1 {};
115+
CPP2_ASSERT_IN_BOUNDS_LITERAL(m1, 1) = 11;
116+
117+
std::map<int,int> m2 {};
118+
CPP2_ASSERT_IN_BOUNDS_LITERAL(m2, 1) = 22;
119+
120+
combine_maps(m1, m2, [](auto const& x, auto const& y) -> auto { return x + y + 33; });
121+
122+
std::cout << "" + cpp2::to_string(CPP2_UFCS(size)(m1)) + ", " + cpp2::to_string(CPP2_UFCS(size)(cpp2::move(m2))) + ", " + cpp2::to_string(CPP2_ASSERT_IN_BOUNDS_LITERAL(m1, 1)) + "\n";
62123
}
63124

0 commit comments

Comments
 (0)