hsutter / cppfront

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

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

Open JohelEGP opened 8 months ago

JohelEGP commented 8 months ago

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: ```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 -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: ```C++ //=== 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 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: ```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 count) mutable -> void{ | ^ main.cpp2:3:33: note: capture 'test_case' by value 3 | auto test_int {[_0 = test_case](cpp2::in count) mutable -> void{ | ^ | , test_case main.cpp2:3:33: note: capture 'test_case' by reference 3 | auto test_int {[_0 = test_case](cpp2::in count) mutable -> void{ | ^ | , &test_case main.cpp2:3:19: note: default capture by value 3 | auto test_int {[_0 = test_case](cpp2::in count) mutable -> void{ | ^ | =, main.cpp2:3:19: note: default capture by reference 3 | auto test_int {[_0 = test_case](cpp2::in count) mutable -> void{ | ^ | &, 1 error generated. ninja: build stopped: subcommand failed. ```

See also: