Rakete1111 updated this revision to Diff 159160.
Rakete1111 added a comment.

Add missing test cases. :)


Repository:
  rC Clang

https://reviews.llvm.org/D50291

Files:
  include/clang/Sema/Sema.h
  lib/Sema/SemaDeclCXX.cpp
  test/CXX/special/class.copy/p10.cpp

Index: test/CXX/special/class.copy/p10.cpp
===================================================================
--- /dev/null
+++ test/CXX/special/class.copy/p10.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// The implicitly-defined copy/move assignment operator is constexpr if
+// - X is a literal type, and
+// - [...]
+//
+// PR38143: Order of defaulted special members matters.
+struct X {
+  constexpr X() = default;
+  constexpr X(const X &) = default;
+  constexpr X &operator=(const X &) = default;
+  ~X() = default;
+  constexpr X(X &&) = default;
+  constexpr X &operator=(X &&) = default;
+};
+
+struct Y {
+  constexpr Y &operator=(const Y &) = default;
+  constexpr Y &operator=(Y &&) = default;
+  constexpr Y() = default;
+  constexpr Y(const Y &) = default;
+  ~Y() = default;
+  constexpr Y(Y &&) = default;
+};
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -6020,6 +6020,7 @@
 
   bool HasMethodWithOverrideControl = false,
        HasOverridingMethodWithoutOverrideControl = false;
+  CXXMethodDecl *CopyAssignment = nullptr, *MoveAssignment = nullptr;
   if (!Record->isDependentType()) {
     for (auto *M : Record->methods()) {
       // See if a method overloads virtual methods in a base
@@ -6049,6 +6050,11 @@
         }
       }
 
+      if (CSM == CXXCopyAssignment)
+        CopyAssignment = M;
+      else if (CSM == CXXMoveAssignment)
+        MoveAssignment = M;
+
       // Set triviality for the purpose of calls if this is a user-provided
       // copy/move constructor or destructor.
       if ((CSM == CXXCopyConstructor || CSM == CXXMoveConstructor ||
@@ -6071,6 +6077,22 @@
         }
       }
     }
+
+    // Now we can decide whether the special members are constexpr.
+    auto ActOnSpecialMember = [this, &Record](CXXMethodDecl *MD) {
+      if (!MD->isInvalidDecl() && MD->isExplicitlyDefaulted()) {
+        CheckExplicitlyDefaultedSpecialMemberConstexpr(MD);
+        Record->finishedDefaultedOrDeletedMember(MD);
+      }
+    };
+    for (auto *M : Record->ctors())
+      ActOnSpecialMember(M);
+    if (CopyAssignment)
+      ActOnSpecialMember(CopyAssignment);
+    if (MoveAssignment)
+      ActOnSpecialMember(MoveAssignment);
+    if (CXXDestructorDecl *CD = Record->getDestructor())
+      ActOnSpecialMember(CD);
   }
 
   if (HasMethodWithOverrideControl &&
@@ -6532,20 +6554,8 @@
   // C++11 [dcl.fct.def.default]p2:
   //   An explicitly-defaulted function may be declared constexpr only if it
   //   would have been implicitly declared as constexpr,
-  // Do not apply this rule to members of class templates, since core issue 1358
-  // makes such functions always instantiate to constexpr functions. For
-  // functions which cannot be constexpr (for non-constructors in C++11 and for
-  // destructors in C++1y), this is checked elsewhere.
-  bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM,
-                                                     HasConstParam);
-  if ((getLangOpts().CPlusPlus14 ? !isa<CXXDestructorDecl>(MD)
-                                 : isa<CXXConstructorDecl>(MD)) &&
-      MD->isConstexpr() && !Constexpr &&
-      MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
-    Diag(MD->getLocStart(), diag::err_incorrect_defaulted_constexpr) << CSM;
-    // FIXME: Explain why the special member can't be constexpr.
-    HadError = true;
-  }
+  // We need to delay this, because at this point we may not have enough
+  // information to determine if MD is really constexpr or not.
 
   //   and may have an explicit exception-specification only if it is compatible
   //   with the exception-specification on the implicit declaration.
@@ -6568,8 +6578,6 @@
   if (First) {
     //  -- it is implicitly considered to be constexpr if the implicit
     //     definition would be,
-    MD->setConstexpr(Constexpr);
-
     //  -- it is implicitly considered to have the same exception-specification
     //     as if it had been implicitly declared,
     FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo();
@@ -6598,6 +6606,39 @@
     MD->setInvalidDecl();
 }
 
+void Sema::CheckExplicitlyDefaultedSpecialMemberConstexpr(CXXMethodDecl *MD) {
+  CXXSpecialMember CSM = getSpecialMember(MD);
+  assert(MD && MD->isExplicitlyDefaulted() && CSM != CXXInvalid &&
+         "not an explicitly-defaulted special member");
+
+  const FunctionProtoType *Type = MD->getType()->getAs<FunctionProtoType>();
+
+  unsigned ExpectedParams =
+      CSM != CXXDefaultConstructor && CSM != CXXDestructor;
+  QualType ArgType = ExpectedParams ? Type->getParamType(0) : QualType();
+
+  bool HasConstParam = ExpectedParams && ArgType->isReferenceType() &&
+                       ArgType->getPointeeType().isConstQualified();
+
+  // Do not apply this rule to members of class templates, since core issue 1358
+  // makes such functions always instantiate to constexpr functions. For
+  // functions which cannot be constexpr (for non-constructors in C++11 and for
+  // destructors in C++1y), this is checked elsewhere.
+  bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, MD->getParent(),
+                                                     CSM, HasConstParam);
+  if ((getLangOpts().CPlusPlus14 ? !isa<CXXDestructorDecl>(MD)
+                                 : isa<CXXConstructorDecl>(MD)) &&
+      MD->isConstexpr() && !Constexpr &&
+      MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
+    Diag(MD->getLocStart(), diag::err_incorrect_defaulted_constexpr) << CSM;
+    // FIXME: Explain why the special member can't be constexpr.
+    MD->setInvalidDecl();
+  }
+
+  if (MD == MD->getCanonicalDecl())
+    MD->setConstexpr(Constexpr);
+}
+
 /// Check whether the exception specification provided for an
 /// explicitly-defaulted special member matches the exception specification
 /// that would have been generated for an implicit special member, per
@@ -14519,6 +14560,7 @@
       return;
 
     CheckExplicitlyDefaultedSpecialMember(MD);
+    CheckExplicitlyDefaultedSpecialMemberConstexpr(MD);
 
     if (!MD->isInvalidDecl())
       DefineImplicitSpecialMember(*this, MD, DefaultLoc);
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5924,6 +5924,7 @@
   void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD);
 
   void CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD);
+  void CheckExplicitlyDefaultedSpecialMemberConstexpr(CXXMethodDecl *MD);
   void CheckExplicitlyDefaultedMemberExceptionSpec(CXXMethodDecl *MD,
                                                    const FunctionProtoType *T);
   void CheckDelayedMemberExceptionSpecs();
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D50291: [... Nicolas Lesser via Phabricator via cfe-commits
    • [PATCH] D502... Nicolas Lesser via Phabricator via cfe-commits
    • [PATCH] D502... Richard Smith - zygoloid via Phabricator via cfe-commits

Reply via email to