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); } |