aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2011-08-15 21:04:07 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2011-08-15 21:04:07 +0000
commitaf1fc7af351758b0ea0d285bdfe5640128109a4e (patch)
tree22b6d9705984486730bbe86f9827df5e3b5f85e9
parentfaef9fcb41cff7c2cbafb86d3af47c422ddf3010 (diff)
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
-rw-r--r--include/clang/AST/Decl.h32
-rw-r--r--include/clang/AST/DeclCXX.h21
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td10
-rw-r--r--lib/AST/ASTImporter.cpp8
-rw-r--r--lib/AST/Decl.cpp6
-rw-r--r--lib/AST/DeclCXX.cpp25
-rw-r--r--lib/AST/Type.cpp24
-rw-r--r--lib/Parse/ParseDeclCXX.cpp3
-rw-r--r--lib/Sema/SemaDecl.cpp56
-rw-r--r--lib/Sema/SemaDeclCXX.cpp17
-rw-r--r--lib/Sema/SemaTemplateInstantiateDecl.cpp15
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp3
-rw-r--r--lib/Serialization/ASTWriterDecl.cpp1
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp35
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p2.cpp20
15 files changed, 212 insertions, 64 deletions
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<CXXDestructorDecl>(D)) {
ToFunction = CXXDestructorDecl::Create(Importer.getToContext(),
cast<CXXRecordDecl>(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<CXXMethodDecl>(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<RecordType>()) {
if (const CXXRecordDecl *ClassDecl =
dyn_cast<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXDestructorDecl>(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<CXXRecordDecl>(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<CXXDestructorDecl>(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(),
Decla