diff options
Diffstat (limited to 'clang/lib/Sema/SemaDeclCXX.cpp')
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 1750 | 
1 files changed, 1568 insertions, 182 deletions
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ff90b9548e29..9916d3be77e1 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -217,8 +217,8 @@ Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc,        Exceptions.push_back(E);  } -void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) { -  if (!E || ComputedEST == EST_MSAny) +void Sema::ImplicitExceptionSpecification::CalledStmt(Stmt *S) { +  if (!S || ComputedEST == EST_MSAny)      return;    // FIXME: @@ -242,7 +242,7 @@ void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) {    // implicit definition. For now, we assume that any non-nothrow expression can    // throw any exception. -  if (Self->canThrow(E)) +  if (Self->canThrow(S))      ComputedEST = EST_None;  } @@ -1501,13 +1501,13 @@ void Sema::MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old) {    // as pointers to member functions.    if (const ReferenceType *R = NewType->getAs<ReferenceType>()) {      NewType = R->getPointeeType(); -    OldType = OldType->getAs<ReferenceType>()->getPointeeType(); +    OldType = OldType->castAs<ReferenceType>()->getPointeeType();    } else if (const PointerType *P = NewType->getAs<PointerType>()) {      NewType = P->getPointeeType(); -    OldType = OldType->getAs<PointerType>()->getPointeeType(); +    OldType = OldType->castAs<PointerType>()->getPointeeType();    } else if (const MemberPointerType *M = NewType->getAs<MemberPointerType>()) {      NewType = M->getPointeeType(); -    OldType = OldType->getAs<MemberPointerType>()->getPointeeType(); +    OldType = OldType->castAs<MemberPointerType>()->getPointeeType();    }    if (!NewType->isFunctionProtoType()) @@ -1627,14 +1627,13 @@ static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,    return true;  } -// CheckConstexprParameterTypes - Check whether a function's parameter types -// are all literal types. If so, return true. If not, produce a suitable -// diagnostic and return false. +/// Check whether a function's parameter types are all literal types. If so, +/// return true. If not, produce a suitable diagnostic and return false.  static bool CheckConstexprParameterTypes(Sema &SemaRef,                                           const FunctionDecl *FD,                                           Sema::CheckConstexprKind Kind) {    unsigned ArgIndex = 0; -  const FunctionProtoType *FT = FD->getType()->getAs<FunctionProtoType>(); +  const auto *FT = FD->getType()->castAs<FunctionProtoType>();    for (FunctionProtoType::param_type_iterator i = FT->param_type_begin(),                                                e = FT->param_type_end();         i != e; ++i, ++ArgIndex) { @@ -1649,6 +1648,17 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef,    return true;  } +/// Check whether a function's return type is a literal type. If so, return +/// true. If not, produce a suitable diagnostic and return false. +static bool CheckConstexprReturnType(Sema &SemaRef, const FunctionDecl *FD, +                                     Sema::CheckConstexprKind Kind) { +  if (CheckLiteralType(SemaRef, Kind, FD->getLocation(), FD->getReturnType(), +                       diag::err_constexpr_non_literal_return, +                       FD->isConsteval())) +    return false; +  return true; +} +  /// Get diagnostic %select index for tag kind for  /// record diagnostic message.  /// WARNING: Indexes apply to particular diagnostics only! @@ -1729,10 +1739,7 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,      }      // - its return type shall be a literal type; -    QualType RT = NewFD->getReturnType(); -    if (CheckLiteralType(*this, Kind, NewFD->getLocation(), RT, -                         diag::err_constexpr_non_literal_return, -                         NewFD->isConsteval())) +    if (!CheckConstexprReturnType(*this, NewFD, Kind))        return false;    } @@ -3800,7 +3807,7 @@ namespace {      const CXXRecordDecl *RD = Constructor->getParent(); -    if (RD->getDescribedClassTemplate()) +    if (RD->isDependentContext())        return;      // Holds fields that are uninitialized. @@ -3861,6 +3868,26 @@ void Sema::ActOnStartCXXInClassMemberInitializer() {    PushFunctionScope();  } +void Sema::ActOnStartTrailingRequiresClause(Scope *S, Declarator &D) { +  if (!D.isFunctionDeclarator()) +    return; +  auto &FTI = D.getFunctionTypeInfo(); +  if (!FTI.Params) +    return; +  for (auto &Param : ArrayRef<DeclaratorChunk::ParamInfo>(FTI.Params, +                                                          FTI.NumParams)) { +    auto *ParamDecl = cast<NamedDecl>(Param.Param); +    if (ParamDecl->getDeclName()) +      PushOnScopeChains(ParamDecl, S, /*AddToContext=*/false); +  } +} + +ExprResult Sema::ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr) { +  if (ConstraintExpr.isInvalid()) +    return ExprError(); +  return CorrectDelayedTyposInExpr(ConstraintExpr); +} +  /// This is invoked after parsing an in-class initializer for a  /// non-static C++ class member, and after instantiating an in-class initializer  /// in a class template. Such actions are deferred until the class is complete. @@ -6084,6 +6111,67 @@ void Sema::propagateDLLAttrToBaseClassTemplate(    }  } +/// Determine the kind of defaulting that would be done for a given function. +/// +/// If the function is both a default constructor and a copy / move constructor +/// (due to having a default argument for the first parameter), this picks +/// CXXDefaultConstructor. +/// +/// FIXME: Check that case is properly handled by all callers. +Sema::DefaultedFunctionKind +Sema::getDefaultedFunctionKind(const FunctionDecl *FD) { +  if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) { +    if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(FD)) { +      if (Ctor->isDefaultConstructor()) +        return Sema::CXXDefaultConstructor; + +      if (Ctor->isCopyConstructor()) +        return Sema::CXXCopyConstructor; + +      if (Ctor->isMoveConstructor()) +        return Sema::CXXMoveConstructor; +    } + +    if (MD->isCopyAssignmentOperator()) +      return Sema::CXXCopyAssignment; + +    if (MD->isMoveAssignmentOperator()) +      return Sema::CXXMoveAssignment; + +    if (isa<CXXDestructorDecl>(FD)) +      return Sema::CXXDestructor; +  } + +  switch (FD->getDeclName().getCXXOverloadedOperator()) { +  case OO_EqualEqual: +    return DefaultedComparisonKind::Equal; + +  case OO_ExclaimEqual: +    return DefaultedComparisonKind::NotEqual; + +  case OO_Spaceship: +    // No point allowing this if <=> doesn't exist in the current language mode. +    if (!getLangOpts().CPlusPlus2a) +      break; +    return DefaultedComparisonKind::ThreeWay; + +  case OO_Less: +  case OO_LessEqual: +  case OO_Greater: +  case OO_GreaterEqual: +    // No point allowing this if <=> doesn't exist in the current language mode. +    if (!getLangOpts().CPlusPlus2a) +      break; +    return DefaultedComparisonKind::Relational; + +  default: +    break; +  } + +  // Not defaultable. +  return DefaultedFunctionKind(); +} +  static void DefineImplicitSpecialMember(Sema &S, CXXMethodDecl *MD,                                          SourceLocation DefaultLoc) {    switch (S.getSpecialMember(MD)) { @@ -6228,7 +6316,11 @@ static bool canPassInRegisters(Sema &S, CXXRecordDecl *D,  /// Perform semantic checks on a class definition that has been  /// completing, introducing implicitly-declared members, checking for  /// abstract types, etc. -void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { +/// +/// \param S The scope in which the class was parsed. Null if we didn't just +///        parse a class definition. +/// \param Record The completed class. +void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {    if (!Record)      return; @@ -6330,10 +6422,30 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {    if (HasTrivialABI)      Record->setHasTrivialSpecialMemberForCall(); +  // Explicitly-defaulted secondary comparison functions (!=, <, <=, >, >=). +  // We check these last because they can depend on the properties of the +  // primary comparison functions (==, <=>). +  llvm::SmallVector<FunctionDecl*, 5> DefaultedSecondaryComparisons; + +  auto CheckForDefaultedFunction = [&](FunctionDecl *FD) { +    if (!FD || FD->isInvalidDecl() || !FD->isExplicitlyDefaulted()) +      return; + +    DefaultedFunctionKind DFK = getDefaultedFunctionKind(FD); +    if (DFK.asComparison() == DefaultedComparisonKind::NotEqual || +        DFK.asComparison() == DefaultedComparisonKind::Relational) +      DefaultedSecondaryComparisons.push_back(FD); +    else +      CheckExplicitlyDefaultedFunction(S, FD); +  }; +    auto CompleteMemberFunction = [&](CXXMethodDecl *M) { -    // Check whether the explicitly-defaulted special members are valid. -    if (!M->isInvalidDecl() && M->isExplicitlyDefaulted()) -      CheckExplicitlyDefaultedSpecialMember(M); +    // Check whether the explicitly-defaulted members are valid. +    CheckForDefaultedFunction(M); + +    // Skip the rest of the checks for a member of a dependent class. +    if (Record->isDependentType()) +      return;      // For an explicitly defaulted or deleted special member, we defer      // determining triviality until the class is complete. That time is now! @@ -6379,40 +6491,52 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {        DefineImplicitSpecialMember(*this, M, M->getLocation());    }; +  // Check the destructor before any other member function. We need to +  // determine whether it's trivial in order to determine whether the claas +  // type is a literal type, which is a prerequisite for determining whether +  // other special member functions are valid and whether they're implicitly +  // 'constexpr'. +  if (CXXDestructorDecl *Dtor = Record->getDestructor()) +    CompleteMemberFunction(Dtor); +    bool HasMethodWithOverrideControl = false,         HasOverridingMethodWithoutOverrideControl = false; -  if (!Record->isDependentType()) { -    // Check the destructor before any other member function. We need to -    // determine whether it's trivial in order to determine whether the claas -    // type is a literal type, which is a prerequisite for determining whether -    // other special member functions are valid and whether they're implicitly -    // 'constexpr'. -    if (CXXDestructorDecl *Dtor = Record->getDestructor()) -      CompleteMemberFunction(Dtor); - -    for (auto *M : Record->methods()) { -      // 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); +    } 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);    } +  // Check the defaulted secondary comparisons after any other member functions. +  for (FunctionDecl *FD : DefaultedSecondaryComparisons) +    CheckExplicitlyDefaultedFunction(S, FD); +    // ms_struct is a request to use the same ABI rules as MSVC.  Check    // whether this class uses any C++ features that are implemented    // completely differently in MSVC, and if so, emit a diagnostic. @@ -6710,20 +6834,50 @@ static bool defaultedSpecialMemberIsConstexpr(    return true;  } +namespace { +/// RAII object to register a defaulted function as having its exception +/// specification computed. +struct ComputingExceptionSpec { +  Sema &S; + +  ComputingExceptionSpec(Sema &S, FunctionDecl *FD, SourceLocation Loc) +      : S(S) { +    Sema::CodeSynthesisContext Ctx; +    Ctx.Kind = Sema::CodeSynthesisContext::ExceptionSpecEvaluation; +    Ctx.PointOfInstantiation = Loc; +    Ctx.Entity = FD; +    S.pushCodeSynthesisContext(Ctx); +  } +  ~ComputingExceptionSpec() { +    S.popCodeSynthesisContext(); +  } +}; +} +  static Sema::ImplicitExceptionSpecification  ComputeDefaultedSpecialMemberExceptionSpec(      Sema &S, SourceLocation Loc, CXXMethodDecl *MD, Sema::CXXSpecialMember CSM,      Sema::InheritedConstructorInfo *ICI);  static Sema::ImplicitExceptionSpecification -computeImplicitExceptionSpec(Sema &S, SourceLocation Loc, CXXMethodDecl *MD) { -  auto CSM = S.getSpecialMember(MD); -  if (CSM != Sema::CXXInvalid) -    return ComputeDefaultedSpecialMemberExceptionSpec(S, Loc, MD, CSM, nullptr); +ComputeDefaultedComparisonExceptionSpec(Sema &S, SourceLocation Loc, +                                        FunctionDecl *FD, +                                        Sema::DefaultedComparisonKind DCK); -  auto *CD = cast<CXXConstructorDecl>(MD); +static Sema::ImplicitExceptionSpecification +computeImplicitExceptionSpec(Sema &S, SourceLocation Loc, FunctionDecl *FD) { +  auto DFK = S.getDefaultedFunctionKind(FD); +  if (DFK.isSpecialMember()) +    return ComputeDefaultedSpecialMemberExceptionSpec( +        S, Loc, cast<CXXMethodDecl>(FD), DFK.asSpecialMember(), nullptr); +  if (DFK.isComparison()) +    return ComputeDefaultedComparisonExceptionSpec(S, Loc, FD, +                                                   DFK.asComparison()); + +  auto *CD = cast<CXXConstructorDecl>(FD);    assert(CD->getInheritedConstructor() && -         "only special members have implicit exception specs"); +         "only defaulted functions and inherited constructors have implicit " +         "exception specs");    Sema::InheritedConstructorInfo ICI(        S, Loc, CD->getInheritedConstructor().getShadowDecl());    return ComputeDefaultedSpecialMemberExceptionSpec( @@ -6745,34 +6899,46 @@ static FunctionProtoType::ExtProtoInfo getImplicitMethodEPI(Sema &S,    return EPI;  } -void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD) { -  const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>(); +void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, FunctionDecl *FD) { +  const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>();    if (FPT->getExceptionSpecType() != EST_Unevaluated)      return;    // Evaluate the exception specification. -  auto IES = computeImplicitExceptionSpec(*this, Loc, MD); +  auto IES = computeImplicitExceptionSpec(*this, Loc, FD);    auto ESI = IES.getExceptionSpec();    // Update the type of the special member to use it. -  UpdateExceptionSpec(MD, ESI); +  UpdateExceptionSpec(FD, ESI); +} + +void Sema::CheckExplicitlyDefaultedFunction(Scope *S, FunctionDecl *FD) { +  assert(FD->isExplicitlyDefaulted() && "not explicitly-defaulted"); -  // A user-provided destructor can be defined outside the class. When that -  // happens, be sure to update the exception specification on both -  // declarations. -  const FunctionProtoType *CanonicalFPT = -    MD->getCanonicalDecl()->getType()->castAs<FunctionProtoType>(); -  if (CanonicalFPT->getExceptionSpecType() == EST_Unevaluated) -    UpdateExceptionSpec(MD->getCanonicalDecl(), ESI); +  DefaultedFunctionKind DefKind = getDefaultedFunctionKind(FD); +  if (!DefKind) { +    assert(FD->getDeclContext()->isDependentContext()); +    return; +  } + +  if (DefKind.isSpecialMember() +          ? CheckExplicitlyDefaultedSpecialMember(cast<CXXMethodDecl>(FD), +                                                  DefKind.asSpecialMember()) +          : CheckExplicitlyDefaultedComparison(S, FD, DefKind.asComparison())) +    FD->setInvalidDecl();  } -void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { +bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD, +                                                 CXXSpecialMember CSM) {    CXXRecordDecl *RD = MD->getParent(); -  CXXSpecialMember CSM = getSpecialMember(MD);    assert(MD->isExplicitlyDefaulted() && CSM != CXXInvalid &&           "not an explicitly-defaulted special member"); +  // Defer all checking for special members of a dependent type. +  if (RD->isDependentType()) +    return false; +    // Whether this was the first-declared instance of the constructor.    // This affects whether we implicitly add an exception spec and constexpr.    bool First = MD == MD->getCanonicalDecl(); @@ -6781,7 +6947,7 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {    // C++11 [dcl.fct.def.default]p1:    //   A function that is explicitly defaulted shall -  //     -- be a special member function (checked elsewhere), +  //     -- be a special member function [...] (checked elsewhere),    //     -- have the same type (except for ref-qualifiers, and except that a    //        copy operation can take a non-const reference) as an implicit    //        declaration, and @@ -6960,8 +7126,1124 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {      }    } -  if (HadError) -    MD->setInvalidDecl(); +  return HadError; +} + +namespace { +/// Helper class for building and checking a defaulted comparison. +/// +/// Defaulted functions are built in two phases: +/// +///  * First, the set of operations that the function will perform are +///    identified, and some of them are checked. If any of the checked +///    operations is invalid in certain ways, the comparison function is +///    defined as deleted and no body is built. +///  * Then, if the function is not defined as deleted, the body is built. +/// +/// This is accomplished by performing two visitation steps over the eventual +/// body of the function. +template<typename Derived, typename ResultList, typename Result, +         typename Subobject> +class DefaultedComparisonVisitor { +public: +  using DefaultedComparisonKind = Sema::DefaultedComparisonKind; + +  DefaultedComparisonVisitor(Sema &S, CXXRecordDecl *RD, FunctionDecl *FD, +                             DefaultedComparisonKind DCK) +      : S(S), RD(RD), FD(FD), DCK(DCK) { +    if (auto *Info = FD->getDefaultedFunctionInfo()) { +      // FIXME: Change CreateOverloadedBinOp to take an ArrayRef instead of an +      // UnresolvedSet to avoid this copy. +      Fns.assign(Info->getUnqualifiedLookups().begin(), +                 Info->getUnqualifiedLookups().end()); +    } +  } + +  ResultList visit() { +    // The type of an lvalue naming a parameter of this function. +    QualType ParamLvalType = +        FD->getParamDecl(0)->getType().getNonReferenceType(); + +    ResultList Results; + +    switch (DCK) { +    case DefaultedComparisonKind::None: +      llvm_unreachable("not a defaulted comparison"); + +    case DefaultedComparisonKind::Equal: +    case DefaultedComparisonKind::ThreeWay: +      getDerived().visitSubobjects(Results, RD, ParamLvalType.getQualifiers()); +      return Results; + +    case DefaultedComparisonKind::NotEqual: +    case DefaultedComparisonKind::Relational: +      Results.add(getDerived().visitExpandedSubobject( +          ParamLvalType, getDerived().getCompleteObject())); +      return Results; +    } +    llvm_unreachable(""); +  } + +protected: +  Derived &getDerived() { return static_cast<Derived&>(*this); } + +  /// Visit the expanded list of subobjects of the given type, as specified in +  /// C++2a [class.compare.default]. +  /// +  /// \return \c true if the ResultList object said we're done, \c false if not. +  bool visitSubobjects(ResultList &Results, CXXRecordDecl *Record, +                       Qualifiers Quals) { +    // C++2a [class.compare.default]p4: +    //   The direct base class subobjects of C +    for (CXXBaseSpecifier &Base : Record->bases()) +      if (Results.add(getDerived().visitSubobject( +              S.Context.getQualifiedType(Base.getType(), Quals), +              getDerived().getBase(&Base)))) +        return true; + +    //   followed by the non-static data members of C +    for (FieldDecl *Field : Record->fields()) { +      // Recursively expand anonymous structs. +      if (Field->isAnonymousStructOrUnion()) { +        if (visitSubobjects(Results, Field->getType()->getAsCXXRecordDecl(), +                            Quals)) +          return true; +        continue; +      } + +      // Figure out the type of an lvalue denoting this field. +      Qualifiers FieldQuals = Quals; +      if (Field->isMutable()) +        FieldQuals.removeConst(); +      QualType FieldType = +          S.Context.getQualifiedType(Field->getType(), FieldQuals); + +      if (Results.add(getDerived().visitSubobject( +              FieldType, getDerived().getField(Field)))) +        return true; +    } + +    //   form a list of subobjects. +    return false; +  } + +  Result visitSubobject(QualType Type, Subobject Subobj) { +    //   In that list, any subobject of array type is recursively expanded +    const ArrayType *AT = S.Context.getAsArrayType(Type); +    if (auto *CAT = dyn_cast_or_null<ConstantArrayType>(AT)) +      return getDerived().visitSubobjectArray(CAT->getElementType(), +                                              CAT->getSize(), Subobj); +    return getDerived().visitExpandedSubobject(Type, Subobj); +  } + +  Result visitSubobjectArray(QualType Type, const llvm::APInt &Size, +                             Subobject Subobj) { +    return getDerived().visitSubobject(Type, Subobj); +  } + +protected: +  Sema &S; +  CXXRecordDecl *RD; +  FunctionDecl *FD; +  DefaultedComparisonKind DCK; +  UnresolvedSet<16> Fns; +}; + +/// Information about a defaulted comparison, as determined by +/// DefaultedComparisonAnalyzer. +struct DefaultedComparisonInfo { +  bool Deleted = false; +  bool Constexpr = true; +  ComparisonCategoryType Category = ComparisonCategoryType::StrongOrdering; + +  static DefaultedComparisonInfo deleted() { +    DefaultedComparisonInfo Deleted; +    Deleted.Deleted = true; +    return Deleted; +  } + +  bool add(const DefaultedComparisonInfo &R) { +    Deleted |= R.Deleted; +    Constexpr &= R.Constexpr; +    Category = commonComparisonType(Category, R.Category); +    return Deleted; +  } +}; + +/// An element in the expanded list of subobjects of a defaulted comparison, as +/// specified in C++2a [class.compare.default]p4. +struct DefaultedComparisonSubobject { +  enum { CompleteObject, Member, Base } Kind; +  NamedDecl *Decl; +  SourceLocation Loc; +}; + +/// A visitor over the notional body of a defaulted comparison that determines +/// whether that body would be deleted or constexpr. +class DefaultedComparisonAnalyzer +    : public DefaultedComparisonVisitor<DefaultedComparisonAnalyzer, +                                        DefaultedComparisonInfo, +                                        DefaultedComparisonInfo, +                                        DefaultedComparisonSubobject> { +public: +  enum DiagnosticKind { NoDiagnostics, ExplainDeleted, ExplainConstexpr }; + +private: +  DiagnosticKind Diagnose; + +public: +  using Base = DefaultedComparisonVisitor; +  using Result = DefaultedComparisonInfo; +  using Subobject = DefaultedComparisonSubobject; + +  friend Base; + +  DefaultedComparisonAnalyzer(Sema &S, CXXRecordDecl *RD, FunctionDecl *FD, +                              DefaultedComparisonKind DCK, +                              DiagnosticKind Diagnose = NoDiagnostics) +      : Base(S, RD, FD, DCK), Diagnose(Diagnose) {} + +  Result visit() { +    if ((DCK == DefaultedComparisonKind::Equal || +         DCK == DefaultedComparisonKind::ThreeWay) && +        RD->hasVariantMembers()) { +      // C++2a [class.compare.default]p2 [P2002R0]: +      //   A defaulted comparison operator function for class C is defined as +      //   deleted if [...] C has variant members. +      if (Diagnose == ExplainDeleted) { +        S.Diag(FD->getLocation(), diag::note_defaulted_comparison_union) +          << FD << RD->isUnion() << RD; +      } +      return Result::deleted(); +    } + +    return Base::visit(); +  } + +private: +  Subobject getCompleteObject() { +    return Subobject{Subobject::CompleteObject, nullptr, FD->getLocation()}; +  } + +  Subobject getBase(CXXBaseSpecifier *Base) { +    return Subobject{Subobject::Base, Base->getType()->getAsCXXRecordDecl(), +                     Base->getBaseTypeLoc()}; +  } + +  Subobject getField(FieldDecl *Field) { +    return Subobject{Subobject::Member, Field, Field->getLocation()}; +  } + +  Result visitExpandedSubobject(QualType Type, Subobject Subobj) { +    // C++2a [class.compare.default]p2 [P2002R0]: +    //   A defaulted <=> or == operator function for class C is defined as +    //   deleted if any non-static data member of C is of reference type +    if (Type->isReferenceType()) { +      if (Diagnose == ExplainDeleted) { +        S.Diag(Subobj.Loc, diag::note_defaulted_comparison_reference_member) +            << FD << RD; +      } +      return Result::deleted(); +    } + +    // [...] Let xi be an lvalue denoting the ith element [...] +    OpaqueValueExpr Xi(FD->getLocation(), Type, VK_LValue); +    Expr *Args[] = {&Xi, &Xi}; + +    // All operators start by trying to apply that same operator recursively. +    OverloadedOperatorKind OO = FD->getOverloadedOperator(); +    assert(OO != OO_None && "not an overloaded operator!"); +    return visitBinaryOperator(OO, Args, Subobj); +  } + +  Result +  visitBinaryOperator(OverloadedOperatorKind OO, ArrayRef<Expr *> Args, +                      Subobject Subobj, +                      OverloadCandidateSet *SpaceshipCandidates = nullptr) { +    // Note that there is no need to consider rewritten candidates here if +    // we've already found there is no viable 'operator<=>' candidate (and are +    // considering synthesizing a '<=>' from '==' and '<'). +    OverloadCandidateSet CandidateSet( +        FD->getLocation(), OverloadCandidateSet::CSK_Operator, +        OverloadCandidateSet::OperatorRewriteInfo( +            OO, /*AllowRewrittenCandidates=*/!SpaceshipCandidates)); + +    /// C++2a [class.compare.default]p1 [P2002R0]: +    ///   [...] the defaulted function itself is never a candidate for overload +    ///   resolution [...] +    CandidateSet.exclude(FD); + +    S.LookupOverloadedBinOp(CandidateSet, OO, Fns, Args); + +    Result R; + +    OverloadCandidateSet::iterator Best; +    switch (CandidateSet.BestViableFunction(S, FD->getLocation(), Best)) { +    case OR_Success: { +      // C++2a [class.compare.secondary]p2 [P2002R0]: +      //   The operator function [...] is defined as deleted if [...] the +      //   candidate selected by overload resolution is not a rewritten +      //   candidate. +      if ((DCK == DefaultedComparisonKind::NotEqual || +           DCK == DefaultedComparisonKind::Relational) && +          !Best->RewriteKind) { +        if (Diagnose == ExplainDeleted) { +          S.Diag(Best->Function->getLocation(), +                 diag::note_defaulted_comparison_not_rewritten_callee) +              << FD; +        } +        return Result::deleted(); +      } + +      // Throughout C++2a [class.compare]: if overload resolution does not +      // result in a usable function, the candidate function is defined as +      // deleted. This requires that we selected an accessible function. +      // +      // Note that this only considers the access of the function when named +      // within the type of the subobject, and not the access path for any +      // derived-to-base conversion. +      CXXRecordDecl *ArgClass = Args[0]->getType()->getAsCXXRecordDecl(); +      if (ArgClass && Best->FoundDecl.getDecl() && +          Best->FoundDecl.getDecl()->isCXXClassMember()) { +        QualType ObjectType = Subobj.Kind == Subobject::Member +                                  ? Args[0]->getType() +                                  : S.Context.getRecordType(RD); +        if (!S.isMemberAccessibleForDeletion( +                ArgClass, Best->FoundDecl, ObjectType, Subobj.Loc, +                Diagnose == ExplainDeleted +                    ? S.PDiag(diag::note_defaulted_comparison_inaccessible) +                          << FD << Subobj.Kind << Subobj.Decl +                    : S.PDiag())) +          return Result::deleted(); +      } + +      // C++2a [class.compare.default]p3 [P2002R0]: +      //   A defaulted comparison function is constexpr-compatible if [...] +      //   no overlod resolution performed [...] results in a non-constexpr +      //   function. +      if (FunctionDecl *BestFD = Best->Function) { +        assert(!BestFD->isDeleted() && "wrong overload resolution result"); +        // If it's not constexpr, explain why not. +        if (Diagnose == ExplainConstexpr && !BestFD->isConstexpr()) { +          if (Subobj.Kind != Subobject::CompleteObject) +            S.Diag(Subobj.Loc, diag::note_defaulted_comparison_not_constexpr) +              << Subobj.Kind << Subobj.Decl; +          S.Diag(BestFD->getLocation(), +                 diag::note_defaulted_comparison_not_constexpr_here); +          // Bail out after explaining; we don't want any more notes. +          return Result::deleted(); +        } +        R.Constexpr &= BestFD->isConstexpr(); +      } + +      if (OO == OO_Spaceship && FD->getReturnType()->isUndeducedAutoType()) { +        if (auto *BestFD = Best->Function) { +          if (auto *Info = S.Context.CompCategories.lookupInfoForType( +              BestFD->getCallResultType())) { +            R.Category = Info->Kind; +          } else { +            if (Diagnose == ExplainDeleted) { +              S.Diag(Subobj.Loc, diag::note_defaulted_comparison_cannot_deduce) +                  << Subobj.Kind << Subobj.Decl +                  << BestFD->getCallResultType().withoutLocalFastQualifiers(); +              S.Diag(BestFD->getLocation(), +                     diag::note_defaulted_comparison_cannot_deduce_callee) +                  << Subobj.Kind << Subobj.Decl; +            } +            return Result::deleted(); +          } +        } else { +          Optional<ComparisonCategoryType> Cat = +              getComparisonCategoryForBuiltinCmp(Args[0]->getType()); +          assert(Cat && "no category for builtin comparison?"); +          R.Category = *Cat; +        } +      } + +      // Note that we might be rewriting to a different operator. That call is +      // not considered until we come to actually build the comparison function. +      break; +    } + +    case OR_Ambiguous: +      if (Diagnose == ExplainDeleted) { +        unsigned Kind = 0; +        if (FD->getOverloadedOperator() == OO_Spaceship && OO != OO_Spaceship) +          Kind = OO == OO_EqualEqual ? 1 : 2; +        CandidateSet.NoteCandidates( +            PartialDiagnosticAt( +                Subobj.Loc, S.PDiag(diag::note_defaulted_comparison_ambiguous) +                                << FD << Kind << Subobj.Kind << Subobj.Decl), +            S, OCD_AmbiguousCandidates, Args); +      } +      R = Result::deleted(); +      break; + +    case OR_Deleted: +      if (Diagnose == ExplainDeleted) { +        if ((DCK == DefaultedComparisonKind::NotEqual || +             DCK == DefaultedComparisonKind::Relational) && +            !Best->RewriteKind) { +          S.Diag(Best->Function->getLocation(), +                 diag::note_defaulted_comparison_not_rewritten_callee) +              << FD; +        } else { +          S.Diag(Subobj.Loc, +                 diag::note_defaulted_comparison_calls_deleted) +              << FD << Subobj.Kind << Subobj.Decl; +          S.NoteDeletedFunction(Best->Function); +        } +      } +      R = Result::deleted(); +      break; + +    case OR_No_Viable_Function: +      // If there's no usable candidate, we're done unless we can rewrite a +      // '<=>' in terms of '==' and '<'. +      if (OO == OO_Spaceship && +          S.Context.CompCategories.lookupInfoForType(FD->getReturnType())) { +        // For any kind of comparison category return type, we need a usable +        // '==' and a usable '<'. +        if (!R.add(visitBinaryOperator(OO_EqualEqual, Args, Subobj, +                                       &CandidateSet))) +          R.add(visitBinaryOperator(OO_Less, Args, Subobj, &CandidateSet)); +        break; +      } + +      if (Diagnose == ExplainDeleted) { +        S.Diag(Subobj.Loc, diag::note_defaulted_comparison_no_viable_function) +            << FD << Subobj.Kind << Subobj.Decl; + +        // For a three-way comparison, list both the candidates for the +        // original operator and the candidates for the synthesized operator. +        if (SpaceshipCandidates) { +          SpaceshipCandidates->NoteCandidates( +              S, Args, +              SpaceshipCandidates->CompleteCandidates(S, OCD_AllCandidates, +                                                      Args, FD->getLocation())); +          S.Diag(Subobj.Loc, +                 diag::note_defaulted_comparison_no_viable_function_synthesized) +              << (OO == OO_EqualEqual ? 0 : 1); +        } + +        CandidateSet.NoteCandidates( +            S, Args, +            CandidateSet.CompleteCandidates(S, OCD_AllCandidates, Args, +                                            FD->getLocation())); +      } +      R = Result::deleted(); +      break; +    } + +    return R; +  } +}; + +/// A list of statements. +struct StmtListResult { +  bool IsInvalid = false; +  llvm::SmallVector<Stmt*, 16> Stmts; + +  bool add(const StmtResult &S) { +    IsInvalid |= S.isInvalid(); +    if (IsInvalid) +      return true; +    Stmts.push_back(S.get()); +    return false; +  } +}; + +/// A visitor over the notional body of a defaulted comparison that synthesizes +/// the actual body. +class DefaultedComparisonSynthesizer +    : public DefaultedComparisonVisitor<DefaultedComparisonSynthesizer, +                                        StmtListResult, StmtResult, +                                        std::pair<ExprResult, ExprResult>> { +  SourceLocation Loc; +  unsigned ArrayDepth = 0; + +public: +  using Base = DefaultedComparisonVisitor; +  using ExprPair = std::pair<ExprResult, ExprResult>; + +  friend Base; + +  DefaultedComparisonSynthesizer(Sema &S, CXXRecordDecl *RD, FunctionDecl *FD, +                                 DefaultedComparisonKind DCK, +                                 SourceLocation BodyLoc) +      : Base(S, RD, FD, DCK), Loc(BodyLoc) {} + +  /// Build a suitable function body for this defaulted comparison operator. +  StmtResult build() { +    Sema::CompoundScopeRAII CompoundScope(S); + +    StmtListResult Stmts = visit(); +    if (Stmts.IsInvalid) +      return StmtError(); + +    ExprResult RetVal; +    switch (DCK) { +    case DefaultedComparisonKind::None: +      llvm_unreachable("not a defaulted comparison"); + +    case DefaultedComparisonKind::Equal: { +      // C++2a [class.eq]p3: +      //   [...] compar[e] the corresponding elements [...] until the first +      //   index i where xi == yi yields [...] false. If no such index exists, +      //   V is true. Otherwise, V is false. +      // +      // Join the comparisons with '&&'s and return the result. Use a right +      // fold (traversing the conditions right-to-left), because that +      // short-circuits more naturally. +      auto OldStmts = std::move(Stmts.Stmts); +      Stmts.Stmts.clear(); +      ExprResult CmpSoFar; +      // Finish a particular comparison chain. +      auto FinishCmp = [&] { +        if (Expr *Prior = CmpSoFar.get()) { +          // Convert the last expression to 'return ...;' +          if (RetVal.isUnset() && Stmts.Stmts.empty()) +            RetVal = CmpSoFar; +          // Convert any prior comparison to 'if (!(...)) return false;' +          else if (Stmts.add(buildIfNotCondReturnFalse(Prior))) +            return true; +          CmpSoFar = ExprResult(); +        } +        return false; +      }; +      for (Stmt *EAsStmt : llvm::reverse(OldStmts)) { +        Expr *E = dyn_cast<Expr>(EAsStmt); +        if (!E) { +          // Found an array comparison. +          if (FinishCmp() || Stmts.add(EAsStmt)) +            return StmtError(); +          continue; +        } + +        if (CmpSoFar.isUnset()) { +          CmpSoFar = E; +          continue; +        } +        CmpSoFar = S.CreateBuiltinBinOp(Loc, BO_LAnd, E, CmpSoFar.get()); +        if (CmpSoFar.isInvalid()) +          return StmtError(); +      } +      if (FinishCmp()) +        return StmtError(); +      std::reverse(Stmts.Stmts.begin(), Stmts.Stmts.end()); +      //   If no such index exists, V is true. +      if (RetVal.isUnset()) +        RetVal = S.ActOnCXXBoolLiteral(Loc, tok::kw_true); +      break; +    } + +    case DefaultedComparisonKind::ThreeWay: { +      // Per C++2a [class.spaceship]p3, as a fallback add: +      // return static_cast<R>(std::strong_ordering::equal); +      QualType StrongOrdering = S.CheckComparisonCategoryType( +          ComparisonCategoryType::StrongOrdering, Loc, +          Sema::ComparisonCategoryUsage::DefaultedOperator); +      if (StrongOrdering.isNull()) +        return StmtError(); +      VarDecl *EqualVD = S.Context.CompCategories.getInfoForType(StrongOrdering) +                             .getValueInfo(ComparisonCategoryResult::Equal) +                             ->VD; +      RetVal = getDecl(EqualVD); +      if (RetVal.isInvalid()) +        return StmtError(); +      RetVal = buildStaticCastToR(RetVal.get()); +      break; +    } + +    case DefaultedComparisonKind::NotEqual: +    case DefaultedComparisonKind::Relational: +      RetVal = cast<Expr>(Stmts.Stmts.pop_back_val()); +      break; +    } + +    // Build the final return statement. +    if (RetVal.isInvalid()) +      return StmtError(); +    StmtResult ReturnStmt = S.BuildReturnStmt(Loc, RetVal.get()); +    if (ReturnStmt.isInvalid()) +      return StmtError(); +    Stmts.Stmts.push_back(ReturnStmt.get()); + +    return S.ActOnCompoundStmt(Loc, Loc, Stmts.Stmts, /*IsStmtExpr=*/false); +  } + +private: +  ExprResult getDecl(ValueDecl *VD) { +    return S.BuildDeclarationNameExpr( +        CXXScopeSpec(), DeclarationNameInfo(VD->getDeclName(), Loc), VD); +  } + +  ExprResult getParam(unsigned I) { +    ParmVarDecl *PD = FD->getParamDecl(I); +    return getDecl(PD); +  } + +  ExprPair getCompleteObject() { +    unsigned Param = 0; +    ExprResult LHS; +    if (isa<CXXMethodDecl>(FD)) { +      // LHS is '*this'. +      LHS = S.ActOnCXXThis(Loc); +      if (!LHS.isInvalid()) +        LHS = S.CreateBuiltinUnaryOp(Loc, UO_Deref, LHS.get()); +    } else { +      LHS = getParam(Param++); +    } +    ExprResult RHS = getParam(Param++); +    assert(Param == FD->getNumParams()); +    return {LHS, RHS}; +  } + +  ExprPair getBase(CXXBaseSpecifier *Base) { +    ExprPair Obj = getCompleteObject(); +    if (Obj.first.isInvalid() || Obj.second.isInvalid()) +      return {ExprError(), ExprError()}; +    CXXCastPath Path = {Base}; +    return {S.ImpCastExprToType(Obj.first.get(), Base->getType(), +                                CK_DerivedToBase, VK_LValue, &Path), +            S.ImpCastExprToType(Obj.second.get(), Base->getType(), +                                CK_DerivedToBase, VK_LValue, &Path)}; +  } + +  ExprPair getField(FieldDecl *Field) { +    ExprPair Obj = getCompleteObject(); +    if (Obj.first.isInvalid() || Obj.second.isInvalid()) +      return {ExprError(), ExprError()}; + +    DeclAccessPair Found = DeclAccessPair::make(Field, Field->getAccess()); +    DeclarationNameInfo NameInfo(Field->getDeclName(), Loc); +    return {S.BuildFieldReferenceExpr(Obj.first.get(), /*IsArrow=*/false, Loc, +                                      CXXScopeSpec(), Field, Found, NameInfo), +            S.BuildFieldReferenceExpr(Obj.second.get(), /*IsArrow=*/false, Loc, +                                      CXXScopeSpec(), Field, Found, NameInfo)}; +  } + +  // FIXME: When expanding a subobject, register a note in the code synthesis +  // stack to say which subobject we're comparing. + +  StmtResult buildIfNotCondReturnFalse(ExprResult Cond) { +    if (Cond.isInvalid()) +      return StmtError(); + +    ExprResult NotCond = S.CreateBuiltinUnaryOp(Loc, UO_LNot, Cond.get()); +    if (NotCond.isInvalid()) +      return StmtError(); + +    ExprResult False = S.ActOnCXXBoolLiteral(Loc, tok::kw_false); +    assert(!False.isInvalid() && "should never fail"); +    StmtResult ReturnFalse = S.BuildReturnStmt(Loc, False.get()); +    if (ReturnFalse.isInvalid()) +      return StmtError(); + +    return S.ActOnIfStmt(Loc, false, nullptr, +                         S.ActOnCondition(nullptr, Loc, NotCond.get(), +                                          Sema::ConditionKind::Boolean), +                         ReturnFalse.get(), SourceLocation(), nullptr); +  } + +  StmtResult visitSubobjectArray(QualType Type, llvm::APInt Size, +                                 ExprPair Subobj) { +    QualType SizeType = S.Context.getSizeType(); +    Size = Size.zextOrTrunc(S.Context.getTypeSize(SizeType)); + +    // Build 'size_t i$n = 0'. +    IdentifierInfo *IterationVarName = nullptr; +    { +      SmallString<8> Str; +      llvm::raw_svector_ostream OS(Str); +      OS << "i" << ArrayDepth; +      IterationVarName = &S.Context.Idents.get(OS.str()); +    } +    VarDecl *IterationVar = VarDecl::Create( +        S.Context, S.CurContext, Loc, Loc, IterationVarName, SizeType, +        S.Context.getTrivialTypeSourceInfo(SizeType, Loc), SC_None); +    llvm::APInt Zero(S.Context.getTypeSize(SizeType), 0); +    IterationVar->setInit( +        IntegerLiteral::Create(S.Context, Zero, SizeType, Loc)); +    Stmt *Init = new (S.Context) DeclStmt(DeclGroupRef(IterationVar), Loc, Loc); + +    auto IterRef = [&] { +      ExprResult Ref = S.BuildDeclarationNameExpr( +          CXXScopeSpec(), DeclarationNameInfo(IterationVarName, Loc), +          IterationVar); +      assert(!Ref.isInvalid() && "can't reference our own variable?"); +      return Ref.get(); +    }; + +    // Build 'i$n != Size'. +    ExprResult Cond = S.CreateBuiltinBinOp( +        Loc, BO_NE, IterRef(), +        IntegerLiteral::Create(S.Context, Size, SizeType, Loc)); +    assert(!Cond.isInvalid() && "should never fail"); + +    // Build '++i$n'. +    ExprResult Inc = S.CreateBuiltinUnaryOp(Loc, UO_PreInc, IterRef()); +    assert(!Inc.isInvalid() && "should never fail"); + +    // Build 'a[i$n]' and 'b[i$n]'. +    auto Index = [&](ExprResult E) { +      if (E.isInvalid()) +        return ExprError(); +      return S.CreateBuiltinArraySubscriptExpr(E.get(), Loc, IterRef(), Loc); +    }; +    Subobj.first = Index(Subobj.first); +    Subobj.second = Index(Subobj.second); + +    // Compare the array elements. +    ++ArrayDepth; +    StmtResult Substmt = visitSubobject(Type, Subobj); +    --ArrayDepth; + +    if (Substmt.isInvalid()) +      return StmtError(); + +    // For the inner level of an 'operator==', build 'if (!cmp) return false;'. +    // For outer levels or for an 'operator<=>' we already have a suitable +    // statement that returns as necessary. +    if (Expr *ElemCmp = dyn_cast<Expr>(Substmt.get())) { +      assert(DCK == DefaultedComparisonKind::Equal && +             "should have non-expression statement"); +      Substmt = buildIfNotCondReturnFalse(ElemCmp); +      if (Substmt.isInvalid()) +        return StmtError(); +    } + +    // Build 'for (...) ...' +    return S.ActOnForStmt(Loc, Loc, Init, +                          S.ActOnCondition(nullptr, Loc, Cond.get(), +                                           Sema::ConditionKind::Boolean), +                          S.MakeFullDiscardedValueExpr(Inc.get()), Loc, +                          Substmt.get()); +  } + +  StmtResult visitExpandedSubobject(QualType Type, ExprPair Obj) { +    if (Obj.first.isInvalid() || Obj.second.isInvalid()) +      return StmtError(); + +    OverloadedOperatorKind OO = FD->getOverloadedOperator(); +    ExprResult Op = S.CreateOverloadedBinOp( +        Loc, BinaryOperator::getOverloadedOpcode(OO), Fns, +        Obj.first.get(), Obj.second.get(), /*PerformADL=*/true, +        /*AllowRewrittenCandidates=*/true, FD); +    if (Op.isInvalid()) +      return StmtError(); + +    switch (DCK) { +    case DefaultedComparisonKind::None: +      llvm_unreachable("not a defaulted comparison"); + +    case DefaultedComparisonKind::Equal: +      // Per C++2a [class.eq]p2, each comparison is individually contextually +      // converted to bool. +      Op = S.PerformContextuallyConvertToBool(Op.get()); +      if (Op.isInvalid()) +        return StmtError(); +      return Op.get(); + +    case DefaultedComparisonKind::ThreeWay: { +      // Per C++2a [class.spaceship]p3, form: +      //   if (R cmp = static_cast<R>(op); cmp != 0) +      //     return cmp; +      QualType R = FD->getReturnType(); +      Op = buildStaticCastToR(Op.get()); +      if (Op.isInvalid()) +        return StmtError(); + +      // R cmp = ...; +      IdentifierInfo *Name = &S.Context.Idents.get("cmp"); +      VarDecl *VD = +          VarDecl::Create(S.Context, S.CurContext, Loc, Loc, Name, R, +                          S.Context.getTrivialTypeSourceInfo(R, Loc), SC_None); +      S.AddInitializerToDecl(VD, Op.get(), /*DirectInit=*/false); +      Stmt *InitStmt = new (S.Context) DeclStmt(DeclGroupRef(VD), Loc, Loc); + +      // cmp != 0 +      ExprResult VDRef = getDecl(VD); +      if (VDRef.isInvalid()) +        return StmtError(); +      llvm::APInt ZeroVal(S.Context.getIntWidth(S.Context.IntTy), 0); +      Expr *Zero = +          IntegerLiteral::Create(S.Context, ZeroVal, S.Context.IntTy, Loc); +      ExprResult Comp = S.CreateOverloadedBinOp(Loc, BO_NE, Fns, VDRef.get(), +                                                Zero, true, true, FD); +      if (Comp.isInvalid()) +        return StmtError(); +      Sema::ConditionResult Cond = S.ActOnCondition( +          nullptr, Loc, Comp.get(), Sema::ConditionKind::Boolean); +      if (Cond.isInvalid()) +        return StmtError(); + +      // return cmp; +      VDRef = getDecl(VD); +      if (VDRef.isInvalid()) +        return StmtError(); +      StmtResult ReturnStmt = S.BuildReturnStmt(Loc, VDRef.get()); +      if (ReturnStmt.isInvalid()) +        return StmtError(); + +      // if (...) +      return S.ActOnIfStmt(Loc, /*IsConstexpr=*/false, InitStmt, Cond, +                           ReturnStmt.get(), /*ElseLoc=*/SourceLocation(), +                           /*Else=*/nullptr); +    } + +    case DefaultedComparisonKind::NotEqual: +    case DefaultedComparisonKind::Relational: +      // C++2a [class.compare.secondary]p2: +      //   Otherwise, the operator function yields x @ y. +      return Op.get(); +    } +    llvm_unreachable(""); +  } + +  /// Build "static_cast<R>(E)". +  ExprResult buildStaticCastToR(Expr *E) { +    QualType R = FD->getReturnType(); +    assert(!R->isUndeducedType() && "type should have been deduced already"); + +    // Don't bother forming a no-op cast in the common case. +    if (E->isRValue() && S.Context.hasSameType(E->getType(), R)) +      return E; +    return S.BuildCXXNamedCast(Loc, tok::kw_static_cast, +                               S.Context.getTrivialTypeSourceInfo(R, Loc), E, +                               SourceRange(Loc, Loc), SourceRange(Loc, Loc)); +  } +}; +} + +/// Perform the unqualified lookups that might be needed to form a defaulted +/// comparison function for the given operator. +static void lookupOperatorsForDefaultedComparison(Sema &Self, Scope *S, +                                                  UnresolvedSetImpl &Operators, +                                                  OverloadedOperatorKind Op) { +  auto Lookup = [&](OverloadedOperatorKind OO) { +    Self.LookupOverloadedOperatorName(OO, S, QualType(), QualType(), Operators); +  }; + +  // Every defaulted operator looks up itself. +  Lookup(Op); +  // ... and the rewritten form of itself, if any. +  if (OverloadedOperatorKind ExtraOp = getRewrittenOverloadedOperator(Op)) +    Lookup(ExtraOp); + +  // For 'operator<=>', we also form a 'cmp != 0' expression, and might +  // 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); +    Lookup(OO_EqualEqual); +  } +} + +bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, +                                              DefaultedComparisonKind DCK) { +  assert(DCK != DefaultedComparisonKind::None && "not a defaulted comparison"); + +  CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(FD->getLexicalDeclContext()); +  assert(RD && "defaulted comparison is not defaulted in a class"); + +  // Perform any unqualified lookups we're going to need to default this +  // function. +  if (S) { +    UnresolvedSet<32> Operators; +    lookupOperatorsForDefaultedComparison(*this, S, Operators, +                                          FD->getOverloadedOperator()); +    FD->setDefaultedFunctionInfo(FunctionDecl::DefaultedFunctionInfo::Create( +        Context, Operators.pairs())); +  } + +  // C++2a [class.compare.default]p1: +  //   A defaulted comparison operator function for some class C shall be a +  //   non-template function declared in the member-specification of C that is +  //    -- a non-static const member of C having one parameter of type +  //       const C&, or +  //    -- a friend of C having two parameters of type const C& or two +  //       parameters of type C. +  QualType ExpectedParmType1 = Context.getRecordType(RD); +  QualType ExpectedParmType2 = +      Context.getLValueReferenceType(ExpectedParmType1.withConst()); +  if (isa<CXXMethodDecl>(FD)) +    ExpectedParmType1 = ExpectedParmType2; +  for (const ParmVarDecl *Param : FD->parameters()) { +    if (!Param->getType()->isDependentType() && +        !Context.hasSameType(Param->getType(), ExpectedParmType1) && +        !Context.hasSameType(Param->getType(), ExpectedParmType2)) { +      // 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() << ExpectedParmType1 +            << !isa<CXXMethodDecl>(FD) +            << ExpectedParmType2 << Param->getSourceRange(); +      } +      return true; +    } +  } +  if (FD->getNumParams() == 2 && +      !Context.hasSameType(FD->getParamDecl(0)->getType(), +                           FD->getParamDecl(1)->getType())) { +    if (!FD->isImplicit()) { +      Diag(FD->getLocation(), diag::err_defaulted_comparison_param_mismatch) +          << (int)DCK +          << FD->getParamDecl(0)->getType() +          << FD->getParamDecl(0)->getSourceRange() +          << FD->getParamDecl(1)->getType() +          << FD->getParamDecl(1)->getSourceRange(); +    } +    return true; +  } + +  // ... non-static const member ... +  if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) { +    assert(!MD->isStatic() && "comparison function cannot be a static member"); +    if (!MD->isConst()) { +      SourceLocation InsertLoc; +      if (FunctionTypeLoc Loc = MD->getFunctionTypeLoc()) +        InsertLoc = getLocForEndOfToken(Loc.getRParenLoc()); +      // 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>(); +      FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); +      EPI.TypeQuals.addConst(); +      MD->setType(Context.getFunctionType(FPT->getReturnType(), +                                          FPT->getParamTypes(), EPI)); +    } +  } else { +    // A non-member function declared in a class must be a friend. +    assert(FD->getFriendObjectKind() && "expected a friend declaration"); +  } + +  // C++2a [class.eq]p1, [class.rel]p1: +  //   A [defaulted comparison other than <=>] shall have a declared return +  //   type bool. +  if (DCK != DefaultedComparisonKind::ThreeWay && +      !FD->getDeclaredReturnType()->isDependentType() && +      !Context.hasSameType(FD->getDeclaredReturnType(), Context.BoolTy)) { +    Diag(FD->getLocation(), diag::err_defaulted_comparison_return_type_not_bool) +        << (int)DCK << FD->getDeclaredReturnType() << Context.BoolTy +        << FD->getReturnTypeSourceRange(); +    return true; +  } +  // C++2a [class.spaceship]p2 [P2002R0]: +  //   Let R be the declared return type [...]. If R is auto, [...]. Otherwise, +  //   R shall not contain a placeholder type. +  if (DCK == DefaultedComparisonKind::ThreeWay && +      FD->getDeclaredReturnType()->getContainedDeducedType() && +      !Context.hasSameType(FD->getDeclaredReturnType(), +                           Context.getAutoDeductType())) { +    Diag(FD->getLocation(), +         diag::err_defaulted_comparison_deduced_return_type_not_auto) +        << (int)DCK << FD->getDeclaredReturnType() << Context.AutoDeductTy +        << FD->getReturnTypeSourceRange(); +    return true; +  } + +  // For a defaulted function in a dependent class, defer all remaining checks +  // until instantiation. +  if (RD->isDependentType()) +    return false; + +  // Determine whether the function should be defined as deleted. +  DefaultedComparisonInfo Info = +      DefaultedComparisonAnalyzer(*this, RD, FD, DCK).visit(); + +  bool First = FD == FD->getCanonicalDecl(); + +  // If we want to delete the function, then do so; there's nothing else to +  // check in that case. +  if (Info.Deleted) { +    if (!First) { +      // C++11 [dcl.fct.def.default]p4: +      //   [For a] user-provided explicitly-defaulted function [...] if such a +      //   function is implicitly defined as deleted, the program is ill-formed. +      // +      // 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) +          << FD->isImplicit() << (int)DCK; +      DefaultedComparisonAnalyzer(*this, RD, FD, DCK, +                                  DefaultedComparisonAnalyzer::ExplainDeleted) +          .visit(); +      return true; +    } + +    SetDeclDeleted(FD, FD->getLocation()); +    if (!inTemplateInstantiation() && !FD->isImplicit()) { +      Diag(FD->getLocation(), diag::warn_defaulted_comparison_deleted) +          << (int)DCK; +      DefaultedComparisonAnalyzer(*this, RD, FD, DCK, +                                  DefaultedComparisonAnalyzer::ExplainDeleted) +          .visit(); +    } +    return false; +  } + +  // C++2a [class.spaceship]p2: +  //   The return type is deduced as the common comparison type of R0, R1, ... +  if (DCK == DefaultedComparisonKind::ThreeWay && +      FD->getDeclaredReturnType()->isUndeducedAutoType()) { +    SourceLocation RetLoc = FD->getReturnTypeSourceRange().getBegin(); +    if (RetLoc.isInvalid()) +      RetLoc = FD->getBeginLoc(); +    // FIXME: Should we really care whether we have the complete type and the +    // 'enumerator' constants here? A forward declaration seems sufficient. +    QualType Cat = CheckComparisonCategoryType( +        Info.Category, RetLoc, ComparisonCategoryUsage::DefaultedOperator); +    if (Cat.isNull()) +      return true; +    Context.adjustDeducedFunctionResultType( +        FD, SubstAutoType(FD->getDeclaredReturnType(), Cat)); +  } + +  // C++2a [dcl.fct.def.default]p3 [P2002R0]: +  //   An explicitly-defaulted function that is not defined as deleted may be +  //   declared constexpr or consteval only if it is constexpr-compatible. +  // C++2a [class.compare.default]p3 [P2002R0]: +  //   A defaulted comparison function is constexpr-compatible if it satisfies +  //   the requirements for a constexpr function [...] +  // The only relevant requirements are that the parameter and return types are +  // literal types. The remaining conditions are checked by the analyzer. +  if (FD->isConstexpr()) { +    if (CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) && +        CheckConstexprParameterTypes(*this, FD, CheckConstexprKind::Diagnose) && +        !Info.Constexpr) { +      Diag(FD->getBeginLoc(), +           diag::err_incorrect_defaulted_comparison_constexpr) +          << FD->isImplicit() << (int)DCK << FD->isConsteval(); +      DefaultedComparisonAnalyzer(*this, RD, FD, DCK, +                                  DefaultedComparisonAnalyzer::ExplainConstexpr) +          .visit(); +    } +  } + +  // C++2a [dcl.fct.def.default]p3 [P2002R0]: +  //   If a constexpr-compatible function is explicitly defaulted on its first +  //   declaration, it is implicitly considered to be constexpr. +  // FIXME: Only applying this to the first declaration seems problematic, as +  // simple reorderings can affect the meaning of the program. +  if (First && !FD->isConstexpr() && Info.Constexpr) +    FD->setConstexprKind(CSK_constexpr); + +  // C++2a [except.spec]p3: +  //   If a declaration of a function does not have a noexcept-specifier +  //   [and] is defaulted on its first declaration, [...] the exception +  //   specification is as specified below +  if (FD->getExceptionSpecType() == EST_None) { +    auto *FPT = FD->getType()->castAs<FunctionProtoType>(); +    FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); +    EPI.ExceptionSpec.Type = EST_Unevaluated; +    EPI.ExceptionSpec.SourceDecl = FD; +    FD->setType(Context.getFunctionType(FPT->getReturnType(), +                                        FPT->getParamTypes(), EPI)); +  } + +  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() && +         !FD->doesThisDeclarationHaveABody()); +  if (FD->willHaveBody() || FD->isInvalidDecl()) +    return; + +  SynthesizedFunctionScope Scope(*this, FD); + +  // Add a context note for diagnostics produced after this point. +  Scope.addContextNote(UseLoc); + +  { +    // Build and set up the function body. +    CXXRecordDecl *RD = cast<CXXRecordDecl>(FD->getLexicalParent()); +    SourceLocation BodyLoc = +        FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation(); +    StmtResult Body = +        DefaultedComparisonSynthesizer(*this, RD, FD, DCK, BodyLoc).build(); +    if (Body.isInvalid()) { +      FD->setInvalidDecl(); +      return; +    } +    FD->setBody(Body.get()); +    FD->markUsed(Context); +  } + +  // The exception specification is needed because we are defining the +  // function. Note that this will reuse the body we just built. +  ResolveExceptionSpec(UseLoc, FD->getType()->castAs<FunctionProtoType>()); + +  if (ASTMutationListener *L = getASTMutationListener()) +    L->CompletedImplicitDefinition(FD); +} + +static Sema::ImplicitExceptionSpecification +ComputeDefaultedComparisonExceptionSpec(Sema &S, SourceLocation Loc, +                                        FunctionDecl *FD, +                                        Sema::DefaultedComparisonKind DCK) { +  ComputingExceptionSpec CES(S, FD, Loc); +  Sema::ImplicitExceptionSpecification ExceptSpec(S); + +  if (FD->isInvalidDecl()) +    return ExceptSpec; + +  // The common case is that we just defined the comparison function. In that +  // case, just look at whether the body can throw. +  if (FD->hasBody()) { +    ExceptSpec.CalledStmt(FD->getBody()); +  } else { +    // Otherwise, build a body so we can check it. This should ideally only +    // happen when we're not actually marking the function referenced. (This is +    // only really important for efficiency: we don't want to build and throw +    // away bodies for comparison functions more than we strictly need to.) + +    // Pretend to synthesize the function body in an unevaluated context. +    // Note that we can't actually just go ahead and define the function here: +    // we are not permitted to mark its callees as referenced. +    Sema::SynthesizedFunctionScope Scope(S, FD); +    EnterExpressionEvaluationContext Context( +        S, Sema::ExpressionEvaluationContext::Unevaluated); + +    CXXRecordDecl *RD = cast<CXXRecordDecl>(FD->getLexicalParent()); +    SourceLocation BodyLoc = +        FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation(); +    StmtResult Body = +        DefaultedComparisonSynthesizer(S, RD, FD, DCK, BodyLoc).build(); +    if (!Body.isInvalid()) +      ExceptSpec.CalledStmt(Body.get()); + +    // FIXME: Can we hold onto this body and just transform it to potentially +    // evaluated when we're asked to define the function rather than rebuilding +    // it? Either that, or we should only build the bits of the body that we +    // need (the expressions, not the statements). +  } + +  return ExceptSpec;  }  void Sema::CheckDelayedMemberExceptionSpecs() { @@ -7157,7 +8439,8 @@ bool SpecialMemberDeletionInfo::isAccessible(Subobject Subobj,      objectTy = S.Context.getTypeDeclType(target->getParent());    } -  return S.isSpecialMemberAccessibleForDeletion(target, access, objectTy); +  return S.isMemberAccessibleForDeletion( +      target->getParent(), DeclAccessPair::make(target, access), objectTy);  }  /// Check whether we should delete a special member due to the implicit @@ -7568,6 +8851,22 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM,    return false;  } +void Sema::DiagnoseDeletedDefaultedFunction(FunctionDecl *FD) { +  DefaultedFunctionKind DFK = getDefaultedFunctionKind(FD); +  assert(DFK && "not a defaultable function"); +  assert(FD->isDefaulted() && FD->isDeleted() && "not defaulted and deleted"); + +  if (DFK.isSpecialMember()) { +    ShouldDeleteSpecialMember(cast<CXXMethodDecl>(FD), DFK.asSpecialMember(), +                              nullptr, /*Diagnose=*/true); +  } else { +    DefaultedComparisonAnalyzer( +        *this, cast<CXXRecordDecl>(FD->getLexicalDeclContext()), FD, +        DFK.asComparison(), DefaultedComparisonAnalyzer::ExplainDeleted) +        .visit(); +  } +} +  /// Perform lookup for a special member of the specified kind, and determine  /// whether it is trivial. If the triviality can be determined without the  /// lookup, skip it. This is intended for use when determining whether a @@ -8187,7 +9486,45 @@ void Sema::ActOnFinishCXXMemberSpecification(                reinterpret_cast<Decl**>(FieldCollector->getCurFields()),                FieldCollector->getCurNumFields()), LBrac, RBrac, AttrList); -  CheckCompletedCXXClass(cast<CXXRecordDecl>(TagDecl)); +  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 @@ -8267,6 +9604,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) { @@ -8478,7 +9829,7 @@ QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R,    // Rebuild the function type "R" without any type qualifiers (in    // case any of the errors above fired) and with "void" as the    // return type, since constructors don't have return types. -  const FunctionProtoType *Proto = R->getAs<FunctionProtoType>(); +  const FunctionProtoType *Proto = R->castAs<FunctionProtoType>();    if (Proto->getReturnType() == Context.VoidTy && !D.isInvalidType())      return R; @@ -8676,7 +10027,7 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, QualType R,    if (!D.isInvalidType())      return R; -  const FunctionProtoType *Proto = R->getAs<FunctionProtoType>(); +  const FunctionProtoType *Proto = R->castAs<FunctionProtoType>();    FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo();    EPI.Variadic = false;    EPI.TypeQuals = Qualifiers(); @@ -8750,7 +10101,7 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R,      D.setInvalidType();    } -  const FunctionProtoType *Proto = R->getAs<FunctionProtoType>(); +  const auto *Proto = R->castAs<FunctionProtoType>();    // Make sure we don't have any parameters.    if (Proto->getNumParams() > 0) { @@ -9334,22 +10685,37 @@ struct InvalidSTLDiagnoser {  } // namespace  QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind, -                                           SourceLocation Loc) { +                                           SourceLocation Loc, +                                           ComparisonCategoryUsage Usage) {    assert(getLangOpts().CPlusPlus &&           "Looking for comparison category type outside of C++."); +  // Use an elaborated type for diagnostics which has a name containing the +  // prepended 'std' namespace but not any inline namespace names. +  auto TyForDiags = [&](ComparisonCategoryInfo *Info) { +    auto *NNS = +        NestedNameSpecifier::Create(Context, nullptr, getStdNamespace()); +    return Context.getElaboratedType(ETK_None, NNS, Info->getType()); +  }; +    // Check if we've already successfully checked the comparison category type    // before. If so, skip checking it again.    ComparisonCategoryInfo *Info = Context.CompCategories.lookupInfo(Kind); -  if (Info && FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)]) +  if (Info && FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)]) { +    // The only thing we need to check is that the type has a reachable +    // definition in the current context. +    if (RequireCompleteType(Loc, TyForDiags(Info), diag::err_incomplete_type)) +      return QualType(); +      return Info->getType(); +  }    // If lookup failed    if (!Info) {      std::string NameForDiags = "std::";      NameForDiags += ComparisonCategories::getCategoryString(Kind);      Diag(Loc, diag::err_implied_comparison_category_type_not_found) -        << NameForDiags; +        << NameForDiags << (int)Usage;      return QualType();    } @@ -9361,18 +10727,10 @@ QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind,    if (Info->Record->hasDefinition())      Info->Record = Info->Record->getDefinition(); -  // Use an elaborated type for diagnostics which has a name containing the -  // prepended 'std' namespace but not any inline namespace names. -  QualType TyForDiags = [&]() { -    auto *NNS = -        NestedNameSpecifier::Create(Context, nullptr, getStdNamespace()); -    return Context.getElaboratedType(ETK_None, NNS, Info->getType()); -  }(); - -  if (RequireCompleteType(Loc, TyForDiags, diag::err_incomplete_type)) +  if (RequireCompleteType(Loc, TyForDiags(Info), diag::err_incomplete_type))      return QualType(); -  InvalidSTLDiagnoser UnsupportedSTLError{*this, Loc, TyForDiags}; +  InvalidSTLDiagnoser UnsupportedSTLError{*this, Loc, TyForDiags(Info)};    if (!Info->Record->isTriviallyCopyable())      return UnsupportedSTLError(USS_NonTrivial); @@ -11070,25 +12428,6 @@ void SpecialMemberExceptionSpecInfo::visitSubobjectCall(      ExceptSpec.CalledDecl(getSubobjectLoc(Subobj), MD);  } -namespace { -/// RAII object to register a special member as being currently declared. -struct ComputingExceptionSpec { -  Sema &S; - -  ComputingExceptionSpec(Sema &S, CXXMethodDecl *MD, SourceLocation Loc) -      : S(S) { -    Sema::CodeSynthesisContext Ctx; -    Ctx.Kind = Sema::CodeSynthesisContext::ExceptionSpecEvaluation; -    Ctx.PointOfInstantiation = Loc; -    Ctx.Entity = MD; -    S.pushCodeSynthesisContext(Ctx); -  } -  ~ComputingExceptionSpec() { -    S.popCodeSynthesisContext(); -  } -}; -} -  bool Sema::tryResolveExplicitSpecifier(ExplicitSpecifier &ExplicitSpec) {    llvm::APSInt Result;    ExprResult Converted = CheckConvertedConstantExpression( @@ -11222,10 +12561,9 @@ void Sema::setupImplicitSpecialMemberType(CXXMethodDecl *SpecialMem,    // Build an exception specification pointing back at this constructor.    FunctionProtoType::ExtProtoInfo EPI = getImplicitMethodEPI(*this, SpecialMem); -  if (getLangOpts().OpenCLCPlusPlus) { -    // OpenCL: Implicitly defaulted special member are of the generic address -    // space. -    EPI.TypeQuals.addAddressSpace(LangAS::opencl_generic); +  LangAS AS = getDefaultCXXMethodAddrSpace(); +  if (AS != LangAS::Default) { +    EPI.TypeQuals.addAddressSpace(AS);    }    auto QT = Context.getFunctionType(ResultTy, Args, EPI); @@ -11384,7 +12722,8 @@ Sema::findInheritingConstructor(SourceLocation Loc,        BaseCtor->getExplicitSpecifier(), /*isInline=*/true,        /*isImplicitlyDeclared=*/true,        Constexpr ? BaseCtor->getConstexprKind() : CSK_unspecified, -      InheritedConstructor(Shadow, BaseCtor)); +      InheritedConstructor(Shadow, BaseCtor), +      BaseCtor->getTrailingRequiresClause());    if (Shadow->isInvalidDecl())      DerivedCtor->setInvalidDecl(); @@ -11635,7 +12974,7 @@ void Sema::ActOnFinishCXXMemberDecls() {    }  } -void Sema::ActOnFinishCXXNonNestedClass(Decl *D) { +void Sema::ActOnFinishCXXNonNestedClass() {    referenceDLLExportedClassMethods();    if (!DelayedDllExportMemberFunctions.empty()) { @@ -11676,8 +13015,7 @@ void Sema::AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor) {    //   A declaration of a destructor that does not have an exception-    //   specification is implicitly considered to have the same exception-    //   specification as an implicit declaration. -  const FunctionProtoType *DtorType = Destructor->getType()-> -                                        getAs<FunctionProtoType>(); +  const auto *DtorType = Destructor->getType()->castAs<FunctionProtoType>();    if (DtorType->hasExceptionSpec())      return; @@ -12135,8 +13473,9 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {      return nullptr;    QualType ArgType = Context.getTypeDeclType(ClassDecl); -  if (Context.getLangOpts().OpenCLCPlusPlus) -    ArgType = Context.getAddrSpaceQualType(ArgType, LangAS::opencl_generic); +  LangAS AS = getDefaultCXXMethodAddrSpace(); +  if (AS != LangAS::Default) +    ArgType = Context.getAddrSpaceQualType(ArgType, AS);    QualType RetType = Context.getLValueReferenceType(ArgType);    bool Const = ClassDecl->implicitCopyAssignmentHasConstParam();    if (Const) @@ -12237,11 +13576,12 @@ static void diagnoseDeprecatedCopyOperation(Sema &S, CXXMethodDecl *CopyOp) {      assert(UserDeclaredOperation);    } -  if (UserDeclaredOperation) { +  if (UserDeclaredOperation && UserDeclaredOperation->isUserProvided()) {      S.Diag(UserDeclaredOperation->getLocation(), -         diag::warn_deprecated_copy_operation) -      << RD << /*copy assignment*/!isa<CXXConstructorDecl>(CopyOp) -      << /*destructor*/isa<CXXDestructorDecl>(UserDeclaredOperation); +           isa<CXXDestructorDecl>(UserDeclaredOperation) +               ? diag::warn_deprecated_copy_dtor_operation +               : diag::warn_deprecated_copy_operation) +        << RD << /*copy assignment*/ !isa<CXXConstructorDecl>(CopyOp);    }  } @@ -12460,8 +13800,9 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) {    // constructor rules.    QualType ArgType = Context.getTypeDeclType(ClassDecl); -  if (Context.getLangOpts().OpenCLCPlusPlus) -    ArgType = Context.getAddrSpaceQualType(ArgType, LangAS::opencl_generic); +  LangAS AS = getDefaultCXXMethodAddrSpace(); +  if (AS != LangAS::Default) +    ArgType = Context.getAddrSpaceQualType(ArgType, AS);    QualType RetType = Context.getLValueReferenceType(ArgType);    ArgType = Context.getRValueReferenceType(ArgType); @@ -12654,8 +13995,8 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,    // The parameter for the "other" object, which we are move from.    ParmVarDecl *Other = MoveAssignOperator->getParamDecl(0); -  QualType OtherRefType = Other->getType()-> -      getAs<RValueReferenceType>()->getPointeeType(); +  QualType OtherRefType = +      Other->getType()->castAs<RValueReferenceType>()->getPointeeType();    // Our location for everything implicitly-generated.    SourceLocation Loc = MoveAssignOperator->getEndLoc().isValid() @@ -12838,8 +14179,9 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(    if (Const)      ArgType = ArgType.withConst(); -  if (Context.getLangOpts().OpenCLCPlusPlus) -    ArgType = Context.getAddrSpaceQualType(ArgType, LangAS::opencl_generic); +  LangAS AS = getDefaultCXXMethodAddrSpace(); +  if (AS != LangAS::Default) +    ArgType = Context.getAddrSpaceQualType(ArgType, AS);    ArgType = Context.getLValueReferenceType(ArgType); @@ -12970,8 +14312,9 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(    QualType ClassType = Context.getTypeDeclType(ClassDecl);    QualType ArgType = ClassType; -  if (Context.getLangOpts().OpenCLCPlusPlus) -    ArgType = Context.getAddrSpaceQualType(ClassType, LangAS::opencl_generic); +  LangAS AS = getDefaultCXXMethodAddrSpace(); +  if (AS != LangAS::Default) +    ArgType = Context.getAddrSpaceQualType(ClassType, AS);    ArgType = Context.getRValueReferenceType(ArgType);    bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, @@ -13447,9 +14790,7 @@ Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,    unsigned NumArgs = ArgsPtr.size();    Expr **Args = ArgsPtr.data(); -  const FunctionProtoType *Proto -    = Constructor->getType()->getAs<FunctionProtoType>(); -  assert(Proto && "Constructor without a prototype?"); +  const auto *Proto = Constructor->getType()->castAs<FunctionProtoType>();    unsigned NumParams = Proto->getNumParams();    // If too few arguments are available, we'll fill in the rest with defaults. @@ -13512,7 +14853,7 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,                              unsigned DependentParamTypeDiag,                              unsigned InvalidParamTypeDiag) {    QualType ResultType = -      FnDecl->getType()->getAs<FunctionType>()->getReturnType(); +      FnDecl->getType()->castAs<FunctionType>()->getReturnType();    // Check that the result type is not dependent.    if (ResultType->isDependentType()) @@ -13741,7 +15082,7 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {    // Overloaded operators other than operator() cannot be variadic.    if (Op != OO_Call && -      FnDecl->getType()->getAs<FunctionProtoType>()->isVariadic()) { +      FnDecl->getType()->castAs<FunctionProtoType>()->isVariadic()) {      return Diag(FnDecl->getLocation(), diag::err_operator_overload_variadic)        << FnDecl->getDeclName();    } @@ -14010,10 +15351,6 @@ Decl *Sema::ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc,      Language = LinkageSpecDecl::lang_c;    else if (Lang == "C++")      Language = LinkageSpecDecl::lang_cxx; -  else if (Lang == "C++11") -    Language = LinkageSpecDecl::lang_cxx_11; -  else if (Lang == "C++14") -    Language = LinkageSpecDecl::lang_cxx_14;    else {      Diag(LangStr->getExprLoc(), diag::err_language_linkage_spec_unknown)        << LangStr->getSourceRange(); @@ -14291,8 +15628,16 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,        std::string InnerCondDescription;        std::tie(InnerCond, InnerCondDescription) =          findFailedBooleanCondition(Converted.get()); -      if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) -                    && !isa<IntegerLiteral>(InnerCond)) { +      if (InnerCond && isa<ConceptSpecializationExpr>(InnerCond)) { +        // Drill down into concept specialization expressions to see why they +        // weren't satisfied. +        Diag(StaticAssertLoc, diag::err_static_assert_failed) +          << !AssertMessage << Msg.str() << AssertExpr->getSourceRange(); +        ConstraintSatisfaction Satisfaction; +        if (!CheckConstraintSatisfaction(InnerCond, Satisfaction)) +          DiagnoseUnsatisfiedConstraint(Satisfaction); +      } else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) +                           && !isa<IntegerLiteral>(InnerCond)) {          Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed)            << InnerCondDescription << !AssertMessage            << Msg.str() << InnerCond->getSourceRange(); @@ -14974,6 +16319,16 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) {    if (Fn->isDeleted())      return; +  // C++11 [basic.start.main]p3: +  //   A program that defines main as deleted [...] is ill-formed. +  if (Fn->isMain()) +    Diag(DelLoc, diag::err_deleted_main); + +  // C++11 [dcl.fct.def.delete]p4: +  //  A deleted function is implicitly inline. +  Fn->setImplicitlyInline(); +  Fn->setDeletedAsWritten(); +    // See if we're deleting a function which is already known to override a    // non-deleted virtual function.    if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Fn)) { @@ -14990,67 +16345,93 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) {      // If this function was implicitly deleted because it was defaulted,      // explain why it was deleted.      if (IssuedDiagnostic && MD->isDefaulted()) -      ShouldDeleteSpecialMember(MD, getSpecialMember(MD), nullptr, -                                /*Diagnose*/true); +      DiagnoseDeletedDefaultedFunction(MD);    } - -  // C++11 [basic.start.main]p3: -  //   A program that defines main as deleted [...] is ill-formed. -  if (Fn->isMain()) -    Diag(DelLoc, diag::err_deleted_main); - -  // C++11 [dcl.fct.def.delete]p4: -  //  A deleted function is implicitly inline. -  Fn->setImplicitlyInline(); -  Fn->setDeletedAsWritten();  }  void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { -  CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Dcl); +  if (!Dcl || Dcl->isInvalidDecl()) +    return; -  if (MD) { -    if (MD->getParent()->isDependentType()) { -      MD->setDefaulted(); -      MD->setExplicitlyDefaulted(); -      return; +  auto *FD = dyn_cast<FunctionDecl>(Dcl); +  if (!FD) { +    if (auto *FTD = dyn_cast<FunctionTemplateDecl>(Dcl)) { +      if (getDefaultedFunctionKind(FTD->getTemplatedDecl()).isComparison()) { +        Diag(DefaultLoc, diag::err_defaulted_comparison_template); +        return; +      }      } -    CXXSpecialMember Member = getSpecialMember(MD); -    if (Member == CXXInvalid) { -      if (!MD->isInvalidDecl()) -        Diag(DefaultLoc, diag::err_default_special_members); -      return; -    } +    Diag(DefaultLoc, diag::err_default_special_members) +        << getLangOpts().CPlusPlus2a; +    return; +  } -    MD->setDefaulted(); -    MD->setExplicitlyDefaulted(); +  // Reject if this can't possibly be a defaultable function. +  DefaultedFunctionKind DefKind = getDefaultedFunctionKind(FD); +  if (!DefKind && +      // A dependent function that doesn't locally look defaultable can +      // still instantiate to a defaultable function if it's a constructor +      // or assignment operator. +      (!FD->isDependentContext() || +       (!isa<CXXConstructorDecl>(FD) && +        FD->getDeclName().getCXXOverloadedOperator() != OO_Equal))) { +    Diag(DefaultLoc, diag::err_default_special_members) +        << getLangOpts().CPlusPlus2a; +    return; +  } -    // Unset that we will have a body for this function. We might not, -    // if it turns out to be trivial, and we don't need this marking now -    // that we've marked it as defaulted. -    MD->setWillHaveBody(false); +  if (DefKind.isComparison() && +      !isa<CXXRecordDecl>(FD->getLexicalDeclContext())) { +    Diag(FD->getLocation(), diag::err_defaulted_comparison_out_of_class) +        << (int)DefKind.asComparison(); +    return; +  } -    // If this definition appears within the record, do the checking when -    // the record is complete. -    const FunctionDecl *Primary = MD; -    if (const FunctionDecl *Pattern = MD->getTemplateInstantiationPattern()) -      // Ask the template instantiation pattern that actually had the -      // '= default' on it. -      Primary = Pattern; +  // Issue compatibility warning. We already warned if the operator is +  // 'operator<=>' when parsing the '<=>' token. +  if (DefKind.isComparison() && +      DefKind.asComparison() != DefaultedComparisonKind::ThreeWay) { +    Diag(DefaultLoc, getLangOpts().CPlusPlus2a +                         ? diag::warn_cxx17_compat_defaulted_comparison +                         : diag::ext_defaulted_comparison); +  } -    // If the method was defaulted on its first declaration, we will have -    // already performed the checking in CheckCompletedCXXClass. Such a -    // declaration doesn't trigger an implicit definition. -    if (Primary->getCanonicalDecl()->isDefaulted()) -      return; +  FD->setDefaulted(); +  FD->setExplicitlyDefaulted(); -    CheckExplicitlyDefaultedSpecialMember(MD); +  // Defer checking functions that are defaulted in a dependent context. +  if (FD->isDependentContext()) +    return; -    if (!MD->isInvalidDecl()) -      DefineImplicitSpecialMember(*this, MD, DefaultLoc); -  } else { -    Diag(DefaultLoc, diag::err_default_special_members); -  } +  // Unset that we will have a body for this function. We might not, +  // if it turns out to be trivial, and we don't need this marking now +  // that we've marked it as defaulted. +  FD->setWillHaveBody(false); + +  // If this definition appears within the record, do the checking when +  // the record is complete. This is always the case for a defaulted +  // comparison. +  if (DefKind.isComparison()) +    return; +  auto *MD = cast<CXXMethodDecl>(FD); + +  const FunctionDecl *Primary = FD; +  if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern()) +    // Ask the template instantiation pattern that actually had the +    // '= default' on it. +    Primary = Pattern; + +  // If the method was defaulted on its first declaration, we will have +  // already performed the checking in CheckCompletedCXXClass. Such a +  // declaration doesn't trigger an implicit definition. +  if (Primary->getCanonicalDecl()->isDefaulted()) +    return; + +  if (CheckExplicitlyDefaultedSpecialMember(MD, DefKind.asSpecialMember())) +    MD->setInvalidDecl(); +  else +    DefineImplicitSpecialMember(*this, MD, DefaultLoc);  }  static void SearchForReturnInStmt(Sema &Self, Stmt *S) { @@ -15074,8 +16455,8 @@ void Sema::DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock) {  bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New,                                               const CXXMethodDecl *Old) { -  const auto *NewFT = New->getType()->getAs<FunctionProtoType>(); -  const auto *OldFT = Old->getType()->getAs<FunctionProtoType>(); +  const auto *NewFT = New->getType()->castAs<FunctionProtoType>(); +  const auto *OldFT = Old->getType()->castAs<FunctionProtoType>();    if (OldFT->hasExtParameterInfos()) {      for (unsigned I = 0, E = OldFT->getNumParams(); I != E; ++I) @@ -15122,8 +16503,8 @@ bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New,  bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,                                               const CXXMethodDecl *Old) { -  QualType NewTy = New->getType()->getAs<FunctionType>()->getReturnType(); -  QualType OldTy = Old->getType()->getAs<FunctionType>()->getReturnType(); +  QualType NewTy = New->getType()->castAs<FunctionType>()->getReturnType(); +  QualType OldTy = Old->getType()->castAs<FunctionType>()->getReturnType();    if (Context.hasSameType(NewTy, OldTy) ||        NewTy->isDependentType() || OldTy->isDependentType()) @@ -15729,6 +17110,11 @@ bool Sema::checkThisInStaticMemberFunctionType(CXXMethodDecl *Method) {    if (checkThisInStaticMemberFunctionExceptionSpec(Method))      return true; +  // Check the trailing requires clause +  if (Expr *E = Method->getTrailingRequiresClause()) +    if (!Finder.TraverseStmt(E)) +      return true; +    return checkThisInStaticMemberFunctionAttributes(Method);  }  | 
