summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Frontend/FrontendActions.cpp2
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp140
-rw-r--r--clang/lib/Sema/SemaOverload.cpp4
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiate.cpp7
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiateDecl.cpp87
5 files changed, 201 insertions, 39 deletions
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index 4d47c768ad3..aeea63ca323 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -413,6 +413,8 @@ private:
return "ExceptionSpecInstantiation";
case CodeSynthesisContext::DeclaringSpecialMember:
return "DeclaringSpecialMember";
+ case CodeSynthesisContext::DeclaringImplicitEqualityComparison:
+ return "DeclaringImplicitEqualityComparison";
case CodeSynthesisContext::DefiningSynthesizedFunction:
return "DefiningSynthesizedFunction";
case CodeSynthesisContext::RewritingOperatorAsSpaceship:
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) {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 344e54b7f3f..5b1394d7b34 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -9723,6 +9723,7 @@ enum OverloadCandidateKind {
oc_implicit_move_constructor,
oc_implicit_copy_assignment,
oc_implicit_move_assignment,
+ oc_implicit_equality_comparison,
oc_inherited_constructor
};
@@ -9751,6 +9752,9 @@ ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn,
}();
OverloadCandidateKind Kind = [&]() {
+ if (Fn->isImplicit() && Fn->getOverloadedOperator() == OO_EqualEqual)
+ return oc_implicit_equality_comparison;
+
if (CRK & CRK_Reversed)
return oc_reversed_binary_operator;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 1451fe4bb4d..6db8eb3e5ed 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -203,6 +203,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
case DefaultTemplateArgumentChecking:
case DeclaringSpecialMember:
+ case DeclaringImplicitEqualityComparison:
case DefiningSynthesizedFunction:
case ExceptionSpecEvaluation:
case ConstraintSubstitution:
@@ -671,6 +672,11 @@ void Sema::PrintInstantiationStack() {
<< cast<CXXRecordDecl>(Active->Entity) << Active->SpecialMember;
break;
+ case CodeSynthesisContext::DeclaringImplicitEqualityComparison:
+ Diags.Report(Active->Entity->getLocation(),
+ diag::note_in_declaration_of_implicit_equality_comparison);
+ break;
+
case CodeSynthesisContext::DefiningSynthesizedFunction: {
// FIXME: For synthesized functions that are not defaulted,
// produce a note.
@@ -772,6 +778,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
return Active->DeductionInfo;
case CodeSynthesisContext::DeclaringSpecialMember:
+ case CodeSynthesisContext::DeclaringImplicitEqualityComparison:
case CodeSynthesisContext::DefiningSynthesizedFunction:
case CodeSynthesisContext::RewritingOperatorAsSpaceship:
// This happens in a context unrelated to template instantiation, so
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 71399ff3590..0bff0747df3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1794,8 +1794,9 @@ static QualType adjustFunctionTypeForInstantiation(ASTContext &Context,
/// 1) instantiating function templates
/// 2) substituting friend declarations
/// 3) substituting deduction guide declarations for nested class templates
-Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
- TemplateParameterList *TemplateParams) {
+Decl *TemplateDeclInstantiator::VisitFunctionDecl(
+ FunctionDecl *D, TemplateParameterList *TemplateParams,
+ RewriteKind FunctionRewriteKind) {
// Check whether there is already a function template specialization for
// this declaration.
FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate();
@@ -1865,6 +1866,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
DeclarationNameInfo NameInfo
= SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
+ if (FunctionRewriteKind != RewriteKind::None)
+ adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo);
+
FunctionDecl *Function;
if (auto *DGuide = dyn_cast<CXXDeductionGuideDecl>(D)) {
Function = CXXDeductionGuideDecl::Create(
@@ -2101,8 +2105,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
CXXMethodDecl *D, TemplateParameterList *TemplateParams,
- Optional<const ASTTemplateArgumentListInfo *>
- ClassScopeSpecializationArgs) {
+ Optional<const ASTTemplateArgumentListInfo *> ClassScopeSpecializationArgs,
+ RewriteKind FunctionRewriteKind) {
FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate();
if (FunctionTemplate && !TemplateParams) {
// We are creating a function template specialization from a function
@@ -2181,13 +2185,17 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
if (!DC) return nullptr;
}
+ DeclarationNameInfo NameInfo
+ = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
+
+ if (FunctionRewriteKind != RewriteKind::None)
+ adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo);
+
// Build the instantiated method declaration.
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
CXXMethodDecl *Method = nullptr;
SourceLocation StartLoc = D->getInnerLocStart();
- DeclarationNameInfo NameInfo
- = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
Method = CXXConstructorDecl::Create(
SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
@@ -3523,6 +3531,73 @@ Decl *Sema::SubstDecl(Decl *D, DeclContext *Owner,
return SubstD;
}
+void TemplateDeclInstantiator::adjustForRewrite(RewriteKind RK,
+ FunctionDecl *Orig, QualType &T,
+ TypeSourceInfo *&TInfo,
+ DeclarationNameInfo &NameInfo) {
+ assert(RK == RewriteKind::RewriteSpaceshipAsEqualEqual);
+
+ // C++2a [class.compare.default]p3:
+ // the return type is replaced with bool
+ auto *FPT = T->castAs<FunctionProtoType>();
+ T = SemaRef.Context.getFunctionType(
+ SemaRef.Context.BoolTy, FPT->getParamTypes(), FPT->getExtProtoInfo());
+
+ // Update the return type in the source info too. The most straightforward
+ // way is to create new TypeSourceInfo for the new type. Use the location of
+ // the '= default' as the location of the new type.
+ //
+ // FIXME: Set the correct return type when we initially transform the type,
+ // rather than delaying it to now.
+ TypeSourceInfo *NewTInfo =
+ SemaRef.Context.getTrivialTypeSourceInfo(T, Orig->getEndLoc());
+ auto OldLoc = TInfo->getTypeLoc().getAsAdjusted<FunctionProtoTypeLoc>();
+ assert(OldLoc && "type of function is not a function type?");
+ auto NewLoc = NewTInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>();
+ for (unsigned I = 0, N = OldLoc.getNumParams(); I != N; ++I)
+ NewLoc.setParam(I, OldLoc.getParam(I));
+ TInfo = NewTInfo;
+
+ // and the declarator-id is replaced with operator==
+ NameInfo.setName(
+ SemaRef.Context.DeclarationNames.getCXXOperatorName(OO_EqualEqual));
+}
+
+FunctionDecl *Sema::SubstSpaceshipAsEqualEqual(CXXRecordDecl *RD,
+ FunctionDecl *Spaceship) {
+ if (Spaceship->isInvalidDecl())
+ return nullptr;
+
+ // C++2a [class.compare.default]p3:
+ // an == operator function is declared implicitly [...] with the same
+ // access and function-definition and in the same class scope as the
+ // three-way comparison operator function
+ MultiLevelTemplateArgumentList NoTemplateArgs;
+ TemplateDeclInstantiator Instantiator(*this, RD, NoTemplateArgs);
+ Decl *R;
+ if (auto *MD = dyn_cast<CXXMethodDecl>(Spaceship)) {
+ R = Instantiator.VisitCXXMethodDecl(
+ MD, nullptr, None,
+ TemplateDeclInstantiator::RewriteKind::RewriteSpaceshipAsEqualEqual);
+ } else {
+ assert(Spaceship->getFriendObjectKind() &&
+ "defaulted spaceship is neither a member nor a friend");
+
+ R = Instantiator.VisitFunctionDecl(
+ Spaceship, nullptr,
+ TemplateDeclInstantiator::RewriteKind::RewriteSpaceshipAsEqualEqual);
+ if (!R)
+ return nullptr;
+
+ FriendDecl *FD =
+ FriendDecl::Create(Context, RD, Spaceship->getLocation(),
+ cast<NamedDecl>(R), Spaceship->getBeginLoc());
+ FD->setAccess(AS_public);
+ RD->addDecl(FD);
+ }
+ return cast_or_null<FunctionDecl>(R);
+}
+
/// Instantiates a nested template parameter list in the current
/// instantiation context.
///
OpenPOWER on IntegriCloud