]> granicus.if.org Git - clang/commitdiff
[cxx2a] P0624R2: Lambdas with no capture-default are
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 27 Sep 2018 22:47:04 +0000 (22:47 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 27 Sep 2018 22:47:04 +0000 (22:47 +0000)
default-constructible and assignable.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@343279 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/DeclCXX.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExpr.cpp
test/SemaCXX/cxx17-compat.cpp
test/SemaCXX/cxx2a-lambda-default-ctor-assign.cpp [new file with mode: 0644]
www/cxx_status.html

index d531ee128716d7044db371428090d56fb181b987..cb1de0bc5715c41dff1150170dedfb63aaa0ef18 100644 (file)
@@ -974,10 +974,7 @@ public:
   bool needsImplicitDefaultConstructor() const {
     return !data().UserDeclaredConstructor &&
            !(data().DeclaredSpecialMembers & SMF_DefaultConstructor) &&
-           // C++14 [expr.prim.lambda]p20:
-           //   The closure type associated with a lambda-expression has no
-           //   default constructor.
-           !isLambda();
+           (!isLambda() || lambdaIsDefaultConstructibleAndAssignable());
   }
 
   /// Determine whether this class has any user-declared constructors.
@@ -1167,10 +1164,7 @@ public:
            !hasUserDeclaredCopyAssignment() &&
            !hasUserDeclaredMoveConstructor() &&
            !hasUserDeclaredDestructor() &&
-           // C++1z [expr.prim.lambda]p21: "the closure type has a deleted copy
-           // assignment operator". The intent is that this counts as a user
-           // declared copy assignment, but we do not model it that way.
-           !isLambda();
+           (!isLambda() || lambdaIsDefaultConstructibleAndAssignable());
   }
 
   /// Determine whether we need to eagerly declare a move assignment
@@ -1210,6 +1204,10 @@ public:
   /// a template).
   bool isGenericLambda() const;
 
+  /// Determine whether this lambda should have an implicit default constructor
+  /// and copy and move assignment operators.
+  bool lambdaIsDefaultConstructibleAndAssignable() const;
+
   /// Retrieve the lambda call operator of the closure type
   /// if this is a closure type.
   CXXMethodDecl *getLambdaCallOperator() const;
index 5ec3e5ceae8549e657791c7ba1de6ece7f422049..508a0c41af7cc47963441c7b2cb61ce93f59beec 100644 (file)
@@ -6633,6 +6633,11 @@ let CategoryName = "Lambda Issue" in {
     InGroup<DeprecatedThisCapture>, DefaultIgnore;
   def note_deprecated_this_capture : Note<
     "add an explicit capture of 'this' to capture '*this' by reference">;
+
+  // C++2a default constructible / assignable lambdas.
+  def warn_cxx17_compat_lambda_def_ctor_assign : Warning<
+    "%select{default construction|assignment}0 of lambda is incompatible with "
+    "C++ standards before C++2a">, InGroup<CXXPre2aCompat>, DefaultIgnore;
 }
 
 def err_return_in_captured_stmt : Error<
index 00ae7c9cda127b0d683c341e6e9d2bdca57b3042..9bb6e3a013a7c3d8927cefb2d61c84aeca0dcd24 100644 (file)
@@ -628,6 +628,24 @@ bool CXXRecordDecl::hasSubobjectAtOffsetZeroOfEmptyBaseType(
   return false;
 }
 
+bool CXXRecordDecl::lambdaIsDefaultConstructibleAndAssignable() const {
+  assert(isLambda() && "not a lambda");
+
+  // C++2a [expr.prim.lambda.capture]p11:
+  //   The closure type associated with a lambda-expression has no default
+  //   constructor if the lambda-expression has a lambda-capture and a
+  //   defaulted default constructor otherwise. It has a deleted copy
+  //   assignment operator if the lambda-expression has a lambda-capture and
+  //   defaulted copy and move assignment operators otherwise.
+  //
+  // C++17 [expr.prim.lambda]p21:
+  //   The closure type associated with a lambda-expression has no default
+  //   constructor and a deleted copy assignment operator.
+  if (getLambdaCaptureDefault() != LCD_None)
+    return false;
+  return getASTContext().getLangOpts().CPlusPlus2a;
+}
+
 void CXXRecordDecl::addedMember(Decl *D) {
   if (!D->isImplicit() &&
       !isa<FieldDecl>(D) &&
index b81e5be56a24d0b85e5a2daa2ce6ef39c76b4af8..a18b8dd59676f824f55d2cbe94e3cab91842e343 100644 (file)
@@ -7088,7 +7088,8 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM,
   //   The closure type associated with a lambda-expression has a
   //   deleted (8.4.3) default constructor and a deleted copy
   //   assignment operator.
-  if (RD->isLambda() &&
+  // C++2a adds back these operators if the lambda has no capture-default.
+  if (RD->isLambda() && !RD->lambdaIsDefaultConstructibleAndAssignable() &&
       (CSM == CXXDefaultConstructor || CSM == CXXCopyAssignment)) {
     if (Diagnose)
       Diag(RD->getLocation(), diag::note_lambda_decl);
index e89e8a2e68f4a57a000abf7fa5da8699566ca97f..26fb107688f998a00b4dc94d028acefb7c5a5f08 100644 (file)
@@ -266,6 +266,17 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
       return true;
   }
 
+  if (auto *MD = dyn_cast<CXXMethodDecl>(D)) {
+    // Lambdas are only default-constructible or assignable in C++2a onwards.
+    if (MD->getParent()->isLambda() &&
+        ((isa<CXXConstructorDecl>(MD) &&
+          cast<CXXConstructorDecl>(MD)->isDefaultConstructor()) ||
+         MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())) {
+      Diag(Loc, diag::warn_cxx17_compat_lambda_def_ctor_assign)
+        << !isa<CXXConstructorDecl>(MD);
+    }
+  }
+
   auto getReferencedObjCProp = [](const NamedDecl *D) ->
                                       const ObjCPropertyDecl * {
     if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
index 3b814340c6daf486d4c8927894adf88795943dcb..9d628da662b8b6827fadfa092e02b0341055c7f8 100644 (file)
@@ -27,3 +27,18 @@ struct B {
     // expected-warning@-4 {{default member initializer for bit-field is incompatible with C++ standards before C++2a}}
 #endif
 };
+
+auto Lambda = []{};
+decltype(Lambda) AnotherLambda;
+#if __cplusplus <= 201703L
+    // expected-error@-2 {{no matching constructor}} expected-note@-3 2{{candidate}}
+#else
+    // expected-warning@-4 {{default construction of lambda is incompatible with C++ standards before C++2a}}
+#endif
+
+void copy_lambda() { Lambda = Lambda; }
+#if __cplusplus <= 201703L
+    // expected-error@-2 {{deleted}} expected-note@-10 {{lambda}}
+#else
+    // expected-warning@-4 {{assignment of lambda is incompatible with C++ standards before C++2a}}
+#endif
diff --git a/test/SemaCXX/cxx2a-lambda-default-ctor-assign.cpp b/test/SemaCXX/cxx2a-lambda-default-ctor-assign.cpp
new file mode 100644 (file)
index 0000000..e555f16
--- /dev/null
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+auto a = []{};
+decltype(a) a2;
+void f(decltype(a) x, decltype(a) y) {
+  x = y;
+  x = static_cast<decltype(a)&&>(y);
+}
+
+template<typename T>
+struct X {
+  constexpr X() { T::error; } // expected-error {{'::'}}
+  X(const X&);
+  constexpr X &operator=(const X&) { T::error; } // expected-error {{'::'}}
+  constexpr X &operator=(X&&) { T::error; } // expected-error {{'::'}}
+};
+extern X<int> x;
+auto b = [x = x]{}; // expected-note 3{{in instantiation of}}
+decltype(b) b2; // expected-note {{in implicit default constructor}}
+void f(decltype(b) x, decltype(b) y) {
+  x = y; // expected-note {{in implicit copy assignment}}
+  x = static_cast<decltype(b)&&>(y); // expected-note {{in implicit move assignment}}
+}
+
+struct Y {
+  Y() = delete; // expected-note {{deleted}}
+  Y(const Y&);
+  Y &operator=(const Y&) = delete; // expected-note 2{{deleted}}
+  Y &operator=(Y&&) = delete;
+};
+extern Y y;
+auto c = [y = y]{}; // expected-note 3{{deleted because}}
+decltype(c) c2; // expected-error {{deleted}}
+void f(decltype(c) x, decltype(c) y) {
+  x = y; // expected-error {{deleted}}
+  x = static_cast<decltype(c)&&>(y); // expected-error {{deleted}}
+}
index 3ba8c9c7d0fa383619b58846da270267041b4795..aed443c4f6c66ebfae0ab7c8817083c3e5a529f9 100755 (executable)
@@ -905,7 +905,7 @@ as the draft C++2a standard evolves.
     <tr>
       <td>Default constructible and assignable stateless lambdas</td>
       <td><a href="http://wg21.link/p0624r2">P0624R2</a></td>
-      <td class="none" align="center">No</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
     <tr>
       <td>Lambdas in unevaluated contexts</td>