cppfront
cppfront copied to clipboard
[BUG] Can't capture outer variable in nested function expression
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$)$:
test_case$is the name of an outer capture, and(test_case$)$captures that.(test_case$)$captures(test_case$), and that capturestest_casein 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: