cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

[BUG] Can't capture outer variable in nested function expression

Open JohelEGP opened this issue 1 year ago • 0 comments
trafficstars

Title: Can't capture outer variable in nested function expression.

Description:

I suspect (test_case$)$ could be made to work here, but that ICEs. test_case$$ errors with $ (capture) can appear at most once in a single postfix-expression (at '$').

There are two possible interpretations for (test_case$)$:

  1. test_case$ is the name of an outer capture, and (test_case$)$ captures that.
  2. (test_case$)$ captures (test_case$), and that captures test_case in the outer context.

Both mean the same thing to the inner context. In 1., test_case$ is already captured in the outer context, and the inner context uses it. In 2., (test_case$) is captured, which makes the outer context capture test_case.

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

f: () = {
  test_case := :(_...) = { };
  test_int := :(count: int) = {
    (test_case := test_case$) {
      test := :(_) test_case$(count$);
      test(0);
      test(1);
    }
    // GCC: error: 'test_case' is not captured
    // Clang: error: variable 'test_case' cannot be implicitly captured in a lambda with no capture-default specified
    test := :(_) test_case$(count$);
    test(0);
    test(1);
  };
  test_int(17);
  test_case(29);
}
main: () = { }
Commands:
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 -Werror=unused-variable -I . main.cpp

Expected result: Some way for capturing expressions not captured in the outer context.

Actual result and error:

Cpp2 lowered to Cpp1:


//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"

#line 1 "/app/example.cpp2"


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

#line 1 "/app/example.cpp2"
auto f() -> void;

#line 18 "/app/example.cpp2"
auto main() -> int;

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

#line 1 "/app/example.cpp2"
auto f() -> void{
#line 2 "/app/example.cpp2"
  auto test_case {[]([[maybe_unused]] auto const& ...unnamed_param_1) mutable -> void{}}; 
  auto test_int {[_0 = test_case](cpp2::in<int> count) mutable -> void{
{
auto const& test_case = _0;
#line 4 "/app/example.cpp2"
    {
      auto test {[_0 = test_case, _1 = count]([[maybe_unused]] auto const& unnamed_param_1) mutable -> auto { return _0(_1);  }}; 
      test(0);
      std::move(test)(1);
    }
}
    // GCC: error: 'test_case' is not captured
    // Clang: error: variable 'test_case' cannot be implicitly captured in a lambda with no capture-default specified
#line 11 "/app/example.cpp2"
    auto test {[_0 = test_case, _1 = count]([[maybe_unused]] auto const& unnamed_param_1) mutable -> auto { return _0(_1);  }}; 
    test(0);
    std::move(test)(1);
  }}; 
  std::move(test_int)(17);
  std::move(test_case)(29);
}
auto main() -> int{}
Output:
Step cmake returned: 0
-- The CXX compiler identification is Clang 18.0.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /opt/compiler-explorer/clang-trunk/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.5s)
-- Generating done (0.0s)
-- Build files have been written to: /app/build
Step build returned: 1
[1/3] Generating main.cpp
main.cpp2... ok (all Cpp2, passes safety checks)

[2/3] Building CXX object CMakeFiles/main.dir/main.cpp.o
FAILED: CMakeFiles/main.dir/main.cpp.o 
/opt/compiler-explorer/clang-trunk/bin/clang++ --gcc-toolchain=/opt/compiler-explorer/gcc-snapshot  -I/app -I/app -std=c++23 -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -Werror=unused-value -Werror=unused-parameter -stdlib=libc++ -Wno-read-modules-implicitly -MD -MT CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /app/build/main.cpp
main.cpp2:11:22: error: variable 'test_case' cannot be implicitly captured in a lambda with no capture-default specified
   11 |     auto test {[_0 = test_case, _1 = count]([[maybe_unused]] auto const& unnamed_param_1) mutable -> auto { return _0(_1);  }}; 
      |                      ^
main.cpp2:2:8: note: 'test_case' declared here
    2 |   auto test_case {[]([[maybe_unused]] auto const& ...unnamed_param_1) mutable -> void{}}; 
      |        ^
main.cpp2:3:18: note: lambda expression begins here
    3 |   auto test_int {[_0 = test_case](cpp2::in<int> count) mutable -> void{
      |                  ^
main.cpp2:3:33: note: capture 'test_case' by value
    3 |   auto test_int {[_0 = test_case](cpp2::in<int> count) mutable -> void{
      |                                 ^
      |                                 , test_case
main.cpp2:3:33: note: capture 'test_case' by reference
    3 |   auto test_int {[_0 = test_case](cpp2::in<int> count) mutable -> void{
      |                                 ^
      |                                 , &test_case
main.cpp2:3:19: note: default capture by value
    3 |   auto test_int {[_0 = test_case](cpp2::in<int> count) mutable -> void{
      |                   ^
      |                   =, 
main.cpp2:3:19: note: default capture by reference
    3 |   auto test_int {[_0 = test_case](cpp2::in<int> count) mutable -> void{
      |                   ^
      |                   &, 
1 error generated.
ninja: build stopped: subcommand failed.

See also:

JohelEGP avatar Dec 08 '23 07:12 JohelEGP