[clang] bc24014 - [c++20] Implement P1185R2 (as modified by P2002R0).

Alex Orlov via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 11 17:50:43 PST 2019


Hi Richard,

http://lab.llvm.org:8011/builders/llvm-clang-win-x-armv7l/builds/1353

The bots are broken for a long time now.
When do you expect the fix?

Thanks,
Alex


On Wed, Dec 11, 2019 at 12:13 AM Yvan Roux via cfe-commits <cfe-commits at lists.llvm.org> wrote:
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 at lists.llvm.org<mailto:cfe-commits at 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 at +2 {{only virtual member functions}}
> +  // expected-note at +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 at +2 {{implicitly declared private here}}
> +  // expected-note-re at +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 at +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 at +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 at lists.llvm.org<mailto:cfe-commits at lists.llvm.org>
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
_______________________________________________
cfe-commits mailing list
cfe-commits at lists.llvm.org<mailto:cfe-commits at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits?

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20191212/e81b160b/attachment-0001.html>


More information about the cfe-commits mailing list