royjacobson created this revision.
Herald added a project: All.
royjacobson updated this revision to Diff 444173.
royjacobson added a comment.
royjacobson updated this revision to Diff 444419.
royjacobson retitled this revision from "[WIP][Clang] Implement P0848 
(Conditionally Trivial Special Member Functions)" to "[Clang] Implement P0848 
(Conditionally Trivial Special Member Functions)".
royjacobson edited the summary of this revision.
royjacobson added reviewers: clang-language-wg, erichkeane, cor3ntin, BRevzin, 
CaseyCarter, aaron.ballman.
royjacobson published this revision for review.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

The implementation is more or less complete, but I still need to add an AST 
test for canPassInRegisters.


royjacobson added a comment.

Implementation ready for CR.



================
Comment at: clang/www/cxx_status.html:930
         <td><a href="https://wg21.link/p0848r3";>P0848R3</a></td>
-        <td rowspan="1" class="none" align="center">No</td>
+        <td rowspan="1" class="none" align="center">Clang 15</td>
       </tr>
----------------
Should be `class="unreleased"`, I believe


================
Comment at: clang/www/cxx_status.html:930
         <td><a href="https://wg21.link/p0848r3";>P0848R3</a></td>
-        <td rowspan="1" class="none" align="center">No</td>
+        <td rowspan="1" class="none" align="center">Clang 15</td>
       </tr>
----------------
h-vetinari wrote:
> Should be `class="unreleased"`, I believe
Thanks, nice catch!


This patch implements P0848 in Clang.

During the instantiation of a C++ class, in `Sema::ActOnFields`, we evaluate 
constraints for all the SMFs and compare the constraints to compute the 
eligibility. We defer the computation of the type's [copy-]trivial bits from 
addedMember to the eligibility computation, like we did for destructors in 
D126194 <https://reviews.llvm.org/D126194>. `canPassInRegisters` is modified as 
well to better respect the ineligibility of functions.

Note: Because of the non-implementation of DR1734 and DR1496, I treat deleted 
member functions as 'eligible' for the purpose of [copy-]triviallity. This is 
unfortunate, but I couldn't think of a way to make this make sense otherwise.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128619

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/AST/DeclCXX.cpp
  clang/lib/Frontend/InitPreprocessor.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/test/AST/conditionally-trivial-smfs.cpp
  clang/test/Lexer/cxx-features.cpp
  clang/test/SemaCXX/constrained-special-member-functions.cpp
  clang/www/cxx_status.html

Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -927,7 +927,7 @@
       </tr>
       <tr> <!-- from Cologne -->
         <td><a href="https://wg21.link/p0848r3";>P0848R3</a></td>
-        <td rowspan="1" class="none" align="center">No</td>
+        <td rowspan="1" class="unreleased" align="center">Clang 15</td>
       </tr>
       <tr>
         <td><a href="https://wg21.link/p1616r1";>P1616R1</a></td>
Index: clang/test/SemaCXX/constrained-special-member-functions.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/constrained-special-member-functions.cpp
@@ -0,0 +1,130 @@
+// RUN: %clang_cc1 -verify -std=c++20 %s
+// expected-no-diagnostics
+
+template <int N>
+concept C0 = (N == 0);
+template <int N>
+concept C1 = (N == 1);
+template <int N>
+concept C2 = (N == 2);
+
+// Checks are indexed by:
+// Definition:
+//  1. Explicitly defaulted definition
+//  2. Deleted definition
+//  3. User provided definition
+// We have a less constrained user provided method that should not disable
+// the (copyable) triviality of the type.
+
+// Note that because Clang does not implement DRs 1496 and 1734, we say some
+// classes are trivial when the SMFs are deleted.
+
+template <int N>
+struct DefaultConstructorChecker {
+    DefaultConstructorChecker() requires C0<N> = default;
+    DefaultConstructorChecker() requires C1<N> = delete;
+    DefaultConstructorChecker() requires C2<N>;
+    DefaultConstructorChecker();
+};
+static_assert(__is_trivially_copyable(DefaultConstructorChecker<0>));
+static_assert(__is_trivially_copyable(DefaultConstructorChecker<1>));
+static_assert(__is_trivially_copyable(DefaultConstructorChecker<2>));
+static_assert(__is_trivially_copyable(DefaultConstructorChecker<3>));
+static_assert(__is_trivial(DefaultConstructorChecker<0>));
+// FIXME: DR1496
+static_assert(__is_trivial(DefaultConstructorChecker<1>));
+static_assert(!__is_trivial(DefaultConstructorChecker<2>));
+static_assert(!__is_trivial(DefaultConstructorChecker<3>));
+
+template <int N>
+struct CopyConstructorChecker {
+    CopyConstructorChecker(const CopyConstructorChecker&) requires C0<N> = default;
+    CopyConstructorChecker(const CopyConstructorChecker&) requires C1<N> = delete;
+    CopyConstructorChecker(const CopyConstructorChecker&) requires C2<N>;
+    CopyConstructorChecker(const CopyConstructorChecker&);
+};
+
+static_assert(__is_trivially_copyable(CopyConstructorChecker<0>));
+// FIXME: DR1734
+static_assert(__is_trivially_copyable(CopyConstructorChecker<1>));
+static_assert(!__is_trivially_copyable(CopyConstructorChecker<2>));
+static_assert(!__is_trivially_copyable(CopyConstructorChecker<3>));
+static_assert(!__is_trivial(CopyConstructorChecker<0>));
+static_assert(!__is_trivial(CopyConstructorChecker<1>));
+static_assert(!__is_trivial(CopyConstructorChecker<2>));
+static_assert(!__is_trivial(CopyConstructorChecker<3>));
+
+template <int N>
+struct MoveConstructorChecker {
+    MoveConstructorChecker(MoveConstructorChecker&&) requires C0<N> = default;
+    MoveConstructorChecker(MoveConstructorChecker&&) requires C1<N> = delete;
+    MoveConstructorChecker(MoveConstructorChecker&&) requires C2<N>;
+    MoveConstructorChecker(MoveConstructorChecker&&);
+};
+
+static_assert(__is_trivially_copyable(MoveConstructorChecker<0>));
+// FIXME: DR1734
+static_assert(__is_trivially_copyable(MoveConstructorChecker<1>));
+static_assert(!__is_trivially_copyable(MoveConstructorChecker<2>));
+static_assert(!__is_trivially_copyable(MoveConstructorChecker<3>));
+static_assert(!__is_trivial(MoveConstructorChecker<0>));
+static_assert(!__is_trivial(MoveConstructorChecker<1>));
+static_assert(!__is_trivial(MoveConstructorChecker<2>));
+static_assert(!__is_trivial(MoveConstructorChecker<3>));
+
+template <int N>
+struct MoveAssignmentChecker {
+    MoveAssignmentChecker& operator=(MoveAssignmentChecker&&) requires C0<N> = default;
+    MoveAssignmentChecker& operator=(MoveAssignmentChecker&&) requires C1<N> = delete;
+    MoveAssignmentChecker& operator=(MoveAssignmentChecker&&) requires C2<N>;
+    MoveAssignmentChecker& operator=(MoveAssignmentChecker&&);
+};
+
+static_assert(__is_trivially_copyable(MoveAssignmentChecker<0>));
+// FIXME: DR1734.
+static_assert(__is_trivially_copyable(MoveAssignmentChecker<1>));
+static_assert(!__is_trivially_copyable(MoveAssignmentChecker<2>));
+static_assert(!__is_trivially_copyable(MoveAssignmentChecker<3>));
+static_assert(__is_trivial(MoveAssignmentChecker<0>));
+// FIXME: DR1734.
+static_assert(__is_trivial(MoveAssignmentChecker<1>));
+static_assert(!__is_trivial(MoveAssignmentChecker<2>));
+static_assert(!__is_trivial(MoveAssignmentChecker<3>));
+
+template <int N>
+struct CopyAssignmentChecker {
+    CopyAssignmentChecker& operator=(const CopyAssignmentChecker&) requires C0<N> = default;
+    CopyAssignmentChecker& operator=(const CopyAssignmentChecker&) requires C1<N> = delete;
+    CopyAssignmentChecker& operator=(const CopyAssignmentChecker&) requires C2<N>;
+    CopyAssignmentChecker& operator=(const CopyAssignmentChecker&);
+};
+
+static_assert(__is_trivially_copyable(CopyAssignmentChecker<0>));
+// FIXME: DR1734.
+static_assert(__is_trivially_copyable(CopyAssignmentChecker<1>));
+static_assert(!__is_trivially_copyable(CopyAssignmentChecker<2>));
+static_assert(!__is_trivially_copyable(CopyAssignmentChecker<3>));
+static_assert(__is_trivial(CopyAssignmentChecker<0>));
+// FIXME: DR1734.
+static_assert(__is_trivial(CopyAssignmentChecker<1>));
+static_assert(!__is_trivial(CopyAssignmentChecker<2>));
+static_assert(!__is_trivial(CopyAssignmentChecker<3>));
+
+
+template <int N>
+struct KindComparisonChecker1 {
+    KindComparisonChecker1& operator=(const KindComparisonChecker1&) requires C0<N> = default;
+    KindComparisonChecker1& operator=(KindComparisonChecker1&);
+};
+
+template <int N>
+struct KindComparisonChecker2 {
+    KindComparisonChecker2& operator=(const KindComparisonChecker2&) requires C0<N> = default;
+    const KindComparisonChecker2& operator=(KindComparisonChecker2&) const;
+};
+
+static_assert(!__is_trivial(KindComparisonChecker1<0>));
+static_assert(!__is_trivially_copyable(KindComparisonChecker1<0>));
+
+static_assert(!__is_trivial(KindComparisonChecker2<0>));
+static_assert(!__is_trivially_copyable(KindComparisonChecker2<0>));
Index: clang/test/Lexer/cxx-features.cpp
===================================================================
--- clang/test/Lexer/cxx-features.cpp
+++ clang/test/Lexer/cxx-features.cpp
@@ -52,7 +52,7 @@
 #error "wrong value for __cpp_char8_t"
 #endif
 
-#if check(concepts, 0, 0, 0, 0, 201907, 201907)
+#if check(concepts, 0, 0, 0, 0, 202002L, 202002L)
 #error "wrong value for __cpp_concepts"
 #endif
 
Index: clang/test/AST/conditionally-trivial-smfs.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/conditionally-trivial-smfs.cpp
@@ -0,0 +1,343 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-linux -ast-dump=json %s | FileCheck %s --check-prefixes=CHECK,LIN
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-win32 -ast-dump=json %s | FileCheck %s
+
+// This test validates that we compute correct AST properties of classes with
+// conditionally trivial special member functions.
+
+template <int N>
+struct DefaultConstructorCheck {
+  DefaultConstructorCheck() requires(N == 1) = default;
+  DefaultConstructorCheck() requires(N == 2) = delete;
+  DefaultConstructorCheck() requires(N == 3);
+  DefaultConstructorCheck();
+};
+
+
+template struct DefaultConstructorCheck<1>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "canPassInRegisters": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "defaultCtor": {
+// CHECK-NEXT:            "defaultedIsConstexpr": true,
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "isConstexpr": true,
+// CHECK-NEXT:            "trivial": true,
+// CHECK-NEXT:            "userProvided": true
+// CHECK-NEXT:          },
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT:          "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isLiteral": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTrivial": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+
+template struct DefaultConstructorCheck<2>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "canPassInRegisters": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "defaultCtor": {
+// CHECK-NEXT:            "defaultedIsConstexpr": true,
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "isConstexpr": true,
+// CHECK-NEXT:            "trivial": true,
+// CHECK-NEXT:            "userProvided": true
+// CHECK-NEXT:          },
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT:          "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isLiteral": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTrivial": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+
+
+template struct DefaultConstructorCheck<3>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "canPassInRegisters": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "defaultCtor": {
+// CHECK-NEXT:            "defaultedIsConstexpr": true,
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "isConstexpr": true,
+// CHECK-NEXT:            "nonTrivial": true,
+// CHECK-NEXT:            "userProvided": true
+// CHECK-NEXT:          },
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT:          "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isLiteral": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+
+template <int N>
+struct CopyConstructorCheck {
+  CopyConstructorCheck(const CopyConstructorCheck&) requires(N == 1) = default;
+  CopyConstructorCheck(const CopyConstructorCheck&) requires(N == 2) = delete;
+  CopyConstructorCheck(const CopyConstructorCheck&) requires(N == 3);
+  CopyConstructorCheck(const CopyConstructorCheck&);
+};
+
+
+template struct CopyConstructorCheck<1>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "canPassInRegisters": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "copyCtor": {
+// CHECK-NEXT:            "hasConstParam": true,
+// CHECK-NEXT:            "implicitHasConstParam": true,
+// CHECK-NEXT:            "trivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          },
+
+// CHECK:               "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+// CHECK-NEXT:          "moveAssign": {},
+
+template struct CopyConstructorCheck<2>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "copyCtor": {
+// CHECK-NEXT:            "hasConstParam": true,
+// CHECK-NEXT:            "implicitHasConstParam": true,
+// CHECK-NEXT:            "trivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          },
+
+// CHECK:               "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+// CHECK-NEXT:          "moveAssign": {},
+
+template struct CopyConstructorCheck<3>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "copyCtor": {
+// CHECK-NEXT:            "hasConstParam": true,
+// CHECK-NEXT:            "implicitHasConstParam": true,
+// CHECK-NEXT:            "nonTrivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          },
+
+// CHECK:               "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "moveAssign": {},
+
+template <int N>
+struct MoveConstructorCheck {
+  MoveConstructorCheck(MoveConstructorCheck&&) requires(N == 1) = default;
+  MoveConstructorCheck(MoveConstructorCheck&&) requires(N == 2) = delete;
+  MoveConstructorCheck(MoveConstructorCheck&&) requires(N == 3);
+  MoveConstructorCheck(MoveConstructorCheck&&);
+};
+
+
+template struct MoveConstructorCheck<1>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// LIN-NEXT:            "canPassInRegisters": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+// CHECK-NEXT:          "moveAssign": {},
+// CHECK-NEXT:          "moveCtor": {
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "trivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          }
+
+template struct MoveConstructorCheck<2>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+// CHECK-NEXT:          "moveAssign": {},
+// CHECK-NEXT:          "moveCtor": {
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "trivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          }
+
+template struct MoveConstructorCheck<3>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "hasUserDeclaredConstructor": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "moveAssign": {},
+// CHECK-NEXT:          "moveCtor": {
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "nonTrivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          }
+
+template <int N>
+struct CopyAssignmentCheck {
+  CopyAssignmentCheck& operator=(const CopyAssignmentCheck&) requires(N == 1) = default;
+  CopyAssignmentCheck& operator=(const CopyAssignmentCheck&) requires(N == 2) = delete;
+  CopyAssignmentCheck& operator=(const CopyAssignmentCheck&) requires(N == 3);
+  CopyAssignmentCheck& operator=(const CopyAssignmentCheck&);
+};
+
+
+template struct CopyAssignmentCheck<1>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "canPassInRegisters": true,
+// CHECK-NEXT           "copyAssign": {
+// CHECK-NEXT             "hasConstParam": true,
+// CHECK-NEXT             "implicitHasConstParam": true,
+// CHECK-NEXT             "trivial": true,
+// CHECK-NEXT             "userDeclared": true
+// CHECK-NEXT           },
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT           "isAggregate": true,
+// CHECK-NEXT           "isEmpty": true,
+// CHECK-NEXT           "isLiteral": true,
+// CHECK-NEXT           "isStandardLayout": true,
+// CHECK-NEXT           "isTrivial": true,
+// CHECK-NEXT           "isTriviallyCopyable": true,
+// CHECK-NEXT           "moveAssign": {},
+
+template struct CopyAssignmentCheck<2>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "canPassInRegisters": true,
+// CHECK-NEXT           "copyAssign": {
+// CHECK-NEXT             "hasConstParam": true,
+// CHECK-NEXT             "implicitHasConstParam": true,
+// CHECK-NEXT             "trivial": true,
+// CHECK-NEXT             "userDeclared": true
+// CHECK-NEXT           },
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT           "isAggregate": true,
+// CHECK-NEXT           "isEmpty": true,
+// CHECK-NEXT           "isLiteral": true,
+// CHECK-NEXT           "isStandardLayout": true,
+// CHECK-NEXT           "isTrivial": true,
+// CHECK-NEXT           "isTriviallyCopyable": true,
+// CHECK-NEXT           "moveAssign": {},
+
+template struct CopyAssignmentCheck<3>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "canPassInRegisters": true,
+// CHECK-NEXT           "copyAssign": {
+// CHECK-NEXT             "hasConstParam": true,
+// CHECK-NEXT             "implicitHasConstParam": true,
+// CHECK-NEXT             "trivial": true,
+// CHECK-NEXT             "userDeclared": true
+// CHECK-NEXT           },
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT           "isAggregate": true,
+// CHECK-NEXT           "isEmpty": true,
+// CHECK-NEXT           "isLiteral": true,
+// CHECK-NEXT           "isStandardLayout": true,
+// CHECK-NEXT           "moveAssign": {},
+
+template <int N>
+struct MoveAssignmentCheck {
+  MoveAssignmentCheck& operator=(MoveAssignmentCheck&&) requires(N == 1) = default;
+  MoveAssignmentCheck& operator=(MoveAssignmentCheck&&) requires(N == 2) = delete;
+  MoveAssignmentCheck& operator=(MoveAssignmentCheck&&) requires(N == 3);
+  MoveAssignmentCheck& operator=(MoveAssignmentCheck&&);
+};
+
+
+template struct MoveAssignmentCheck<1>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT:          "isAggregate": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isLiteral": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTrivial": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+// CHECK-NEXT:          "moveAssign": {
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "trivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          },
+
+template struct MoveAssignmentCheck<2>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT:          "isAggregate": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isLiteral": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "isTrivial": true,
+// CHECK-NEXT:          "isTriviallyCopyable": true,
+// CHECK-NEXT:          "moveAssign": {
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "trivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          },
+
+template struct MoveAssignmentCheck<3>;
+// CHECK:             "kind": "ClassTemplateSpecializationDecl",
+// CHECK:             "definitionData": {
+// CHECK-NEXT:          "canConstDefaultInit": true,
+// CHECK-NEXT:          "copyAssign": {
+
+// CHECK:               "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT:          "isAggregate": true,
+// CHECK-NEXT:          "isEmpty": true,
+// CHECK-NEXT:          "isLiteral": true,
+// CHECK-NEXT:          "isStandardLayout": true,
+// CHECK-NEXT:          "moveAssign": {
+// CHECK-NEXT:            "exists": true,
+// CHECK-NEXT:            "nonTrivial": true,
+// CHECK-NEXT:            "userDeclared": true
+// CHECK-NEXT:          },
\ No newline at end of file
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2473,6 +2473,8 @@
         Constructor->getConstexprKind(), InheritedConstructor(),
         TrailingRequiresClause);
     Method->setRangeEnd(Constructor->getEndLoc());
+    if (Constructor->isDefaultConstructor() || Constructor->isCopyOrMoveConstructor())
+      Method->setIneligibleOrNotSelected(true);
   } else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(D)) {
     Method = CXXDestructorDecl::Create(
         SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
@@ -2495,6 +2497,8 @@
         SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, SC,
         D->UsesFPIntrin(), D->isInlineSpecified(), D->getConstexprKind(),
         D->getEndLoc(), TrailingRequiresClause);
+    if (D->isMoveAssignmentOperator() || D->isCopyAssignmentOperator())
+      Method->setIneligibleOrNotSelected(true);
   }
 
   if (D->isInlined())
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -6607,7 +6607,7 @@
     bool CopyCtorIsTrivial = false, CopyCtorIsTrivialForCall = false;
     bool DtorIsTrivialForCall = false;
 
-    // If a class has at least one non-deleted, trivial copy constructor, it
+    // If a class has at least one eligible, trivial copy constructor, it
     // is passed according to the C ABI. Otherwise, it is passed indirectly.
     //
     // Note: This permits classes with non-trivial copy or move ctors to be
@@ -6622,7 +6622,8 @@
       }
     } else {
       for (const CXXConstructorDecl *CD : D->ctors()) {
-        if (CD->isCopyConstructor() && !CD->isDeleted()) {
+        if (CD->isCopyConstructor() && !CD->isDeleted() &&
+            !CD->isIneligibleOrNotSelected()) {
           if (CD->isTrivial())
             CopyCtorIsTrivial = true;
           if (CD->isTrivialForCall())
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -17859,6 +17859,123 @@
     Record->addedSelectedDestructor(dyn_cast<CXXDestructorDecl>(OCS.begin()->Function));
   }
 }
+
+/// Assuming the suggested solution to CWG2595 gets accepted:
+/// [class.mem.special]p6:
+/// An eligible special member function is a special member function for which:
+/// - the function is not deleted,
+/// - the associated constraints, if any, are satisfied, and
+/// - no special member function of the same kind whose associated constraints,
+///   if any, are satisfied is more constrained.
+void ComputeSpecialMemberMethodsEligiblity(Sema &S, CXXRecordDecl *Record) {
+  SmallVector<CXXMethodDecl *, 4> DefaultConstructors;
+  SmallVector<CXXMethodDecl *, 4> CopyConstructors;
+  SmallVector<CXXMethodDecl *, 4> MoveConstructors;
+  SmallVector<CXXMethodDecl *, 4> CopyAssignmentOperators;
+  SmallVector<CXXMethodDecl *, 4> MoveAssignmentOperators;
+
+  for (auto *Decl : Record->decls()) {
+    auto* MD = dyn_cast<CXXMethodDecl>(Decl);
+    if (!MD)
+      continue;
+    if (auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {
+      if (CD->isInvalidDecl())
+        continue;
+      if (CD->isDefaultConstructor())
+        DefaultConstructors.push_back(MD);
+      else if (CD->isCopyConstructor())
+        CopyConstructors.push_back(MD);
+      else if (CD->isMoveConstructor())
+        MoveConstructors.push_back(MD);
+    } else if (MD->isCopyAssignmentOperator()) {
+      CopyAssignmentOperators.push_back(MD);
+    } else if (MD->isMoveAssignmentOperator()) {
+      MoveAssignmentOperators.push_back(MD);
+    }
+  }
+
+  auto AreSameKind = [](CXXMethodDecl *M1, CXXMethodDecl *M2,
+                        Sema::CXXSpecialMember CSM) {
+    /// [class.mem.special]p5
+    /// Two special member functions are of the same kind if:
+    /// - they are both default constructors,
+    /// - they are both copy or move constructors with the same first parameter
+    ///   type, or
+    /// - they are both copy or move assignment operators with the same first
+    ///   parameter type and the same cv-qualifiers and ref-qualifier, if any.
+    if (CSM == Sema::CXXDefaultConstructor)
+      return true;
+    if (M1->getParamDecl(0)->getType() != M2->getParamDecl(0)->getType())
+      return false;
+    if (M1->getThisType() != M2->getThisType())
+      return false;
+
+    return true;
+  };
+
+  auto SetEligibleMethods = [&S, &Record,
+                             AreSameKind](ArrayRef<CXXMethodDecl *> Methods,
+                                          Sema::CXXSpecialMember CSM) {
+    SmallVector<bool, 4> SatisfactionStatus;
+
+    for (auto *Method : Methods) {
+      const Expr *Constraints = Method->getTrailingRequiresClause();
+      if (!Constraints)
+        SatisfactionStatus.push_back(true);
+      else {
+        ConstraintSatisfaction Satisfaction;
+        if (S.CheckFunctionConstraints(Method, Satisfaction))
+          SatisfactionStatus.push_back(false);
+        else
+          SatisfactionStatus.push_back(Satisfaction.IsSatisfied);
+      }
+    }
+
+    for (size_t i = 0; i < Methods.size(); i++) {
+      if (!SatisfactionStatus[i])
+        continue;
+      auto *MethodI = Methods[i];
+      const Expr *ConstraintsI = MethodI->getTrailingRequiresClause();
+      bool AnotherMethodIsMoreConstrained = false;
+      for (size_t j = 0; j < Methods.size(); j++) {
+        if (i == j || !SatisfactionStatus[j])
+          continue;
+        auto *MethodJ = Methods[j];
+        if (!AreSameKind(MethodI, MethodJ, CSM))
+          continue;
+
+        const Expr *ConstraintsJ = MethodJ->getTrailingRequiresClause();
+        if (!ConstraintsJ)
+          continue;
+        if (!ConstraintsI) {
+          AnotherMethodIsMoreConstrained = true;
+          break;
+        }
+        if (S.IsAtLeastAsConstrained(MethodJ, {ConstraintsJ}, MethodI,
+                                     {ConstraintsI},
+                                     AnotherMethodIsMoreConstrained)) {
+          AnotherMethodIsMoreConstrained = true;
+          break;
+        }
+        if (AnotherMethodIsMoreConstrained)
+          break;
+      }
+      // FIXME: Do not consider deleted methods as eligible after implementing
+      // DR1734 and DR1496.
+      if (!AnotherMethodIsMoreConstrained) {
+        MethodI->setIneligibleOrNotSelected(false);
+        Record->addedEligibleSpecialMemberFunction(MethodI, 1 << CSM);
+      }
+    }
+  };
+
+  SetEligibleMethods(DefaultConstructors, Sema::CXXDefaultConstructor);
+  SetEligibleMethods(CopyConstructors, Sema::CXXCopyConstructor);
+  SetEligibleMethods(MoveConstructors, Sema::CXXMoveConstructor);
+  SetEligibleMethods(CopyAssignmentOperators, Sema::CXXCopyAssignment);
+  SetEligibleMethods(MoveAssignmentOperators, Sema::CXXMoveAssignment);
+}
+
 } // namespace
 
 void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
@@ -17887,9 +18004,6 @@
   RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl);
   CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(EnclosingDecl);
 
-  if (CXXRecord && !CXXRecord->isDependentType())
-    ComputeSelectedDestructor(*this, CXXRecord);
-
   // Start counting up the number of named members; make sure to include
   // members of anonymous structs and unions in the total.
   unsigned NumNamedMembers = 0;
@@ -18175,6 +18289,8 @@
             Completed = true;
           }
         }
+      ComputeSelectedDestructor(*this, CXXRecord);
+      ComputeSpecialMemberMethodsEligiblity(*this, CXXRecord);
       }
     }
 
Index: clang/lib/Frontend/InitPreprocessor.cpp
===================================================================
--- clang/lib/Frontend/InitPreprocessor.cpp
+++ clang/lib/Frontend/InitPreprocessor.cpp
@@ -674,7 +674,7 @@
   // C++20 features.
   if (LangOpts.CPlusPlus20) {
     //Builder.defineMacro("__cpp_aggregate_paren_init", "201902L");
-    Builder.defineMacro("__cpp_concepts", "201907L");
+    Builder.defineMacro("__cpp_concepts", "202002L");
     Builder.defineMacro("__cpp_conditional_explicit", "201806L");
     //Builder.defineMacro("__cpp_consteval", "201811L");
     Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L");
Index: clang/lib/AST/DeclCXX.cpp
===================================================================
--- clang/lib/AST/DeclCXX.cpp
+++ clang/lib/AST/DeclCXX.cpp
@@ -894,9 +894,11 @@
         // This is an extension in C++03.
         data().PlainOldData = false;
       }
-      // We delay updating destructor relevant properties until
-      // addedSelectedDestructor.
-      // FIXME: Defer this for the other special member functions as well.
+      // When instantiating a class, we delay updating the destructor and
+      // triviality properties of the class until selecting a destructor and
+      // computing the eligibility of SMFs. This is because those member
+      // functions may have constraints that we need to evaluate and compare
+      // later in the instantiation.
       if (!Method->isIneligibleOrNotSelected()) {
         addedEligibleSpecialMemberFunction(Method, SMKind);
       }
@@ -1437,10 +1439,13 @@
 
   // Update which trivial / non-trivial special members we have.
   // addedMember will have skipped this step for this member.
-  if (D->isTrivial())
-    data().HasTrivialSpecialMembers |= SMKind;
-  else
-    data().DeclaredNonTrivialSpecialMembers |= SMKind;
+  if (!D->isIneligibleOrNotSelected()) {
+    if (D->isTrivial()) {
+      data().HasTrivialSpecialMembers |= SMKind;
+    }
+    else
+      data().DeclaredNonTrivialSpecialMembers |= SMKind;
+  }
 }
 
 void CXXRecordDecl::setCaptures(ASTContext &Context,
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -491,9 +491,10 @@
   that can be used for such compatibility. The demangler now demangles
   symbols with named module attachment.
 
-- As per "Conditionally Trivial Special Member Functions" (P0848), it is
-  now possible to overload destructors using concepts. Note that the rest
-  of the paper about other special member functions is not yet implemented.
+- Implemented "Conditionally Trivial Special Member Functions" (`P0848 <https://wg21.link/p0848r3>`_).
+  Note: The handling of deleted functions is not yet compliant, as Clang
+  does not implement `DR1496 <https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1496>`_
+  and `DR1734 <https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1734>`_.
 
 C++2b Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to