hsutter / cppfront

A personal experimental C++ Syntax 2 -> Syntax 1 compiler
Other
5.39k stars 232 forks source link

[BUG] Need `decltype(auto)` for perfect backwarding #876

Open JohelEGP opened 8 months ago

JohelEGP commented 8 months ago

Title: Need decltype(auto) for perfect backwarding.

Description:

At first, a Cpp2 -> forward _ return-list lowered to the Cpp1 return type auto&&.

175 noted this was unsafe,

and commit b59f539f7275b880d4e623adb473864e49756546 changed it to decltype(auto).

274 noted that this makes accessors return by copy,

then commit 413de0ed5747a74e8226c6474072925cf21bacd8 changed it back to auto&&, and commit 43cdf3e651ebe51cf1d68efbe50128c248f63364 addressed some safety concerns of #175.

Now perfect backwarding doesn't work or returns a temporary. This means we can't implement std::invoke or simpler equivalents. For example, implementing a simple function_ref: https://cpp2.godbolt.org/z/r53zfnxbT.

Minimal reproducer (https://cpp2.godbolt.org/z/bjjfqEc9e):

f: (x: int) x;
f: (x: long) _ = x;
g: (x) -> forward _ = f(x);
main: () = {
  _ = g(0);
  _ = g(0L);
}

Commands: ```bash cppfront main.cpp2 clang++18 -std=c++23 -stdlib=libc++ -lc++abi -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -Werror=unused-value -Werror=unused-parameter -I . main.cpp ```

Expected result: Perfect backwarding to work.

Actual result and error:

Cpp2 lowered to Cpp1: ```C++ //=== Cpp2 type declarations ==================================================== #include "cpp2util.h" #line 1 "/app/example.cpp2" //=== Cpp2 type definitions and function declarations =========================== #line 1 "/app/example.cpp2" [[nodiscard]] auto f(cpp2::in x) -> auto; #line 2 "/app/example.cpp2" [[nodiscard]] auto f(cpp2::in x) -> auto; [[nodiscard]] auto g(auto const& x) -> auto&&; auto main() -> int; //=== Cpp2 function definitions ================================================= #line 1 "/app/example.cpp2" [[nodiscard]] auto f(cpp2::in x) -> auto { return x; } #line 2 "/app/example.cpp2" [[nodiscard]] auto f(cpp2::in x) -> auto { return static_cast(x); } [[nodiscard]] auto g(auto const& x) -> auto&& { return f(x); } auto main() -> int{ static_cast(g(0)); static_cast(g(0L)); } ```

Output:

main.cpp2:3:56: warning: returning reference to local temporary object [-Wreturn-stack-address]
    3 | [[nodiscard]] auto g(auto const& x) -> auto&& { return f(x);  }
      |                                                        ^~~~
main.cpp2:5:21: note: in instantiation of function template specialization 'g<int>' requested here
    5 |   static_cast<void>(g(0));
      |                     ^
main.cpp2:3:44: error: cannot form a reference to 'void'
    3 | [[nodiscard]] auto g(auto const& x) -> auto&& { return f(x);  }
      |                                            ^
main.cpp2:6:21: note: in instantiation of function template specialization 'g<long>' requested here
    6 |   static_cast<void>(g(0L));
      |                     ^
1 warning and 1 error generated.

See also:

JohelEGP commented 8 months ago

An alternative is to change back to decltype(auto) and lower returned member names in parentheses. That should make -> forward _ return-list "work as expected" more. The relevant features are https://en.cppreference.com/w/cpp/language/auto and https://en.cppreference.com/w/cpp/language/decltype.

JohelEGP commented 8 months ago

Yet another alternative is to change back to decltype(auto), but reject returning unparenthesized member names. They should be (member) or (copy member) (https://github.com/hsutter/cppfront/issues/466#copy).