diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -446,9 +446,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 `_). + Note that the handling of deleted functions is not yet compliant, as Clang + does not implement `DR1496 `_ + and `DR1734 `_. C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -264,6 +264,7 @@ friend void FunctionDecl::setPure(bool); friend void TagDecl::startDefinition(); +public: /// Values used in DefinitionData fields to represent special members. enum SpecialMemberFlags { SMF_DefaultConstructor = 0x1, @@ -275,7 +276,6 @@ SMF_All = 0x3f }; -public: enum LambdaDependencyKind { LDK_Unknown = 0, LDK_AlwaysDependent, 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,8 @@ // 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. + // We delay updating destructor related and triviality properties until + // selecting destructor and computing eligibility of SMFs. if (!Method->isIneligibleOrNotSelected()) { addedEligibleSpecialMemberFunction(Method, SMKind); } @@ -1437,10 +1436,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, 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,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"); 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 @@ -17782,6 +17782,100 @@ Record->addedSelectedDestructor(dyn_cast(OCS.begin()->Function)); } } + +/// This assumes 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 DefaultConstructors; + SmallVector CopyConstructors; + SmallVector MoveConstructors; + SmallVector CopyAssignmentOperators; + SmallVector MoveAssignmentOperators; + + for (auto *Decl : Record->decls()) { + auto* MD = dyn_cast(Decl); + 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); + } + } + + auto SetEligibleMethods = [&S, &Record](ArrayRef Methods, unsigned int SMKind) { + SmallVector SatisfactionStatus; + + for (auto *Method : Methods) { + const Expr *Constraints = Method->getTrailingRequiresClause(); + if (!Constraints) + SatisfactionStatus.push_back(true); + else { + ConstraintSatisfaction Satisfaction; + if (S.CheckConstraintSatisfaction(Constraints, 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]; + // TODO: Need to validate they have same kind. + 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, SMKind); + } + } + }; + + SetEligibleMethods(DefaultConstructors, CXXRecordDecl::SMF_DefaultConstructor); + SetEligibleMethods(CopyConstructors, CXXRecordDecl::SMF_CopyConstructor); + SetEligibleMethods(MoveConstructors, CXXRecordDecl::SMF_MoveConstructor); + SetEligibleMethods(CopyAssignmentOperators, CXXRecordDecl::SMF_CopyAssignment); + SetEligibleMethods(MoveAssignmentOperators, CXXRecordDecl::SMF_MoveAssignment); +} + } // namespace void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, @@ -17810,8 +17904,10 @@ RecordDecl *Record = dyn_cast(EnclosingDecl); CXXRecordDecl *CXXRecord = dyn_cast(EnclosingDecl); - if (CXXRecord && !CXXRecord->isDependentType()) + if (CXXRecord && !CXXRecord->isDependentType()) { ComputeSelectedDestructor(*this, CXXRecord); + ComputeSpecialMemberMethodsEligiblity(*this, CXXRecord); + } // Start counting up the number of named members; make sure to include // members of anonymous structs and unions in the total. 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 @@ -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(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()) diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp --- a/clang/test/Lexer/cxx-features.cpp +++ b/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 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,111 @@ +// RUN: %clang_cc1 -verify -std=c++20 %s +// expected-no-diagnostics + +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>)); 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,7 +927,7 @@ P0848R3 - No + Clang 15 P1616R1