diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-10-01 02:31:28 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-10-01 02:31:28 +0000 |
commit | 9f569cca2a4c5fb6026005434e27025b9e71309d (patch) | |
tree | 4623644f5d65a1ca5526ea45bb24069efa896bfa | |
parent | 00ca8483cf8e86a5ea08310c6727fd7848980043 (diff) |
constexpr: semantic checking for constexpr functions and constructors. Based in
part on patches by Peter Collingbourne.
We diverge from the C++11 standard in a few areas, mostly related to checking
constexpr function declarations, and not just definitions. See WG21 paper
N3308=11-0078 for details.
Function invocation substitution is not available in this patch; constexpr
functions cannot yet be used from within constant expressions.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@140926 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/DeclCXX.h | 19 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 63 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 20 | ||||
-rw-r--r-- | lib/AST/Type.cpp | 34 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 14 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 408 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiateDecl.cpp | 22 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 112 | ||||
-rw-r--r-- | test/CXX/basic/basic.types/p10.cpp | 108 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp | 37 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp | 125 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp | 188 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p6.cpp | 74 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp | 28 |
14 files changed, 1206 insertions, 46 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 43454bec50..7e60773e44 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1004,6 +1004,25 @@ public: return isTriviallyCopyable() && hasTrivialDefaultConstructor(); } + // isLiteral - Whether this class is a literal type. + // + // C++0x [basic.types]p10 + // A class type that has all the following properties: + // -- a trivial destructor + // -- every constructor call and full-expression in the + // brace-or-equal-intializers for non-static data members (if any) is + // a constant expression. + // -- it is an aggregate type or has at least one constexpr constructor or + // constructor template that is not a copy or move constructor, and + // -- all non-static data members and base classes of literal types + // + // We resolve DR1361 by ignoring the second bullet. + bool isLiteral() const { + return hasTrivialDestructor() && + (isAggregate() || hasConstexprNonCopyMoveConstructor()) && + !hasNonLiteralTypeFieldsOrBases(); + } + /// \brief If this record is an instantiation of a member class, /// retrieves the member class from which it was instantiated. /// diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index b11b94c73b..017e4e68b4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1193,6 +1193,69 @@ def err_constexpr_initialized_static_member : Error< "definition of initialized static data member %0 cannot be marked constexpr">; def err_constexpr_var_requires_const_init : Error< "constexpr variable %0 must be initialized by a constant expression">; +def err_constexpr_redecl_mismatch : Error< + "%select{non-constexpr declaration of %0 follows constexpr declaration" + "|constexpr declaration of %0 follows non-constexpr declaration}1">; +def note_constexpr_redecl_mismatch : Note< + "previous declaration was %select{not |}0marked constexpr">; +def err_constexpr_virtual : Error<"virtual function cannot be constexpr">; +def note_constexpr_tmpl_virtual : Note<"function template instantiation is not " + "constexpr because it is virtual">; +def err_constexpr_virtual_base : Error<"constexpr constructor not allowed in " + "%select{class|struct}0 with virtual base %plural{1:class|:classes}1">; +def note_constexpr_tmpl_virtual_base : Note<"constructor template instantiation is " + "not constexpr because %select{class|struct}0 has virtual base " + "%plural{1:class|:classes}1">; +def note_non_literal_virtual_base : Note<"%select{class|struct}0 with virtual " + "base %plural{1:class|:classes}1 is not a literal type">; +def note_constexpr_virtual_base_here : Note<"virtual base class declared here">; +def err_constexpr_non_literal_return : Error< + "constexpr function's return type %0 is not a literal type">; +def note_constexpr_tmpl_non_literal_return : Note< + "function template instantiation is not constexpr because return type %0 is " + "not a literal type">; +def err_constexpr_non_literal_param : Error< + "constexpr %select{function|constructor}1's %ordinal0 parameter type %2 is " + "not a literal type">; +def note_constexpr_tmpl_non_literal_param : Note< + "%select{function|constructor}1 template instantiation is not constexpr " + "because %ordinal0 parameter type %2 is not a literal type">; +def err_constexpr_body_invalid_stmt : Error< + "statement not allowed in constexpr %select{function|constructor}0">; +def err_constexpr_type_definition : Error< + "types cannot be defined in a constexpr %select{function|constructor}0">; +def err_constexpr_vla : Error< + "variably-modified type %0 cannot be used in a constexpr " + "%select{function|constructor}1">; +def err_constexpr_var_declaration : Error< + "variables cannot be declared in a constexpr %select{function|constructor}0">; +def err_constexpr_body_no_return : Error< + "no return statement in constexpr function">; +def err_constexpr_body_multiple_return : Error< + "multiple return statements in constexpr function">; +def note_constexpr_body_previous_return : Note< + "previous return statement is here">; +def err_constexpr_function_try_block : Error< + "function try block not allowed in constexpr %select{function|constructor}0">; +def err_constexpr_union_ctor_no_init : Error< + "constexpr union constructor does not initialize any member">; +def err_constexpr_ctor_missing_init : Error< + "constexpr constructor must initialize all members">; +def note_constexpr_ctor_missing_init : Note< + "member not initialized by constructor">; +def err_constexpr_method_non_literal : Error< + "non-literal type %0 cannot have constexpr members">; +def note_non_literal_no_constexpr_ctors : Note< + "%0 is not literal because it is not an aggregate and has no constexpr " + "constructors other than copy or move constructors">; +def note_non_literal_base_class : Note< + "%0 is not literal because it has base class %1 of non-literal type">; +def note_non_literal_field : Note< + "%0 is not literal because it has data member %1 of non-literal type %2">; +def note_non_literal_user_provided_dtor : Note< + "%0 is not literal because it has a user-provided destructor">; +def note_non_literal_nontrivial_dtor : Note< + "%0 is not literal because it has a non-trivial destructor">; // Objective-C++ def err_objc_decls_may_only_appear_in_global_scope : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 42d2734bde..cf3219042f 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -842,6 +842,9 @@ public: std::pair<SourceLocation, PartialDiagnostic> Note); + bool RequireLiteralType(SourceLocation Loc, QualType T, + const PartialDiagnostic &PD, + bool AllowIncompleteType = false); QualType getElaboratedType(ElaboratedTypeKeyword Keyword, const CXXScopeSpec &SS, QualType T); @@ -1010,6 +1013,23 @@ public: bool &Redeclaration, bool &AddToScope); bool AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD); + + /// \brief The kind of constexpr declaration checking we are performing. + /// + /// The kind affects which diagnostics (if any) are emitted if the function + /// does not satisfy the requirements of a constexpr function declaration. + enum CheckConstexprKind { + /// \brief Check a constexpr function declaration, and produce errors if it + /// does not satisfy the requirements. + CCK_Declaration, + /// \brief Check a constexpr function template instantiation. + CCK_Instantiation, + /// \brief Produce notes explaining why an instantiation was not constexpr. + CCK_NoteNonConstexprInstantiation + }; + bool CheckConstexprFunctionDecl(const FunctionDecl *FD, CheckConstexprKind CCK); + bool CheckConstexprFunctionBody(const FunctionDecl *FD, Stmt *Body); + void DiagnoseHiddenVirtualMethods(CXXRecordDecl *DC, CXXMethodDecl *MD); void CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, LookupResult &Previous, diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 7471a3da5d..a2c6954d75 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1134,29 +1134,19 @@ bool Type::isLiteralType() const { return true; // -- a class type that has all of the following properties: if (const RecordType *RT = BaseTy->getAs<RecordType>()) { + // -- a trivial destructor, + // -- every constructor call and full-expression in the + // brace-or-equal-initializers for non-static data members (if any) + // is a constant expression, + // -- it is an aggregate type or has at least one constexpr + // constructor or constructor template that is not a copy or move + // constructor, and + // -- all non-static data members and base classes of literal types + // + // We resolve DR1361 by ignoring the second bullet. if (const CXXRecordDecl *ClassDecl = - dyn_cast<CXXRecordDecl>(RT->getDecl())) { - // -- a trivial destructor, - if (!ClassDecl->hasTrivialDestructor()) - return false; - - // -- every constructor call and full-expression in the - // brace-or-equal-initializers for non-static data members (if any) - // is a constant expression, - // We deliberately do not implement this restriction. It isn't necessary - // and doesn't make any sense. - - // -- it is an aggregate type or has at least one constexpr - // constructor or constructor template that is not a copy or move - // constructor, and - if (!ClassDecl->isAggregate() && - !ClassDecl->hasConstexprNonCopyMoveConstructor()) - return false; - - // -- all non-static data members and base classes of literal types - if (ClassDecl->hasNonLiteralTypeFieldsOrBases()) - return false; - } + dyn_cast<CXXRecordDecl>(RT->getDecl())) + return ClassDecl->isLiteral(); return true; } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 942c1bc5c2..f4b6ce92e5 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4770,15 +4770,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // are implicitly inline. NewFD->setImplicitlyInline(); - // FIXME: If this is a redeclaration, check the original declaration was - // marked constepr. - // C++0x [dcl.constexpr]p3: functions declared constexpr are required to // be either constructors or to return a literal type. Therefore, // destructors cannot be declared constexpr. if (isa<CXXDestructorDecl>(NewFD)) - Diag(D.getDeclSpec().getConstexprSpecLoc(), - diag::err_constexpr_dtor); + Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor); } // If __module_private__ was specified, mark the function accordingly. @@ -5050,6 +5046,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, Previous.getResultKind() != LookupResult::FoundOverloaded) && "previous declaration set still overloaded"); + if (NewFD->isConstexpr() && !NewFD->isInvalidDecl() && + !CheckConstexprFunctionDecl(NewFD, CCK_Declaration)) + NewFD->setInvalidDecl(); + NamedDecl *PrincipalDecl = (FunctionTemplate ? cast<NamedDecl>(FunctionTemplate) : NewFD); @@ -6963,6 +6963,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, ActivePolicy = &WP; } + if (FD && FD->isConstexpr() && !FD->isInvalidDecl() && + !CheckConstexprFunctionBody(FD, Body)) + FD->setInvalidDecl(); + assert(ExprTemporaries.empty() && "Leftover temporaries in function"); assert(!ExprNeedsCleanups && "Unaccounted cleanups in function"); } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index ae31e61c43..73fd18889e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -502,6 +502,20 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old) { } } + // C++0x [dcl.constexpr]p1: If any declaration of a function or function + // template has a constexpr specifier then all its declarations shall + // contain the constexpr specifier. [Note: An explicit specialization can + // differ from the template declaration with respect to the constexpr + // specifier. -- end note] + // + // FIXME: Don't reject changes in constexpr in explicit specializations. + if (New->isConstexpr() != Old->isConstexpr()) { + Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch) + << New << New->isConstexpr(); + Diag(Old->getLocation(), diag::note_previous_declaration); + Invalid = true; + } + if (CheckEquivalentExceptionSpec(Old, New)) Invalid = true; @@ -602,6 +616,359 @@ 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 depending on @p CCK and return false. +static bool CheckConstexprParameterTypes(Sema &SemaRef, const FunctionDecl *FD, + Sema::CheckConstexprKind CCK) { + unsigned ArgIndex = 0; + const FunctionProtoType *FT = FD->getType()->getAs<FunctionProtoType>(); + for (FunctionProtoType::arg_type_iterator i = FT->arg_type_begin(), + e = FT->arg_type_end(); i != e; ++i, ++ArgIndex) { + const ParmVarDecl *PD = FD->getParamDecl(ArgIndex); + SourceLocation ParamLoc = PD->getLocation(); + if (!(*i)->isDependentType() && + SemaRef.RequireLiteralType(ParamLoc, *i, CCK == Sema::CCK_Declaration ? + SemaRef.PDiag(diag::err_constexpr_non_literal_param) + << ArgIndex+1 << PD->getSourceRange() + << isa<CXXConstructorDecl>(FD) : + SemaRef.PDiag(), + /*AllowIncompleteType*/ true)) { + if (CCK == Sema::CCK_NoteNonConstexprInstantiation) + SemaRef.Diag(ParamLoc, diag::note_constexpr_tmpl_non_literal_param) + << ArgIndex+1 << PD->getSourceRange() + << isa<CXXConstructorDecl>(FD) << *i; + return false; + } + } + return true; +} + +// CheckConstexprFunctionDecl - Check whether a function declaration satisfies +// the requirements of a constexpr function declaration or a constexpr +// constructor declaration. Return true if it does, false if not. +// +// This implements C++0x [dcl.constexpr]p3,4, as amended by N3308. +// +// \param CCK Specifies whether to produce diagnostics if the function does not +// satisfy the requirements. +bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD, + CheckConstexprKind CCK) { + assert((CCK != CCK_NoteNonConstexprInstantiation || + (NewFD->getTemplateInstantiationPattern() && + NewFD->getTemplateInstantiationPattern()->isConstexpr())) && + "only constexpr templates can be instantiated non-constexpr"); + + if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(NewFD)) { + // C++0x [dcl.constexpr]p4: + // In the definition of a constexpr constructor, each of the parameter + // types shall be a literal type. + if (!CheckConstexprParameterTypes(*this, NewFD, CCK)) + return false; + + // In addition, either its function-body shall be = delete or = default or + // it shall satisfy the following constraints: + // - the class shall not have any virtual base classes; + const CXXRecordDecl *RD = CD->getParent(); + if (RD->getNumVBases()) { + // Note, this is still illegal if the body is = default, since the + // implicit body does not satisfy the requirements of a constexpr + // constructor. We also reject cases where the body is = delete, as + // required by N3308. + if (CCK != CCK_Instantiation) { + Diag(NewFD->getLocation(), + CCK == CCK_Declaration ? diag::err_constexpr_virtual_base + : diag::note_constexpr_tmpl_virtual_base) + << RD->isStruct() << RD->getNumVBases(); + for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(), + E = RD->vbases_end(); I != E; ++I) + Diag(I->getSourceRange().getBegin(), + diag::note_constexpr_virtual_base_here) << I->getSourceRange(); + } + return false; + } + } else { + // C++0x [dcl.constexpr]p3: + // The definition of a constexpr function shall satisfy the following + // constraints: + // - it shall not be virtual; + const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(NewFD); + if (Method && Method->isVirtual()) { + if (CCK != CCK_Instantiation) { + Diag(NewFD->getLocation(), + CCK == CCK_Declaration ? diag::err_constexpr_virtual + : diag::note_constexpr_tmpl_virtual); + + // If it's not obvious why this function is virtual, find an overridden + // function which uses the 'virtual' keyword. + const CXXMethodDecl *WrittenVirtual = Method; + while (!WrittenVirtual->isVirtualAsWritten()) + WrittenVirtual = *WrittenVirtual->begin_overridden_methods(); + if (WrittenVirtual != Method) + Diag(WrittenVirtual->getLocation(), + diag::note_overridden_virtual_function); + } + return false; + } + + // - its return type shall be a literal type; + QualType RT = NewFD->getResultType(); + if (!RT->isDependentType() && + RequireLiteralType(NewFD->getLocation(), RT, CCK == CCK_Declaration ? + PDiag(diag::err_constexpr_non_literal_return) : + PDiag(), + /*AllowIncompleteType*/ true)) { + if (CCK == CCK_NoteNonConstexprInstantiation) + Diag(NewFD->getLocation(), + diag::note_constexpr_tmpl_non_literal_return) << RT; + return false; + } + + // - each of its parameter types shall be a literal type; + if (!CheckConstexprParameterTypes(*this, NewFD, CCK)) + return false; + } + + return true; +} + +/// Check the given declaration statement is legal within a constexpr function +/// body. C++0x [dcl.constexpr]p3,p4. +/// +/// \return true if the body is OK, false if we have diagnosed a problem. +static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, + DeclStmt *DS) { + // C++0x [dcl.constexpr]p3 and p4: + // The definition of a constexpr function(p3) or constructor(p4) [...] shall + // contain only + for (DeclStmt::decl_iterator DclIt = DS->decl_begin(), + DclEnd = DS->decl_end(); DclIt != DclEnd; ++DclIt) { + switch ((*DclIt)->getKind()) { + case Decl::StaticAssert: + case Decl::Using: + case Decl::UsingShadow: + case Decl::UsingDirective: + case Decl::UnresolvedUsingTypename: + // - static_assert-declarations + // - using-declarations, + // - using-directives, + continue; + + case Decl::Typedef: + case Decl::TypeAlias: { + // - typedef declarations and alias-declarations that do not define + // classes or enumerations, + TypedefNameDecl *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); + return false; + } + continue; + } + + case Decl::Enum: + case Decl::CXXRecord: + // As an extension, we allow the declaration (but not the definition) of + // classes and enumerations in all declarations, not just in typedef and + // alias declarations. + if (cast<TagDecl>(*DclIt)->isThisDeclarationADefinition()) { + SemaRef.Diag(DS->getLocStart(), diag::err_constexpr_type_definition) + << isa<CXXConstructorDecl>(Dcl); + return false; + } + continue; + + case Decl::Var: + SemaRef.Diag(DS->getLocStart(), diag::err_constexpr_var_declaration) + << isa<CXXConstructorDecl>(Dcl); + return false; + + default: + SemaRef.Diag(DS->getLocStart(), diag::err_constexpr_body_invalid_stmt) + << isa<CXXConstructorDecl>(Dcl); + return false; + } + } + + return true; +} + +/// Check that the given field is initialized within a constexpr constructor. +/// +/// \param Dcl The constexpr constructor being checked. +/// \param Field The field being checked. This may be a member of an anonymous +/// 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, + const FunctionDecl *Dcl, + FieldDecl *Field, + llvm::SmallSet<Decl*, 16> &Inits, + bool &Diagnosed) { + if (!Inits.count(Field)) { + if (!Diagnosed) { + SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init); + Diagnosed = true; + } + SemaRef.Diag(Field->getLocation(), diag::note_constexpr_ctor_missing_init); + } else if (Field->isAnonymousStructOrUnion()) { + const RecordDecl *RD = Field->getType()->castAs<RecordType>()->getDecl(); + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) + // 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); + } +} + +/// 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) { + if (isa<CXXTryStmt>(Body)) { + // C++0x [dcl.constexpr]p3: + // The definition of a constexpr function shall satisfy the following + // constraints: [...] + // - its function-body shall be = delete, = default, or a + // compound-statement + // + // C++0x [dcl.constexpr]p4: + // In the definition of a constexpr constructor, [...] + // - its function-body shall not be a function-try-block; + Diag(Body->getLocStart(), diag::err_constexpr_function_try_block) + << isa<CXXConstructorDecl>(Dcl); + return false; + } + + // - its function-body shall be [...] a compound-statement that contains only + CompoundStmt *CompBody = cast<CompoundStmt>(Body); + + llvm::SmallVector<SourceLocation, 4> ReturnStmts; + for (CompoundStmt::body_iterator BodyIt = CompBody->body_begin(), + BodyEnd = CompBody->body_end(); BodyIt != BodyEnd; ++BodyIt) { + switch ((*BodyIt)->getStmtClass()) { + case Stmt::NullStmtClass: + // - null statements, + continue; + + case Stmt::DeclStmtClass: + // - static_assert-declarations + // - using-declarations, + // - using-directives, + // - typedef declarations and alias-declarations that do not define + // classes or enumerations, + if (!CheckConstexprDeclStmt(*this, Dcl, cast<DeclStmt>(*BodyIt))) + return false; + continue; + + case Stmt::ReturnStmtClass: + // - and exactly one return statement; + if (isa<CXXConstructorDecl>(Dcl)) + break; + + ReturnStmts.push_back((*BodyIt)->getLocStart()); + // FIXME + // - every constructor call and implicit conversion used in initializing + // the return value shall be one of those allowed in a constant + // expression. + // Deal with this as part of a general check that the function can produce + // a constant expression (for [dcl.constexpr]p5). + continue; + + default: + break; + } + + Diag((*BodyIt)->getLocStart(), diag::err_constexpr_body_invalid_stmt) + << isa<CXXConstructorDecl>(Dcl); + return false; + } + + if (const CXXConstructorDecl *Constructor + = dyn_cast<CXXConstructorDecl>(Dcl)) { + const CXXRecordDecl *RD = Constructor->getParent(); + // - every non-static data member and base class sub-object shall be + // initialized; + if (RD->isUnion()) { + // DR1359: Exactly one member of a union shall be initialized. + if (Constructor->getNumCtorInitializers() == 0) { + Diag(Dcl->getLocation(), diag::err_constexpr_union_ctor_no_init); + return false; + } + } else if (!Constructor->isDelegatingConstructor()) { + assert(RD->getNumVBases() == 0 && "constexpr ctor with virtual bases"); + + // Skip detailed checking if we have enough initializers, and we would + // allow at most one initializer per member. + bool AnyAnonStructUnionMembers = false; + unsigned Fields = 0; + for (CXXRecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I != E; ++I, ++Fields) { + if ((*I)->isAnonymousStructOrUnion()) { + AnyAnonStructUnionMembers = true; + break; + } + } + if (AnyAnonStructUnionMembers || + Constructor->getNumCtorInitializers() != RD->getNumBases() + Fields) { + // Check initialization of non-static data members. Base classes are + // always initialized so do not need to be checked. Dependent bases + // might not have initializers in the member initializer list. + llvm::SmallSet<Decl*, 16> Inits; + for (CXXConstructorDecl::init_const_iterator + I = Constructor->init_begin(), E = Constructor->init_end(); + I != E; ++I) { + if (FieldDecl *FD = (*I)->getMember()) + Inits.insert(FD); + else if (IndirectFieldDecl *ID = (*I)->getIndirectMember()) + Inits.insert(ID->chain_begin(), ID->chain_end()); + } + + bool Diagnosed = false; + for (CXXRecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I != E; ++I) + CheckConstexprCtorInitializer(*this, Dcl, *I, Inits, Diagnosed); + if (Diagnosed) + return false; + } + } + + // FIXME + // - every constructor involved in initializing non-static data members + // and base class sub-objects shall be a constexpr constructor; + // - every assignment-expression that is an initializer-clause appearing + // directly or indirectly within a brace-or-equal-initializer for + // a non-static data member that is not named by a mem-initializer-id + // shall be a constant expression; and + // - every implicit conversion used in converting a constructor argument + // to the corresponding parameter type and converting + // a full-expression to the corresponding member type shall be one of + // those allowed in a constant expression. + // Deal with these as part of a general check that the function can produce + // a constant expression (for [dcl.constexpr]p5). + } else { + if (ReturnStmts.empty()) { + Diag(Dcl->getLocation(), diag::err_constexpr_body_no_return); + return false; + } + if (ReturnStmts.size() > 1) { + Diag(ReturnStmts.back(), diag::err_constexpr_body_multiple_return); + for (unsigned I = 0; I < ReturnStmts.size() - 1; ++I) + Diag(ReturnStmts[I], diag::note_constexpr_body_previous_return); + return false; + } + } + + return true; +} + /// isCurrentClassName - Determine whether the identifier II is the /// name of the class type currently being defined. In the case of /// nested classes, this will only return true if II is the name of @@ -3226,6 +3593,47 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { } } + // C++0x [dcl.constexpr]p8: A constexpr specifier for a non-static member + // function that is not a constructor declares that member function to be + // const. [...] The class of which that function is a member shall be + // a literal type. + // + // It's fine to diagnose constructors here too: such constructors cannot + // produce a constant expression, so are ill-formed (no diagnostic required). + // + // If the class has virtual bases, any constexpr members will already have + // been diagnosed by the checks performed on the member declaration, so + // suppress this (less useful) diagnostic. + if (LangOpts.CPlusPlus0x && !Record->isDependentType() && + !Record->isLiteral() && !Record->getNumVBases()) { + for (CXXRecordDecl::method_iterator M = Record->method_begin(), + MEnd = Record->method_end(); + M != MEnd; ++M) { + if ((*M)->isConstexpr()) { + switch (Record->getTemplateSpecializationKind()) { + case TSK_ImplicitInstantiation: + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + // If a template instantiates to a non-literal type, but its members + // instantiate to constexpr functions, the template is technically + // ill-formed, but we allow it for sanity. Such members are treated as + // non-constexpr. + (*M)->setConstexpr(false); + continue; + + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + RequireLiteralType((*M)->getLocation(), Context.getRecordType(Record), + PDiag(diag::err_constexpr_method_non_literal)); + break; + } + + // Only produce one error per class. + break; + } + } + } + // Declare inherited constructors. We do this eagerly here because: // - The standard requires an eager diagnostic for conflicting inherited // constructors from different classes. diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index e29b75a2c2..694d94432f 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1067,15 +1067,12 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, TemplateArgs); } - bool isConstexpr = D->isConstexpr(); - // FIXME: check whether the instantiation produces a constexpr function. - FunctionDecl *Function = FunctionDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(), D->getLocation(), D->getDeclName(), T, TInfo, D->getStorageClass(), D->getStorageClassAsWritten(), D->isInlineSpecified(), D->hasWrittenPrototype(), - isConstexpr); + /*isConstexpr*/ false); if (QualifierLoc) Function->setQualifierInfo(QualifierLoc); @@ -1388,9 +1385,6 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, if (!DC) return 0; } - bool isConstexpr = D->isConstexpr(); - // FIXME: check whether the instantiation produces a constexpr function. - // Build the instantiated method declaration. CXXRecordDecl *Record = cast<CXXRecordDecl>(DC); CXXMethodDecl *Method = 0; @@ -1403,7 +1397,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, StartLoc, NameInfo, T, TInfo, Constructor->isExplicit(), Constructor->isInlineSpecified(), - false, isConstexpr); + false, /*isConstexpr*/ false); } else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(D)) { Method = CXXDestructorDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, @@ -1414,14 +1408,15 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, StartLoc, NameInfo, T, TInfo, Conversion->isInlineSpecified(), Conversion->isExplicit(), - isConstexpr, Conversion->getLocEnd()); + /*isConstexpr*/ false, + Conversion->getLocEnd()); } else { Method = CXXMethodDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, D->isStatic(), D->getStorageClassAsWritten(), D->isInlineSpecified(), - isConstexpr, D->getLocEnd()); + /*isConstexpr*/ false, D->getLocEnd()); } if (QualifierLoc) @@ -2312,6 +2307,13 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, EPI)); } + // C++0x [dcl.constexpr]p6: If the instantiated template specialization of + // a constexpr function template satisfies the requirements for a constexpr + // function, then it is a constexpr function. + if (Tmpl->isConstexpr() && + SemaRef.CheckConstexprFunctionDecl(New, Sema::CCK_Instantiation)) + New->setConstexpr(true); + const FunctionDecl* Definition = Tmpl; // Get the definition. Leaves the variable unchanged if undefined. diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 480dd3b85a..25ec0b1477 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -3992,6 +3992,118 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, std::make_pair(SourceLocation(), PDiag(0))); } +/// @brief Ensure that the type T is a literal type. +/// +/// This routine checks whether the type @p T is a literal type. If @p T is an +/// incomplete type, an attempt is made to complete it. If @p T is a literal +/// type, or @p AllowIncompleteType is true and @p T is an incomplete type, +/// returns false. Otherwise, this routine issues the diagnostic @p PD (giving +/// it the type @p T), along with notes explaining why the type is not a +/// literal type, and returns true. +/// |