From af1fc7af351758b0ea0d285bdfe5640128109a4e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 15 Aug 2011 21:04:07 +0000 Subject: [PATCH] Track in the AST whether a function is constexpr. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@137653 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 32 +++++++---- include/clang/AST/DeclCXX.h | 21 ++++--- include/clang/Basic/DiagnosticSemaKinds.td | 10 ++++ lib/AST/ASTImporter.cpp | 8 ++- lib/AST/Decl.cpp | 6 +- lib/AST/DeclCXX.cpp | 25 +++++---- lib/AST/Type.cpp | 24 +++++--- lib/Parse/ParseDeclCXX.cpp | 3 +- lib/Sema/SemaDecl.cpp | 56 +++++++++++++++++-- lib/Sema/SemaDeclCXX.cpp | 17 ++++-- lib/Sema/SemaTemplateInstantiateDecl.cpp | 15 +++-- lib/Serialization/ASTReaderDecl.cpp | 3 +- lib/Serialization/ASTWriterDecl.cpp | 1 + .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp | 35 ++++++++++-- .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p2.cpp | 20 +++++++ 15 files changed, 212 insertions(+), 64 deletions(-) create mode 100644 test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p2.cpp diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 39b53dd071..487a7ae242 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -706,8 +706,11 @@ private: /// \brief Whether this variable is an ARC pseudo-__strong /// variable; see isARCPseudoStrong() for details. unsigned ARCPseudoStrong : 1; + + /// \brief Whether this variable is (C++0x) constexpr. + unsigned IsConstexpr : 1; }; - enum { NumVarDeclBits = 13 }; // one reserved bit + enum { NumVarDeclBits = 13 }; friend class ASTDeclReader; friend class StmtIteratorBase; @@ -1128,6 +1131,10 @@ public: bool isARCPseudoStrong() const { return VarDeclBits.ARCPseudoStrong; } void setARCPseudoStrong(bool ps) { VarDeclBits.ARCPseudoStrong = ps; } + /// Whether this variable is (C++0x) constexpr. + bool isConstexpr() const { return VarDeclBits.IsConstexpr; } + void setConstexpr(bool IC) { VarDeclBits.IsConstexpr = IC; } + /// \brief If this variable is an instantiated static data member of a /// class template specialization, returns the templated static data member /// from which it was instantiated. @@ -1396,6 +1403,7 @@ private: bool IsExplicitlyDefaulted : 1; //sunk from CXXMethodDecl bool HasImplicitReturnZero : 1; bool IsLateTemplateParsed : 1; + bool IsConstexpr : 1; /// \brief End part of this FunctionDecl's source range. /// @@ -1468,7 +1476,8 @@ protected: FunctionDecl(Kind DK, DeclContext *DC, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, - StorageClass S, StorageClass SCAsWritten, bool isInlineSpecified) + StorageClass S, StorageClass SCAsWritten, bool isInlineSpecified, + bool isConstexprSpecified) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), DeclContext(DK), @@ -1479,7 +1488,7 @@ protected: HasWrittenPrototype(true), IsDeleted(false), IsTrivial(false), IsDefaulted(false), IsExplicitlyDefaulted(false), HasImplicitReturnZero(false), IsLateTemplateParsed(false), - EndRangeLoc(NameInfo.getEndLoc()), + IsConstexpr(isConstexprSpecified), EndRangeLoc(NameInfo.getEndLoc()), TemplateOrSpecialization(), DNLoc(NameInfo.getInfo()) {} @@ -1502,11 +1511,13 @@ public: StorageClass SC = SC_None, StorageClass SCAsWritten = SC_None, bool isInlineSpecified = false, - bool hasWrittenPrototype = true) { + bool hasWrittenPrototype = true, + bool isConstexprSpecified = false) { DeclarationNameInfo NameInfo(N, NLoc); return FunctionDecl::Create(C, DC, StartLoc, NameInfo, T, TInfo, SC, SCAsWritten, - isInlineSpecified, hasWrittenPrototype); + isInlineSpecified, hasWrittenPrototype, + isConstexprSpecified); } static FunctionDecl *Create(ASTContext &C, DeclContext *DC, @@ -1516,7 +1527,8 @@ public: StorageClass SC = SC_None, StorageClass SCAsWritten = SC_None, bool isInlineSpecified = false, - bool hasWrittenPrototype = true); + bool hasWrittenPrototype = true, + bool isConstexprSpecified = false); DeclarationNameInfo getNameInfo() const { return DeclarationNameInfo(getDeclName(), getLocation(), DNLoc); @@ -1602,10 +1614,6 @@ public: bool isPure() const { return IsPure; } void setPure(bool P = true); - /// Whether this is a constexpr function or constexpr constructor. - // FIXME: C++0x: Implement tracking of the constexpr specifier. - bool isConstexpr() const { return false; } - /// Whether this templated function will be late parsed. bool isLateTemplateParsed() const { return IsLateTemplateParsed; } void setLateTemplateParsed(bool ILT = true) { IsLateTemplateParsed = ILT; } @@ -1648,6 +1656,10 @@ public: bool hasInheritedPrototype() const { return HasInheritedPrototype; } void setHasInheritedPrototype(bool P = true) { HasInheritedPrototype = P; } + /// Whether this is a (C++0x) constexpr function or constexpr constructor. + bool isConstexpr() const { return IsConstexpr; } + void setConstexpr(bool IC) { IsConstexpr = IC; } + /// \brief Whether this function has been deleted. /// /// A function that is "deleted" (via the C++0x "= delete" syntax) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 01ca2e312d..3ac6534030 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -890,7 +890,7 @@ public: } // hasConstexprNonCopyMoveConstructor - Whether this class has at least one - // constexpr constructor other than the copy or move constructors + // constexpr constructor other than the copy or move constructors. bool hasConstexprNonCopyMoveConstructor() const { return data().HasConstexprNonCopyMoveConstructor; } @@ -1237,10 +1237,10 @@ protected: const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isStatic, StorageClass SCAsWritten, bool isInline, - SourceLocation EndLocation) + bool isConstexpr, SourceLocation EndLocation) : FunctionDecl(DK, RD, StartLoc, NameInfo, T, TInfo, (isStatic ? SC_Static : SC_None), - SCAsWritten, isInline) { + SCAsWritten, isInline, isConstexpr) { if (EndLocation.isValid()) setRangeEnd(EndLocation); } @@ -1253,6 +1253,7 @@ public: bool isStatic, StorageClass SCAsWritten, bool isInline, + bool isConstexpr, SourceLocation EndLocation); bool isStatic() const { return getStorageClass() == SC_Static; } @@ -1631,9 +1632,9 @@ class CXXConstructorDecl : public CXXMethodDecl { const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isExplicitSpecified, bool isInline, - bool isImplicitlyDeclared) + bool isImplicitlyDeclared, bool isConstexpr) : CXXMethodDecl(CXXConstructor, RD, StartLoc, NameInfo, T, TInfo, false, - SC_None, isInline, SourceLocation()), + SC_None, isInline, isConstexpr, SourceLocation()), IsExplicitSpecified(isExplicitSpecified), ImplicitlyDefined(false), CtorInitializers(0), NumCtorInitializers(0) { setImplicit(isImplicitlyDeclared); @@ -1646,7 +1647,8 @@ public: const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isExplicit, - bool isInline, bool isImplicitlyDeclared); + bool isInline, bool isImplicitlyDeclared, + bool isConstexpr); /// isExplicitSpecified - Whether this constructor declaration has the /// 'explicit' keyword specified. @@ -1854,7 +1856,7 @@ class CXXDestructorDecl : public CXXMethodDecl { QualType T, TypeSourceInfo *TInfo, bool isInline, bool isImplicitlyDeclared) : CXXMethodDecl(CXXDestructor, RD, StartLoc, NameInfo, T, TInfo, false, - SC_None, isInline, SourceLocation()), + SC_None, isInline, /*isConstexpr=*/false, SourceLocation()), ImplicitlyDefined(false), OperatorDelete(0) { setImplicit(isImplicitlyDeclared); } @@ -1917,9 +1919,9 @@ class CXXConversionDecl : public CXXMethodDecl { const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, bool isExplicitSpecified, - SourceLocation EndLocation) + bool isConstexpr, SourceLocation EndLocation) : CXXMethodDecl(CXXConversion, RD, StartLoc, NameInfo, T, TInfo, false, - SC_None, isInline, EndLocation), + SC_None, isInline, isConstexpr, EndLocation), IsExplicitSpecified(isExplicitSpecified) { } public: @@ -1929,6 +1931,7 @@ public: const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, bool isExplicit, + bool isConstexpr, SourceLocation EndLocation); /// IsExplicitSpecified - Whether this conversion function declaration is diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 2430372c3d..aac80e7334 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1158,6 +1158,16 @@ def err_for_range_begin_end_types_differ : Error< def note_for_range_type : Note<"range has type %0">; def note_for_range_begin_end : Note< "selected '%select{begin|end}0' %select{function|template }1%2 with iterator type %3">; + +// C++0x constexpr +def err_invalid_constexpr : Error< + "%select{function parameter|typedef|non-static data member}0 " + "cannot be constexpr">; +def err_constexpr_tag : Error< + "%select{class|struct|union|enum}0 cannot be marked constexpr">; +def err_constexpr_dtor : Error<"destructor cannot be marked constexpr">; +def err_constexpr_no_declarators : Error< + "constexpr can only be used in variable and function declarations">; // Objective-C++ def err_objc_decls_may_only_appear_in_global_scope : Error< diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 9ea9a5789d..8f8c055ef8 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -2468,7 +2468,8 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { NameInfo, T, TInfo, FromConstructor->isExplicit(), D->isInlineSpecified(), - D->isImplicit()); + D->isImplicit(), + D->isConstexpr()); } else if (isa(D)) { ToFunction = CXXDestructorDecl::Create(Importer.getToContext(), cast(DC), @@ -2484,6 +2485,7 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { NameInfo, T, TInfo, D->isInlineSpecified(), FromConversion->isExplicit(), + D->isConstexpr(), Importer.Import(D->getLocEnd())); } else if (CXXMethodDecl *Method = dyn_cast(D)) { ToFunction = CXXMethodDecl::Create(Importer.getToContext(), @@ -2493,6 +2495,7 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { Method->isStatic(), Method->getStorageClassAsWritten(), Method->isInlineSpecified(), + D->isConstexpr(), Importer.Import(D->getLocEnd())); } else { ToFunction = FunctionDecl::Create(Importer.getToContext(), DC, @@ -2500,7 +2503,8 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { NameInfo, T, TInfo, D->getStorageClass(), D->getStorageClassAsWritten(), D->isInlineSpecified(), - D->hasWrittenPrototype()); + D->hasWrittenPrototype(), + D->isConstexpr()); } // Import the qualifier, if any. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f917d32f5e..db7b570e05 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2525,10 +2525,12 @@ FunctionDecl *FunctionDecl::Create(ASTContext &C, DeclContext *DC, QualType T, TypeSourceInfo *TInfo, StorageClass SC, StorageClass SCAsWritten, bool isInlineSpecified, - bool hasWrittenPrototype) { + bool hasWrittenPrototype, + bool isConstexprSpecified) { FunctionDecl *New = new (C) FunctionDecl(Function, DC, StartLoc, NameInfo, T, TInfo, SC, SCAsWritten, - isInlineSpecified); + isInlineSpecified, + isConstexprSpecified); New->HasWrittenPrototype = hasWrittenPrototype; return New; } diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 9e62f49ad7..518210a3f7 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1159,9 +1159,10 @@ CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isStatic, StorageClass SCAsWritten, bool isInline, - SourceLocation EndLocation) { + bool isConstexpr, SourceLocation EndLocation) { return new (C) CXXMethodDecl(CXXMethod, RD, StartLoc, NameInfo, T, TInfo, - isStatic, SCAsWritten, isInline, EndLocation); + isStatic, SCAsWritten, isInline, isConstexpr, + EndLocation); } bool CXXMethodDecl::isUsualDeallocationFunction() const { @@ -1401,7 +1402,7 @@ SourceRange CXXCtorInitializer::getSourceRange() const { CXXConstructorDecl * CXXConstructorDecl::Create(ASTContext &C, EmptyShell Empty) { return new (C) CXXConstructorDecl(0, SourceLocation(), DeclarationNameInfo(), - QualType(), 0, false, false, false); + QualType(), 0, false, false, false, false); } CXXConstructorDecl * @@ -1409,14 +1410,14 @@ CXXConstructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, - bool isExplicit, - bool isInline, - bool isImplicitlyDeclared) { + bool isExplicit, bool isInline, + bool isImplicitlyDeclared, bool isConstexpr) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName && "Name must refer to a constructor"); return new (C) CXXConstructorDecl(RD, StartLoc, NameInfo, T, TInfo, - isExplicit, isInline, isImplicitlyDeclared); + isExplicit, isInline, isImplicitlyDeclared, + isConstexpr); } bool CXXConstructorDecl::isDefaultConstructor() const { @@ -1544,8 +1545,7 @@ CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, - bool isInline, - bool isImplicitlyDeclared) { + bool isInline, bool isImplicitlyDeclared) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXDestructorName && "Name must refer to a destructor"); @@ -1556,7 +1556,7 @@ CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, CXXConversionDecl * CXXConversionDecl::Create(ASTContext &C, EmptyShell Empty) { return new (C) CXXConversionDecl(0, SourceLocation(), DeclarationNameInfo(), - QualType(), 0, false, false, + QualType(), 0, false, false, false, SourceLocation()); } @@ -1566,12 +1566,13 @@ CXXConversionDecl::Create(ASTContext &C, CXXRecordDecl *RD, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, bool isExplicit, - SourceLocation EndLocation) { + bool isConstexpr, SourceLocation EndLocation) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConversionFunctionName && "Name must refer to a conversion function"); return new (C) CXXConversionDecl(RD, StartLoc, NameInfo, T, TInfo, - isInline, isExplicit, EndLocation); + isInline, isExplicit, isConstexpr, + EndLocation); } LinkageSpecDecl *LinkageSpecDecl::Create(ASTContext &C, diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 3b2154f1f6..a2cfe546de 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1105,7 +1105,7 @@ bool Type::isLiteralType() const { // C++0x [basic.types]p10: // A type is a literal type if it is: // [...] - // -- an array of literal type + // -- an array of literal type. // Extension: variable arrays cannot be literal types, since they're // runtime-sized. if (isVariableArrayType()) @@ -1125,33 +1125,41 @@ bool Type::isLiteralType() const { // C++0x [basic.types]p10: // A type is a literal type if it is: // -- a scalar type; or - // As an extension, Clang treats vector types as Scalar types. - if (BaseTy->isScalarType() || BaseTy->isVectorType()) return true; + // As an extension, Clang treats vector types as literal types. + if (BaseTy->isScalarType() || BaseTy->isVectorType()) + return true; // -- a reference type; or - if (BaseTy->isReferenceType()) return true; + if (BaseTy->isReferenceType()) + return true; // -- a class type that has all of the following properties: if (const RecordType *RT = BaseTy->getAs()) { if (const CXXRecordDecl *ClassDecl = dyn_cast(RT->getDecl())) { // -- a trivial destructor, - if (!ClassDecl->hasTrivialDestructor()) return false; + 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, - // FIXME: C++0x: Clang doesn't yet support non-static data member - // declarations with initializers, or constexprs. + // 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; + if (ClassDecl->hasNonLiteralTypeFieldsOrBases()) + return false; } return true; } + return false; } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index df2c5f8e62..7c38857f5a 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1264,7 +1264,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, case tok::kw_typedef: // struct foo {...} typedef x; case tok::kw_register: // struct foo {...} register x; case tok::kw_auto: // struct foo {...} auto x; - case tok::kw_mutable: // struct foo {...} mutable x; + case tok::kw_mutable: // struct foo {...} mutable x; + case tok::kw_constexpr: // struct foo {...} constexpr x; // As shown above, type qualifiers and storage class specifiers absolutely // can occur after class specifiers according to the grammar. However, // almost no one actually writes code like this. If we see one of these, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c1a6c60d28..67493c7716 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2229,6 +2229,20 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, << DS.getSourceRange(); } + if (DS.isConstexprSpecified()) { + // C++0x [dcl.constexpr]p1: constexpr can only be applied to declarations + // and definitions of functions and variables. + if (Tag) + Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_tag) + << (DS.getTypeSpecType() == DeclSpec::TST_class ? 0 : + DS.getTypeSpecType() == DeclSpec::TST_struct ? 1 : + DS.getTypeSpecType() == DeclSpec::TST_union ? 2 : 3); + else + Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_no_declarators); + // Don't emit warnings after this error. + return TagD; + } + if (DS.isFriendSpecified()) { // If we're dealing with a decl but not a TagDecl, assume that // whatever routines created it handled the friendship aspect. @@ -3434,6 +3448,9 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, if (D.getDeclSpec().isThreadSpecified()) Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + if (D.getDeclSpec().isConstexprSpecified()) + Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) + << 1; if (D.getName().Kind != UnqualifiedId::IK_Identifier) { Diag(D.getName().StartLocation, diag::err_typedef_not_identifier) @@ -3767,6 +3784,11 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, TemplateParamLists.size(), TemplateParamLists.release()); } + + if (D.getDeclSpec().isConstexprSpecified()) { + // FIXME: check this is a valid use of constexpr. + NewVD->setConstexpr(true); + } } if (D.getDeclSpec().isThreadSpecified()) { @@ -4304,6 +4326,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, isFriend = D.getDeclSpec().isFriendSpecified(); bool isVirtual = D.getDeclSpec().isVirtualSpecified(); bool isExplicit = D.getDeclSpec().isExplicitSpecified(); + bool isConstexpr = D.getDeclSpec().isConstexprSpecified(); bool isVirtualOkay = false; // Check that the return type is not an abstract class type. @@ -4330,7 +4353,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, D.getSourceRange().getBegin(), NameInfo, R, TInfo, isExplicit, isInline, - /*isImplicitlyDeclared=*/false); + /*isImplicitlyDeclared=*/false, + isConstexpr); NewFD = NewCD; } else if (Name.getNameKind() == DeclarationName::CXXDestructorName) { @@ -4364,7 +4388,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewFD = FunctionDecl::Create(Context, DC, D.getSourceRange().getBegin(), D.getIdentifierLoc(), Name, R, TInfo, SC, SCAsWritten, isInline, - /*hasPrototype=*/true); + /*hasPrototype=*/true, isConstexpr); D.setInvalidType(); } } else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) { @@ -4378,7 +4402,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewFD = CXXConversionDecl::Create(Context, cast(DC), D.getSourceRange().getBegin(), NameInfo, R, TInfo, - isInline, isExplicit, + isInline, isExplicit, isConstexpr, SourceLocation()); isVirtualOkay = true; @@ -4416,6 +4440,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, D.getSourceRange().getBegin(), NameInfo, R, TInfo, isStatic, SCAsWritten, isInline, + isConstexpr, SourceLocation()); NewFD = NewMD; @@ -4426,7 +4451,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // - we're in C++ (where every function has a prototype), NewFD = FunctionDecl::Create(Context, DC, D.getSourceRange().getBegin(), NameInfo, R, TInfo, SC, SCAsWritten, isInline, - true/*HasPrototype*/); + true/*HasPrototype*/, isConstexpr); } if (isFriend && !isInline && IsFunctionDefinition) { @@ -4590,6 +4615,23 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } } + if (isConstexpr) { + // C++0x [dcl.constexpr]p2: constexpr functions and constexpr constructors + // 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(NewFD)) + Diag(D.getDeclSpec().getConstexprSpecLoc(), + diag::err_constexpr_dtor); + } + + // Filter out previous declarations that don't match the scope. FilterLookupForScope(Previous, DC, S, NewFD->hasLinkage(), isExplicitSpecialization || @@ -6105,6 +6147,9 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { if (D.getDeclSpec().isThreadSpecified()) Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + if (D.getDeclSpec().isConstexprSpecified()) + Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) + << 0; DiagnoseFunctionSpecifiers(D); @@ -7813,6 +7858,9 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, if (D.getDeclSpec().isThreadSpecified()) Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + if (D.getDeclSpec().isConstexprSpecified()) + Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) + << 2; // Check to see if this name was declared as a member previously LookupResult Previous(*this, II, Loc, LookupMemberName, ForRedeclaration); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 76461a0ca6..428316297c 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6012,7 +6012,9 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( /*TInfo=*/0, /*isExplicit=*/false, /*isInline=*/true, - /*isImplicitlyDeclared=*/true); + /*isImplicitlyDeclared=*/true, + // FIXME: apply the rules for definitions here + /*isConstexpr=*/false); DefaultCon->setAccess(AS_public); DefaultCon->setDefaulted(); DefaultCon->setImplicit(); @@ -6263,12 +6265,15 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { // OK, we're there, now add the constructor. // C++0x [class.inhctor]p8: [...] that would be performed by a - // user-writtern inline constructor [...] + // user-written inline constructor [...] DeclarationNameInfo DNI(CreatedCtorName, UsingLoc); CXXConstructorDecl *NewCtor = CXXConstructorDecl::Create( Context, ClassDecl, UsingLoc, DNI, QualType(NewCtorType, 0), /*TInfo=*/0, BaseCtor->isExplicit(), /*Inline=*/true, - /*ImplicitlyDeclared=*/true); + /*ImplicitlyDeclared=*/true, + // FIXME: Due to a defect in the standard, we treat inherited + // constructors as constexpr even if that makes them ill-formed. + /*Constexpr=*/BaseCtor->isConstexpr()); NewCtor->setAccess(BaseCtor->getAccess()); // Build up the parameter decls and add them. @@ -6777,7 +6782,7 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { Context.getFunctionType(RetType, &ArgType, 1, EPI), /*TInfo=*/0, /*isStatic=*/false, /*StorageClassAsWritten=*/SC_None, - /*isInline=*/true, + /*isInline=*/true, /*isConstexpr=*/false, SourceLocation()); CopyAssignment->setAccess(AS_public); CopyAssignment->setDefaulted(); @@ -7249,7 +7254,9 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( /*TInfo=*/0, /*isExplicit=*/false, /*isInline=*/true, - /*isImplicitlyDeclared=*/true); + /*isImplicitlyDeclared=*/true, + // FIXME: apply the rules for definitions here + /*isConstexpr=*/false); CopyConstructor->setAccess(AS_public); CopyConstructor->setDefaulted(); CopyConstructor->setTrivial(ClassDecl->hasTrivialCopyConstructor()); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 2554b82d97..812482e436 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1070,11 +1070,15 @@ 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()); + D->isInlineSpecified(), D->hasWrittenPrototype(), + isConstexpr); if (QualifierLoc) Function->setQualifierInfo(QualifierLoc); @@ -1383,6 +1387,9 @@ 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(DC); CXXMethodDecl *Method = 0; @@ -1395,7 +1402,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, StartLoc, NameInfo, T, TInfo, Constructor->isExplicit(), Constructor->isInlineSpecified(), - false); + false, isConstexpr); } else if (CXXDestructorDecl *Destructor = dyn_cast(D)) { Method = CXXDestructorDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, @@ -1406,14 +1413,14 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, StartLoc, NameInfo, T, TInfo, Conversion->isInlineSpecified(), Conversion->isExplicit(), - Conversion->getLocEnd()); + isConstexpr, Conversion->getLocEnd()); } else { Method = CXXMethodDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, D->isStatic(), D->getStorageClassAsWritten(), D->isInlineSpecified(), - D->getLocEnd()); + isConstexpr, D->getLocEnd()); } if (QualifierLoc) diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index e9d421f0ff..66316bc1e1 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -447,6 +447,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { FD->IsDefaulted = Record[Idx++]; FD->IsExplicitlyDefaulted = Record[Idx++]; FD->HasImplicitReturnZero = Record[Idx++]; + FD->IsConstexpr = Record[Idx++]; FD->EndRangeLoc = ReadSourceLocation(Record, Idx); // Read in the parameters. @@ -1538,7 +1539,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_CXX_METHOD: D = CXXMethodDecl::Create(*Context, 0, SourceLocation(), DeclarationNameInfo(), QualType(), 0, - false, SC_None, false, SourceLocation()); + false, SC_None, false, false, SourceLocation()); break; case DECL_CXX_CONSTRUCTOR: D = CXXConstructorDecl::Create(*Context, Decl::EmptyShell()); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 98dad1c90e..3e1a42c52d 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -373,6 +373,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { Record.push_back(D->isDefaulted()); Record.push_back(D->isExplicitlyDefaulted()); Record.push_back(D->hasImplicitReturnZero()); + Record.push_back(D->isConstexpr()); Writer.AddSourceLocation(D->getLocEnd(), Record); Record.push_back(D->param_size()); diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp index cd71d55fc5..d9b09c64da 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -// XFAIL: * +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s struct notlit { notlit() {} @@ -21,16 +20,40 @@ constexpr extern int i2; // x // not a literal type constexpr notlit nl1; // x // function parameters -void f2(constexpr int i) {} // x +void f2(constexpr int i) {} // expected-error {{function parameter cannot be constexpr}} // non-static member struct s2 { - constexpr int mi; // x + constexpr int mi; // expected-error {{non-static data member cannot be constexpr}} }; +// typedef +typedef constexpr int CI; // expected-error {{typedef cannot be constexpr}} +// tag +constexpr class C1 {}; // expected-error {{class cannot be marked constexpr}} +constexpr struct S1 {}; // expected-error {{struct cannot be marked constexpr}} +constexpr union U1 {}; // expected-error {{union cannot be marked constexpr}} +constexpr enum E1 {}; // expected-error {{enum cannot be marked constexpr}} +class C2 {} constexpr; // expected-error {{class cannot be marked constexpr}} +struct S2 {} constexpr; // expected-error {{struct cannot be marked constexpr}} +union U2 {} constexpr; // expected-error {{union cannot be marked constexpr}} +enum E2 {} constexpr; // expected-error {{enum cannot be marked constexpr}} +constexpr class C3 {} c3 = C3(); +constexpr struct S3 {} s3 = S3(); +constexpr union U3 {} u3 = {}; +constexpr enum E3 { V3 } e3 = V3; +class C4 {} constexpr c4 = C4(); +struct S4 {} constexpr s4 = S4(); +union U4 {} constexpr u4 = {}; +enum E4 { V4 } constexpr e4 = V4; +constexpr int; // expected-error {{constexpr can only be used in variable and function declarations}} // redeclaration mismatch constexpr int f3(); // n int f3(); // x int f4(); // n constexpr int f4(); // x +// destructor +struct ConstexprDtor { + constexpr ~ConstexprDtor() = default; // expected-error {{destructor cannot be marked constexpr}} +}; // template stuff template @@ -52,7 +75,7 @@ void test() { constexpr int square(int x); constexpr int bufsz = 1024; -constexpr struct pixel { // x +constexpr struct pixel { // expected-error {{struct cannot be marked constexpr}} int x; int y; constexpr pixel(int); @@ -71,7 +94,7 @@ constexpr int square(int x) { constexpr pixel large(4); // now valid -int next(constexpr int x) { // x +int next(constexpr int x) { // expected-error {{function parameter cannot be constexpr}} return x + 1; } diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p2.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p2.cpp new file mode 100644 index 0000000000..25470779e6 --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p2.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -std=c++0x -emit-llvm %s -o - | FileCheck %s + +// constexpr functions and constexpr constructors are implicitly inline. +struct S { + constexpr S(int n); + int n; +}; + +constexpr S::S(int n) : n(n) {} + +constexpr S f(S s) { + return s.n * 2; +} + +// CHECK: define linkonce_odr {{.*}} @_Z1f1S( +// CHECK: define linkonce_odr {{.*}} @_ZN1SC1Ei( + +int g() { + return f(42).n; +} -- 2.40.0