diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -167,6 +167,10 @@ (C++14 [dcl.constexpr]p6 (CWG DR647/CWG DR1358)) - Correctly defer dependent immediate function invocations until template instantiation. This fixes `GH55601 `_. +- Implemented "Conditionally Trivial Special Member Functions" (`P0848 `_). + Note: The handling of deleted functions is not yet compliant, as Clang + does not implement `DR1496 `_ + and `DR1734 `_. diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/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 its special member functions. This is + // because there might be function constraints that we need to evaluate + // and compare later in the instantiation. if (!Method->isIneligibleOrNotSelected()) { addedEligibleSpecialMemberFunction(Method, SMKind); } @@ -1437,10 +1439,12 @@ // 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, diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -674,6 +674,9 @@ // C++20 features. if (LangOpts.CPlusPlus20) { //Builder.defineMacro("__cpp_aggregate_paren_init", "201902L"); + // P0848 is implemented, but we're still waiting for other concepts + // issues to be addressed before bumping __cpp_concepts up to 202002L. + // Refer to the discussion of this at https://reviews.llvm.org/D128619. Builder.defineMacro("__cpp_concepts", "201907L"); Builder.defineMacro("__cpp_conditional_explicit", "201806L"); //Builder.defineMacro("__cpp_consteval", "201811L"); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -17968,7 +17968,6 @@ AllIvarDecls.push_back(Ivar); } -namespace { /// [class.dtor]p4: /// At the end of the definition of a class, overload resolution is /// performed among the prospective destructors declared in that class with @@ -17977,7 +17976,7 @@ /// /// We do the overload resolution here, then mark the selected constructor in the AST. /// Later CXXRecordDecl::getDestructor() will return the selected constructor. -void ComputeSelectedDestructor(Sema &S, CXXRecordDecl *Record) { +static void ComputeSelectedDestructor(Sema &S, CXXRecordDecl *Record) { if (!Record->hasUserDeclaredDestructor()) { return; } @@ -18035,7 +18034,135 @@ Record->addedSelectedDestructor(dyn_cast(OCS.begin()->Function)); } } -} // namespace + +/// [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. +static bool AreSpecialMemberFunctionsSameKind(ASTContext &Context, + CXXMethodDecl *M1, + CXXMethodDecl *M2, + Sema::CXXSpecialMember CSM) { + if (CSM == Sema::CXXDefaultConstructor) + return true; + if (!Context.hasSameType(M1->getParamDecl(0)->getType(), + M2->getParamDecl(0)->getType())) + return false; + if (!Context.hasSameType(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. +static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record, + ArrayRef Methods, + Sema::CXXSpecialMember CSM) { + SmallVector 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(S.Context, 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)) { + // There was an error with the constraints comparison. Exit the loop + // and don't consider this function eligible. + AnotherMethodIsMoreConstrained = true; + } + if (AnotherMethodIsMoreConstrained) + break; + } + // FIXME: Do not consider deleted methods as eligible after implementing + // DR1734 and DR1496. + if (!AnotherMethodIsMoreConstrained) { + Method->setIneligibleOrNotSelected(false); + Record->addedEligibleSpecialMemberFunction(Method, 1 << CSM); + } + } +} + +static void ComputeSpecialMemberFunctionsEligiblity(Sema &S, + CXXRecordDecl *Record) { + SmallVector DefaultConstructors; + SmallVector CopyConstructors; + SmallVector MoveConstructors; + SmallVector CopyAssignmentOperators; + SmallVector MoveAssignmentOperators; + + for (auto *Decl : Record->decls()) { + auto *MD = dyn_cast(Decl); + if (!MD) { + auto *FTD = dyn_cast(Decl); + if (FTD) + MD = dyn_cast(FTD->getTemplatedDecl()); + } + if (!MD) + continue; + if (auto *CD = dyn_cast(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); +} void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, ArrayRef Fields, SourceLocation LBrac, @@ -18063,9 +18190,6 @@ RecordDecl *Record = dyn_cast(EnclosingDecl); CXXRecordDecl *CXXRecord = dyn_cast(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; @@ -18351,6 +18475,8 @@ Completed = true; } } + ComputeSelectedDestructor(*this, CXXRecord); + ComputeSpecialMemberFunctionsEligiblity(*this, CXXRecord); } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6618,7 +6618,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 @@ -6633,7 +6633,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()) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3358,6 +3358,7 @@ Result->setKind(DD && !DD->isDeleted() ? SpecialMemberOverloadResult::Success : SpecialMemberOverloadResult::NoMemberOrDeleted); + assert(!DD->isIneligibleOrNotSelected() && "Method is ineligible."); return *Result; } @@ -3502,6 +3503,7 @@ OverloadCandidateSet::iterator Best; switch (OCS.BestViableFunction(*this, LookupLoc, Best)) { case OR_Success: + assert(!Best->Function->isIneligibleOrNotSelected() && "Method is ineligible."); Result->setMethod(cast(Best->Function)); Result->setKind(SpecialMemberOverloadResult::Success); break; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2481,6 +2481,9 @@ Constructor->getConstexprKind(), InheritedConstructor(), TrailingRequiresClause); Method->setRangeEnd(Constructor->getEndLoc()); + if (Constructor->isDefaultConstructor() || + Constructor->isCopyOrMoveConstructor()) + Method->setIneligibleOrNotSelected(true); } else if (CXXDestructorDecl *Destructor = dyn_cast(D)) { Method = CXXDestructorDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, @@ -2503,6 +2506,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()) diff --git a/clang/test/AST/conditionally-trivial-smfs-2.cpp b/clang/test/AST/conditionally-trivial-smfs-2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/conditionally-trivial-smfs-2.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-linux %s -ast-dump | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-win32 %s -ast-dump | FileCheck %s + +template +struct DefaultConstructibleWithTemplate { + template + DefaultConstructibleWithTemplate(); +}; + +void f() { + DefaultConstructibleWithTemplate x; +} + +// CHECK: | `-ClassTemplateSpecializationDecl {{.*}} struct DefaultConstructibleWithTemplate definition +// CHECK: | | |-CXXConstructorDecl {{.*}} DefaultConstructibleWithTemplate 'void ()' diff --git a/clang/test/AST/conditionally-trivial-smfs.cpp b/clang/test/AST/conditionally-trivial-smfs.cpp new file mode 100644 --- /dev/null +++ b/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 +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 +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 +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 +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 +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: }, diff --git a/clang/test/SemaCXX/constrained-special-member-functions.cpp b/clang/test/SemaCXX/constrained-special-member-functions.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/constrained-special-member-functions.cpp @@ -0,0 +1,231 @@ +// RUN: %clang_cc1 -verify -std=c++20 %s + +template +concept C0 = (N == 0); +template +concept C1 = (N == 1); +template +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 +struct DefaultConstructorChecker { + DefaultConstructorChecker() requires C0 = default; + DefaultConstructorChecker() requires C1 = delete; + DefaultConstructorChecker() requires C2; + 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 +struct CopyConstructorChecker { + CopyConstructorChecker(const CopyConstructorChecker&) requires C0 = default; + CopyConstructorChecker(const CopyConstructorChecker&) requires C1 = delete; + CopyConstructorChecker(const CopyConstructorChecker&) requires C2; + 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 +struct MoveConstructorChecker { + MoveConstructorChecker(MoveConstructorChecker&&) requires C0 = default; + MoveConstructorChecker(MoveConstructorChecker&&) requires C1 = delete; + MoveConstructorChecker(MoveConstructorChecker&&) requires C2; + 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 +struct MoveAssignmentChecker { + MoveAssignmentChecker& operator=(MoveAssignmentChecker&&) requires C0 = default; + MoveAssignmentChecker& operator=(MoveAssignmentChecker&&) requires C1 = delete; + MoveAssignmentChecker& operator=(MoveAssignmentChecker&&) requires C2; + 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 +struct CopyAssignmentChecker { + CopyAssignmentChecker& operator=(const CopyAssignmentChecker&) requires C0 = default; + CopyAssignmentChecker& operator=(const CopyAssignmentChecker&) requires C1 = delete; + CopyAssignmentChecker& operator=(const CopyAssignmentChecker&) requires C2; + 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 +struct KindComparisonChecker1 { + KindComparisonChecker1& operator=(const KindComparisonChecker1&) requires C0 = default; + KindComparisonChecker1& operator=(KindComparisonChecker1&); +}; + +template +struct KindComparisonChecker2 { + KindComparisonChecker2& operator=(const KindComparisonChecker2&) requires C0 = default; + const KindComparisonChecker2& operator=(KindComparisonChecker2&) const; +}; + +template +struct KindComparisonChecker3 { + using Alias = KindComparisonChecker3; + Alias& operator=(const Alias&) requires C0 = default; + KindComparisonChecker3& operator=(const KindComparisonChecker3&); +}; + +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>)); + +static_assert(__is_trivial(KindComparisonChecker3<0>)); +static_assert(__is_trivially_copyable(KindComparisonChecker3<0>)); + +template +concept HasA = requires(T t) { + { t.a() }; +}; + +template +concept HasAB = HasA && requires(T t) { + { t.b() }; +}; + +template +concept HasAC = HasA && requires(T t) { + { t.c() }; +}; + +template +concept HasABC = HasAB && HasAC && requires(T t) { + { t.c() }; +}; + +template +struct ComplexConstraints { + ComplexConstraints() requires HasABC = default; + ComplexConstraints() requires HasAB; + ComplexConstraints() requires HasAC; + ComplexConstraints() requires HasA = 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), ""); +static_assert(!__is_trivial(ComplexConstraints), ""); +static_assert(!__is_trivial(ComplexConstraints), ""); +static_assert(__is_trivial(ComplexConstraints), ""); +static_assert(!__is_trivial(ComplexConstraints), ""); + + +// This is evaluated at the completion of CRTPBase, while `T` is not yet completed. +// This is probably correct behavior. +// FIXME: We should not throw an error, instead SFINAE should make the constraint +// silently unsatisfied. See [temp.constr.constr]p5 +template +struct CRTPBase { + CRTPBase() requires (sizeof(T) > 0); // expected-error {{to an incomplete type}} + CRTPBase() = default; +}; + +struct Child : CRTPBase { int x; }; +// expected-note@-1 {{definition of 'Child' is not complete until}} +// expected-note@-2 {{in instantiation of template class 'CRTPBase' requested here}} +static Child c; + + +namespace GH57046 { +template +struct Foo { + Foo() requires (N==1) {} // expected-note {{declared here}} + Foo() requires (N==2) = default; +}; + +template +struct S { + Foo data; + S() requires (N==1) {} + consteval S() requires (N==2) = default; // expected-note {{non-constexpr constructor 'Foo' cannot be used in a constant expression}} +}; + +void func() { + S<2, 1> s1; // expected-error {{is not a constant expression}} expected-note {{in call to 'S()'}} + S<2, 2> s2; +} +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -927,8 +927,16 @@ P0848R3 - No - + +
+ Clang 16 (Partial) + Because of other concepts implementation deficits, the __cpp_concepts macro is not yet set to 202002L. + Also, the related defect reports DR1496 and + DR1734 are not yet implemented. Accordingly, deleted + special member functions are treated as eligible even though they shouldn't be. +
+ + P1616R1 Clang 10