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 @@ -102,24 +102,31 @@ return S.Diag(DRE->getBeginLoc(), diag::err_param_default_argument_references_param) << Param->getDeclName() << DefaultArg->getSourceRange(); - } else if (const auto *VDecl = dyn_cast(Decl)) { - // C++ [dcl.fct.default]p7: - // Local variables shall not be used in default argument - // expressions. - // - // C++17 [dcl.fct.default]p7 (by CWG 2082): - // A local variable shall not appear as a potentially-evaluated - // expression in a default argument. - // - // C++20 [dcl.fct.default]p7 (DR as part of P0588R1, see also CWG 2346): - // Note: A local variable cannot be odr-used (6.3) in a default argument. - // - if (VDecl->isLocalVarDecl() && !DRE->isNonOdrUse()) - return S.Diag(DRE->getBeginLoc(), - diag::err_param_default_argument_references_local) - << VDecl->getDeclName() << DefaultArg->getSourceRange(); + } else { + const VarDecl *VD = nullptr; + if (const auto *BD = dyn_cast(Decl)) + VD = dyn_cast_if_present(BD->getDecomposedDecl()); + else + VD = dyn_cast(Decl); + if (VD) { + // C++ [dcl.fct.default]p7: + // Local variables shall not be used in default argument + // expressions. + // + // C++17 [dcl.fct.default]p7 (by CWG 2082): + // A local variable shall not appear as a potentially-evaluated + // expression in a default argument. + // + // C++20 [dcl.fct.default]p7 (DR as part of P0588R1, see also CWG 2346): + // Note: A local variable cannot be odr-used (6.3) in a default + // argument. + // + if (VD->isLocalVarDecl() && !DRE->isNonOdrUse()) + return S.Diag(DRE->getBeginLoc(), + diag::err_param_default_argument_references_local) + << Decl->getDeclName() << DefaultArg->getSourceRange(); + } } - return false; } @@ -149,13 +156,20 @@ } 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/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp --- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp @@ -27,5 +27,7 @@ struct S { int i; }; auto [x] = S(); - extern void h7(int = x); // FIXME: reject + extern void h7(int = x); + // expected-error@-1 {{default argument references local variable 'x' of enclosing function}} + } 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 = [xxx=1] { return xxx; }()); // OK + void g2(int = [xxx=i] { return xxx; }()); // 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,4 +1,8 @@ -// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify +// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -Wno-c++14-extensions -verify +// RUN: %clang_cc1 -std=c++17 %s -Wunused -Wno-unused-lambda-capture -Wno-c++14-extensions -verify + + +const int global = 0; void f2() { int i = 1; @@ -7,7 +11,20 @@ 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}} +} + +#if __cplusplus >= 201703L +int global_array[] = { 1, 2 }; +auto [ga, gb] = global_array; + +void structured_bindings() { + int array[] = { 1, 2 }; + auto [a, b] = array; + void func(int c = [x = a, &xref = a, y = ga, &yref = ga] { return x; }()); // expected-error 2{{default argument references local variable 'a' of enclosing function}} } +#endif namespace lambda_in_default_args { int f(int = [] () -> int { int n; return ++n; } ()); 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