Skip to content

Commit

Permalink
Fix default argument lowering (#1262)
Browse files Browse the repository at this point in the history
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 <herb.sutter@gmail.com>
  • Loading branch information
hsutter authored Aug 27, 2024
1 parent c599f41 commit 2e23597
Show file tree
Hide file tree
Showing 48 changed files with 302 additions and 156 deletions.
39 changes: 36 additions & 3 deletions regression-tests/pure2-default-arguments.cpp2
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,52 @@ my_function_name: (

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

combine_maps:
<
AssocContainer,
Func: type = std::plus<>
>
(
inout map1: AssocContainer,
map2: AssocContainer,
func: Func = ()
)
= {
for map2 do(kv) {
map1[kv.first] = func(map1[kv.first], kv.second);
}
}

myclass: <T: type = int, N: int = 42> type = {
memfunc: <TT: type = int, NN: int = 42> (MM: int = 43) = { _ = MM; }
}
myfunc: <T: type = int, N: int = 42> (M: int = 43) = {
_ = M;
: <TT: type = int, NN: int = 42> (MM: int = 43) = { _ = MM; };
}

main: (args) = {
my_function_name();
f();
f(1);
f(2);

: <V: bool = gcc_clang_msvc_min_versions( 1400, 1600, 1920)> () = {
: <V: bool = gcc_clang_msvc_min_versions( 1400, 1600, 1920 )> () = {
if constexpr V {
std::cout << "a newer compiler\n";
std::cout << "\na newer compiler\n";
}
else {
std::cout << "an older compiler\n";
std::cout << "\nan older compiler\n";
}
} ();

m1: std::map<int, int> = ();
m1[1] = 11;

m2: std::map<int, int> = ();
m2[1] = 22;

combine_maps( m1, m2, :(x,y) x+y+33 );

std::cout << "(m1.size())$, (m2.size())$, (m1[1])$\n";
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ mixed-bugfix-for-ufcs-non-local.cpp2:27:29: error: a lambda expression cannot ap
#define CPP2_UFCS_(LAMBDADEFCAPT,SFINAE,MVFWD,QUALID,TEMPKW,...) \
^
mixed-bugfix-for-ufcs-non-local.cpp2:41:84: error: lambda expression in an unevaluated operand
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).
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).
^
../../../include/cpp2util.h:2137:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL'
#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
pure2-bugfix-for-non-local-function-expression.cpp2:5:34: error: lambda expression in an unevaluated operand
template<typename T> concept v = []() mutable -> bool { return true; }();
template<typename T> concept v = []() -> bool { return true; }();
^
pure2-bugfix-for-non-local-function-expression.cpp2:7:41: error: lambda expression in an unevaluated operand
using u = std::type_identity_t<decltype([]() mutable -> void{})>;
using u = std::type_identity_t<decltype([]() -> void{})>;
^
pure2-bugfix-for-non-local-function-expression.cpp2:9:47: error: lambda expression in an unevaluated operand
class t: public std::type_identity_t<decltype([]() mutable -> void{})> {
class t: public std::type_identity_t<decltype([]() -> void{})> {
^
3 errors generated.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pure2-last-use.cpp2:945:44: error: a lambda expression cannot appear in this context
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).
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).
^
1 error generated.
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
calling: int main(int, char**)
012an older compiler
012
a newer compiler
1, 1, 66
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ auto g() -> void{

#line 40 "mixed-bugfix-for-ufcs-non-local.cpp2"
inline CPP2_CONSTEXPR bool u::b{ CPP2_UFCS_NONLOCAL(f)(o) };
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).
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).
auto u::g(auto const& s, auto const& sz) -> void{
if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(sz)(s) != 0) ) { cpp2::cpp2_default.report_violation(""); }}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@
// Passing a function expression
std::ranges::for_each(
vec,
[](auto& x) mutable -> void { x += "-ish"; }
[](auto& x) -> void { x += "-ish"; }
);

// Initializing from a function expression
auto callback {[](auto& x) mutable -> void { x += " maybe"; }};
auto callback {[](auto& x) -> void { x += " maybe"; }};
std::ranges::for_each(
vec,
cpp2::move(callback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
std::ranges::for_each
(vec, [_0 = cpp2::move(y)](auto const& x) mutable -> void { std::cout << x << _0; });

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

for ( auto const& str : cpp2::move(vec) )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
"hello", "2022"};

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

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

for ( auto const& str : cpp2::move(vec) )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
std::cout << CPP2_UFCS(c_str)((*cpp2::impl::assert_not_null(_0))) << x << *cpp2::impl::assert_not_null(_0); }
);

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

for ( auto const& str : cpp2::move(vec) )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
std::ranges::for_each
(vec, [_0 = cpp2::move(y)](auto const& x) mutable -> void { std::cout << _0 << x << _0; });

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

for ( auto const& str : cpp2::move(vec) )
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
calling: int __cdecl main(const int,char **)
012a newer compiler
012
a newer compiler
1, 1, 66
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ class t;
// Standalone Cpp1 repro: https://godbolt.org/z/dznnYTvc6

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

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

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

};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ auto main() -> int{
static_cast<void>(CPP2_UFCS_TEMPLATE(f<t,t>)(n, 0, 0));
static_cast<void>(CPP2_UFCS_TEMPLATE(f<t,t>)(a<t,t>, 0, 0));

static_cast<void>([](auto const& a, auto const& f) mutable -> void{static_cast<void>(CPP2_UFCS(f)(CPP2_UFCS(f)(a, a))); });
static_cast<void>([](auto const& a, auto const& f) -> void{static_cast<void>(CPP2_UFCS(f)(CPP2_UFCS(f)(a, a))); });
// _ = 0.std::min<int>(0);
static_cast<void>(CPP2_UFCS_QUALIFIED_TEMPLATE((ns::t<0,0>::),f<0>)(0));
}
Expand Down
22 changes: 11 additions & 11 deletions regression-tests/test-results/pure2-bugfix-for-ufcs-name-lookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ auto const& f{t().f()};
static_assert(f == 0);
}
{
auto f {[](auto const& f) mutable -> auto{
auto f {[](auto const& f) -> auto{
if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(f)(t()) == 0) ) { cpp2::cpp2_default.report_violation(""); }
return CPP2_UFCS(f)(u());
}(identity())};
Expand All @@ -112,7 +112,7 @@ auto const& f{t().f()};
// _ = f;
}
{
auto f {[]() mutable -> void{
auto f {[]() -> void{
{
cpp2::impl::in<identity> f{identity()};
#line 59 "pure2-bugfix-for-ufcs-name-lookup.cpp2"
Expand All @@ -128,26 +128,26 @@ cpp2::impl::in<identity> f{identity()};
static_cast<void>(cpp2::move(f));
}
{
auto f {[]() mutable -> void{
auto f {[]() -> void{
using ns::f;
static_assert(CPP2_UFCS(f)(t()) == 0);
// static_assert(u().f() == 1);
}};
static_cast<void>(cpp2::move(f));
}
{
auto f {[]() mutable -> void{
auto f {[]() -> void{
static_assert(t().f() == 0);
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> std::void_t<decltype(T().f())>{}};
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) -> std::void_t<decltype(T().f())>{}};
static_assert(!(std::is_invocable_v<decltype(cpp2::move(g)),u>));
using ns::f;
}};
static_cast<void>(cpp2::move(f));
}
{
auto f {[]() mutable -> void{
auto f {[]() -> void{
using ns::f;
static_cast<void>([]() mutable -> void{
static_cast<void>([]() -> void{
static_assert(CPP2_UFCS(f)(t()) == 0);
// static_assert(u().f() == 1);
});
Expand All @@ -159,15 +159,15 @@ cpp2::impl::in<identity> f{identity()};
static_cast<void>(cpp2::move(f));
}
{
auto f {[]() mutable -> void{
static_cast<void>([]() mutable -> void{
auto f {[]() -> void{
static_cast<void>([]() -> void{
static_assert(t().f() == 0);
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> std::void_t<decltype(T().f())>{}};
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) -> std::void_t<decltype(T().f())>{}};
static_assert(!(std::is_invocable_v<decltype(cpp2::move(g)),u>));
});
{
static_assert(t().f() == 0);
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) mutable -> std::void_t<decltype(T().f())>{}};
auto g {[]<typename T>([[maybe_unused]] T const& unnamed_param_1) -> std::void_t<decltype(T().f())>{}};
static_assert(!(std::is_invocable_v<decltype(cpp2::move(g)),u>));
}
using ns::f;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ template<typename T> [[nodiscard]] auto f() -> std::type_identity_t<decltype(CPP

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

Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ extern cpp2::i32 y;
{
auto const& x{t()};
#line 6 "pure2-bugfix-for-unbraced-function-expression.cpp2"
{CPP2_ASSERT_IN_BOUNDS(x, []() mutable -> auto { return 0; }); }
{CPP2_ASSERT_IN_BOUNDS(x, []() -> auto { return 0; }); }
}
{
auto const& x{t()};
#line 7 "pure2-bugfix-for-unbraced-function-expression.cpp2"
{CPP2_ASSERT_IN_BOUNDS(x, []() mutable -> auto { return 0; }); }
{CPP2_ASSERT_IN_BOUNDS(x, []() -> auto { return 0; }); }
}

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

return cpp2::i32{0};
}
Expand Down
69 changes: 65 additions & 4 deletions regression-tests/test-results/pure2-default-arguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

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

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


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

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

template
<
typename AssocContainer,
typename Func = std::plus<>
> auto combine_maps
(
AssocContainer& map1,
AssocContainer const& map2,
Func const& func = {}
) -> void;

#line 30 "pure2-default-arguments.cpp2"
template<typename T, int N> class myclass {
public: template<typename TT = int, int NN = 42> static auto memfunc(cpp2::impl::in<int> MM = 43) -> void;
public: myclass() = default;
public: myclass(myclass const&) = delete; /* No 'that' constructor, suppress copy */
public: auto operator=(myclass const&) -> void = delete;

#line 32 "pure2-default-arguments.cpp2"
};
template <typename T = int, int N = 42> auto myfunc(cpp2::impl::in<int> M = 43) -> void;

#line 38 "pure2-default-arguments.cpp2"
auto main(int const argc_, char** argv_) -> int;

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

#line 14 "pure2-default-arguments.cpp2"
template
<
typename AssocContainer,
typename Func
> auto combine_maps
(
AssocContainer& map1,
AssocContainer const& map2,
Func const& func
) -> void
{
for ( auto const& kv : map2 ) {
CPP2_ASSERT_IN_BOUNDS(map1, kv.first) = func(CPP2_ASSERT_IN_BOUNDS(map1, kv.first), kv.second);
}
}

#line 31 "pure2-default-arguments.cpp2"
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); }

#line 33 "pure2-default-arguments.cpp2"
template <typename T, int N> auto myfunc(cpp2::impl::in<int> M) -> void{
static_cast<void>(M);
[]<typename TT = int, int NN = 42>(cpp2::impl::in<int> MM = 43) -> void{static_cast<void>(MM); };
}

#line 38 "pure2-default-arguments.cpp2"
auto main(int const argc_, char** argv_) -> int{
auto const args = cpp2::make_args(argc_, argv_);
#line 15 "pure2-default-arguments.cpp2"
#line 39 "pure2-default-arguments.cpp2"
my_function_name();
f();
f(1);
f(2);

[]<bool V = gcc_clang_msvc_min_versions(1400, 1600, 1920)>() mutable -> void{
[]<bool V = gcc_clang_msvc_min_versions(1400, 1600, 1920)>() -> void{
if constexpr (V) {
std::cout << "a newer compiler\n";
std::cout << "\na newer compiler\n";
}
else {
std::cout << "an older compiler\n";
std::cout << "\nan older compiler\n";
}
}();

std::map<int,int> m1 {};
CPP2_ASSERT_IN_BOUNDS_LITERAL(m1, 1) = 11;

std::map<int,int> m2 {};
CPP2_ASSERT_IN_BOUNDS_LITERAL(m2, 1) = 22;

combine_maps(m1, m2, [](auto const& x, auto const& y) -> auto { return x + y + 33; });

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";
}

Loading

0 comments on commit 2e23597

Please sign in to comment.