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
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index 9ea9a57..8f8c055 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -2468,7 +2468,8 @@
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 @@
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 @@
Method->isStatic(),
Method->getStorageClassAsWritten(),
Method->isInlineSpecified(),
+ D->isConstexpr(),
Importer.Import(D->getLocEnd()));
} else {
ToFunction = FunctionDecl::Create(Importer.getToContext(), DC,
@@ -2500,7 +2503,8 @@
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 f917d32..db7b570 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -2525,10 +2525,12 @@
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 9e62f49..518210a 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -1159,9 +1159,10 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 3b2154f..a2cfe54 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -1105,7 +1105,7 @@
// 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 @@
// 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 df2c5f8..7c38857 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -1264,7 +1264,8 @@
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 c1a6c60..67493c7 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2229,6 +2229,20 @@
<< 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
D.getSourceRange().getBegin(),
NameInfo, R, TInfo,
isStatic, SCAsWritten, isInline,
+ isConstexpr,
SourceLocation());
NewFD = NewMD;
@@ -4426,7 +4451,7 @@
// - 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 @@
}
}
+ 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 @@
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 @@
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 76461a0..4283162 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -6012,7 +6012,9 @@
/*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 @@
// 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 @@
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 @@
/*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 2554b82..812482e 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1070,11 +1070,15 @@
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 @@
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 @@
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 @@
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 e9d421f..66316bc 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -447,6 +447,7 @@
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 @@
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 98dad1c..3e1a42c 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -373,6 +373,7 @@
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());