diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaDeclCXX.cpp')
| -rw-r--r-- | contrib/llvm-project/clang/lib/Sema/SemaDeclCXX.cpp | 2367 |
1 files changed, 2044 insertions, 323 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaDeclCXX.cpp b/contrib/llvm-project/clang/lib/Sema/SemaDeclCXX.cpp index 2f9e4f961f4d..831e55046e80 100644 --- a/contrib/llvm-project/clang/lib/Sema/SemaDeclCXX.cpp +++ b/contrib/llvm-project/clang/lib/Sema/SemaDeclCXX.cpp @@ -24,6 +24,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeOrdering.h" +#include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/LiteralSupport.h" @@ -216,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: @@ -241,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; } @@ -774,6 +775,13 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, return nullptr; } + // C++2a [dcl.struct.bind]p1: + // A cv that includes volatile is deprecated + if ((DS.getTypeQualifiers() & DeclSpec::TQ_volatile) && + getLangOpts().CPlusPlus2a) + Diag(DS.getVolatileSpecLoc(), + diag::warn_deprecated_volatile_structured_binding); + TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); QualType R = TInfo->getType(); @@ -1493,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()) @@ -1567,28 +1575,90 @@ void Sema::CheckCXXDefaultArguments(FunctionDecl *FD) { } } -// 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 that the given type is a literal type. Issue a diagnostic if not, +/// if Kind is Diagnose. +/// \return \c true if a problem has been found (and optionally diagnosed). +template <typename... Ts> +static bool CheckLiteralType(Sema &SemaRef, Sema::CheckConstexprKind Kind, + SourceLocation Loc, QualType T, unsigned DiagID, + Ts &&...DiagArgs) { + if (T->isDependentType()) + return false; + + switch (Kind) { + case Sema::CheckConstexprKind::Diagnose: + return SemaRef.RequireLiteralType(Loc, T, DiagID, + std::forward<Ts>(DiagArgs)...); + + case Sema::CheckConstexprKind::CheckValid: + return !T->isLiteralType(SemaRef.Context); + } + + llvm_unreachable("unknown CheckConstexprKind"); +} + +/// Determine whether a destructor cannot be constexpr due to +static bool CheckConstexprDestructorSubobjects(Sema &SemaRef, + const CXXDestructorDecl *DD, + Sema::CheckConstexprKind Kind) { + auto Check = [&](SourceLocation Loc, QualType T, const FieldDecl *FD) { + const CXXRecordDecl *RD = + T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + if (!RD || RD->hasConstexprDestructor()) + return true; + + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag(DD->getLocation(), diag::err_constexpr_dtor_subobject) + << DD->getConstexprKind() << !FD + << (FD ? FD->getDeclName() : DeclarationName()) << T; + SemaRef.Diag(Loc, diag::note_constexpr_dtor_subobject) + << !FD << (FD ? FD->getDeclName() : DeclarationName()) << T; + } + return false; + }; + + const CXXRecordDecl *RD = DD->getParent(); + for (const CXXBaseSpecifier &B : RD->bases()) + if (!Check(B.getBaseTypeLoc(), B.getType(), nullptr)) + return false; + for (const FieldDecl *FD : RD->fields()) + if (!Check(FD->getLocation(), FD->getType(), FD)) + return false; + return true; +} + +/// 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) { + 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) { const ParmVarDecl *PD = FD->getParamDecl(ArgIndex); SourceLocation ParamLoc = PD->getLocation(); - if (!(*i)->isDependentType() && - SemaRef.RequireLiteralType( - ParamLoc, *i, diag::err_constexpr_non_literal_param, ArgIndex + 1, - PD->getSourceRange(), isa<CXXConstructorDecl>(FD), - FD->isConsteval())) + if (CheckLiteralType(SemaRef, Kind, ParamLoc, *i, + diag::err_constexpr_non_literal_param, ArgIndex + 1, + PD->getSourceRange(), isa<CXXConstructorDecl>(FD), + FD->isConsteval())) return false; } 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! @@ -1603,13 +1673,18 @@ static unsigned getRecordDiagFromTagKind(TagTypeKind Tag) { } } -// CheckConstexprFunctionDecl - Check whether a function declaration satisfies -// the requirements of a constexpr function definition or a constexpr -// constructor definition. If so, return true. If not, produce appropriate -// diagnostics and return false. +static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl, + Stmt *Body, + Sema::CheckConstexprKind Kind); + +// Check whether a function declaration satisfies the requirements of a +// constexpr function definition or a constexpr constructor definition. If so, +// return true. If not, produce appropriate diagnostics (unless asked not to by +// Kind) and return false. // // This implements C++11 [dcl.constexpr]p3,4, as amended by DR1360. -bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { +bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD, + CheckConstexprKind Kind) { const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD); if (MD && MD->isInstance()) { // C++11 [dcl.constexpr]p4: @@ -1617,10 +1692,13 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { // constraints: // - the class shall not have any virtual base classes; // - // FIXME: This only applies to constructors, not arbitrary member - // functions. + // FIXME: This only applies to constructors and destructors, not arbitrary + // member functions. const CXXRecordDecl *RD = MD->getParent(); if (RD->getNumVBases()) { + if (Kind == CheckConstexprKind::CheckValid) + return false; + Diag(NewFD->getLocation(), diag::err_constexpr_virtual_base) << isa<CXXConstructorDecl>(NewFD) << getRecordDiagFromTagKind(RD->getTagKind()) << RD->getNumVBases(); @@ -1639,8 +1717,12 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(NewFD); if (Method && Method->isVirtual()) { if (getLangOpts().CPlusPlus2a) { - Diag(Method->getLocation(), diag::warn_cxx17_compat_constexpr_virtual); + if (Kind == CheckConstexprKind::Diagnose) + Diag(Method->getLocation(), diag::warn_cxx17_compat_constexpr_virtual); } else { + if (Kind == CheckConstexprKind::CheckValid) + return false; + Method = Method->getCanonicalDecl(); Diag(Method->getLocation(), diag::err_constexpr_virtual); @@ -1657,19 +1739,30 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { } // - its return type shall be a literal type; - QualType RT = NewFD->getReturnType(); - if (!RT->isDependentType() && - RequireLiteralType(NewFD->getLocation(), RT, - diag::err_constexpr_non_literal_return, - NewFD->isConsteval())) + if (!CheckConstexprReturnType(*this, NewFD, Kind)) return false; } + if (auto *Dtor = dyn_cast<CXXDestructorDecl>(NewFD)) { + // A destructor can be constexpr only if the defaulted destructor could be; + // we don't need to check the members and bases if we already know they all + // have constexpr destructors. + if (!Dtor->getParent()->defaultedDestructorIsConstexpr()) { + if (Kind == CheckConstexprKind::CheckValid) + return false; + if (!CheckConstexprDestructorSubobjects(*this, Dtor, Kind)) + return false; + } + } + // - each of its parameter types shall be a literal type; - if (!CheckConstexprParameterTypes(*this, NewFD)) + if (!CheckConstexprParameterTypes(*this, NewFD, Kind)) return false; - return true; + Stmt *Body = NewFD->getBody(); + assert(Body && + "CheckConstexprFunctionDefinition called on function with no body"); + return CheckConstexprFunctionBody(*this, NewFD, Body, Kind); } /// Check the given declaration statement is legal within a constexpr function @@ -1678,7 +1771,8 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { /// \return true if the body is OK (maybe only as an extension), false if we /// have diagnosed a problem. static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, - DeclStmt *DS, SourceLocation &Cxx1yLoc) { + DeclStmt *DS, SourceLocation &Cxx1yLoc, + Sema::CheckConstexprKind Kind) { // C++11 [dcl.constexpr]p3 and p4: // The definition of a constexpr function(p3) or constructor(p4) [...] shall // contain only @@ -1702,10 +1796,12 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, const auto *TN = cast<TypedefNameDecl>(DclIt); if (TN->getUnderlyingType()->isVariablyModifiedType()) { // Don't allow variably-modified types in constexpr functions. - TypeLoc TL = TN->getTypeSourceInfo()->getTypeLoc(); - SemaRef.Diag(TL.getBeginLoc(), diag::err_constexpr_vla) - << TL.getSourceRange() << TL.getType() - << isa<CXXConstructorDecl>(Dcl); + if (Kind == Sema::CheckConstexprKind::Diagnose) { + TypeLoc TL = TN->getTypeSourceInfo()->getTypeLoc(); + SemaRef.Diag(TL.getBeginLoc(), diag::err_constexpr_vla) + << TL.getSourceRange() << TL.getType() + << isa<CXXConstructorDecl>(Dcl); + } return false; } continue; @@ -1714,12 +1810,17 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, case Decl::Enum: case Decl::CXXRecord: // C++1y allows types to be defined, not just declared. - if (cast<TagDecl>(DclIt)->isThisDeclarationADefinition()) - SemaRef.Diag(DS->getBeginLoc(), - SemaRef.getLangOpts().CPlusPlus14 - ? diag::warn_cxx11_compat_constexpr_type_definition - : diag::ext_constexpr_type_definition) - << isa<CXXConstructorDecl>(Dcl); + if (cast<TagDecl>(DclIt)->isThisDeclarationADefinition()) { + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag(DS->getBeginLoc(), + SemaRef.getLangOpts().CPlusPlus14 + ? diag::warn_cxx11_compat_constexpr_type_definition + : diag::ext_constexpr_type_definition) + << isa<CXXConstructorDecl>(Dcl); + } else if (!SemaRef.getLangOpts().CPlusPlus14) { + return false; + } + } continue; case Decl::EnumConstant: @@ -1733,35 +1834,47 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, case Decl::Decomposition: { // C++1y [dcl.constexpr]p3 allows anything except: // a definition of a variable of non-literal type or of static or - // thread storage duration or for which no initialization is performed. + // thread storage duration or [before C++2a] for which no + // initialization is performed. const auto *VD = cast<VarDecl>(DclIt); if (VD->isThisDeclarationADefinition()) { if (VD->isStaticLocal()) { - SemaRef.Diag(VD->getLocation(), - diag::err_constexpr_local_var_static) - << isa<CXXConstructorDecl>(Dcl) - << (VD->getTLSKind() == VarDecl::TLS_Dynamic); + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag(VD->getLocation(), + diag::err_constexpr_local_var_static) + << isa<CXXConstructorDecl>(Dcl) + << (VD->getTLSKind() == VarDecl::TLS_Dynamic); + } return false; } - if (!VD->getType()->isDependentType() && - SemaRef.RequireLiteralType( - VD->getLocation(), VD->getType(), - diag::err_constexpr_local_var_non_literal_type, - isa<CXXConstructorDecl>(Dcl))) + if (CheckLiteralType(SemaRef, Kind, VD->getLocation(), VD->getType(), + diag::err_constexpr_local_var_non_literal_type, + isa<CXXConstructorDecl>(Dcl))) return false; if (!VD->getType()->isDependentType() && !VD->hasInit() && !VD->isCXXForRangeDecl()) { - SemaRef.Diag(VD->getLocation(), - diag::err_constexpr_local_var_no_init) - << isa<CXXConstructorDecl>(Dcl); - return false; + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag( + VD->getLocation(), + SemaRef.getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_constexpr_local_var_no_init + : diag::ext_constexpr_local_var_no_init) + << isa<CXXConstructorDecl>(Dcl); + } else if (!SemaRef.getLangOpts().CPlusPlus2a) { + return false; + } + continue; } } - SemaRef.Diag(VD->getLocation(), - SemaRef.getLangOpts().CPlusPlus14 - ? diag::warn_cxx11_compat_constexpr_local_var - : diag::ext_constexpr_local_var) - << isa<CXXConstructorDecl>(Dcl); + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag(VD->getLocation(), + SemaRef.getLangOpts().CPlusPlus14 + ? diag::warn_cxx11_compat_constexpr_local_var + : diag::ext_constexpr_local_var) + << isa<CXXConstructorDecl>(Dcl); + } else if (!SemaRef.getLangOpts().CPlusPlus14) { + return false; + } continue; } @@ -1774,8 +1887,10 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, continue; default: - SemaRef.Diag(DS->getBeginLoc(), diag::err_constexpr_body_invalid_stmt) - << isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval(); + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag(DS->getBeginLoc(), diag::err_constexpr_body_invalid_stmt) + << isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval(); + } return false; } } @@ -1790,17 +1905,28 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, /// struct or union nested within the class being checked. /// \param Inits All declarations, including anonymous struct/union members and /// indirect members, for which any initialization was provided. -/// \param Diagnosed Set to true if an error is produced. -static void CheckConstexprCtorInitializer(Sema &SemaRef, +/// \param Diagnosed Whether we've emitted the error message yet. Used to attach +/// multiple notes for different members to the same error. +/// \param Kind Whether we're diagnosing a constructor as written or determining +/// whether the formal requirements are satisfied. +/// \return \c false if we're checking for validity and the constructor does +/// not satisfy the requirements on a constexpr constructor. +static bool CheckConstexprCtorInitializer(Sema &SemaRef, const FunctionDecl *Dcl, FieldDecl *Field, llvm::SmallSet<Decl*, 16> &Inits, - bool &Diagnosed) { + bool &Diagnosed, + Sema::CheckConstexprKind Kind) { + // In C++20 onwards, there's nothing to check for validity. + if (Kind == Sema::CheckConstexprKind::CheckValid && + SemaRef.getLangOpts().CPlusPlus2a) + return true; + if (Field->isInvalidDecl()) - return; + return true; if (Field->isUnnamedBitfield()) - return; + return true; // Anonymous unions with no variant members and empty anonymous structs do not // need to be explicitly initialized. FIXME: Anonymous structs that contain no @@ -1809,22 +1935,33 @@ static void CheckConstexprCtorInitializer(Sema &SemaRef, (Field->getType()->isUnionType() ? !Field->getType()->getAsCXXRecordDecl()->hasVariantMembers() : Field->getType()->getAsCXXRecordDecl()->isEmpty())) - return; + return true; if (!Inits.count(Field)) { - if (!Diagnosed) { - SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init); - Diagnosed = true; + if (Kind == Sema::CheckConstexprKind::Diagnose) { + if (!Diagnosed) { + SemaRef.Diag(Dcl->getLocation(), + SemaRef.getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_constexpr_ctor_missing_init + : diag::ext_constexpr_ctor_missing_init); + Diagnosed = true; + } + SemaRef.Diag(Field->getLocation(), + diag::note_constexpr_ctor_missing_init); + } else if (!SemaRef.getLangOpts().CPlusPlus2a) { + return false; } - SemaRef.Diag(Field->getLocation(), diag::note_constexpr_ctor_missing_init); } else if (Field->isAnonymousStructOrUnion()) { const RecordDecl *RD = Field->getType()->castAs<RecordType>()->getDecl(); for (auto *I : RD->fields()) // If an anonymous union contains an anonymous struct of which any member // is initialized, all members must be initialized. if (!RD->isUnion() || Inits.count(I)) - CheckConstexprCtorInitializer(SemaRef, Dcl, I, Inits, Diagnosed); + if (!CheckConstexprCtorInitializer(SemaRef, Dcl, I, Inits, Diagnosed, + Kind)) + return false; } + return true; } /// Check the provided statement is allowed in a constexpr function @@ -1832,7 +1969,8 @@ static void CheckConstexprCtorInitializer(Sema &SemaRef, static bool CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, SmallVectorImpl<SourceLocation> &ReturnStmts, - SourceLocation &Cxx1yLoc, SourceLocation &Cxx2aLoc) { + SourceLocation &Cxx1yLoc, SourceLocation &Cxx2aLoc, + Sema::CheckConstexprKind Kind) { // - its function-body shall be [...] a compound-statement that contains only switch (S->getStmtClass()) { case Stmt::NullStmtClass: @@ -1845,7 +1983,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, // - using-directives, // - typedef declarations and alias-declarations that do not define // classes or enumerations, - if (!CheckConstexprDeclStmt(SemaRef, Dcl, cast<DeclStmt>(S), Cxx1yLoc)) + if (!CheckConstexprDeclStmt(SemaRef, Dcl, cast<DeclStmt>(S), Cxx1yLoc, Kind)) return false; return true; @@ -1869,7 +2007,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, CompoundStmt *CompStmt = cast<CompoundStmt>(S); for (auto *BodyIt : CompStmt->body()) { if (!CheckConstexprFunctionStmt(SemaRef, Dcl, BodyIt, ReturnStmts, - Cxx1yLoc, Cxx2aLoc)) + Cxx1yLoc, Cxx2aLoc, Kind)) return false; } return true; @@ -1887,11 +2025,11 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, IfStmt *If = cast<IfStmt>(S); if (!CheckConstexprFunctionStmt(SemaRef, Dcl, If->getThen(), ReturnStmts, - Cxx1yLoc, Cxx2aLoc)) + Cxx1yLoc, Cxx2aLoc, Kind)) return false; if (If->getElse() && !CheckConstexprFunctionStmt(SemaRef, Dcl, If->getElse(), ReturnStmts, - Cxx1yLoc, Cxx2aLoc)) + Cxx1yLoc, Cxx2aLoc, Kind)) return false; return true; } @@ -1910,7 +2048,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, for (Stmt *SubStmt : S->children()) if (SubStmt && !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts, - Cxx1yLoc, Cxx2aLoc)) + Cxx1yLoc, Cxx2aLoc, Kind)) return false; return true; @@ -1925,17 +2063,20 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, for (Stmt *SubStmt : S->children()) if (SubStmt && !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts, - Cxx1yLoc, Cxx2aLoc)) + Cxx1yLoc, Cxx2aLoc, Kind)) return false; return true; + case Stmt::GCCAsmStmtClass: + case Stmt::MSAsmStmtClass: + // C++2a allows inline assembly statements. case Stmt::CXXTryStmtClass: if (Cxx2aLoc.isInvalid()) Cxx2aLoc = S->getBeginLoc(); for (Stmt *SubStmt : S->children()) { if (SubStmt && !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts, - Cxx1yLoc, Cxx2aLoc)) + Cxx1yLoc, Cxx2aLoc, Kind)) return false; } return true; @@ -1945,7 +2086,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, // try block check). if (!CheckConstexprFunctionStmt(SemaRef, Dcl, cast<CXXCatchStmt>(S)->getHandlerBlock(), - ReturnStmts, Cxx1yLoc, Cxx2aLoc)) + ReturnStmts, Cxx1yLoc, Cxx2aLoc, Kind)) return false; return true; @@ -1959,16 +2100,21 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, return true; } - SemaRef.Diag(S->getBeginLoc(), diag::err_constexpr_body_invalid_stmt) - << isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval(); + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag(S->getBeginLoc(), diag::err_constexpr_body_invalid_stmt) + << isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval(); + } return false; } /// Check the body for the given constexpr function declaration only contains /// the permitted types of statement. C++11 [dcl.constexpr]p3,p4. /// -/// \return true if the body is OK, false if we have diagnosed a problem. -bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { +/// \return true if the body is OK, false if we have found or diagnosed a +/// problem. +static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl, + Stmt *Body, + Sema::CheckConstexprKind Kind) { SmallVector<SourceLocation, 4> ReturnStmts; if (isa<CXXTryStmt>(Body)) { @@ -1984,11 +2130,20 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { // // This restriction is lifted in C++2a, as long as inner statements also // apply the general constexpr rules. - Diag(Body->getBeginLoc(), - !getLangOpts().CPlusPlus2a - ? diag::ext_constexpr_function_try_block_cxx2a - : diag::warn_cxx17_compat_constexpr_function_try_block) - << isa<CXXConstructorDecl>(Dcl); + switch (Kind) { + case Sema::CheckConstexprKind::CheckValid: + if (!SemaRef.getLangOpts().CPlusPlus2a) + return false; + break; + + case Sema::CheckConstexprKind::Diagnose: + SemaRef.Diag(Body->getBeginLoc(), + !SemaRef.getLangOpts().CPlusPlus2a + ? diag::ext_constexpr_function_try_block_cxx2a + : diag::warn_cxx17_compat_constexpr_function_try_block) + << isa<CXXConstructorDecl>(Dcl); + break; + } } // - its function-body shall be [...] a compound-statement that contains only @@ -1999,23 +2154,30 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { SourceLocation Cxx1yLoc, Cxx2aLoc; for (Stmt *SubStmt : Body->children()) { if (SubStmt && - !CheckConstexprFunctionStmt(*this, Dcl, SubStmt, ReturnStmts, - Cxx1yLoc, Cxx2aLoc)) + !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts, + Cxx1yLoc, Cxx2aLoc, Kind)) return false; } - if (Cxx2aLoc.isValid()) - Diag(Cxx2aLoc, - getLangOpts().CPlusPlus2a + if (Kind == Sema::CheckConstexprKind::CheckValid) { + // If this is only valid as an extension, report that we don't satisfy the + // constraints of the current language. + if ((Cxx2aLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus2a) || + (Cxx1yLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus17)) + return false; + } else if (Cxx2aLoc.isValid()) { + SemaRef.Diag(Cxx2aLoc, + SemaRef.getLangOpts().CPlusPlus2a ? diag::warn_cxx17_compat_constexpr_body_invalid_stmt : diag::ext_constexpr_body_invalid_stmt_cxx2a) << isa<CXXConstructorDecl>(Dcl); - if (Cxx1yLoc.isValid()) - Diag(Cxx1yLoc, - getLangOpts().CPlusPlus14 + } else if (Cxx1yLoc.isValid()) { + SemaRef.Diag(Cxx1yLoc, + SemaRef.getLangOpts().CPlusPlus14 ? diag::warn_cxx11_compat_constexpr_body_invalid_stmt : diag::ext_constexpr_body_invalid_stmt) << isa<CXXConstructorDecl>(Dcl); + } if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Dcl)) { @@ -2029,8 +2191,15 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { if (RD->isUnion()) { if (Constructor->getNumCtorInitializers() == 0 && RD->hasVariantMembers()) { - Diag(Dcl->getLocation(), diag::err_constexpr_union_ctor_no_init); - return false; + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag( + Dcl->getLocation(), + SemaRef.getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_constexpr_union_ctor_no_init + : diag::ext_constexpr_union_ctor_no_init); + } else if (!SemaRef.getLangOpts().CPlusPlus2a) { + return false; + } } } else if (!Constructor->isDependentContext() && !Constructor->isDelegatingConstructor()) { @@ -2066,9 +2235,9 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { bool Diagnosed = false; for (auto *I : RD->fields()) - CheckConstexprCtorInitializer(*this, Dcl, I, Inits, Diagnosed); - if (Diagnosed) - return false; + if (!CheckConstexprCtorInitializer(SemaRef, Dcl, I, Inits, Diagnosed, + Kind)) + return false; } } } else { @@ -2077,22 +2246,45 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { // statement. We still do, unless the return type might be void, because // otherwise if there's no return statement, the function cannot // be used in a core constant expression. - bool OK = getLangOpts().CPlusPlus14 && + bool OK = SemaRef.getLangOpts().CPlusPlus14 && (Dcl->getReturnType()->isVoidType() || Dcl->getReturnType()->isDependentType()); - Diag(Dcl->getLocation(), - OK ? diag::warn_cxx11_compat_constexpr_body_no_return - : diag::err_constexpr_body_no_return) - << Dcl->isConsteval(); - if (!OK) - return false; + switch (Kind) { + case Sema::CheckConstexprKind::Diagnose: + SemaRef.Diag(Dcl->getLocation(), + OK ? diag::warn_cxx11_compat_constexpr_body_no_return + : diag::err_constexpr_body_no_return) + << Dcl->isConsteval(); + if (!OK) + return false; + break; + + case Sema::CheckConstexprKind::CheckValid: + // The formal requirements don't include this rule in C++14, even + // though the "must be able to produce a constant expression" rules + // still imply it in some cases. + if (!SemaRef.getLangOpts().CPlusPlus14) + return false; + break; + } } else if (ReturnStmts.size() > 1) { - Diag(ReturnStmts.back(), - getLangOpts().CPlusPlus14 - ? diag::warn_cxx11_compat_constexpr_body_multiple_return - : diag::ext_constexpr_body_multiple_return); - for (unsigned I = 0; I < ReturnStmts.size() - 1; ++I) - Diag(ReturnStmts[I], diag::note_constexpr_body_previous_return); + switch (Kind) { + case Sema::CheckConstexprKind::Diagnose: + SemaRef.Diag( + ReturnStmts.back(), + SemaRef.getLangOpts().CPlusPlus14 + ? diag::warn_cxx11_compat_constexpr_body_multiple_return + : diag::ext_constexpr_body_multiple_return); + for (unsigned I = 0; I < ReturnStmts.size() - 1; ++I) + SemaRef.Diag(ReturnStmts[I], + diag::note_constexpr_body_previous_return); + break; + + case Sema::CheckConstexprKind::CheckValid: + if (!SemaRef.getLangOpts().CPlusPlus14) + return false; + break; + } } } @@ -2106,12 +2298,17 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { // C++11 [dcl.constexpr]p4: // - every constructor involved in initializing non-static data members and // base class sub-objects shall be a constexpr constructor. + // + // Note that this rule is distinct from the "requirements for a constexpr + // function", so is not checked in CheckValid mode. SmallVector<PartialDiagnosticAt, 8> Diags; - if (!Expr::isPotentialConstantExpr(Dcl, Diags)) { - Diag(Dcl->getLocation(), diag::ext_constexpr_function_never_constant_expr) - << isa<CXXConstructorDecl>(Dcl); + if (Kind == Sema::CheckConstexprKind::Diagnose && + !Expr::isPotentialConstantExpr(Dcl, Diags)) { + SemaRef.Diag(Dcl->getLocation(), + diag::ext_constexpr_function_never_constant_expr) + << isa<CXXConstructorDecl>(Dcl); for (size_t I = 0, N = Diags.size(); I != N; ++I) - Diag(Diags[I].first, Diags[I].second); + SemaRef.Diag(Diags[I].first, Diags[I].second); // Don't return false here: we allow this for compatibility in // system headers. } @@ -2296,7 +2493,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, } // If the base class is polymorphic or isn't empty, the new one is/isn't, too. - RecordDecl *BaseDecl = BaseType->getAs<RecordType>()->getDecl(); + RecordDecl *BaseDecl = BaseType->castAs<RecordType>()->getDecl(); assert(BaseDecl && "Record type has no declaration"); BaseDecl = BaseDecl->getDefinition(); assert(BaseDecl && "Base type is not incomplete, but has no definition"); @@ -2379,7 +2576,7 @@ Sema::ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange, Diag(AL.getLoc(), AL.getKind() == ParsedAttr::UnknownAttribute ? (unsigned)diag::warn_unknown_attribute_ignored : (unsigned)diag::err_base_specifier_attribute) - << AL.getName(); + << AL; } TypeSourceInfo *TInfo = nullptr; @@ -3223,10 +3420,12 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, } if (VS.isOverrideSpecified()) - Member->addAttr(new (Context) OverrideAttr(VS.getOverrideLoc(), Context, 0)); + Member->addAttr(OverrideAttr::Create(Context, VS.getOverrideLoc(), + AttributeCommonInfo::AS_Keyword)); if (VS.isFinalSpecified()) - Member->addAttr(new (Context) FinalAttr(VS.getFinalLoc(), Context, - VS.isFinalSpelledSealed())); + Member->addAttr(FinalAttr::Create( + Context, VS.getFinalLoc(), AttributeCommonInfo::AS_Keyword, + static_cast<FinalAttr::Spelling>(VS.isFinalSpelledSealed()))); if (VS.getLastLocation().isValid()) { // Update the end location of a method that has a virt-specifiers. @@ -3608,7 +3807,7 @@ namespace { const CXXRecordDecl *RD = Constructor->getParent(); - if (RD->getDescribedClassTemplate()) + if (RD->isDependentContext()) return; // Holds fields that are uninitialized. @@ -3669,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. @@ -3824,7 +4043,7 @@ public: } std::unique_ptr<CorrectionCandidateCallback> clone() override { - return llvm::make_unique<MemInitializerValidatorCCC>(*this); + return std::make_unique<MemInitializerValidatorCCC>(*this); } private: @@ -5799,14 +6018,10 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { TSK != TSK_ExplicitInstantiationDefinition) { if (ClassExported) { NewAttr = ::new (getASTContext()) - DLLExportStaticLocalAttr(ClassAttr->getRange(), - getASTContext(), - ClassAttr->getSpellingListIndex()); + DLLExportStaticLocalAttr(getASTContext(), *ClassAttr); } else { NewAttr = ::new (getASTContext()) - DLLImportStaticLocalAttr(ClassAttr->getRange(), - getASTContext(), - ClassAttr->getSpellingListIndex()); + DLLImportStaticLocalAttr(getASTContext(), *ClassAttr); } } else { NewAttr = cast<InheritableAttr>(ClassAttr->clone(getASTContext())); @@ -5896,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)) { @@ -6040,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; @@ -6115,6 +6395,22 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { } } + // Warn if the class has a final destructor but is not itself marked final. + if (!Record->hasAttr<FinalAttr>()) { + if (const CXXDestructorDecl *dtor = Record->getDestructor()) { + if (const FinalAttr *FA = dtor->getAttr<FinalAttr>()) { + Diag(FA->getLocation(), diag::warn_final_dtor_non_final_class) + << FA->isSpelledAsSealed() + << FixItHint::CreateInsertion( + getLocForEndOfToken(Record->getLocation()), + (FA->isSpelledAsSealed() ? " sealed" : " final")); + Diag(Record->getLocation(), + diag::note_final_dtor_non_final_class_silence) + << Context.getRecordType(Record) << FA->isSpelledAsSealed(); + } + } + } + // See if trivial_abi has to be dropped. if (Record->hasAttr<TrivialABIAttr>()) checkIllFormedTrivialABIStruct(*Record); @@ -6126,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! @@ -6167,42 +6483,60 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { DelayedDllExportMemberFunctions.push_back(M); } } + + // Define defaulted constexpr virtual functions that override a base class + // function right away. + // FIXME: We can defer doing this until the vtable is marked as used. + if (M->isDefaulted() && M->isConstexpr() && M->size_overridden_methods()) + 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. @@ -6380,6 +6714,8 @@ specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, if (CSM == Sema::CXXDefaultConstructor) return ClassDecl->hasConstexprDefaultConstructor(); + if (CSM == Sema::CXXDestructor) + return ClassDecl->hasConstexprDestructor(); Sema::SpecialMemberOverloadResult SMOR = lookupCallFromSpecialMember(S, ClassDecl, CSM, Quals, ConstRHS); @@ -6428,6 +6764,8 @@ static bool defaultedSpecialMemberIsConstexpr( break; case Sema::CXXDestructor: + return ClassDecl->defaultedDestructorIsConstexpr(); + case Sema::CXXInvalid: return false; } @@ -6496,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( @@ -6531,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(); @@ -6567,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 @@ -6680,13 +7060,14 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { // Do not apply this rule to members of class templates, since core issue 1358 // makes such functions always instantiate to constexpr functions. For // functions which cannot be constexpr (for non-constructors in C++11 and for - // destructors in C++1y), this is checked elsewhere. + // destructors in C++14 and C++17), this is checked elsewhere. // // FIXME: This should not apply if the member is deleted. bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM, HasConstParam); - if ((getLangOpts().CPlusPlus14 ? !isa<CXXDestructorDecl>(MD) - : isa<CXXConstructorDecl>(MD)) && + if ((getLangOpts().CPlusPlus2a || + (getLangOpts().CPlusPlus14 ? !isa<CXXDestructorDecl>(MD) + : isa<CXXConstructorDecl>(MD))) && MD->isConstexpr() && !Constexpr && MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { Diag(MD->getBeginLoc(), MD->isConsteval() @@ -6745,8 +7126,1164 @@ 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); + + if (Args[0]->getType()->isOverloadableType()) + S.LookupOverloadedBinOp(CandidateSet, OO, Fns, Args); + else { + // FIXME: We determine whether this is a valid expression by checking to + // see if there's a viable builtin operator candidate for it. That isn't + // really what the rules ask us to do, but should give the right results. + S.AddBuiltinOperatorCandidates(OO, FD->getLocation(), Args, CandidateSet); + } + + 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 any callee has an undeduced return type, deduce it now. + // FIXME: It's not clear how a failure here should be handled. For + // now, we produce an eager diagnostic, because that is forward + // compatible with most (all?) other reasonable options. + if (BestFD->getReturnType()->isUndeducedType() && + S.DeduceReturnType(BestFD, FD->getLocation(), + /*Diagnose=*/false)) { + // Don't produce a duplicate error when asked to explain why the + // comparison is deleted: we diagnosed that when initially checking + // the defaulted operator. + if (Diagnose == NoDiagnostics) { + S.Diag( + FD->getLocation(), + diag::err_defaulted_comparison_cannot_deduce_undeduced_auto) + << Subobj.Kind << Subobj.Decl; + S.Diag( + Subobj.Loc, + diag::note_defaulted_comparison_cannot_deduce_undeduced_auto) + << Subobj.Kind << Subobj.Decl; + S.Diag(BestFD->getLocation(), + diag::note_defaulted_comparison_cannot_deduce_callee) + << Subobj.Kind << Subobj.Decl; + } + return Result::deleted(); + } + 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(); + BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(OO); + ExprResult Op; + if (Type->isOverloadableType()) + Op = S.CreateOverloadedBinOp(Loc, Opc, Fns, Obj.first.get(), + Obj.second.get(), /*PerformADL=*/true, + /*AllowRewrittenCandidates=*/true, FD); + else + Op = S.CreateBuiltinBinOp(Loc, Opc, Obj.first.get(), Obj.second.get()); + 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; + if (VDRef.get()->getType()->isOverloadableType()) + Comp = S.CreateOverloadedBinOp(Loc, BO_NE, Fns, VDRef.get(), Zero, true, + true, FD); + else + Comp = S.CreateBuiltinBinOp(Loc, BO_NE, VDRef.get(), Zero); + 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() { @@ -6942,7 +8479,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 @@ -7353,6 +8891,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 @@ -7796,7 +9350,7 @@ public: /// to be used with CXXRecordDecl::lookupInBases(). bool operator()(const CXXBaseSpecifier *Specifier, CXXBasePath &Path) { RecordDecl *BaseRecord = - Specifier->getType()->getAs<RecordType>()->getDecl(); + Specifier->getType()->castAs<RecordType>()->getDecl(); DeclarationName Name = Method->getDeclName(); assert(Name.getNameKind() == DeclarationName::Identifier); @@ -7964,8 +9518,7 @@ void Sema::ActOnFinishCXXMemberSpecification( if (AL.getKind() != ParsedAttr::AT_Visibility) continue; AL.setInvalid(); - Diag(AL.getLoc(), diag::warn_attribute_after_definition_ignored) - << AL.getName(); + Diag(AL.getLoc(), diag::warn_attribute_after_definition_ignored) << AL; } ActOnFields(S, RLoc, TagDecl, llvm::makeArrayRef( @@ -7973,7 +9526,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 @@ -8053,6 +9644,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) { @@ -8264,7 +9869,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; @@ -8462,7 +10067,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(); @@ -8536,7 +10141,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) { @@ -9120,22 +10725,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(); } @@ -9147,18 +10767,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); @@ -9384,7 +10996,7 @@ public: } std::unique_ptr<CorrectionCandidateCallback> clone() override { - return llvm::make_unique<NamespaceValidatorCCC>(*this); + return std::make_unique<NamespaceValidatorCCC>(*this); } }; @@ -9880,7 +11492,8 @@ static CXXBaseSpecifier *findDirectBaseWithType(CXXRecordDecl *Derived, QualType DesiredBase, bool &AnyDependentBases) { // Check whether the named type is a direct base class. - CanQualType CanonicalDesiredBase = DesiredBase->getCanonicalTypeUnqualified(); + CanQualType CanonicalDesiredBase = DesiredBase->getCanonicalTypeUnqualified() + .getUnqualifiedType(); for (auto &Base : Derived->bases()) { CanQualType BaseType = Base.getType()->getCanonicalTypeUnqualified(); if (CanonicalDesiredBase == BaseType) @@ -9963,7 +11576,7 @@ public: } std::unique_ptr<CorrectionCandidateCallback> clone() override { - return llvm::make_unique<UsingValidatorCCC>(*this); + return std::make_unique<UsingValidatorCCC>(*this); } private: @@ -10855,25 +12468,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( @@ -11007,10 +12601,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); @@ -11169,7 +12762,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(); @@ -11309,6 +12903,10 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { if (DSM.isAlreadyBeingDeclared()) return nullptr; + bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, + CXXDestructor, + false); + // Create the actual destructor declaration. CanQualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); @@ -11316,10 +12914,11 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { DeclarationName Name = Context.DeclarationNames.getCXXDestructorName(ClassType); DeclarationNameInfo NameInfo(Name, ClassLoc); - CXXDestructorDecl *Destructor - = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, - QualType(), nullptr, /*isInline=*/true, - /*isImplicitlyDeclared=*/true); + CXXDestructorDecl *Destructor = + CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, + QualType(), nullptr, /*isInline=*/true, + /*isImplicitlyDeclared=*/true, + Constexpr ? CSK_constexpr : CSK_unspecified); Destructor->setAccess(AS_public); Destructor->setDefaulted(); @@ -11415,7 +13014,7 @@ void Sema::ActOnFinishCXXMemberDecls() { } } -void Sema::ActOnFinishCXXNonNestedClass(Decl *D) { +void Sema::ActOnFinishCXXNonNestedClass() { referenceDLLExportedClassMethods(); if (!DelayedDllExportMemberFunctions.empty()) { @@ -11456,8 +13055,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; @@ -11632,7 +13230,8 @@ buildMemcpyForAssignmentOp(Sema &S, SourceLocation Loc, QualType T, const Type *E = T->getBaseElementTypeUnsafe(); bool NeedsCollectableMemCpy = - E->isRecordType() && E->getAs<RecordType>()->getDecl()->hasObjectMember(); + E->isRecordType() && + E->castAs<RecordType>()->getDecl()->hasObjectMember(); // Create a reference to the __builtin_objc_memmove_collectable function StringRef MemCpyName = NeedsCollectableMemCpy ? @@ -11914,8 +13513,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) @@ -12016,11 +13616,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); } } @@ -12239,8 +13840,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); @@ -12433,8 +14035,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() @@ -12617,8 +14219,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); @@ -12749,8 +14352,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, @@ -13185,6 +14789,20 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) { } if (Destructor->isTrivial()) return; + + // If the destructor is constexpr, check whether the variable has constant + // destruction now. + if (Destructor->isConstexpr() && VD->getInit() && + !VD->getInit()->isValueDependent() && VD->evaluateValue()) { + SmallVector<PartialDiagnosticAt, 8> Notes; + if (!VD->evaluateDestruction(Notes) && VD->isConstexpr()) { + Diag(VD->getLocation(), + diag::err_constexpr_var_requires_const_destruction) << VD; + for (unsigned I = 0, N = Notes.size(); I != N; ++I) + Diag(Notes[I].first, Notes[I].second); + } + } + if (!VD->hasGlobalStorage()) return; // Emit warning for non-trivial dtor in global scope (a real global, @@ -13212,9 +14830,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. @@ -13277,7 +14893,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()) @@ -13506,7 +15122,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(); } @@ -14027,8 +15643,17 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, if (Converted.isInvalid()) Failed = true; + ExprResult FullAssertExpr = + ActOnFinishFullExpr(Converted.get(), StaticAssertLoc, + /*DiscardedValue*/ false, + /*IsConstexpr*/ true); + if (FullAssertExpr.isInvalid()) + Failed = true; + else + AssertExpr = FullAssertExpr.get(); + llvm::APSInt Cond; - if (!Failed && VerifyIntegerConstantExpression(Converted.get(), &Cond, + if (!Failed && VerifyIntegerConstantExpression(AssertExpr, &Cond, diag::err_static_assert_expression_is_not_constant, /*AllowFold=*/false).isInvalid()) Failed = true; @@ -14043,8 +15668,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(); @@ -14054,16 +15687,16 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, } Failed = true; } + } else { + ExprResult FullAssertExpr = ActOnFinishFullExpr(AssertExpr, StaticAssertLoc, + /*DiscardedValue*/false, + /*IsConstexpr*/true); + if (FullAssertExpr.isInvalid()) + Failed = true; + else + AssertExpr = FullAssertExpr.get(); } - ExprResult FullAssertExpr = ActOnFinishFullExpr(AssertExpr, StaticAssertLoc, - /*DiscardedValue*/false, - /*IsConstexpr*/true); - if (FullAssertExpr.isInvalid()) - Failed = true; - else - AssertExpr = FullAssertExpr.get(); - Decl *Decl = StaticAssertDecl::Create(Context, CurContext, StaticAssertLoc, AssertExpr, AssertMessage, RParenLoc, Failed); @@ -14726,6 +16359,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)) { @@ -14742,67 +16385,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) { @@ -14826,8 +16495,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) @@ -14874,8 +16543,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()) @@ -15295,8 +16964,8 @@ void Sema::MarkVirtualMembersReferenced(SourceLocation Loc, return; for (const auto &I : RD->bases()) { - const CXXRecordDecl *Base = - cast<CXXRecordDecl>(I.getType()->getAs<RecordType>()->getDecl()); + const auto *Base = + cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl()); if (Base->getNumVBases() == 0) continue; MarkVirtualMembersReferenced(Loc, Base); @@ -15481,6 +17150,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); } @@ -15752,3 +17426,50 @@ MSPropertyDecl *Sema::HandleMSProperty(Scope *S, RecordDecl *Record, return NewPD; } + +void Sema::ActOnStartFunctionDeclarationDeclarator( + Declarator &Declarator, unsigned TemplateParameterDepth) { + auto &Info = InventedParameterInfos.emplace_back(); + TemplateParameterList *ExplicitParams = nullptr; + ArrayRef<TemplateParameterList *> ExplicitLists = + Declarator.getTemplateParameterLists(); + if (!ExplicitLists.empty()) { + bool IsMemberSpecialization, IsInvalid; + ExplicitParams = MatchTemplateParametersToScopeSpecifier( + Declarator.getBeginLoc(), Declarator.getIdentifierLoc(), + Declarator.getCXXScopeSpec(), /*TemplateId=*/nullptr, + ExplicitLists, /*IsFriend=*/false, IsMemberSpecialization, IsInvalid, + /*SuppressDiagnostic=*/true); + } + if (ExplicitParams) { + Info.AutoTemplateParameterDepth = ExplicitParams->getDepth(); + for (NamedDecl *Param : *ExplicitParams) + Info.TemplateParams.push_back(Param); + Info.NumExplicitTemplateParams = ExplicitParams->size(); + } else { + Info.AutoTemplateParameterDepth = TemplateParameterDepth; + Info.NumExplicitTemplateParams = 0; + } +} + +void Sema::ActOnFinishFunctionDeclarationDeclarator(Declarator &Declarator) { + auto &FSI = InventedParameterInfos.back(); + if (FSI.TemplateParams.size() > FSI.NumExplicitTemplateParams) { + if (FSI.NumExplicitTemplateParams != 0) { + TemplateParameterList *ExplicitParams = + Declarator.getTemplateParameterLists().back(); + Declarator.setInventedTemplateParameterList( + TemplateParameterList::Create( + Context, ExplicitParams->getTemplateLoc(), + ExplicitParams->getLAngleLoc(), FSI.TemplateParams, + ExplicitParams->getRAngleLoc(), + ExplicitParams->getRequiresClause())); + } else { + Declarator.setInventedTemplateParameterList( + TemplateParameterList::Create( + Context, SourceLocation(), SourceLocation(), FSI.TemplateParams, + SourceLocation(), /*RequiresClause=*/nullptr)); + } + } + InventedParameterInfos.pop_back(); +} |
