summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaDeclCXX.cpp
diff options
context:
space:
mode:
authorRichard Smith <richard@metafoo.co.uk>2019-12-10 16:45:02 -0800
committerRichard Smith <richard@metafoo.co.uk>2019-12-10 17:24:27 -0800
commitbc24014b9765a454cb5214f4871451a41ffb7d29 (patch)
tree8824f31e7dc574ce2a567dcfedc2f259c2a3fa6f /clang/lib/Sema/SemaDeclCXX.cpp
parentf364686f34d80e0873b478933952c6b664177ce4 (diff)
downloadbcm5719-llvm-bc24014b9765a454cb5214f4871451a41ffb7d29.tar.gz
bcm5719-llvm-bc24014b9765a454cb5214f4871451a41ffb7d29.zip
[c++20] Implement P1185R2 (as modified by P2002R0).
For each defaulted operator<=> in a class that doesn't explicitly declare any operator==, also inject a matching implicit defaulted operator==.
Diffstat (limited to 'clang/lib/Sema/SemaDeclCXX.cpp')
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp140
1 files changed, 107 insertions, 33 deletions
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 7bbd0114b1e..72ac81776aa 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6481,40 +6481,38 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
bool HasMethodWithOverrideControl = false,
HasOverridingMethodWithoutOverrideControl = false;
- for (auto *M : Record->methods()) {
- // FIXME: We could do this check for dependent types with non-dependent
- // bases.
- if (!Record->isDependentType()) {
- // See if a method overloads virtual methods in a base
- // class without overriding any.
- if (!M->isStatic())
- DiagnoseHiddenVirtualMethods(M);
- if (M->hasAttr<OverrideAttr>())
- HasMethodWithOverrideControl = true;
- else if (M->size_overridden_methods() > 0)
- HasOverridingMethodWithoutOverrideControl = true;
- }
+ for (auto *D : Record->decls()) {
+ if (auto *M = dyn_cast<CXXMethodDecl>(D)) {
+ // FIXME: We could do this check for dependent types with non-dependent
+ // bases.
+ if (!Record->isDependentType()) {
+ // See if a method overloads virtual methods in a base
+ // class without overriding any.
+ if (!M->isStatic())
+ DiagnoseHiddenVirtualMethods(M);
+ if (M->hasAttr<OverrideAttr>())
+ HasMethodWithOverrideControl = true;
+ else if (M->size_overridden_methods() > 0)
+ HasOverridingMethodWithoutOverrideControl = true;
+ }
- if (!isa<CXXDestructorDecl>(M))
- CompleteMemberFunction(M);
+ if (!isa<CXXDestructorDecl>(M))
+ CompleteMemberFunction(M);
+ } else if (auto *F = dyn_cast<FriendDecl>(D)) {
+ CheckForDefaultedFunction(
+ dyn_cast_or_null<FunctionDecl>(F->getFriendDecl()));
+ }
}
if (HasMethodWithOverrideControl &&
HasOverridingMethodWithoutOverrideControl) {
// At least one method has the 'override' control declared.
- // Diagnose all other overridden methods which do not have 'override' specified on them.
+ // Diagnose all other overridden methods which do not have 'override'
+ // specified on them.
for (auto *M : Record->methods())
DiagnoseAbsenceOfOverrideControl(M);
}
- // Process any defaulted friends in the member-specification.
- if (!Record->isDependentType()) {
- for (FriendDecl *D : Record->friends()) {
- CheckForDefaultedFunction(
- dyn_cast_or_null<FunctionDecl>(D->getFriendDecl()));
- }
- }
-
// Check the defaulted secondary comparisons after any other member functions.
for (FunctionDecl *FD : DefaultedSecondaryComparisons)
CheckExplicitlyDefaultedFunction(S, FD);
@@ -7868,7 +7866,9 @@ static void lookupOperatorsForDefaultedComparison(Sema &Self, Scope *S,
Lookup(ExtraOp);
// For 'operator<=>', we also form a 'cmp != 0' expression, and might
- // synthesize a three-way comparison from '<' and '=='.
+ // synthesize a three-way comparison from '<' and '=='. In a dependent
+ // context, we also need to look up '==' in case we implicitly declare a
+ // defaulted 'operator=='.
if (Op == OO_Spaceship) {
Lookup(OO_ExclaimEqual);
Lookup(OO_Less);
@@ -7904,9 +7904,13 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
for (const ParmVarDecl *Param : FD->parameters()) {
if (!Param->getType()->isDependentType() &&
!Context.hasSameType(Param->getType(), ExpectedParmType)) {
- Diag(FD->getLocation(), diag::err_defaulted_comparison_param)
- << (int)DCK << Param->getType() << ExpectedParmType
- << Param->getSourceRange();
+ // Don't diagnose an implicit 'operator=='; we will have diagnosed the
+ // corresponding defaulted 'operator<=>' already.
+ if (!FD->isImplicit()) {
+ Diag(FD->getLocation(), diag::err_defaulted_comparison_param)
+ << (int)DCK << Param->getType() << ExpectedParmType
+ << Param->getSourceRange();
+ }
return true;
}
}
@@ -7918,8 +7922,12 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
SourceLocation InsertLoc;
if (FunctionTypeLoc Loc = MD->getFunctionTypeLoc())
InsertLoc = getLocForEndOfToken(Loc.getRParenLoc());
- Diag(MD->getLocation(), diag::err_defaulted_comparison_non_const)
- << (int)DCK << FixItHint::CreateInsertion(InsertLoc, " const");
+ // Don't diagnose an implicit 'operator=='; we will have diagnosed the
+ // corresponding defaulted 'operator<=>' already.
+ if (!MD->isImplicit()) {
+ Diag(MD->getLocation(), diag::err_defaulted_comparison_non_const)
+ << (int)DCK << FixItHint::CreateInsertion(InsertLoc, " const");
+ }
// Add the 'const' to the type to recover.
const auto *FPT = MD->getType()->castAs<FunctionProtoType>();
@@ -7980,7 +7988,7 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
// This is really just a consequence of the general rule that you can
// only delete a function on its first declaration.
Diag(FD->getLocation(), diag::err_non_first_default_compare_deletes)
- << (int)DCK;
+ << FD->isImplicit() << (int)DCK;
DefaultedComparisonAnalyzer(*this, RD, FD, DCK,
DefaultedComparisonAnalyzer::ExplainDeleted)
.visit();
@@ -7988,7 +7996,7 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
}
SetDeclDeleted(FD, FD->getLocation());
- if (!inTemplateInstantiation()) {
+ if (!inTemplateInstantiation() && !FD->isImplicit()) {
Diag(FD->getLocation(), diag::warn_defaulted_comparison_deleted)
<< (int)DCK;
DefaultedComparisonAnalyzer(*this, RD, FD, DCK,
@@ -8028,7 +8036,7 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
!Info.Constexpr) {
Diag(FD->getBeginLoc(),
diag::err_incorrect_defaulted_comparison_constexpr)
- << (int)DCK << FD->isConsteval();
+ << FD->isImplicit() << (int)DCK << FD->isConsteval();
DefaultedComparisonAnalyzer(*this, RD, FD, DCK,
DefaultedComparisonAnalyzer::ExplainConstexpr)
.visit();
@@ -8051,6 +8059,20 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
return false;
}
+void Sema::DeclareImplicitEqualityComparison(CXXRecordDecl *RD,
+ FunctionDecl *Spaceship) {
+ Sema::CodeSynthesisContext Ctx;
+ Ctx.Kind = Sema::CodeSynthesisContext::DeclaringImplicitEqualityComparison;
+ Ctx.PointOfInstantiation = Spaceship->getEndLoc();
+ Ctx.Entity = Spaceship;
+ pushCodeSynthesisContext(Ctx);
+
+ if (FunctionDecl *EqualEqual = SubstSpaceshipAsEqualEqual(RD, Spaceship))
+ EqualEqual->setImplicit();
+
+ popCodeSynthesisContext();
+}
+
void Sema::DefineDefaultedComparison(SourceLocation UseLoc, FunctionDecl *FD,
DefaultedComparisonKind DCK) {
assert(FD->isDefaulted() && !FD->isDeleted() &&
@@ -9330,6 +9352,44 @@ void Sema::ActOnFinishCXXMemberSpecification(
CheckCompletedCXXClass(S, cast<CXXRecordDecl>(TagDecl));
}
+/// Find the equality comparison functions that should be implicitly declared
+/// in a given class definition, per C++2a [class.compare.default]p3.
+static void findImplicitlyDeclaredEqualityComparisons(
+ ASTContext &Ctx, CXXRecordDecl *RD,
+ llvm::SmallVectorImpl<FunctionDecl *> &Spaceships) {
+ DeclarationName EqEq = Ctx.DeclarationNames.getCXXOperatorName(OO_EqualEqual);
+ if (!RD->lookup(EqEq).empty())
+ // Member operator== explicitly declared: no implicit operator==s.
+ return;
+
+ // Traverse friends looking for an '==' or a '<=>'.
+ for (FriendDecl *Friend : RD->friends()) {
+ FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Friend->getFriendDecl());
+ if (!FD) continue;
+
+ if (FD->getOverloadedOperator() == OO_EqualEqual) {
+ // Friend operator== explicitly declared: no implicit operator==s.
+ Spaceships.clear();
+ return;
+ }
+
+ if (FD->getOverloadedOperator() == OO_Spaceship &&
+ FD->isExplicitlyDefaulted())
+ Spaceships.push_back(FD);
+ }
+
+ // Look for members named 'operator<=>'.
+ DeclarationName Cmp = Ctx.DeclarationNames.getCXXOperatorName(OO_Spaceship);
+ for (NamedDecl *ND : RD->lookup(Cmp)) {
+ // Note that we could find a non-function here (either a function template
+ // or a using-declaration). Neither case results in an implicit
+ // 'operator=='.
+ if (auto *FD = dyn_cast<FunctionDecl>(ND))
+ if (FD->isExplicitlyDefaulted())
+ Spaceships.push_back(FD);
+ }
+}
+
/// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared
/// special functions, such as the default constructor, copy
/// constructor, or destructor, to the given C++ class (C++
@@ -9407,6 +9467,20 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
ClassDecl->needsOverloadResolutionForDestructor())
DeclareImplicitDestructor(ClassDecl);
}
+
+ // C++2a [class.compare.default]p3:
+ // If the member-specification does not explicitly declare any member or
+ // friend named operator==, an == operator function is declared implicitly
+ // for each defaulted three-way comparison operator function defined in the
+ // member-specification
+ // FIXME: Consider doing this lazily.
+ if (getLangOpts().CPlusPlus2a) {
+ llvm::SmallVector<FunctionDecl*, 4> DefaultedSpaceships;
+ findImplicitlyDeclaredEqualityComparisons(Context, ClassDecl,
+ DefaultedSpaceships);
+ for (auto *FD : DefaultedSpaceships)
+ DeclareImplicitEqualityComparison(ClassDecl, FD);
+ }
}
unsigned Sema::ActOnReenterTemplateScope(Scope *S, Decl *D) {
OpenPOWER on IntegriCloud