cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

[BUG] Multiple return values failed to work in presence of lambda

Open filipsajdak opened this issue 3 years ago • 0 comments
trafficstars

When dealing with multiple return values cppfront creates local cpp2::deferred_init values and moves them on the return of the function.

It works in the below code:

fun: () -> (ri : int) = {
    ri = 0;

    ri = 42;

    return;
}

main: () -> int = {
    r := fun(1);
    return r.ri;
}

and generates

// ----- Cpp2 support -----
#include "cpp2util.h"


#line 1 "tests/get_declarations_return_vals.cpp2"
struct fun__ret {
    int ri;
    };
#line 2 "tests/get_declarations_return_vals.cpp2"
[[nodiscard]] auto fun() -> fun__ret;
#line 9 "tests/get_declarations_return_vals.cpp2"
[[nodiscard]] auto main() -> int;

//=== Cpp2 definitions ==========================================================

#line 1 "tests/get_declarations_return_vals.cpp2"
[[nodiscard]] auto fun() -> fun__ret{
        cpp2::deferred_init<int> ri;
#line 2 "tests/get_declarations_return_vals.cpp2"
    ri.construct(0);

    ri.value() = 42;

    return  { std::move(ri.value()) }; 
}

[[nodiscard]] auto main() -> int{
    auto r { fun(1) }; 
    return r.ri; 
}

Adding a lambda between two assignments to ri variable (skipping boilerplate):

fun: () -> (ri : int) = {
    ri = 0;

    pred := :(e:_) -> bool = { return e == 1; };

    ri = 42;

    return;
}

breaks that and generates (skipping boilerplate):

#line 1 "tests/get_declarations_return_vals.cpp2"
[[nodiscard]] auto fun() -> fun__ret{
        cpp2::deferred_init<int> ri;
#line 2 "tests/get_declarations_return_vals.cpp2"
    ri.construct(0);

    auto pred { [](auto const& e) -> bool{return e == 1; } }; 

    ri = 42;

    return  { std::move(ri.value()) }; 
}

The above code does not compile as cpp2::deferred_init<int> has no operator= defined. cppfront does not add a call to deferred_init::value() method and generates ri = 42; call that does not compile.

If we remove the first assignment and run cppfront on the code:

fun: () -> (ri : int) = {
    pred := :(e:_) -> bool = { return e == 1; };

    ri = 42;

    return;
}

then cppfront will generate:

#line 1 "tests/get_declarations_return_vals.cpp2"
[[nodiscard]] auto fun() -> fun__ret{
        cpp2::deferred_init<int> ri;
#line 2 "tests/get_declarations_return_vals.cpp2"
    auto pred { [](auto const& e) -> bool{return e == 1; } }; 

    ri.construct(42);

    return  { std::move(ri.value()) }; 
}

And will successfully compile.

We can also move the setting of the initial value to the declaration of multiple return values:

fun: () -> (ri : int = 0) = {
    pred := :(e:_) -> bool = { return e == 1; };

    ri = 42;

    return;
}

and cppfront will generate:

#line 1 "/tests/get_declarations_return_vals.cpp2"
[[nodiscard]] auto fun() -> fun__ret{
    int ri = 0;
#line 2 "tests/get_declarations_return_vals.cpp2"
    auto pred { [](auto const& e) -> bool{return e == 1; } }; 

    ri = 42;

    return  { std::move(ri.value()) }; 
}

The cpp2::deferred_init is not created at all and the int value is created - and that is fine as a variable is initialized. Unfortunately, cppfront calls the deferred_init::value() method on the return statement.

filipsajdak avatar Nov 12 '22 20:11 filipsajdak