[clang] 7429709 - [Clang] Implement CWG2358 Explicit capture of value

Corentin Jabot via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 3 05:27:17 PDT 2022


Author: Corentin Jabot
Date: 2022-11-03T13:27:11+01:00
New Revision: 742970920b7a7fc2fe1cb6bca6fb04f03ab7d5d9

URL: https://github.com/llvm/llvm-project/commit/742970920b7a7fc2fe1cb6bca6fb04f03ab7d5d9
DIFF: https://github.com/llvm/llvm-project/commit/742970920b7a7fc2fe1cb6bca6fb04f03ab7d5d9.diff

LOG: [Clang] Implement CWG2358 Explicit capture of value

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D137172

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp
    clang/test/CXX/drs/dr23xx.cpp
    clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp
    clang/www/cxx_dr_status.html

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 763f4cece4634..ebf280f4da4a8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -574,6 +574,7 @@ C++ Language Changes in Clang
   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
index 80b748cbb3744..ea7997b347959 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -102,24 +102,31 @@ bool CheckDefaultArgumentVisitor::VisitDeclRefExpr(const DeclRefExpr *DRE) {
       return S.Diag(DRE->getBeginLoc(),
                     diag::err_param_default_argument_references_param)
              << Param->getDeclName() << DefaultArg->getSourceRange();
-  } else if (const auto *VDecl = dyn_cast<VarDecl>(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<BindingDecl>(Decl))
+      VD = dyn_cast_if_present<VarDecl>(BD->getDecomposedDecl());
+    else
+      VD = dyn_cast<VarDecl>(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::VisitPseudoObjectExpr(
 }
 
 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<VarDecl>(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
index 52986faa4e859..97b227222eb09 100644
--- 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 @@ void h() {
   struct S { int i; };
   auto [x] = S();
 
-  extern void h7(int = x); // FIXME: reject
+  extern void h7(int = x);
+  // expected-error at -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
index 8d6b4a5dc16ea..371ead504bf32 100644
--- a/clang/test/CXX/drs/dr23xx.cpp
+++ b/clang/test/CXX/drs/dr23xx.cpp
@@ -89,6 +89,16 @@ namespace dr2353 { // dr2353: 9
 #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<E> names a complete class
 // type **with a member value**, the expression std::tuple_size<E>::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
index b55beb7d4ed78..0635a01466afb 100644
--- 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 f2() {
   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
index 24beb7fc88b65..2affb6cec8e4f 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -13956,7 +13956,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg2358">2358</a></td>
     <td>CD5</td>
     <td>Explicit capture of value</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 16</td>
   </tr>
   <tr id="2359">
     <td><a href="https://wg21.link/cwg2359">2359</a></td>


        


More information about the cfe-commits mailing list