[Clang] Implement P0848 (Conditionally Trivial Special Member Functions)
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. `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. Reviewed By: #clang-language-wg, cor3ntin, aaron.ballman Differential Revision: https://reviews.llvm.org/D128619
This commit is contained in:
parent
613336da8c
commit
b1c960fc6d
|
@ -167,6 +167,10 @@ C++20 Feature Support
|
|||
(C++14 [dcl.constexpr]p6 (CWG DR647/CWG DR1358))
|
||||
- Correctly defer dependent immediate function invocations until template instantiation.
|
||||
This fixes `GH55601 <https://github.com/llvm/llvm-project/issues/55601>`_.
|
||||
- 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>`_.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -894,9 +894,11 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
// 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 @@ void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
|
|||
|
||||
// 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,
|
||||
|
|
|
@ -673,7 +673,11 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
|
|||
|
||||
// C++20 features.
|
||||
if (LangOpts.CPlusPlus20) {
|
||||
//Builder.defineMacro("__cpp_aggregate_paren_init", "201902L");
|
||||
// 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");
|
||||
|
|
|
@ -17968,7 +17968,6 @@ void Sema::ActOnLastBitfield(SourceLocation DeclLoc,
|
|||
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 @@ namespace {
|
|||
///
|
||||
/// 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 @@ void ComputeSelectedDestructor(Sema &S, CXXRecordDecl *Record) {
|
|||
Record->addedSelectedDestructor(dyn_cast<CXXDestructorDecl>(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<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(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<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) {
|
||||
auto *FTD = dyn_cast<FunctionTemplateDecl>(Decl);
|
||||
if (FTD)
|
||||
MD = dyn_cast<CXXMethodDecl>(FTD->getTemplatedDecl());
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
|
||||
ArrayRef<Decl *> Fields, SourceLocation LBrac,
|
||||
|
@ -18063,9 +18190,6 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
|
|||
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;
|
||||
|
@ -18351,6 +18475,8 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
|
|||
Completed = true;
|
||||
}
|
||||
}
|
||||
ComputeSelectedDestructor(*this, CXXRecord);
|
||||
ComputeSpecialMemberFunctionsEligiblity(*this, CXXRecord);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6618,7 +6618,7 @@ static bool canPassInRegisters(Sema &S, CXXRecordDecl *D,
|
|||
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 @@ static bool canPassInRegisters(Sema &S, CXXRecordDecl *D,
|
|||
}
|
||||
} 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())
|
||||
|
|
|
@ -2481,6 +2481,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
|
|||
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,
|
||||
|
@ -2503,6 +2506,8 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
|
|||
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())
|
||||
|
|
|
@ -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<class X>
|
||||
struct DefaultConstructibleWithTemplate {
|
||||
template<class T = int>
|
||||
DefaultConstructibleWithTemplate();
|
||||
};
|
||||
|
||||
void f() {
|
||||
DefaultConstructibleWithTemplate<int> x;
|
||||
}
|
||||
|
||||
// CHECK: | `-ClassTemplateSpecializationDecl {{.*}} struct DefaultConstructibleWithTemplate definition
|
||||
// CHECK: | | |-CXXConstructorDecl {{.*}} DefaultConstructibleWithTemplate 'void ()'
|
|
@ -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: },
|
|
@ -0,0 +1,231 @@
|
|||
// RUN: %clang_cc1 -verify -std=c++20 %s
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template <int N>
|
||||
struct KindComparisonChecker3 {
|
||||
using Alias = KindComparisonChecker3;
|
||||
Alias& operator=(const Alias&) requires C0<N> = 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 <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 HasAC = HasA<T> && requires(T t) {
|
||||
{ t.c() };
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept HasABC = HasAB<T> && HasAC<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>), "");
|
||||
|
||||
|
||||
// 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 <class T>
|
||||
struct CRTPBase {
|
||||
CRTPBase() requires (sizeof(T) > 0); // expected-error {{to an incomplete type}}
|
||||
CRTPBase() = default;
|
||||
};
|
||||
|
||||
struct Child : CRTPBase<Child> { int x; };
|
||||
// expected-note@-1 {{definition of 'Child' is not complete until}}
|
||||
// expected-note@-2 {{in instantiation of template class 'CRTPBase<Child>' requested here}}
|
||||
static Child c;
|
||||
|
||||
|
||||
namespace GH57046 {
|
||||
template<unsigned N>
|
||||
struct Foo {
|
||||
Foo() requires (N==1) {} // expected-note {{declared here}}
|
||||
Foo() requires (N==2) = default;
|
||||
};
|
||||
|
||||
template <unsigned N, unsigned M>
|
||||
struct S {
|
||||
Foo<M> 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;
|
||||
}
|
||||
}
|
|
@ -927,8 +927,16 @@ code. This issue is expected to be rectified soon.
|
|||
</tr>
|
||||
<tr> <!-- from Cologne -->
|
||||
<td><a href="https://wg21.link/p0848r3">P0848R3</a></td>
|
||||
<td rowspan="1" class="none" align="center">No</td>
|
||||
</tr>
|
||||
<td rowspan="1" class="unreleased" align="center">
|
||||
<details>
|
||||
<summary>Clang 16 (Partial)</summary>
|
||||
Because of other concepts implementation deficits, the __cpp_concepts macro is not yet set to 202002L.
|
||||
Also, the related defect reports <a href="https://wg21.link/cwg1496">DR1496</a> and
|
||||
<a href="https://wg21.link/cwg1734">DR1734</a> are not yet implemented. Accordingly, deleted
|
||||
special member functions are treated as eligible even though they shouldn't be.
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://wg21.link/p1616r1">P1616R1</a></td>
|
||||
<td rowspan="2" class="full" align="center">Clang 10</td>
|
||||
|
|
Loading…
Reference in New Issue