Skip to content

Commit

Permalink
Emit anonymous functions as mutable lambdas
Browse files Browse the repository at this point in the history
This example now works:

    main: () = {
        v: std::vector = ( 1, 2, 3, 4, 5 );

        //  Definite last use of v => move-capture v into f's closure
        f := :() -> forward _ = v$;

        //  Now we can access the vector captured inside f()...
        f().push_back(6);
        for f() do(e) std::cout << e;       // prints 123456
    }
  • Loading branch information
hsutter committed Nov 21, 2023
1 parent 0b333f3 commit 4bd0c04
Show file tree
Hide file tree
Showing 17 changed files with 40 additions and 34 deletions.
8 changes: 4 additions & 4 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,7 @@ auto is( X const& x ) -> bool {

// Values
//
inline constexpr auto is( auto const& x, auto const& value ) -> bool
inline constexpr auto is( auto const& x, auto&& value ) -> bool
{
// Value with customized operator_is case
if constexpr (requires{ x.op_is(value); }) {
Expand Down Expand Up @@ -1313,7 +1313,7 @@ auto is( std::variant<Ts...> const& x );
// is Value
//
template<typename... Ts>
constexpr auto is( std::variant<Ts...> const& x, auto const& value ) -> bool
constexpr auto is( std::variant<Ts...> const& x, auto&& value ) -> bool
{
// Predicate case
if constexpr (requires{ bool{ value(operator_as< 0>(x)) }; }) { if (x.index() == 0) return value(operator_as< 0>(x)); }
Expand Down Expand Up @@ -1494,7 +1494,7 @@ constexpr auto is( X const& x ) -> bool

// is Value
//
inline constexpr auto is( std::any const& x, auto const& value ) -> bool
inline constexpr auto is( std::any const& x, auto&& value ) -> bool
{
// Predicate case
if constexpr (requires{ bool{ value(x) }; }) {
Expand Down Expand Up @@ -1542,7 +1542,7 @@ constexpr auto is( std::optional<U> const& x ) -> bool
// is Value
//
template<typename T>
constexpr auto is( std::optional<T> const& x, auto const& value ) -> bool
constexpr auto is( std::optional<T> const& x, auto&& value ) -> bool
{
// Predicate case
if constexpr (requires{ bool{ value(x) }; }) {
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 = []() -> bool { return true; }();
template<typename T> concept v = []() mutable -> 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([]() -> void{})>;
using u = std::type_identity_t<decltype([]() mutable -> 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([]() -> void{})> {
class t: public std::type_identity_t<decltype([]() mutable -> void{})> {
^
3 errors generated.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ auto insert_at(cpp2::in<int> where, cpp2::in<int> val) -> void;
"hello", "2022"};

std::string y {"\n"};
auto callback {[_0 = (&y)](auto const& x) -> void { std::cout << x << *cpp2::assert_not_null(_0); }};
auto callback {[_0 = (&y)](auto const& x) mutable -> void { std::cout << x << *cpp2::assert_not_null(_0); }};

std::ranges::for_each(vec, callback);
y = "-ish\n";
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) -> void { x += "-ish"; }
[](auto& x) mutable -> void { x += "-ish"; }
);

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

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

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

for ( auto const& str : 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) -> void { std::cout << x << "\n"; });
(vec, [](auto const& x) mutable -> void { std::cout << x << "\n"; });

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

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

std::string y {"\n"};
std::ranges::for_each(vec, [_0 = (&y)](auto const& x) -> void {
std::ranges::for_each(vec, [_0 = (&y)](auto const& x) mutable -> void {
std::cout << CPP2_UFCS_0(c_str, (*cpp2::assert_not_null(_0))) << x << *cpp2::assert_not_null(_0); }
);

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

for ( auto const& str : vec )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@

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

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

for ( auto const& str : vec )
Expand Down
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 = []() -> bool { return true; }();
template<typename T> concept v = []() mutable -> bool { return true; }();

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

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

};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ using g_ret = int;
[[nodiscard]] auto f() -> f_ret{
int ri {0};
#line 3 "pure2-look-up-parameter-across-unnamed-function.cpp2"
auto pred {[](auto const& e) -> auto { return e == 1; }};
auto pred {[](auto const& e) mutable -> auto { return e == 1; }};
ri = 42;
std::move(pred)(ri);
return std::move(ri); // "return;" is implicit"
Expand All @@ -43,7 +43,7 @@ using g_ret = int;
cpp2::deferred_init<int> ri;
#line 10 "pure2-look-up-parameter-across-unnamed-function.cpp2"
ri.construct(0);
auto pred {[](auto const& e) -> auto { return e == 1; }};
auto pred {[](auto const& e) mutable -> auto { return e == 1; }};
ri.value() = 42;
std::move(pred)(ri.value());
return std::move(ri.value());
Expand Down
2 changes: 1 addition & 1 deletion regression-tests/test-results/pure2-more-wildcards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#line 1 "pure2-more-wildcards.cpp2"

#line 2 "pure2-more-wildcards.cpp2"
[[nodiscard]] auto less_than(auto const& value) -> auto { return [_0 = value](auto const& x) -> auto { return cpp2::cmp_less(x,_0); }; }
[[nodiscard]] auto less_than(auto const& value) -> auto { return [_0 = value](auto const& x) mutable -> auto { return cpp2::cmp_less(x,_0); }; }

[[nodiscard]] auto main() -> int
{
Expand Down
8 changes: 4 additions & 4 deletions regression-tests/test-results/pure2-print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ requires (true) inline CPP2_CONSTEXPR T outer::object_alias = 42;
cpp2::Default.expects(CPP2_UFCS_0(empty, m) == false || false, "message");
cpp2::Bounds.expects([_0 = 0, _1 = CPP2_UFCS_0(ssize, m), _2 = 100]{ return cpp2::cmp_less(_0,_1) && cpp2::cmp_less(_1,_2); }() && true != false, "");
#line 35 "pure2-print.cpp2"
auto a {[]() -> void{}};
auto b {[]() -> void{}};
auto c {[]() -> void{}};
auto a {[]() mutable -> void{}};
auto b {[]() mutable -> void{}};
auto c {[]() mutable -> void{}};

for( ; CPP2_UFCS_0(empty, s); a() ) {break; }

Expand All @@ -133,7 +133,7 @@ requires (true) inline CPP2_CONSTEXPR T outer::object_alias = 42;

cpp2::Default.expects(true, "");

return [_0 = (s + CPP2_ASSERT_IN_BOUNDS(m, 0))]() -> std::string { return _0; }();
return [_0 = (s + CPP2_ASSERT_IN_BOUNDS(m, 0))]() mutable -> std::string { return _0; }();
}

template<typename T> [[nodiscard]] auto outer::mytype::values([[maybe_unused]] T const& unnamed_param_2) const& -> values_ret{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ namespace N {
#line 35 "pure2-types-order-independence-and-nesting.cpp2"
auto X::exx(cpp2::in<int> count) const& -> void{
// Exercise '_' anonymous objects too while we're at it
cpp2::finally auto_37_9 {[&]() -> void { std::cout << "leaving call to 'why(" + cpp2::to_string(count) + ")'\n"; }};
cpp2::finally auto_37_9 {[&]() mutable -> void { std::cout << "leaving call to 'why(" + cpp2::to_string(count) + ")'\n"; }};
if (cpp2::cmp_less(count,5)) {
CPP2_UFCS(why, (*cpp2::assert_not_null(py)), count + 1);// use Y object from X
}
Expand Down
2 changes: 1 addition & 1 deletion regression-tests/test-results/version
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

cppfront compiler v0.3.0 Build 8B20:1424
cppfront compiler v0.3.0 Build 8B21:1401
Copyright(c) Herb Sutter All rights reserved

SPDX-License-Identifier: CC-BY-NC-ND-4.0
Expand Down
2 changes: 1 addition & 1 deletion source/build.info
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"8B20:1424"
"8B21:1401"
6 changes: 3 additions & 3 deletions source/reflect.h
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ namespace meta {
CPP2_UFCS(push_back, generated_lines, std::vector<source_line>());
auto lines {&CPP2_UFCS_0(back, generated_lines)};

auto add_line {[&, _1 = lines](cpp2::in<std::string_view> s) -> void{
auto add_line {[&, _1 = lines](cpp2::in<std::string_view> s) mutable -> void{
static_cast<void>(CPP2_UFCS(emplace_back, (*cpp2::assert_not_null(_1)), s, source_line::category::cpp2));
}};
{
Expand Down Expand Up @@ -1531,7 +1531,7 @@ auto cpp2_enum(meta::type_declaration& t) -> void
{
// Let basic_enum do its thing, with an incrementing value generator
CPP2_UFCS(basic_enum, t,
[](std::string& value, cpp2::in<std::string> specified_value) -> void{
[](std::string& value, cpp2::in<std::string> specified_value) mutable -> void{
if (!(CPP2_UFCS_0(empty, specified_value))) {
value = specified_value;
}else {
Expand All @@ -1548,7 +1548,7 @@ auto flag_enum(meta::type_declaration& t) -> void
{
// Let basic_enum do its thing, with a power-of-two value generator
CPP2_UFCS(basic_enum, t,
[](std::string& value, cpp2::in<std::string> specified_value) -> void{
[](std::string& value, cpp2::in<std::string> specified_value) mutable -> void{
if (!(CPP2_UFCS_0(empty, specified_value))) {
value = specified_value;
}else {
Expand Down
6 changes: 6 additions & 0 deletions source/to_cpp1.h
Original file line number Diff line number Diff line change
Expand Up @@ -4421,6 +4421,12 @@ class cppfront
emit(*n.parameters);
}

// For an anonymous function, make the emitted lambda 'mutable'
if (!n.my_decl->has_name())
{
printer.print_cpp2( " mutable", n.position() );
}

// For now, adding implicit noexcept only for move/swap/dtor functions
if (
n.is_move()
Expand Down

0 comments on commit 4bd0c04

Please sign in to comment.