Skip to content

Rejecting captures of structured bindings in generic lambdas in C++20 #57826

Closed
@PatrickAMoran

Description

@PatrickAMoran

Building the following in --std=c++20

struct P { int a; int b; };
void repro() {
  const auto [a, b] = P{1, 2};
  (void)[&](const auto c) { return c - b; }(2);
}

Produces a failure with error message:

 /home/pmoran/clang_testcase.cpp:13:41: error: reference to local binding 'b' declared in enclosing function 'main'
  (void)[&](const auto c) { return c - b; }(2);
                                       ^
/home/pmoran/clang_testcase.cpp:13:45: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<int>' requested here
  (void)[&](const auto c) { return c - b; }(2);
                                           ^
/home/pmoran/clang_testcase.cpp:12:18: note: 'b' declared here
  const auto [a, b] = P{1, 2};
                 ^

It is my understanding that the capturing structured bindings in lambdas may not have been standards conforming in earlier language versions, but that it is standards-conforming in C++20. Commit 127bf44 added support for capturing structured bindings in lambdas to fix #54300 and #52720. The error seems to persist here specifically because the lambda is a generic lambda. If you change the argument from const auto c to instead be the const int c that the lambda's call will resolve to, the error goes away.

I chased the source of the error as far as clang/lib/Sema/SemaExpr.cpp line 18831 where we see this comment:

// If we are instantiating a generic lambda call operator body,
// we do not want to capture new variables.  What was captured
// during either a lambdas transformation or initial parsing
// should be used. 

If you capture b by reference explicitly by name rather than using a default capture like so:

(void)[&b](const auto c) { return c - b; }(2);

Then you instead get this warning:

/home/pmoran/clang_testcase.cpp:13:12: warning: lambda capture 'b' is not required to be captured for this use [-Wunused-lambda-capture]
  (void)[&b](const auto c) { return c - b; }(2);
          ^

So I think the issue resides in the detection of which values need to be captured by a lambda's body (even though here neither b's type nor its value depend on the type parameter of the generic operator()).

Platform: I have only tested on linux x86_64,. but I don't think it's platform-specific
Versions Affected: I believe all versions up to and including the HEAD I pulled earlier today (2022-09-19).

Metadata

Metadata

Assignees

No one assigned

    Labels

    c++20clang:frontendLanguage frontend issues, e.g. anything involving "Sema"

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions