royjacobson updated this revision to Diff 445350.
royjacobson marked an inline comment as not done.
royjacobson added a comment.
Add a test case with more subsumption
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D128619/new/
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,186 @@
+// 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>));
+
+template <class T>
+concept HasA = requires(T t) {
+ { t.a(); }
+};
+
+template <class T>
+concept HasAB = HasA<T> && requires(T t) {
+ { t.b(); }
+};
+
+template <class T>
+concept HasABC = HasAB<T> && requires(T t) {
+ { t.c(); }
+};
+
+template <class T>
+concept HasAC = HasA<T> && requires(T t) {
+ { t.c(); }
+};
+
+template <class T>
+struct ComplexConstraints {
+ ComplexConstraints() requires HasABC<T> = default;
+ ComplexConstraints() requires HasAB<T>;
+ ComplexConstraints() requires HasAC<T>;
+ ComplexConstraints() requires HasA<T> = delete;
+ ComplexConstraints();
+};
+
+struct A {
+ void a();
+};
+
+struct AB {
+ void a();
+ void b();
+};
+
+struct ABC {
+ void a();
+ void b();
+ void c();
+};
+
+struct AC {
+ void a();
+ void c();
+};
+
+
+static_assert(__is_trivial(ComplexConstraints<ABC>), "");
+static_assert(!__is_trivial(ComplexConstraints<AB>), "");
+static_assert(!__is_trivial(ComplexConstraints<AC>), "");
+static_assert(__is_trivial(ComplexConstraints<A>), "");
+static_assert(!__is_trivial(ComplexConstraints<int>), "");
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,120 @@
Record->addedSelectedDestructor(dyn_cast<CXXDestructorDecl>(OCS.begin()->Function));
}
}
+
+/// [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.
+bool AreSpecialMemberFunctionsSameKind(CXXMethodDecl *M1, CXXMethodDecl *M2,
+ Sema::CXXSpecialMember CSM) {
+ 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;
+}
+
+/// [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
+/// [CWG2595], if any, are satisfied is more constrained.
+void SetEligibleMethods(Sema &S, CXXRecordDecl* Record,
+ ArrayRef<CXXMethodDecl *> Methods,
+ Sema::CXXSpecialMember CSM) {
+ SmallVector<bool, 4> SatisfactionStatus;
+
+ for (CXXMethodDecl *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;
+ CXXMethodDecl *Method = Methods[i];
+ const Expr *Constraints = Method->getTrailingRequiresClause();
+ bool AnotherMethodIsMoreConstrained = false;
+ for (size_t j = 0; j < Methods.size(); j++) {
+ if (i == j || !SatisfactionStatus[j])
+ continue;
+ CXXMethodDecl *OtherMethod = Methods[j];
+ if (!AreSpecialMemberFunctionsSameKind(Method, OtherMethod, CSM))
+ continue;
+
+ const Expr *OtherConstraints = OtherMethod->getTrailingRequiresClause();
+ if (!OtherConstraints)
+ continue;
+ if (!Constraints) {
+ AnotherMethodIsMoreConstrained = true;
+ break;
+ }
+ if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, Method,
+ {Constraints},
+ AnotherMethodIsMoreConstrained)) {
+ AnotherMethodIsMoreConstrained = true;
+ break;
+ }
+ }
+ // FIXME: Do not consider deleted methods as eligible after implementing
+ // DR1734 and DR1496.
+ if (!AnotherMethodIsMoreConstrained) {
+ Method->setIneligibleOrNotSelected(false);
+ Record->addedEligibleSpecialMemberFunction(Method, 1 << CSM);
+ }
+ }
+}
+
+void ComputeSpecialMemberFunctionsEligiblity(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);
+ }
+ }
+
+ SetEligibleMethods(S, Record, DefaultConstructors, Sema::CXXDefaultConstructor);
+ SetEligibleMethods(S, Record, CopyConstructors, Sema::CXXCopyConstructor);
+ SetEligibleMethods(S, Record, MoveConstructors, Sema::CXXMoveConstructor);
+ SetEligibleMethods(S, Record, CopyAssignmentOperators, Sema::CXXCopyAssignment);
+ SetEligibleMethods(S, Record, MoveAssignmentOperators, Sema::CXXMoveAssignment);
+}
+
} // namespace
void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
@@ -17887,9 +18001,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 +18286,8 @@
Completed = true;
}
}
+ ComputeSelectedDestructor(*this, CXXRecord);
+ ComputeSpecialMemberFunctionsEligiblity(*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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits