Hi Richard, ARM bots are broken after this commit, logs are available here:
http://lab.llvm.org:8011/builders/clang-cmake-armv7-quick/builds/12109/steps/ninja%20check%201/logs/FAIL%3A%20Clang%3A%3Acxx2a-three-way-comparison.cpp Thanks, Yvan On Wed, 11 Dec 2019 at 02:24, Richard Smith via cfe-commits <cfe-commits@lists.llvm.org> wrote: > > > Author: Richard Smith > Date: 2019-12-10T17:24:27-08:00 > New Revision: bc24014b9765a454cb5214f4871451a41ffb7d29 > > URL: > https://github.com/llvm/llvm-project/commit/bc24014b9765a454cb5214f4871451a41ffb7d29 > DIFF: > https://github.com/llvm/llvm-project/commit/bc24014b9765a454cb5214f4871451a41ffb7d29.diff > > LOG: [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==. > > Added: > clang/test/CXX/class/class.compare/class.compare.default/p4.cpp > > Modified: > clang/include/clang/Basic/DiagnosticSemaKinds.td > clang/include/clang/Sema/Sema.h > clang/include/clang/Sema/Template.h > clang/lib/Frontend/FrontendActions.cpp > clang/lib/Sema/SemaDeclCXX.cpp > clang/lib/Sema/SemaOverload.cpp > clang/lib/Sema/SemaTemplateInstantiate.cpp > clang/lib/Sema/SemaTemplateInstantiateDecl.cpp > clang/test/CodeGenCXX/cxx2a-three-way-comparison.cpp > > Removed: > > > > ################################################################################ > diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td > b/clang/include/clang/Basic/DiagnosticSemaKinds.td > index aeeff2b9a76e..a5f35996cfdc 100644 > --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td > +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td > @@ -3840,6 +3840,7 @@ def select_ovl_candidate_kind : TextSubstitution< > "constructor (the implicit move constructor)|" > "function (the implicit copy assignment operator)|" > "function (the implicit move assignment operator)|" > + "function (the implicit 'operator==' for this 'operator<=>)'|" > "inherited constructor}0%select{| template| %2}1">; > > def note_ovl_candidate : Note< > @@ -8207,7 +8208,8 @@ def warn_defaulted_comparison_deleted : Warning< > "explicitly defaulted %sub{select_defaulted_comparison_kind}0 is > implicitly " > "deleted">, InGroup<DefaultedFunctionDeleted>; > def err_non_first_default_compare_deletes : Error< > - "defaulting this %sub{select_defaulted_comparison_kind}0 " > + "defaulting %select{this %sub{select_defaulted_comparison_kind}1|" > + "the corresponding implicit 'operator==' for this defaulted > 'operator<=>'}0 " > "would delete it after its first declaration">; > def note_defaulted_comparison_union : Note< > "defaulted %0 is implicitly deleted because " > @@ -8237,14 +8239,19 @@ def note_defaulted_comparison_cannot_deduce : Note< > def note_defaulted_comparison_cannot_deduce_callee : Note< > "selected 'operator<=>' for %select{|member|base class}0 %1 declared > here">; > def err_incorrect_defaulted_comparison_constexpr : Error< > - "defaulted definition of %sub{select_defaulted_comparison_kind}0 " > - "cannot be declared %select{constexpr|consteval}1 because it invokes " > - "a non-constexpr comparison function">; > + "defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|" > + "three-way comparison operator}0 " > + "cannot be declared %select{constexpr|consteval}2 because " > + "%select{it|the corresponding implicit 'operator=='}0 " > + "invokes a non-constexpr comparison function">; > def note_defaulted_comparison_not_constexpr : Note< > "non-constexpr comparison function would be used to compare " > "%select{|member %1|base class %1}0">; > def note_defaulted_comparison_not_constexpr_here : Note< > "non-constexpr comparison function declared here">; > +def note_in_declaration_of_implicit_equality_comparison : Note< > + "while declaring the corresponding implicit 'operator==' " > + "for this defaulted 'operator<=>'">; > > def ext_implicit_exception_spec_mismatch : ExtWarn< > "function previously declared with an %select{explicit|implicit}0 > exception " > > diff --git a/clang/include/clang/Sema/Sema.h > b/clang/include/clang/Sema/Sema.h > index 1cdaab3dc28c..5a1ee507218c 100644 > --- a/clang/include/clang/Sema/Sema.h > +++ b/clang/include/clang/Sema/Sema.h > @@ -6524,6 +6524,8 @@ class Sema final { > > bool CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *MD, > DefaultedComparisonKind DCK); > + void DeclareImplicitEqualityComparison(CXXRecordDecl *RD, > + FunctionDecl *Spaceship); > void DefineDefaultedComparison(SourceLocation Loc, FunctionDecl *FD, > DefaultedComparisonKind DCK); > > @@ -7838,6 +7840,10 @@ class Sema final { > /// We are declaring an implicit special member function. > DeclaringSpecialMember, > > + /// We are declaring an implicit 'operator==' for a defaulted > + /// 'operator<=>'. > + DeclaringImplicitEqualityComparison, > + > /// We are defining a synthesized function (such as a defaulted special > /// member). > DefiningSynthesizedFunction, > @@ -8468,6 +8474,11 @@ class Sema final { > Decl *SubstDecl(Decl *D, DeclContext *Owner, > const MultiLevelTemplateArgumentList &TemplateArgs); > > + /// Substitute the name and return type of a defaulted 'operator<=>' to > form > + /// an implicit 'operator=='. > + FunctionDecl *SubstSpaceshipAsEqualEqual(CXXRecordDecl *RD, > + FunctionDecl *Spaceship); > + > ExprResult SubstInitializer(Expr *E, > const MultiLevelTemplateArgumentList &TemplateArgs, > bool CXXDirectInit); > > diff --git a/clang/include/clang/Sema/Template.h > b/clang/include/clang/Sema/Template.h > index 102e525e2e16..4c1cfecd4de6 100644 > --- a/clang/include/clang/Sema/Template.h > +++ b/clang/include/clang/Sema/Template.h > @@ -473,13 +473,21 @@ class VarDecl; > > #include "clang/AST/DeclNodes.inc" > > + enum class RewriteKind { None, RewriteSpaceshipAsEqualEqual }; > + > + void adjustForRewrite(RewriteKind RK, FunctionDecl *Orig, QualType &T, > + TypeSourceInfo *&TInfo, > + DeclarationNameInfo &NameInfo); > + > // A few supplemental visitor functions. > Decl *VisitCXXMethodDecl(CXXMethodDecl *D, > TemplateParameterList *TemplateParams, > Optional<const ASTTemplateArgumentListInfo *> > - ClassScopeSpecializationArgs = llvm::None); > + ClassScopeSpecializationArgs = llvm::None, > + RewriteKind RK = RewriteKind::None); > Decl *VisitFunctionDecl(FunctionDecl *D, > - TemplateParameterList *TemplateParams); > + TemplateParameterList *TemplateParams, > + RewriteKind RK = RewriteKind::None); > Decl *VisitDecl(Decl *D); > Decl *VisitVarDecl(VarDecl *D, bool InstantiatingVarTemplate, > ArrayRef<BindingDecl *> *Bindings = nullptr); > > diff --git a/clang/lib/Frontend/FrontendActions.cpp > b/clang/lib/Frontend/FrontendActions.cpp > index 4d47c768ad3c..aeea63ca323f 100644 > --- a/clang/lib/Frontend/FrontendActions.cpp > +++ b/clang/lib/Frontend/FrontendActions.cpp > @@ -413,6 +413,8 @@ class DefaultTemplateInstCallback : public > TemplateInstantiationCallback { > 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 7bbd0114b1e2..72ac81776aae 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 344e54b7f3fc..5b1394d7b34f 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 1451fe4bb4d1..6db8eb3e5ed6 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 71399ff35908..0bff0747df34 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. > /// > > diff --git a/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp > b/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp > new file mode 100644 > index 000000000000..1ab77075277f > --- /dev/null > +++ b/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp > @@ -0,0 +1,146 @@ > +// RUN: %clang_cc1 -std=c++2a -verify %s > + > +// This test is for [class.compare.default]p3 as modified and renumbered to > p4 > +// by P2002R0. > + > +namespace std { > + struct strong_ordering { > + int n; > + constexpr operator int() const { return n; } > + static const strong_ordering less, equal, greater; > + }; > + constexpr strong_ordering strong_ordering::less = {-1}; > + constexpr strong_ordering strong_ordering::equal = {0}; > + constexpr strong_ordering strong_ordering::greater = {1}; > +} > + > +namespace N { > + struct A { > + friend constexpr std::strong_ordering operator<=>(const A&, const A&) = > default; > + }; > + > + constexpr bool (*test_a_not_found)(const A&, const A&) = &operator==; // > expected-error {{undeclared}} > + > + constexpr bool operator==(const A&, const A&); > + constexpr bool (*test_a)(const A&, const A&) = &operator==; > + static_assert((*test_a)(A(), A())); > +} > + > +struct B1 { > + virtual std::strong_ordering operator<=>(const B1&) const = default; > +}; > +bool (B1::*test_b)(const B1&) const = &B1::operator==; > + > +struct C1 : B1 { > + // OK, B1::operator== is virtual. > + bool operator==(const B1&) const override; > +}; > + > +struct B2 { > + std::strong_ordering operator<=>(const B2&) const = default; > +}; > + > +struct C2 : B2 { > + bool operator==(const B2&) const override; // expected-error {{only > virtual member functions}} > +}; > + > +struct D { > + std::strong_ordering operator<=>(const D&) const; > + virtual std::strong_ordering operator<=>(const struct E&) const = 0; > +}; > +struct E : D { > + // expected-error@+2 {{only virtual member functions}} > + // expected-note@+1 {{while declaring the corresponding implicit > 'operator==' for this defaulted 'operator<=>'}} > + std::strong_ordering operator<=>(const E&) const override = default; > +}; > + > +struct F { > + [[deprecated("oh no")]] std::strong_ordering operator<=>(const F&) const = > default; // expected-note 4{{deprecated}} > +}; > +void use_f(F f) { > + void(f <=> f); // expected-warning {{oh no}} > + void(f < f); // expected-warning {{oh no}} > + void(f == f); // expected-warning {{oh no}} > + void(f != f); // expected-warning {{oh no}} > +} > + > +class G { > + // expected-note@+2 {{implicitly declared private here}} > + // expected-note-re@+1 {{{{^}}declared private here}} > + std::strong_ordering operator<=>(const G&) const = default; > +public: > +}; > +void use_g(G g) { > + void(g <=> g); // expected-error {{private}} > + void(g == g); // expected-error {{private}} > +} > + > +struct H { > + bool operator==(const H&) const; // expected-note {{here}} > + constexpr std::strong_ordering operator<=>(const H&) const { return > std::strong_ordering::equal; } > +}; > + > +struct I { > + H h; // expected-note {{used to compare}} > + // expected-error@+1 {{defaulted definition of three-way comparison > operator cannot be declared constexpr because the corresponding implicit > 'operator==' invokes a non-constexpr comparison function}} > + constexpr std::strong_ordering operator<=>(const I&) const = default; > +}; > + > +struct J { > + std::strong_ordering operator<=>(const J&) const & = default; // > expected-note {{candidate function (the implicit 'operator==' for this > 'operator<=>)'}} > + friend std::strong_ordering operator<=>(const J&, const J&) = default; // > expected-note {{candidate function (the implicit 'operator==' for this > 'operator<=>)'}} > +}; > +void use_j(J j) { > + void(j == j); // expected-error {{ambiguous}} > +} > + > +namespace DeleteAfterFirstDecl { > + bool operator==(const struct Q&, const struct Q&); > + struct Q { > + struct X { > + friend std::strong_ordering operator<=>(const X&, const X&); > + } x; // expected-note {{no viable comparison}} > + // expected-error@+1 {{defaulting the corresponding implicit > 'operator==' for this defaulted 'operator<=>' would delete it after its first > declaration}} > + friend std::strong_ordering operator<=>(const Q&, const Q&) = default; > + }; > +} > + > +// Note, substitution here results in the second parameter of 'operator==' > +// referring to the first parameter of 'operator==', not to the first > parameter > +// of 'operator<=>'. > +// FIXME: Find a case where this matters (attribute enable_if?). > +struct K { > + friend std::strong_ordering operator<=>(const K &k, decltype(k)) = default; > +}; > +bool test_k = K() == K(); > + > +namespace NoInjectionIfOperatorEqualsDeclared { > + struct A { > + void operator==(int); // expected-note 2{{not viable}} > + std::strong_ordering operator<=>(const A&) const = default; > + }; > + bool test_a = A() == A(); // expected-error {{invalid operands}} > + > + struct B { > + friend void operator==(int, struct Q); // expected-note {{not viable}} > + std::strong_ordering operator<=>(const B&) const = default; > + }; > + bool test_b = B() == B(); // expected-error {{invalid operands}} > + > + struct C { > + void operator==(int); // expected-note 2{{not viable}} > + friend std::strong_ordering operator<=>(const C&, const C&) = default; > + }; > + bool test_c = C() == C(); // expected-error {{invalid operands}} > + > + struct D { > + void f() { > + void operator==(const D&, int); > + } > + struct X { > + friend void operator==(const D&, int); > + }; > + friend std::strong_ordering operator<=>(const D&, const D&) = default; > + }; > + bool test_d = D() == D(); > +} > > diff --git a/clang/test/CodeGenCXX/cxx2a-three-way-comparison.cpp > b/clang/test/CodeGenCXX/cxx2a-three-way-comparison.cpp > index e3c1535815f0..e6be640a1f7f 100644 > --- a/clang/test/CodeGenCXX/cxx2a-three-way-comparison.cpp > +++ b/clang/test/CodeGenCXX/cxx2a-three-way-comparison.cpp > @@ -1,6 +1,41 @@ > -// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %itanium_abi_triple > | FileCheck %s --check-prefix=ITANIUM > -// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple x86_64-pc-win32 > 2>&1 | FileCheck %s --check-prefix=MSABI > -// RUN: not %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple > %itanium_abi_triple -DBUILTIN 2>&1 | FileCheck %s --check-prefix=BUILTIN > +// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %itanium_abi_triple > | FileCheck %s --check-prefixes=CHECK,ITANIUM > +// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple x86_64-pc-win32 > 2>&1 | FileCheck %s --check-prefixes=CHECK,MSABI > + > +namespace std { > + struct strong_ordering { > + int n; > + constexpr operator int() const { return n; } > + static const strong_ordering less, equal, greater; > + }; > + constexpr strong_ordering strong_ordering::less = {-1}; > + constexpr strong_ordering strong_ordering::equal = {0}; > + constexpr strong_ordering strong_ordering::greater = {1}; > +} > + > +struct Primary { > + virtual void f(); > + std::strong_ordering operator<=>(const Primary&) const = default; > +}; > +struct X { > + virtual struct Y &operator=(Y&&); > + virtual struct Y &operator=(const Y&); > + std::strong_ordering operator<=>(const X&) const = default; > +}; > +// The vtable for Y should contain the following entries in order: > +// - Primary::f > +// - Y::operator<=> > +// - Y::operator=(const Y&) (implicit) > +// - Y::operator=(Y&&) (implicit) > +// - Y::operator==(const Y&) const (implicit) > +// See: > +// https://github.com/itanium-cxx-abi/cxx-abi/issues/83 for assignment > operator > +// https://github.com/itanium-cxx-abi/cxx-abi/issues/88 for equality > comparison > +// FIXME: What rule does MSVC use? > +struct Y : Primary, X { > + virtual std::strong_ordering operator<=>(const Y&) const = default; > +}; > +Y y; > +// ITANIUM: @_ZTV1Y = {{.*}}constant {{.*}} null, {{.*}} @_ZTI1Y {{.*}} > @_ZN7Primary1fEv {{.*}} @_ZNK1YssERKS_ {{.*}} @_ZN1YaSERKS_ {{.*}} > @_ZN1YaSEOS_ {{.*}} @_ZNK1YeqERKS_ {{.*}} -{{4|8}} {{.*}} @_ZTI1Y {{.*}} > @_ZThn8_N1YaSERKS_ > > struct A { > void operator<=>(int); > @@ -26,8 +61,11 @@ int f(A a) { > return a <=> a; > } > > -#ifdef BUILTIN > -void builtin(int a) { > - a <=> a; // BUILTIN: cannot compile this scalar expression yet > +// CHECK-LABEL: define {{.*}}builtin_cmp > +void builtin_cmp(int a) { > + // CHECK: icmp slt > + // CHECK: select > + // CHECK: icmp eq > + // CHECK: select > + a <=> a; > } > -#endif > > > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits