From: Richard Smith Date: Thu, 27 Sep 2018 22:47:04 +0000 (+0000) Subject: [cxx2a] P0624R2: Lambdas with no capture-default are X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5f182380386153bf4d9522dc2a13d50a7eed597c;p=clang [cxx2a] P0624R2: Lambdas with no capture-default are default-constructible and assignable. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@343279 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index d531ee1287..cb1de0bc57 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -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; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 5ec3e5ceae..508a0c41af 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -6633,6 +6633,11 @@ let CategoryName = "Lambda Issue" in { InGroup, 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, DefaultIgnore; } def err_return_in_captured_stmt : Error< diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 00ae7c9cda..9bb6e3a013 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -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(D) && diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index b81e5be56a..a18b8dd596 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index e89e8a2e68..26fb107688 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -266,6 +266,17 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, return true; } + if (auto *MD = dyn_cast(D)) { + // Lambdas are only default-constructible or assignable in C++2a onwards. + if (MD->getParent()->isLambda() && + ((isa(MD) && + cast(MD)->isDefaultConstructor()) || + MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())) { + Diag(Loc, diag::warn_cxx17_compat_lambda_def_ctor_assign) + << !isa(MD); + } + } + auto getReferencedObjCProp = [](const NamedDecl *D) -> const ObjCPropertyDecl * { if (const auto *MD = dyn_cast(D)) diff --git a/test/SemaCXX/cxx17-compat.cpp b/test/SemaCXX/cxx17-compat.cpp index 3b814340c6..9d628da662 100644 --- a/test/SemaCXX/cxx17-compat.cpp +++ b/test/SemaCXX/cxx17-compat.cpp @@ -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 index 0000000000..e555f16ecc --- /dev/null +++ b/test/SemaCXX/cxx2a-lambda-default-ctor-assign.cpp @@ -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(y); +} + +template +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 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(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(y); // expected-error {{deleted}} +} diff --git a/www/cxx_status.html b/www/cxx_status.html index 3ba8c9c7d0..aed443c4f6 100755 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -905,7 +905,7 @@ as the draft C++2a standard evolves. Default constructible and assignable stateless lambdas P0624R2 - No + SVN Lambdas in unevaluated contexts