diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -507,6 +507,7 @@ This means Clang will by default accept code using features from C++17 and conforming GNU extensions. Projects incompatible with C++17 can add ``-std=gnu++14`` to their build settings to restore the previous behaviour. +- Implemented DR2358 allowing init captures in lambdas in default arguments. C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -149,13 +149,19 @@ } bool CheckDefaultArgumentVisitor::VisitLambdaExpr(const LambdaExpr *Lambda) { - // C++11 [expr.lambda.prim]p13: - // A lambda-expression appearing in a default argument shall not - // implicitly or explicitly capture any entity. - if (Lambda->capture_begin() == Lambda->capture_end()) - return false; - - return S.Diag(Lambda->getBeginLoc(), diag::err_lambda_capture_default_arg); + // [expr.prim.lambda.capture]/p9 + // a lambda-expression appearing in a default argument cannot implicitly or explicitly capture any local entity. + // Such a lambda-expression can still have an init-capture if any full-expression in its initializer + // satisfies the constraints of an expression appearing in a default argument. + bool Invalid = false; + for(const LambdaCapture& LC : Lambda->captures()) { + if(!Lambda->isInitCapture(&LC)) + return S.Diag(LC.getLocation(), diag::err_lambda_capture_default_arg); + // Init captures are always VarDecl. + auto *D = cast(LC.getCapturedVar()); + Invalid |= Visit(D->getInit()); + } + return Invalid; } } // namespace diff --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp --- a/clang/test/CXX/drs/dr23xx.cpp +++ b/clang/test/CXX/drs/dr23xx.cpp @@ -89,6 +89,16 @@ #pragma clang __debug dump not_use_2 } +#if __cplusplus >= 201402L +namespace dr2358 { // dr2358: 16 + void f2() { + int i = 1; + void g1(int = ([x=1] { return x; }))(); // OK + void g2(int = ([x=i] { return x; }))(); // expected-error {{default argument references local variable 'i' of enclosing function}} + } +} +#endif + #if __cplusplus >= 201707L // Otherwise, if the qualified-id std::tuple_size names a complete class // type **with a member value**, the expression std::tuple_size::value shall diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify +const int global = 0; void f2() { int i = 1; void g1(int = ([i]{ return i; })()); // expected-error{{lambda expression in default argument cannot capture any entity}} @@ -7,6 +8,8 @@ void g3(int = ([=]{ return i; })()); // expected-error{{lambda expression in default argument cannot capture any entity}} void g4(int = ([=]{ return 0; })()); void g5(int = ([]{ return sizeof i; })()); + void g6(int = ([x=1, y = global, &z = global]{ return x; })()); + void g7(int = ([x=i, &y=i]{ return x; })()); // expected-error 2{{default argument references local variable 'i' of enclosing function}} } namespace lambda_in_default_args { diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -13956,7 +13956,7 @@ 2358 CD5 Explicit capture of value - Unknown + Clang 16 2359